1.1 --- a/imip_manager.py Mon Oct 27 17:59:35 2014 +0100
1.2 +++ b/imip_manager.py Mon Oct 27 23:06:55 2014 +0100
1.3 @@ -5,11 +5,10 @@
1.4 sys.path.append("/var/lib/imip-agent")
1.5
1.6 from imiptools import make_message, sendmail
1.7 -from imiptools.content import format_datetime, get_address, get_datetime, \
1.8 - get_item, get_items, get_periods, get_uri, \
1.9 - get_utc_datetime, get_value, get_values, \
1.10 - parse_object, to_part, to_timezone, \
1.11 - update_freebusy
1.12 +from imiptools.content import Handler, \
1.13 + format_datetime, get_address, get_datetime, \
1.14 + get_item, get_uri, get_utc_datetime, get_values, \
1.15 + parse_object, to_part, to_timezone
1.16 from imiptools.period import have_conflict
1.17 from vCalendar import to_node
1.18 import markup
1.19 @@ -64,6 +63,67 @@
1.20 path_info = self.get_path_info()
1.21 return "%s%s" % (path.rstrip("/"), path_info)
1.22
1.23 +class ManagerHandler(Handler):
1.24 +
1.25 + "A content handler for use by the manager."
1.26 +
1.27 + def __init__(self, details, objtype, user):
1.28 + Handler.__init__(self, details)
1.29 + self.objtype = objtype
1.30 + self.user = user
1.31 + self.organisers = map(get_address, self.get_values("ORGANIZER"))
1.32 +
1.33 + # Communication methods.
1.34 +
1.35 + def send_message(self, sender):
1.36 +
1.37 + """
1.38 + Create a full calendar object and send it to the organisers from the
1.39 + given 'sender'.
1.40 + """
1.41 +
1.42 + # NOTE: Should parameterise the subject and body text.
1.43 +
1.44 + node = to_node({self.objtype : [(self.details, {})]})
1.45 + part = to_part("REPLY", [node])
1.46 + message = make_message([part], self.organisers, sender, "Response to request", "Response to a calendar request")
1.47 + sendmail(sender, self.organisers, message.as_string())
1.48 +
1.49 + # Action methods.
1.50 +
1.51 + def process_request(self, accept):
1.52 +
1.53 + """
1.54 + Process the current request for the given 'user', accepting any request
1.55 + when 'accept' is true, declining requests otherwise. Return whether any
1.56 + action was taken.
1.57 + """
1.58 +
1.59 + # When accepting or declining, do so only on behalf of this user,
1.60 + # preserving any other attributes set as an attendee.
1.61 +
1.62 + for attendee, attendee_attr in self.get_items("ATTENDEE"):
1.63 +
1.64 + if attendee == self.user:
1.65 + freebusy = self.store.get_freebusy(attendee)
1.66 +
1.67 + attendee_attr["PARTSTAT"] = accept and "ACCEPTED" or "DECLINED"
1.68 + self.details["ATTENDEE"] = [(attendee, attendee_attr)]
1.69 + self.send_message(get_address(attendee))
1.70 +
1.71 + # Update the free/busy information.
1.72 +
1.73 + if accept:
1.74 + periods = self.get_periods()
1.75 + self.update_freebusy(freebusy, attendee, periods)
1.76 +
1.77 + if self.publisher:
1.78 + self.publisher.set_freebusy(attendee, freebusy)
1.79 +
1.80 + return True
1.81 +
1.82 + return False
1.83 +
1.84 class Manager:
1.85
1.86 "A simple manager application."
1.87 @@ -83,18 +143,6 @@
1.88 except OSError:
1.89 self.publisher = None
1.90
1.91 - # Communication methods.
1.92 -
1.93 - def send_message(self, objtype, obj, sender, recipients):
1.94 -
1.95 - # Create a full calendar object and send it.
1.96 - # NOTE: Should parameterise the subject and body text.
1.97 -
1.98 - node = to_node({objtype : [(obj, {})]})
1.99 - part = to_part("REPLY", [node])
1.100 - message = make_message([part], recipients, sender, "Response to request", "Response to a calendar request")
1.101 - sendmail(sender, recipients, message.as_string())
1.102 -
1.103 # Data management methods.
1.104
1.105 def remove_request(self, uid):
1.106 @@ -163,37 +211,18 @@
1.107 args = self.env.get_args()
1.108 show_form = False
1.109
1.110 - organisers = map(get_address, get_values(request, "ORGANIZER"))
1.111 - freebusy = self.store.get_freebusy(self.user)
1.112 -
1.113 accept = args.has_key("accept")
1.114 decline = args.has_key("decline")
1.115
1.116 if accept or decline:
1.117
1.118 - # When accepting or declining, do so only on behalf of this user,
1.119 - # preserving any other attributes set as an attendee.
1.120 + handler = ManagerHandler(request, objtype, self.user)
1.121
1.122 - for attendee, attendee_attr in get_items(request, "ATTENDEE"):
1.123 - if attendee == self.user:
1.124 - attendee_attr["PARTSTAT"] = accept and "ACCEPTED" or "DECLINED"
1.125 - request["ATTENDEE"] = [(attendee, attendee_attr)]
1.126 - self.send_message(objtype, request, get_address(attendee), organisers)
1.127 -
1.128 - # Remove the request from the list.
1.129 + if handler.process_request(accept):
1.130
1.131 - self.remove_request(uid)
1.132 -
1.133 - # Update the free/busy information.
1.134 + # Remove the request from the list.
1.135
1.136 - if accept:
1.137 - periods = get_periods(request)
1.138 - update_freebusy(freebusy, attendee, periods, get_value(request, "TRANSP"), uid, self.store)
1.139 -
1.140 - if self.publisher:
1.141 - self.publisher.set_freebusy(attendee, freebusy)
1.142 -
1.143 - break
1.144 + self.remove_request(uid)
1.145
1.146 elif args.has_key("ignore"):
1.147
1.148 @@ -223,6 +252,8 @@
1.149
1.150 # Indicate whether there are conflicting events.
1.151
1.152 + freebusy = self.store.get_freebusy(self.user)
1.153 +
1.154 if freebusy:
1.155
1.156 # Obtain any time zone details from the suggested event.
2.1 --- a/imiptools/content.py Mon Oct 27 17:59:35 2014 +0100
2.2 +++ b/imiptools/content.py Mon Oct 27 23:06:55 2014 +0100
2.3 @@ -318,15 +318,15 @@
2.4
2.5 "General handler support."
2.6
2.7 - def __init__(self, details, recipients):
2.8 + def __init__(self, details, recipients=None):
2.9
2.10 """
2.11 Initialise the handler with the 'details' of a calendar object and the
2.12 - 'recipients' of the object.
2.13 + 'recipients' of the object (if specifically indicated).
2.14 """
2.15
2.16 self.details = details
2.17 - self.recipients = set(recipients)
2.18 + self.recipients = recipients and set(recipients)
2.19
2.20 self.uid = get_value(details, "UID")
2.21 self.sequence = get_value(details, "SEQUENCE")
2.22 @@ -369,7 +369,8 @@
2.23 return can_schedule(freebusy, periods, self.uid)
2.24
2.25 def filter_by_recipients(self, values):
2.26 - return self.recipients.intersection(map(get_address, values))
2.27 + addresses = map(get_address, values)
2.28 + return self.recipients and self.recipients.intersection(addresses) or addresses
2.29
2.30 def require_organiser_and_attendees(self):
2.31