1.1 --- a/docs/preferences.txt Sat Sep 05 00:36:05 2015 +0200
1.2 +++ b/docs/preferences.txt Sat Sep 05 00:36:31 2015 +0200
1.3 @@ -17,6 +17,16 @@
1.4
1.5 The default time zone/regime for calendars, new events and local times.
1.6
1.7 +event_refreshing
1.8 +----------------
1.9 +
1.10 +Default: never
1.11 +Alternative: always
1.12 +
1.13 +Indicate whether messages requesting a refresh of event details shall be
1.14 +handled automatically. If not, such messages will be passed on to the
1.15 +recipient for their mail program to handle.
1.16 +
1.17 freebusy_bundling
1.18 -----------------
1.19
2.1 --- a/imiptools/client.py Sat Sep 05 00:36:05 2015 +0200
2.2 +++ b/imiptools/client.py Sat Sep 05 00:36:31 2015 +0200
2.3 @@ -86,6 +86,10 @@
2.4 prefs = self.get_preferences()
2.5 return prefs and prefs.get("freebusy_messages") == "notify" or False
2.6
2.7 + def is_refreshing(self):
2.8 + prefs = self.get_preferences()
2.9 + return prefs and prefs.get("event_refreshing") == "always" or False
2.10 +
2.11 def have_manager(self):
2.12 return MANAGER_INTERFACE
2.13
3.1 --- a/imiptools/handlers/person.py Sat Sep 05 00:36:05 2015 +0200
3.2 +++ b/imiptools/handlers/person.py Sat Sep 05 00:36:31 2015 +0200
3.3 @@ -19,7 +19,7 @@
3.4 this program. If not, see <http://www.gnu.org/licenses/>.
3.5 """
3.6
3.7 -from imiptools.data import uri_dict
3.8 +from imiptools.data import get_address, to_part, uri_dict, uri_item
3.9 from imiptools.handlers import Handler
3.10 from imiptools.handlers.common import CommonFreebusy, CommonEvent
3.11 from imiptools.period import FreeBusyPeriod, Period, replace_overlapping
3.12 @@ -175,6 +175,62 @@
3.13 replace_overlapping(stored_freebusy, period, freebusy)
3.14 self.store.set_freebusy_for_other(self.user, stored_freebusy, sender)
3.15
3.16 + def _refresh(self):
3.17 +
3.18 + """
3.19 + Respond to a refresh message by providing complete event details to
3.20 + attendees.
3.21 + """
3.22 +
3.23 + # Obtain valid organiser and attendee details.
3.24 +
3.25 + oa = self.require_organiser_and_attendees(False)
3.26 + if not oa:
3.27 + return False
3.28 +
3.29 + (organiser, organiser_attr), attendees = oa
3.30 +
3.31 + # Filter by stored attendees.
3.32 +
3.33 + obj = self.get_stored_object_version()
3.34 + stored_attendees = set(obj.get_values("ATTENDEE"))
3.35 + attendees = stored_attendees.intersection(attendees)
3.36 +
3.37 + if not attendees:
3.38 + return False
3.39 +
3.40 + # Assume that the outcome will be a request. It would not seem
3.41 + # completely bizarre to produce a publishing message instead if a
3.42 + # refresh message was unprovoked.
3.43 +
3.44 + method = "REQUEST"
3.45 +
3.46 + # Get the parent event, add SENT-BY details to the organiser.
3.47 +
3.48 + obj = self.get_stored_object_version()
3.49 + organiser, organiser_attr = uri_item(obj.get_item("ORGANIZER"))
3.50 + self.update_sender(organiser_attr)
3.51 + responses = [obj.to_node()]
3.52 +
3.53 + # Get recurrences.
3.54 +
3.55 + cancelled = self.store.get_cancellations(self.user)
3.56 +
3.57 + if not self.recurrenceid:
3.58 + for recurrenceid in self.store.get_recurrences(self.user, self.uid):
3.59 + if (self.uid, recurrenceid) not in cancelled:
3.60 +
3.61 + # Get the recurrence, add SENT-BY details to the organiser.
3.62 +
3.63 + obj = self.get_stored_object(self.uid, recurrenceid)
3.64 + organiser, organiser_attr = uri_item(obj.get_item("ORGANIZER"))
3.65 + self.update_sender(organiser_attr)
3.66 + responses.append(obj.to_node())
3.67 +
3.68 + self.add_result(method, map(get_address, attendees), to_part(method, responses))
3.69 +
3.70 + return True
3.71 +
3.72 class Event(PersonHandler):
3.73
3.74 "An event handler."
3.75 @@ -214,11 +270,12 @@
3.76
3.77 def refresh(self):
3.78
3.79 - "Generate details of any active event."
3.80 + "Requests to refresh events are handled either here or by the client."
3.81
3.82 - # NOTE: Return event details if configured to do so.
3.83 -
3.84 - return self.wrap("A request for updated event details has been received.")
3.85 + if self.is_refreshing():
3.86 + return self._refresh()
3.87 + else:
3.88 + return self.wrap("A request for updated event details has been received.")
3.89
3.90 def reply(self):
3.91
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/tests/templates/event-refresh-person-recurring-non-attendee.txt Sat Sep 05 00:36:31 2015 +0200
4.3 @@ -0,0 +1,30 @@
4.4 +Content-Type: multipart/alternative; boundary="===============0047278175=="
4.5 +MIME-Version: 1.0
4.6 +From: oliver.otter@example.com
4.7 +To: paul.boddie@example.com
4.8 +Subject: Refresh!
4.9 +
4.10 +--===============0047278175==
4.11 +Content-Type: text/plain; charset="us-ascii"
4.12 +MIME-Version: 1.0
4.13 +Content-Transfer-Encoding: 7bit
4.14 +
4.15 +This message contains an event.
4.16 +--===============0047278175==
4.17 +MIME-Version: 1.0
4.18 +Content-Transfer-Encoding: 7bit
4.19 +Content-Type: text/calendar; charset="us-ascii"; method="REFRESH"
4.20 +
4.21 +BEGIN:VCALENDAR
4.22 +PRODID:-//imip-agent/test//EN
4.23 +METHOD:REFRESH
4.24 +VERSION:2.0
4.25 +BEGIN:VEVENT
4.26 +ORGANIZER:mailto:paul.boddie@example.com
4.27 +ATTENDEE;RSVP=TRUE:mailto:oliver.otter@example.com
4.28 +DTSTAMP:20141009T182400Z
4.29 +UID:event8@example.com
4.30 +END:VEVENT
4.31 +END:VCALENDAR
4.32 +
4.33 +--===============0047278175==--
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/tests/templates/event-refresh-person-recurring.txt Sat Sep 05 00:36:31 2015 +0200
5.3 @@ -0,0 +1,30 @@
5.4 +Content-Type: multipart/alternative; boundary="===============0047278175=="
5.5 +MIME-Version: 1.0
5.6 +From: vincent.vole@example.com
5.7 +To: paul.boddie@example.com
5.8 +Subject: Refresh!
5.9 +
5.10 +--===============0047278175==
5.11 +Content-Type: text/plain; charset="us-ascii"
5.12 +MIME-Version: 1.0
5.13 +Content-Transfer-Encoding: 7bit
5.14 +
5.15 +This message contains an event.
5.16 +--===============0047278175==
5.17 +MIME-Version: 1.0
5.18 +Content-Transfer-Encoding: 7bit
5.19 +Content-Type: text/calendar; charset="us-ascii"; method="REFRESH"
5.20 +
5.21 +BEGIN:VCALENDAR
5.22 +PRODID:-//imip-agent/test//EN
5.23 +METHOD:REFRESH
5.24 +VERSION:2.0
5.25 +BEGIN:VEVENT
5.26 +ORGANIZER:mailto:paul.boddie@example.com
5.27 +ATTENDEE;RSVP=TRUE:mailto:vincent.vole@example.com
5.28 +DTSTAMP:20141009T182400Z
5.29 +UID:event8@example.com
5.30 +END:VEVENT
5.31 +END:VCALENDAR
5.32 +
5.33 +--===============0047278175==--
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/tests/test_person_invitation_refresh.sh Sat Sep 05 00:36:31 2015 +0200
6.3 @@ -0,0 +1,100 @@
6.4 +#!/bin/sh
6.5 +
6.6 +THIS_DIR=`dirname $0`
6.7 +
6.8 +TEMPLATES="$THIS_DIR/templates"
6.9 +PERSON_SCRIPT="$THIS_DIR/../imip_person.py"
6.10 +SHOWMAIL="$THIS_DIR/../tools/showmail.py"
6.11 +STORE=/tmp/store
6.12 +STATIC=/tmp/static
6.13 +PREFS=/tmp/prefs
6.14 +ARGS="-S $STORE -P $STATIC -p $PREFS -d"
6.15 +USER="mailto:vincent.vole@example.com"
6.16 +SENDER="mailto:paul.boddie@example.com"
6.17 +FBFILE="$STORE/$USER/freebusy"
6.18 +FBOTHERFILE="$STORE/$USER/freebusy-other/$SENDER"
6.19 +FBSENDERFILE="$STORE/$SENDER/freebusy"
6.20 +TAB=`printf '\t'`
6.21 +
6.22 +OUTGOING_SCRIPT="$THIS_DIR/../imip_person_outgoing.py"
6.23 +
6.24 +PYTHONPATH="$THIS_DIR/.."
6.25 +export PYTHONPATH
6.26 +
6.27 +ACCEPT_SCRIPT="$THIS_DIR/test_handle.py"
6.28 +ACCEPT_ARGS="accept $STORE"
6.29 +
6.30 +DECLINE_SCRIPT="$THIS_DIR/test_handle.py"
6.31 +DECLINE_ARGS="decline $STORE"
6.32 +
6.33 +ERROR=err.tmp
6.34 +
6.35 +rm -r $STORE
6.36 +rm -r $STATIC
6.37 +rm -r $PREFS
6.38 +rm $ERROR
6.39 +rm out*.tmp
6.40 +
6.41 +mkdir -p "$PREFS/$USER"
6.42 +echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
6.43 +echo 'share' > "$PREFS/$USER/freebusy_sharing"
6.44 +
6.45 +mkdir -p "$PREFS/$SENDER"
6.46 +echo 'Europe/Oslo' > "$PREFS/$USER/TZID"
6.47 +echo 'always' > "$PREFS/$SENDER/event_refreshing"
6.48 +
6.49 +# Publish an event, testing registration in the outgoing handler.
6.50 +
6.51 +"$OUTGOING_SCRIPT" $ARGS < "$TEMPLATES/event-request-person-recurring.txt" 2>> $ERROR
6.52 +
6.53 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBSENDERFILE" \
6.54 +&& echo "Success" \
6.55 +|| echo "Failed"
6.56 +
6.57 +# Test a request from an attendee for the event details to be refreshed.
6.58 +
6.59 + "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/event-refresh-person-recurring.txt" 2>> $ERROR \
6.60 +| "$SHOWMAIL" \
6.61 +> out2.tmp
6.62 +
6.63 + grep -q 'METHOD:REQUEST' out2.tmp \
6.64 +&& echo "Success" \
6.65 +|| echo "Failed"
6.66 +
6.67 +# Present the result to the recipient.
6.68 +
6.69 + "$PERSON_SCRIPT" $ARGS < out2.tmp 2>> $ERROR \
6.70 +| "$SHOWMAIL" \
6.71 +> out3.tmp
6.72 +
6.73 + ! grep -q 'METHOD:REPLY' out3.tmp \
6.74 +&& echo "Success" \
6.75 +|| echo "Failed"
6.76 +
6.77 + ! grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
6.78 +&& echo "Success" \
6.79 +|| echo "Failed"
6.80 +
6.81 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBOTHERFILE" \
6.82 +&& echo "Success" \
6.83 +|| echo "Failed"
6.84 +
6.85 +# Test acceptance and registration in the outgoing handler.
6.86 +
6.87 + "$ACCEPT_SCRIPT" $ACCEPT_ARGS "$USER" "event8@example.com" 2>> $ERROR \
6.88 +| tee out4.tmp \
6.89 +| "$OUTGOING_SCRIPT" $ARGS 2>> $ERROR
6.90 +
6.91 + grep -q "^20141212T090000Z${TAB}20141212T100000Z" "$FBFILE" \
6.92 +&& echo "Success" \
6.93 +|| echo "Failed"
6.94 +
6.95 +# Test a request from a non-attendee for the event details to be refreshed.
6.96 +
6.97 + "$PERSON_SCRIPT" $ARGS < "$TEMPLATES/event-refresh-person-recurring-non-attendee.txt" 2>> $ERROR \
6.98 +| "$SHOWMAIL" \
6.99 +> out5.tmp
6.100 +
6.101 + ! grep -q 'METHOD:REQUEST' out5.tmp \
6.102 +&& echo "Success" \
6.103 +|| echo "Failed"