1.1 --- a/imip_manager.py Wed Mar 25 18:15:37 2015 +0100
1.2 +++ b/imip_manager.py Wed Mar 25 18:23:37 2015 +0100
1.3 @@ -32,14 +32,12 @@
1.4 sys.path.append(LIBRARY_PATH)
1.5
1.6 from imiptools.client import Client, update_attendees
1.7 -from imiptools.data import get_address, get_uri, get_window_end, make_freebusy, \
1.8 - Object, to_part, \
1.9 - uri_dict, uri_item, uri_items, uri_values
1.10 +from imiptools.data import get_address, get_uri, get_window_end, Object, \
1.11 + uri_dict, uri_values
1.12 from imiptools.dates import format_datetime, format_time, to_date, get_datetime, \
1.13 get_datetime_item, get_end_of_day, get_period_item, \
1.14 get_start_of_day, get_start_of_next_day, get_timestamp, \
1.15 ends_on_same_day, to_timezone
1.16 -from imiptools.handlers import Handler
1.17 from imiptools.mail import Messenger
1.18 from imiptools.period import add_day_start_points, add_empty_days, add_slots, \
1.19 convert_periods, get_freebusy_details, \
1.20 @@ -47,156 +45,10 @@
1.21 partition_by_day, remove_period, remove_affected_period, \
1.22 update_freebusy
1.23 from imipweb.env import CGIEnvironment
1.24 +from imipweb.handler import ManagerHandler
1.25 import imip_store
1.26 import markup
1.27
1.28 -class ManagerHandler(Client, Handler):
1.29 -
1.30 - """
1.31 - A content handler for use by the manager, as opposed to operating within the
1.32 - mail processing pipeline.
1.33 - """
1.34 -
1.35 - def __init__(self, obj, user, messenger):
1.36 - Handler.__init__(self, messenger=messenger)
1.37 - Client.__init__(self, user)
1.38 -
1.39 - self.set_object(obj)
1.40 -
1.41 - # Communication methods.
1.42 -
1.43 - def send_message(self, method, sender, for_organiser):
1.44 -
1.45 - """
1.46 - Create a full calendar object employing the given 'method', and send it
1.47 - to the appropriate recipients, also sending a copy to the 'sender'. The
1.48 - 'for_organiser' value indicates whether the organiser is sending this
1.49 - message.
1.50 - """
1.51 -
1.52 - parts = [self.obj.to_part(method)]
1.53 -
1.54 - # As organiser, send an invitation to attendees, excluding oneself if
1.55 - # also attending. The updated event will be saved by the outgoing
1.56 - # handler.
1.57 -
1.58 - organiser = get_uri(self.obj.get_value("ORGANIZER"))
1.59 - attendees = uri_values(self.obj.get_values("ATTENDEE"))
1.60 -
1.61 - if for_organiser:
1.62 - recipients = [get_address(attendee) for attendee in attendees if attendee != self.user]
1.63 - else:
1.64 - recipients = [get_address(organiser)]
1.65 -
1.66 - # Bundle free/busy information if appropriate.
1.67 -
1.68 - if self.is_sharing() and self.is_bundling():
1.69 -
1.70 - # Invent a unique identifier.
1.71 -
1.72 - utcnow = get_timestamp()
1.73 - uid = "imip-agent-%s-%s" % (utcnow, get_address(self.user))
1.74 -
1.75 - freebusy = self.store.get_freebusy(self.user)
1.76 -
1.77 - # Replace the non-updated free/busy details for this event with
1.78 - # newer details (since the outgoing handler updates this user's
1.79 - # free/busy details).
1.80 -
1.81 - update_freebusy(freebusy,
1.82 - self.obj.get_periods_for_freebusy(self.get_tzid(), self.get_window_end()),
1.83 - self.obj.get_value("TRANSP") or "OPAQUE",
1.84 - self.uid, self.recurrenceid,
1.85 - self.obj.get_value("SUMMARY"),
1.86 - organiser)
1.87 -
1.88 - user_attr = self.messenger and self.messenger.sender != get_address(self.user) and \
1.89 - {"SENT-BY" : get_uri(self.messenger.sender)} or {}
1.90 -
1.91 - parts.append(to_part("PUBLISH", [
1.92 - make_freebusy(freebusy, uid, self.user, user_attr)
1.93 - ]))
1.94 -
1.95 - message = self.messenger.make_outgoing_message(parts, recipients, outgoing_bcc=sender)
1.96 - self.messenger.sendmail(recipients, message.as_string(), outgoing_bcc=sender)
1.97 -
1.98 - # Action methods.
1.99 -
1.100 - def process_received_request(self, update=False):
1.101 -
1.102 - """
1.103 - Process the current request for the given 'user'. Return whether any
1.104 - action was taken.
1.105 -
1.106 - If 'update' is given, the sequence number will be incremented in order
1.107 - to override any previous response.
1.108 - """
1.109 -
1.110 - # Reply only on behalf of this user.
1.111 -
1.112 - for attendee, attendee_attr in uri_items(self.obj.get_items("ATTENDEE")):
1.113 -
1.114 - if attendee == self.user:
1.115 - if attendee_attr.has_key("RSVP"):
1.116 - del attendee_attr["RSVP"]
1.117 - if self.messenger and self.messenger.sender != get_address(attendee):
1.118 - attendee_attr["SENT-BY"] = get_uri(self.messenger.sender)
1.119 - self.obj["ATTENDEE"] = [(attendee, attendee_attr)]
1.120 -
1.121 - self.update_dtstamp()
1.122 - self.set_sequence(update)
1.123 -
1.124 - self.send_message("REPLY", get_address(attendee), for_organiser=False)
1.125 -
1.126 - return True
1.127 -
1.128 - return False
1.129 -
1.130 - def process_created_request(self, method, update=False, removed=None, added=None):
1.131 -
1.132 - """
1.133 - Process the current request for the given 'user', sending a created
1.134 - request of the given 'method' to attendees. Return whether any action
1.135 - was taken.
1.136 -
1.137 - If 'update' is given, the sequence number will be incremented in order
1.138 - to override any previous message.
1.139 -
1.140 - If 'removed' is specified, a list of participants to be removed is
1.141 - provided.
1.142 -
1.143 - If 'added' is specified, a list of participants to be added is provided.
1.144 - """
1.145 -
1.146 - organiser, organiser_attr = uri_item(self.obj.get_item("ORGANIZER"))
1.147 -
1.148 - if self.messenger and self.messenger.sender != get_address(organiser):
1.149 - organiser_attr["SENT-BY"] = get_uri(self.messenger.sender)
1.150 -
1.151 - # Update the attendees in the event.
1.152 -
1.153 - to_cancel = update_attendees(self.obj, added, removed)
1.154 -
1.155 - self.update_dtstamp()
1.156 - self.set_sequence(update)
1.157 -
1.158 - self.send_message(method, get_address(organiser), for_organiser=True)
1.159 -
1.160 - # When cancelling, replace the attendees with those for whom the event
1.161 - # is now cancelled.
1.162 -
1.163 - if to_cancel:
1.164 - remaining = self.obj["ATTENDEE"]
1.165 - self.obj["ATTENDEE"] = to_cancel
1.166 - self.send_message("CANCEL", get_address(organiser), for_organiser=True)
1.167 -
1.168 - # Just in case more work is done with this event, the attendees are
1.169 - # now restored.
1.170 -
1.171 - self.obj["ATTENDEE"] = remaining
1.172 -
1.173 - return True
1.174 -
1.175 class Manager(Client):
1.176
1.177 "A simple manager application."
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/imipweb/handler.py Wed Mar 25 18:23:37 2015 +0100
2.3 @@ -0,0 +1,176 @@
2.4 +#!/usr/bin/env python
2.5 +
2.6 +"""
2.7 +Interaction with the mail system for the manager interface.
2.8 +
2.9 +Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>
2.10 +
2.11 +This program is free software; you can redistribute it and/or modify it under
2.12 +the terms of the GNU General Public License as published by the Free Software
2.13 +Foundation; either version 3 of the License, or (at your option) any later
2.14 +version.
2.15 +
2.16 +This program is distributed in the hope that it will be useful, but WITHOUT
2.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
2.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
2.19 +details.
2.20 +
2.21 +You should have received a copy of the GNU General Public License along with
2.22 +this program. If not, see <http://www.gnu.org/licenses/>.
2.23 +"""
2.24 +
2.25 +from imiptools.client import Client, update_attendees
2.26 +from imiptools.data import get_address, get_uri, get_window_end, make_freebusy, \
2.27 + to_part, uri_item, uri_items, uri_values
2.28 +from imiptools.dates import get_timestamp
2.29 +from imiptools.handlers import Handler
2.30 +from imiptools.period import update_freebusy
2.31 +
2.32 +class ManagerHandler(Client, Handler):
2.33 +
2.34 + """
2.35 + A content handler for use by the manager, as opposed to operating within the
2.36 + mail processing pipeline.
2.37 + """
2.38 +
2.39 + def __init__(self, obj, user, messenger):
2.40 + Handler.__init__(self, messenger=messenger)
2.41 + Client.__init__(self, user)
2.42 +
2.43 + self.set_object(obj)
2.44 +
2.45 + # Communication methods.
2.46 +
2.47 + def send_message(self, method, sender, for_organiser):
2.48 +
2.49 + """
2.50 + Create a full calendar object employing the given 'method', and send it
2.51 + to the appropriate recipients, also sending a copy to the 'sender'. The
2.52 + 'for_organiser' value indicates whether the organiser is sending this
2.53 + message.
2.54 + """
2.55 +
2.56 + parts = [self.obj.to_part(method)]
2.57 +
2.58 + # As organiser, send an invitation to attendees, excluding oneself if
2.59 + # also attending. The updated event will be saved by the outgoing
2.60 + # handler.
2.61 +
2.62 + organiser = get_uri(self.obj.get_value("ORGANIZER"))
2.63 + attendees = uri_values(self.obj.get_values("ATTENDEE"))
2.64 +
2.65 + if for_organiser:
2.66 + recipients = [get_address(attendee) for attendee in attendees if attendee != self.user]
2.67 + else:
2.68 + recipients = [get_address(organiser)]
2.69 +
2.70 + # Bundle free/busy information if appropriate.
2.71 +
2.72 + if self.is_sharing() and self.is_bundling():
2.73 +
2.74 + # Invent a unique identifier.
2.75 +
2.76 + utcnow = get_timestamp()
2.77 + uid = "imip-agent-%s-%s" % (utcnow, get_address(self.user))
2.78 +
2.79 + freebusy = self.store.get_freebusy(self.user)
2.80 +
2.81 + # Replace the non-updated free/busy details for this event with
2.82 + # newer details (since the outgoing handler updates this user's
2.83 + # free/busy details).
2.84 +
2.85 + update_freebusy(freebusy,
2.86 + self.obj.get_periods_for_freebusy(self.get_tzid(), self.get_window_end()),
2.87 + self.obj.get_value("TRANSP") or "OPAQUE",
2.88 + self.uid, self.recurrenceid,
2.89 + self.obj.get_value("SUMMARY"),
2.90 + organiser)
2.91 +
2.92 + user_attr = self.messenger and self.messenger.sender != get_address(self.user) and \
2.93 + {"SENT-BY" : get_uri(self.messenger.sender)} or {}
2.94 +
2.95 + parts.append(to_part("PUBLISH", [
2.96 + make_freebusy(freebusy, uid, self.user, user_attr)
2.97 + ]))
2.98 +
2.99 + message = self.messenger.make_outgoing_message(parts, recipients, outgoing_bcc=sender)
2.100 + self.messenger.sendmail(recipients, message.as_string(), outgoing_bcc=sender)
2.101 +
2.102 + # Action methods.
2.103 +
2.104 + def process_received_request(self, update=False):
2.105 +
2.106 + """
2.107 + Process the current request for the given 'user'. Return whether any
2.108 + action was taken.
2.109 +
2.110 + If 'update' is given, the sequence number will be incremented in order
2.111 + to override any previous response.
2.112 + """
2.113 +
2.114 + # Reply only on behalf of this user.
2.115 +
2.116 + for attendee, attendee_attr in uri_items(self.obj.get_items("ATTENDEE")):
2.117 +
2.118 + if attendee == self.user:
2.119 + if attendee_attr.has_key("RSVP"):
2.120 + del attendee_attr["RSVP"]
2.121 + if self.messenger and self.messenger.sender != get_address(attendee):
2.122 + attendee_attr["SENT-BY"] = get_uri(self.messenger.sender)
2.123 + self.obj["ATTENDEE"] = [(attendee, attendee_attr)]
2.124 +
2.125 + self.update_dtstamp()
2.126 + self.set_sequence(update)
2.127 +
2.128 + self.send_message("REPLY", get_address(attendee), for_organiser=False)
2.129 +
2.130 + return True
2.131 +
2.132 + return False
2.133 +
2.134 + def process_created_request(self, method, update=False, removed=None, added=None):
2.135 +
2.136 + """
2.137 + Process the current request for the given 'user', sending a created
2.138 + request of the given 'method' to attendees. Return whether any action
2.139 + was taken.
2.140 +
2.141 + If 'update' is given, the sequence number will be incremented in order
2.142 + to override any previous message.
2.143 +
2.144 + If 'removed' is specified, a list of participants to be removed is
2.145 + provided.
2.146 +
2.147 + If 'added' is specified, a list of participants to be added is provided.
2.148 + """
2.149 +
2.150 + organiser, organiser_attr = uri_item(self.obj.get_item("ORGANIZER"))
2.151 +
2.152 + if self.messenger and self.messenger.sender != get_address(organiser):
2.153 + organiser_attr["SENT-BY"] = get_uri(self.messenger.sender)
2.154 +
2.155 + # Update the attendees in the event.
2.156 +
2.157 + to_cancel = update_attendees(self.obj, added, removed)
2.158 +
2.159 + self.update_dtstamp()
2.160 + self.set_sequence(update)
2.161 +
2.162 + self.send_message(method, get_address(organiser), for_organiser=True)
2.163 +
2.164 + # When cancelling, replace the attendees with those for whom the event
2.165 + # is now cancelled.
2.166 +
2.167 + if to_cancel:
2.168 + remaining = self.obj["ATTENDEE"]
2.169 + self.obj["ATTENDEE"] = to_cancel
2.170 + self.send_message("CANCEL", get_address(organiser), for_organiser=True)
2.171 +
2.172 + # Just in case more work is done with this event, the attendees are
2.173 + # now restored.
2.174 +
2.175 + self.obj["ATTENDEE"] = remaining
2.176 +
2.177 + return True
2.178 +
2.179 +# vim: tabstop=4 expandtab shiftwidth=4