Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117922448
D3050.1775435443.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
9 KB
Referenced Files
None
Subscribers
None
D3050.1775435443.diff
View Options
diff --git a/pykolab/imap/__init__.py b/pykolab/imap/__init__.py
--- a/pykolab/imap/__init__.py
+++ b/pykolab/imap/__init__.py
@@ -285,6 +285,9 @@
else:
raise AttributeError(_("%r has no attribute %s") % (self, name))
+ def append(self, folder, message):
+ return self.imap.m.append(self.folder_utf7(folder), None, None, message)
+
def folder_utf7(self, folder):
from pykolab import imap_utf7
return imap_utf7.encode(folder)
diff --git a/tests/unit/test-011-wallace_resources.py b/tests/unit/test-011-wallace_resources.py
--- a/tests/unit/test-011-wallace_resources.py
+++ b/tests/unit/test-011-wallace_resources.py
@@ -3,6 +3,7 @@
import datetime
from pykolab import itip
+from pykolab.imap import IMAP
from icalendar import Calendar
from email import message
from email import message_from_string
@@ -41,7 +42,7 @@
METHOD:REQUEST
BEGIN:VEVENT
UID:626421779C777FBE9C9B85A80D04DDFA-A4BF5BBB9FEAA271
-DTSTAMP:20120713T1254140
+DTSTAMP:20120713T125414
DTSTART;TZID=3DEurope/London:20120713T100000
DTEND;TZID=3DEurope/London:20120713T110000
SUMMARY:test
@@ -75,7 +76,7 @@
METHOD:REQUEST
BEGIN:VEVENT
UID:626421779C777FBE9C9B85A80D04DDFA-A4BF5BBB9FEAA271
-DTSTAMP:20120713T1254140
+DTSTAMP:20120713T125414
DTSTART;TZID=3DEurope/London:20120713T100000
DTEND;TZID=3DEurope/London:20120713T110000
SUMMARY:test
@@ -105,6 +106,12 @@
self.patch(pykolab.auth.Auth, "get_entry_attributes", self._mock_get_entry_attributes)
self.patch(pykolab.auth.Auth, "search_entry_by_attribute", self._mock_search_entry_by_attribute)
+ # Mock IMAP operations
+ self.patch(pykolab.imap.IMAP, "connect", self._mock_nop)
+ self.patch(pykolab.imap.IMAP, "disconnect", self._mock_nop)
+ self.patch(pykolab.imap.IMAP, "set_acl", self._mock_nop)
+ self.patch(pykolab.imap.IMAP, "append", self._mock_imap_append)
+
# intercept calls to smtplib.SMTP.sendmail()
import smtplib
self.patch(smtplib.SMTP, "__init__", self._mock_smtp_init)
@@ -113,8 +120,9 @@
self.patch(smtplib.SMTP, "sendmail", self._mock_smtp_sendmail)
self.smtplog = []
+ self.imap_append_log = []
- def _mock_nop(self, domain=None):
+ def _mock_nop(self, domain=None, arg3=None, arg4=None):
pass
def _mock_find_resource(self, address):
@@ -142,6 +150,10 @@
self.smtplog.append((from_addr, to_addr, message))
return []
+ def _mock_imap_append(self, folder, msg):
+ self.imap_append_log.append((folder, msg))
+ return True
+
def _get_ics_part(self, message):
ics_part = None
for part in message.walk():
@@ -234,3 +246,51 @@
self.assertIn("ACCEPTED".lower(), response2['subject'].lower(), "Delegation message subject: %r" % (response2['subject']))
self.assertEqual(ical2['attendee'].__str__(), "MAILTO:resource-car-audi-a4@example.org")
self.assertEqual(ical2['attendee'].params['PARTSTAT'], u"ACCEPTED")
+
+ def test_007_accept_reservation_request_tentative_and_notify(self):
+ itip_event = itip.events_from_message(message_from_string(itip_multipart))[0]
+
+ resource = {
+ 'mail': 'resource-collection-car@example.org',
+ 'kolabtargetfolder': 'shared/Resources/Test@example.org',
+ 'dn': 'cn=cars,ou=Resources,cd=example,dc=org',
+ 'cn': 'Cars',
+ 'owner': 'uid=foo,ou=People,cd=example,dc=org',
+ 'kolabinvitationpolicy': [module_resources.ACT_TENTATIVE_AND_NOTIFY]
+ }
+
+ conf.command_set('wallace', 'webmail_url', 'https://%(domain)s/roundcube')
+ module_resources.imap = IMAP()
+
+ module_resources.accept_reservation_request(itip_event, resource)
+
+ self.assertEqual(len(self.smtplog), 2)
+ self.assertEqual(len(self.imap_append_log), 1)
+
+ # Assert the reply message sent to the organizer
+ mail = message_from_string(self.smtplog[0][2])
+
+ self.assertEqual("resource-collection-car@example.org", self.smtplog[0][0])
+ self.assertEqual("resource-collection-car@example.org", mail['from'])
+ self.assertEqual("doe@example.org", self.smtplog[0][1])
+ self.assertEqual("doe@example.org", mail['to'])
+ self.assertTrue(mail.is_multipart())
+ self.assertIn("Reservation Request for test was Tentatively Accepted", mail['subject'])
+ self.assertIn("PARTSTAT=TENTATIVE", self.smtplog[0][2])
+
+ # Assert the notification sent to the resource owner
+ mail = message_from_string(self.smtplog[1][2])
+
+ self.assertEqual("resource-collection-car@example.org", self.smtplog[1][0])
+ self.assertEqual("resource-collection-car@example.org", mail['from'])
+ self.assertEqual("foo@example.org", self.smtplog[1][1])
+ self.assertEqual("foo@example.org", mail['to'])
+ self.assertFalse(mail.is_multipart())
+ self.assertIn("Booking for Cars has been Tentatively Accepted", mail['subject'])
+ body = mail.get_payload(decode=True)
+ self.assertIn("The resource booking for Cars by Doe, John <doe@example.org>", body)
+ self.assertIn("You can change the status via https://example.org/roundcube?_task=calendar", body)
+
+ # Assert the message appended to the resource folder
+ self.assertEqual(resource['kolabtargetfolder'], self.imap_append_log[0][0])
+ self.assertIn("<text>TENTATIVE</text>", self.imap_append_log[0][1])
diff --git a/wallace/module_resources.py b/wallace/module_resources.py
--- a/wallace/module_resources.py
+++ b/wallace/module_resources.py
@@ -58,14 +58,17 @@
ACT_MANUAL = 1
ACT_ACCEPT = 2
ACT_REJECT = 8
+ACT_TENTATIVE = 16
ACT_ACCEPT_AND_NOTIFY = ACT_ACCEPT + COND_NOTIFY
+ACT_TENTATIVE_AND_NOTIFY = ACT_TENTATIVE + COND_NOTIFY
# noqa: E241
policy_name_map = {
'ACT_MANUAL': ACT_MANUAL, # noqa: E241
'ACT_ACCEPT': ACT_ACCEPT, # noqa: E241
'ACT_REJECT': ACT_REJECT, # noqa: E241
- 'ACT_ACCEPT_AND_NOTIFY': ACT_ACCEPT_AND_NOTIFY
+ 'ACT_ACCEPT_AND_NOTIFY': ACT_ACCEPT_AND_NOTIFY,
+ 'ACT_TENTATIVE_AND_NOTIFY': ACT_TENTATIVE_AND_NOTIFY
}
# pylint: disable=invalid-name
@@ -1032,10 +1035,11 @@
"""
Accepts the given iTip event by booking it into the resource's
calendar. Then set the attendee status of the given resource to
- ACCEPTED and sends an iTip reply message to the organizer.
+ ACCEPTED/TENTATIVE and sends an iTip reply message to the organizer.
"""
owner = get_resource_owner(resource)
confirmation_required = False
+ partstat = 'ACCEPTED'
if not confirmed and owner:
if invitationpolicy is None:
@@ -1046,9 +1050,10 @@
for policy in invitationpolicy:
if policy & ACT_MANUAL and owner['mail']:
confirmation_required = True
+ partstat = 'TENTATIVE'
break
-
- partstat = 'TENTATIVE' if confirmation_required else 'ACCEPTED'
+ if policy & ACT_TENTATIVE:
+ partstat = 'TENTATIVE'
itip_event['xml'].set_transparency(False)
itip_event['xml'].set_attendee_participant_status(
@@ -1063,7 +1068,8 @@
level=8
)
- if saved:
+ # Only send a response back to the organizer if this is a confirmation from the resource owner.
+ if saved and confirmed:
send_response(delegator['mail'] if delegator else resource['mail'], itip_event, owner)
if owner and confirmation_required:
@@ -1110,7 +1116,6 @@
"""
try:
save_event = itip_event['xml']
- targetfolder = imap.folder_quote(resource['kolabtargetfolder'])
# add exception to existing recurring main event
if resource.get('existing_master') is not None:
@@ -1132,18 +1137,17 @@
else:
imap.set_acl(
- targetfolder,
+ resource['kolabtargetfolder'],
conf.get(conf.get('kolab', 'imap_backend'), 'admin_login'),
"lrswipkxtecda"
)
# append new version
- result = imap.imap.m.append(
- targetfolder,
- None,
- None,
+ result = imap.append(
+ resource['kolabtargetfolder'],
save_event.to_message(creator="Kolab Server <wallace@localhost>").as_string()
)
+
return result
# pylint: disable=broad-except
@@ -1666,6 +1670,8 @@
def owner_notification_text(resource, owner, event, success):
organizer = event.get_organizer()
status = event.get_attendee_by_email(resource['mail']).get_participant_status(True)
+ domain = resource['mail'].split('@')[1]
+ url = conf.get('wallace', 'webmail_url')
if success:
message_text = _(
@@ -1676,6 +1682,12 @@
*** This is an automated message, sent to you as the resource owner. ***
"""
)
+
+ if url:
+ message_text += (
+ "\n "
+ + _("You can change the status via %(url)s") % { 'url': url } + '?_task=calendar'
+ )
else:
message_text = _(
"""
@@ -1695,7 +1707,8 @@
'date': event.get_date_text(),
'status': participant_status_label(status),
'orgname': organizer.name(),
- 'orgemail': organizer.email()
+ 'orgemail': organizer.email(),
+ 'domain': domain
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Apr 6, 12:30 AM (5 h, 23 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18835044
Default Alt Text
D3050.1775435443.diff (9 KB)
Attached To
Mode
D3050: Implement ACT_TENTATIVE_AND_NOTIFY policy for resources, add webmail url to the notification body
Attached
Detach File
Event Timeline