# HG changeset patch # User Paul Boddie # Date 1427304217 -3600 # Node ID ca59ce0f3198d4e92dacee0057f9813ea0513b11 # Parent 8ef96b8e11f4c41bcd6810aed996b0d82ab95a52 Moved the ManagerHandler class into a separate handler module. diff -r 8ef96b8e11f4 -r ca59ce0f3198 imip_manager.py --- a/imip_manager.py Wed Mar 25 18:15:37 2015 +0100 +++ b/imip_manager.py Wed Mar 25 18:23:37 2015 +0100 @@ -32,14 +32,12 @@ sys.path.append(LIBRARY_PATH) from imiptools.client import Client, update_attendees -from imiptools.data import get_address, get_uri, get_window_end, make_freebusy, \ - Object, to_part, \ - uri_dict, uri_item, uri_items, uri_values +from imiptools.data import get_address, get_uri, get_window_end, Object, \ + uri_dict, uri_values from imiptools.dates import format_datetime, format_time, to_date, get_datetime, \ get_datetime_item, get_end_of_day, get_period_item, \ get_start_of_day, get_start_of_next_day, get_timestamp, \ ends_on_same_day, to_timezone -from imiptools.handlers import Handler from imiptools.mail import Messenger from imiptools.period import add_day_start_points, add_empty_days, add_slots, \ convert_periods, get_freebusy_details, \ @@ -47,156 +45,10 @@ partition_by_day, remove_period, remove_affected_period, \ update_freebusy from imipweb.env import CGIEnvironment +from imipweb.handler import ManagerHandler import imip_store import markup -class ManagerHandler(Client, Handler): - - """ - A content handler for use by the manager, as opposed to operating within the - mail processing pipeline. - """ - - def __init__(self, obj, user, messenger): - Handler.__init__(self, messenger=messenger) - Client.__init__(self, user) - - self.set_object(obj) - - # Communication methods. - - def send_message(self, method, sender, for_organiser): - - """ - Create a full calendar object employing the given 'method', and send it - to the appropriate recipients, also sending a copy to the 'sender'. The - 'for_organiser' value indicates whether the organiser is sending this - message. - """ - - parts = [self.obj.to_part(method)] - - # As organiser, send an invitation to attendees, excluding oneself if - # also attending. The updated event will be saved by the outgoing - # handler. - - organiser = get_uri(self.obj.get_value("ORGANIZER")) - attendees = uri_values(self.obj.get_values("ATTENDEE")) - - if for_organiser: - recipients = [get_address(attendee) for attendee in attendees if attendee != self.user] - else: - recipients = [get_address(organiser)] - - # Bundle free/busy information if appropriate. - - if self.is_sharing() and self.is_bundling(): - - # Invent a unique identifier. - - utcnow = get_timestamp() - uid = "imip-agent-%s-%s" % (utcnow, get_address(self.user)) - - freebusy = self.store.get_freebusy(self.user) - - # Replace the non-updated free/busy details for this event with - # newer details (since the outgoing handler updates this user's - # free/busy details). - - update_freebusy(freebusy, - self.obj.get_periods_for_freebusy(self.get_tzid(), self.get_window_end()), - self.obj.get_value("TRANSP") or "OPAQUE", - self.uid, self.recurrenceid, - self.obj.get_value("SUMMARY"), - organiser) - - user_attr = self.messenger and self.messenger.sender != get_address(self.user) and \ - {"SENT-BY" : get_uri(self.messenger.sender)} or {} - - parts.append(to_part("PUBLISH", [ - make_freebusy(freebusy, uid, self.user, user_attr) - ])) - - message = self.messenger.make_outgoing_message(parts, recipients, outgoing_bcc=sender) - self.messenger.sendmail(recipients, message.as_string(), outgoing_bcc=sender) - - # Action methods. - - def process_received_request(self, update=False): - - """ - Process the current request for the given 'user'. Return whether any - action was taken. - - If 'update' is given, the sequence number will be incremented in order - to override any previous response. - """ - - # Reply only on behalf of this user. - - for attendee, attendee_attr in uri_items(self.obj.get_items("ATTENDEE")): - - if attendee == self.user: - if attendee_attr.has_key("RSVP"): - del attendee_attr["RSVP"] - if self.messenger and self.messenger.sender != get_address(attendee): - attendee_attr["SENT-BY"] = get_uri(self.messenger.sender) - self.obj["ATTENDEE"] = [(attendee, attendee_attr)] - - self.update_dtstamp() - self.set_sequence(update) - - self.send_message("REPLY", get_address(attendee), for_organiser=False) - - return True - - return False - - def process_created_request(self, method, update=False, removed=None, added=None): - - """ - Process the current request for the given 'user', sending a created - request of the given 'method' to attendees. Return whether any action - was taken. - - If 'update' is given, the sequence number will be incremented in order - to override any previous message. - - If 'removed' is specified, a list of participants to be removed is - provided. - - If 'added' is specified, a list of participants to be added is provided. - """ - - organiser, organiser_attr = uri_item(self.obj.get_item("ORGANIZER")) - - if self.messenger and self.messenger.sender != get_address(organiser): - organiser_attr["SENT-BY"] = get_uri(self.messenger.sender) - - # Update the attendees in the event. - - to_cancel = update_attendees(self.obj, added, removed) - - self.update_dtstamp() - self.set_sequence(update) - - self.send_message(method, get_address(organiser), for_organiser=True) - - # When cancelling, replace the attendees with those for whom the event - # is now cancelled. - - if to_cancel: - remaining = self.obj["ATTENDEE"] - self.obj["ATTENDEE"] = to_cancel - self.send_message("CANCEL", get_address(organiser), for_organiser=True) - - # Just in case more work is done with this event, the attendees are - # now restored. - - self.obj["ATTENDEE"] = remaining - - return True - class Manager(Client): "A simple manager application." diff -r 8ef96b8e11f4 -r ca59ce0f3198 imipweb/handler.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imipweb/handler.py Wed Mar 25 18:23:37 2015 +0100 @@ -0,0 +1,176 @@ +#!/usr/bin/env python + +""" +Interaction with the mail system for the manager interface. + +Copyright (C) 2014, 2015 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from imiptools.client import Client, update_attendees +from imiptools.data import get_address, get_uri, get_window_end, make_freebusy, \ + to_part, uri_item, uri_items, uri_values +from imiptools.dates import get_timestamp +from imiptools.handlers import Handler +from imiptools.period import update_freebusy + +class ManagerHandler(Client, Handler): + + """ + A content handler for use by the manager, as opposed to operating within the + mail processing pipeline. + """ + + def __init__(self, obj, user, messenger): + Handler.__init__(self, messenger=messenger) + Client.__init__(self, user) + + self.set_object(obj) + + # Communication methods. + + def send_message(self, method, sender, for_organiser): + + """ + Create a full calendar object employing the given 'method', and send it + to the appropriate recipients, also sending a copy to the 'sender'. The + 'for_organiser' value indicates whether the organiser is sending this + message. + """ + + parts = [self.obj.to_part(method)] + + # As organiser, send an invitation to attendees, excluding oneself if + # also attending. The updated event will be saved by the outgoing + # handler. + + organiser = get_uri(self.obj.get_value("ORGANIZER")) + attendees = uri_values(self.obj.get_values("ATTENDEE")) + + if for_organiser: + recipients = [get_address(attendee) for attendee in attendees if attendee != self.user] + else: + recipients = [get_address(organiser)] + + # Bundle free/busy information if appropriate. + + if self.is_sharing() and self.is_bundling(): + + # Invent a unique identifier. + + utcnow = get_timestamp() + uid = "imip-agent-%s-%s" % (utcnow, get_address(self.user)) + + freebusy = self.store.get_freebusy(self.user) + + # Replace the non-updated free/busy details for this event with + # newer details (since the outgoing handler updates this user's + # free/busy details). + + update_freebusy(freebusy, + self.obj.get_periods_for_freebusy(self.get_tzid(), self.get_window_end()), + self.obj.get_value("TRANSP") or "OPAQUE", + self.uid, self.recurrenceid, + self.obj.get_value("SUMMARY"), + organiser) + + user_attr = self.messenger and self.messenger.sender != get_address(self.user) and \ + {"SENT-BY" : get_uri(self.messenger.sender)} or {} + + parts.append(to_part("PUBLISH", [ + make_freebusy(freebusy, uid, self.user, user_attr) + ])) + + message = self.messenger.make_outgoing_message(parts, recipients, outgoing_bcc=sender) + self.messenger.sendmail(recipients, message.as_string(), outgoing_bcc=sender) + + # Action methods. + + def process_received_request(self, update=False): + + """ + Process the current request for the given 'user'. Return whether any + action was taken. + + If 'update' is given, the sequence number will be incremented in order + to override any previous response. + """ + + # Reply only on behalf of this user. + + for attendee, attendee_attr in uri_items(self.obj.get_items("ATTENDEE")): + + if attendee == self.user: + if attendee_attr.has_key("RSVP"): + del attendee_attr["RSVP"] + if self.messenger and self.messenger.sender != get_address(attendee): + attendee_attr["SENT-BY"] = get_uri(self.messenger.sender) + self.obj["ATTENDEE"] = [(attendee, attendee_attr)] + + self.update_dtstamp() + self.set_sequence(update) + + self.send_message("REPLY", get_address(attendee), for_organiser=False) + + return True + + return False + + def process_created_request(self, method, update=False, removed=None, added=None): + + """ + Process the current request for the given 'user', sending a created + request of the given 'method' to attendees. Return whether any action + was taken. + + If 'update' is given, the sequence number will be incremented in order + to override any previous message. + + If 'removed' is specified, a list of participants to be removed is + provided. + + If 'added' is specified, a list of participants to be added is provided. + """ + + organiser, organiser_attr = uri_item(self.obj.get_item("ORGANIZER")) + + if self.messenger and self.messenger.sender != get_address(organiser): + organiser_attr["SENT-BY"] = get_uri(self.messenger.sender) + + # Update the attendees in the event. + + to_cancel = update_attendees(self.obj, added, removed) + + self.update_dtstamp() + self.set_sequence(update) + + self.send_message(method, get_address(organiser), for_organiser=True) + + # When cancelling, replace the attendees with those for whom the event + # is now cancelled. + + if to_cancel: + remaining = self.obj["ATTENDEE"] + self.obj["ATTENDEE"] = to_cancel + self.send_message("CANCEL", get_address(organiser), for_organiser=True) + + # Just in case more work is done with this event, the attendees are + # now restored. + + self.obj["ATTENDEE"] = remaining + + return True + +# vim: tabstop=4 expandtab shiftwidth=4