1.1 --- a/imip_manager.py Tue Dec 09 00:26:01 2014 +0100
1.2 +++ b/imip_manager.py Tue Dec 09 00:27:14 2014 +0100
1.3 @@ -65,11 +65,15 @@
1.4
1.5 class ManagerHandler(Handler):
1.6
1.7 - "A content handler for use by the manager."
1.8 + """
1.9 + A content handler for use by the manager, as opposed to operating within the
1.10 + mail processing pipeline.
1.11 + """
1.12
1.13 - def __init__(self, details, objtype, user, messenger):
1.14 + def __init__(self, obj, user, messenger):
1.15 + details, details_attr = obj.values()[0]
1.16 Handler.__init__(self, details)
1.17 - self.objtype = objtype
1.18 + self.obj = obj
1.19 self.user = user
1.20 self.messenger = messenger
1.21
1.22 @@ -84,7 +88,7 @@
1.23 given 'sender'.
1.24 """
1.25
1.26 - node = to_node({self.objtype : [(self.details, {})]})
1.27 + node = to_node(self.obj)
1.28 part = to_part("REPLY", [node])
1.29 message = self.messenger.make_message([part], self.organisers, sender=sender)
1.30 self.messenger.sendmail(self.organisers, message.as_string(), sender=sender)
1.31 @@ -125,6 +129,8 @@
1.32 self.env = CGIEnvironment()
1.33 user = self.env.get_user()
1.34 self.user = user and get_uri(user) or None
1.35 + self.requests = None
1.36 +
1.37 self.out = self.env.get_output()
1.38 self.page = markup.page()
1.39 self.encoding = "utf-8"
1.40 @@ -136,6 +142,9 @@
1.41 except OSError:
1.42 self.publisher = None
1.43
1.44 + def _get_uid(self, path_info):
1.45 + return path_info.lstrip("/").split("/", 1)[0]
1.46 +
1.47 def _get_object(self, uid):
1.48 f = uid and self.store.get_event(self.user, uid) or None
1.49
1.50 @@ -147,8 +156,16 @@
1.51 if not obj:
1.52 return None
1.53
1.54 - objtype = obj.keys()[0]
1.55 - return obj[objtype][0]
1.56 + return obj
1.57 +
1.58 + def _get_details(self, obj):
1.59 + details, details_attr = obj.values()[0]
1.60 + return details
1.61 +
1.62 + def _get_requests(self):
1.63 + if self.requests is None:
1.64 + self.requests = self.store.get_requests(self.user)
1.65 + return self.requests
1.66
1.67 # Data management methods.
1.68
1.69 @@ -173,16 +190,107 @@
1.70 self.new_page(title="Not Found")
1.71 self.page.p("No page is provided at the given address.")
1.72
1.73 - def show_requests(self):
1.74 + # Request logic and page fragment methods.
1.75 +
1.76 + def handle_request(self, uid, request):
1.77 +
1.78 + "Handle actions involving the given 'uid' and 'request' object."
1.79 +
1.80 + # Handle a submitted form.
1.81 +
1.82 + args = self.env.get_args()
1.83 + show_form = False
1.84 +
1.85 + accept = args.has_key("accept")
1.86 + decline = args.has_key("decline")
1.87 +
1.88 + if accept or decline:
1.89 +
1.90 + handler = ManagerHandler(request, self.user, self.messenger)
1.91 +
1.92 + if handler.process_request(accept):
1.93 +
1.94 + # Remove the request from the list.
1.95 +
1.96 + self.remove_request(uid)
1.97 +
1.98 + elif args.has_key("ignore"):
1.99 +
1.100 + # Remove the request from the list.
1.101 +
1.102 + self.remove_request(uid)
1.103 +
1.104 + else:
1.105 + show_form = True
1.106 +
1.107 + return show_form
1.108 +
1.109 + def show_request_form(self):
1.110 +
1.111 + "Show a form for a request."
1.112 +
1.113 + self.page.p("Action to take for this request:")
1.114 + self.page.form(method="POST")
1.115 + self.page.p()
1.116 + self.page.input(name="accept", type="submit", value="Accept")
1.117 + self.page.add(" ")
1.118 + self.page.input(name="decline", type="submit", value="Decline")
1.119 + self.page.add(" ")
1.120 + self.page.input(name="ignore", type="submit", value="Ignore")
1.121 + self.page.p.close()
1.122 + self.page.form.close()
1.123 +
1.124 + def show_object_on_page(self, uid, obj):
1.125 +
1.126 + """
1.127 + Show the calendar object with the given 'uid' and representation 'obj'
1.128 + on the current page.
1.129 + """
1.130 +
1.131 + details = self._get_details(obj)
1.132 +
1.133 + # Provide a summary of the object.
1.134 +
1.135 + self.page.dl()
1.136 +
1.137 + for name in ["SUMMARY", "DTSTART", "DTEND", "ORGANIZER", "ATTENDEE"]:
1.138 + for value in get_values(details, name):
1.139 + self.page.dt(name)
1.140 + self.page.dd(value)
1.141 +
1.142 + self.page.dl.close()
1.143 +
1.144 + dtstart = format_datetime(get_utc_datetime(details, "DTSTART"))
1.145 + dtend = format_datetime(get_utc_datetime(details, "DTEND"))
1.146 +
1.147 + # Indicate whether there are conflicting events.
1.148 +
1.149 + freebusy = self.store.get_freebusy(self.user)
1.150 +
1.151 + if freebusy:
1.152 +
1.153 + # Obtain any time zone details from the suggested event.
1.154 +
1.155 + _dtstart, attr = get_item(details, "DTSTART")
1.156 + tzid = attr.get("TZID")
1.157 +
1.158 + # Show any conflicts.
1.159 +
1.160 + for t in have_conflict(freebusy, [(dtstart, dtend)], True):
1.161 + start, end, found_uid = t[:3]
1.162 + if uid != found_uid:
1.163 + start = format_datetime(to_timezone(get_datetime(start), tzid))
1.164 + end = format_datetime(to_timezone(get_datetime(end), tzid))
1.165 + self.page.p("Event conflicts with another from %s to %s." % (start, end))
1.166 +
1.167 + def show_requests_on_page(self):
1.168
1.169 "Show requests for the current user."
1.170
1.171 # NOTE: This list could be more informative, but it is envisaged that
1.172 # NOTE: the requests would be visited directly anyway.
1.173
1.174 - self.new_page(title="Pending Requests")
1.175 -
1.176 - requests = self.store.get_requests(self.user)
1.177 + requests = self._get_requests()
1.178
1.179 if requests:
1.180 self.page.p("Pending requests:")
1.181 @@ -199,93 +307,28 @@
1.182 else:
1.183 self.page.p("There are no pending requests.")
1.184
1.185 - def show_request(self, path_info):
1.186 + # Full page output methods.
1.187
1.188 - "Show a request using the given 'path_info' for the current user."
1.189 + def show_object(self, path_info):
1.190
1.191 - uid = path_info.lstrip("/").split("/", 1)[0]
1.192 - request = self._get_object(uid)
1.193 + "Show an object request using the given 'path_info' for the current user."
1.194
1.195 - if not request:
1.196 + uid = self._get_uid(path_info)
1.197 + obj = self._get_object(uid)
1.198 +
1.199 + if not obj:
1.200 return False
1.201
1.202 - # Handle a submitted form.
1.203 -
1.204 - args = self.env.get_args()
1.205 - show_form = False
1.206 -
1.207 - accept = args.has_key("accept")
1.208 - decline = args.has_key("decline")
1.209 -
1.210 - if accept or decline:
1.211 -
1.212 - handler = ManagerHandler(request, objtype, self.user, self.messenger)
1.213 -
1.214 - if handler.process_request(accept):
1.215 + self.new_page(title="Event")
1.216
1.217 - # Remove the request from the list.
1.218 -
1.219 - self.remove_request(uid)
1.220 -
1.221 - elif args.has_key("ignore"):
1.222 -
1.223 - # Remove the request from the list.
1.224 -
1.225 - self.remove_request(uid)
1.226 -
1.227 - else:
1.228 - show_form = True
1.229 -
1.230 - self.new_page(title="Request")
1.231 -
1.232 - # Provide a summary of the request.
1.233 + is_request = uid in self._get_requests()
1.234
1.235 - self.page.p("The following request was received:")
1.236 - self.page.dl()
1.237 -
1.238 - for name in ["SUMMARY", "DTSTART", "DTEND", "ORGANIZER", "ATTENDEE"]:
1.239 - for value in get_values(request, name):
1.240 - self.page.dt(name)
1.241 - self.page.dd(value)
1.242 -
1.243 - self.page.dl.close()
1.244 -
1.245 - dtstart = format_datetime(get_utc_datetime(request, "DTSTART"))
1.246 - dtend = format_datetime(get_utc_datetime(request, "DTEND"))
1.247 -
1.248 - # Indicate whether there are conflicting events.
1.249 -
1.250 - freebusy = self.store.get_freebusy(self.user)
1.251 + show_form = is_request and self.handle_request(uid, obj)
1.252
1.253 - if freebusy:
1.254 -
1.255 - # Obtain any time zone details from the suggested event.
1.256 -
1.257 - _dtstart, attr = get_item(request, "DTSTART")
1.258 - tzid = attr.get("TZID")
1.259 -
1.260 - # Show any conflicts.
1.261 -
1.262 - for t in have_conflict(freebusy, [(dtstart, dtend)], True):
1.263 - start, end, found_uid = t[:3]
1.264 - if uid != found_uid:
1.265 - start = format_datetime(to_timezone(get_datetime(start), tzid))
1.266 - end = format_datetime(to_timezone(get_datetime(end), tzid))
1.267 - self.page.p("Event conflicts with another from %s to %s." % (start, end))
1.268 -
1.269 - # Show a form if no action has just been taken.
1.270 + self.show_object_on_page(uid, obj)
1.271
1.272 if show_form:
1.273 - self.page.p("Action to take for this request:")
1.274 - self.page.form(method="POST")
1.275 - self.page.p()
1.276 - self.page.input(name="accept", type="submit", value="Accept")
1.277 - self.page.add(" ")
1.278 - self.page.input(name="decline", type="submit", value="Decline")
1.279 - self.page.add(" ")
1.280 - self.page.input(name="ignore", type="submit", value="Ignore")
1.281 - self.page.p.close()
1.282 - self.page.form.close()
1.283 + self.show_request_form()
1.284
1.285 return True
1.286
1.287 @@ -294,6 +337,7 @@
1.288 "Show the calendar for the current user."
1.289
1.290 self.new_page(title="Calendar")
1.291 + self.show_requests_on_page()
1.292
1.293 freebusy = self.store.get_freebusy(self.user)
1.294 page = self.page
1.295 @@ -335,7 +379,8 @@
1.296 page.td(class_="event", rowspan=span)
1.297 obj = self._get_object(uid)
1.298 if obj:
1.299 - page.add(get_value(obj, "SUMMARY"))
1.300 + details = self._get_details(obj)
1.301 + page.a(get_value(details, "SUMMARY"), href="%s/%s" % (self.env.get_url().rstrip("/"), uid))
1.302 page.td.close()
1.303 else:
1.304 page.td(class_="empty")
1.305 @@ -349,12 +394,11 @@
1.306
1.307 "Select the desired action and show the result."
1.308
1.309 - path_info = self.env.get_path_info().rstrip("/")
1.310 + path_info = self.env.get_path_info().strip("/")
1.311 +
1.312 if not path_info:
1.313 - self.show_requests()
1.314 - elif path_info.rsplit("/", 1)[-1] == "calendar":
1.315 self.show_calendar()
1.316 - elif self.show_request(path_info):
1.317 + elif self.show_object(path_info):
1.318 pass
1.319 else:
1.320 self.no_page()