diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index e463220eb..9ac964b0e 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -1391,7 +1391,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. participationStatus = @"DECLINED"; [appointmentObject changeParticipationStatus: participationStatus - withDelegate: nil]; + withDelegate: nil + alarm: nil]; [s appendString: @""]; [s appendString: @""]; @@ -2516,8 +2517,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (NSURL *) folderTableURL { - NSString *urlString, *ocFSTableName; + NSMutableString *ocFSTableName; NSMutableArray *parts; + NSString *urlString; SOGoUser *user; if (!folderTableURL) @@ -2536,10 +2538,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* If "OCSFolderInfoURL" is properly configured, we must have 5 parts in this url. We strip the '-' character in case we have this in the domain part - like foo@bar-zot.com */ - ocFSTableName = [NSString stringWithFormat: @"sogo_cache_folder_%@", - [[[user loginInDomain] asCSSIdentifier] - stringByReplacingOccurrencesOfString: @"-" - withString: @"_"]]; + ocFSTableName = [NSMutableString stringWithFormat: @"sogo_cache_folder_%@", + [[user loginInDomain] asCSSIdentifier]]; + [ocFSTableName replaceOccurrencesOfString: @"-" + withString: @"_" + options: 0 + range: NSMakeRange(0, [ocFSTableName length])]; [parts replaceObjectAtIndex: 4 withObject: ocFSTableName]; folderTableURL = [NSURL URLWithString: [parts componentsJoinedByString: @"/"]]; diff --git a/ActiveSync/SOGoMailObject+ActiveSync.m b/ActiveSync/SOGoMailObject+ActiveSync.m index 85a1bb61e..6cc06e3d0 100644 --- a/ActiveSync/SOGoMailObject+ActiveSync.m +++ b/ActiveSync/SOGoMailObject+ActiveSync.m @@ -282,7 +282,7 @@ struct GlobalObjectId { performed: b]; } } - else if ([thePart isKindOfClass: [NGMimeBodyPart class]]) + else if ([thePart isKindOfClass: [NGMimeBodyPart class]] || [thePart isKindOfClass: [NGMimeMessage class]]) { NGMimeFileData *fdata; id body; @@ -366,8 +366,7 @@ struct GlobalObjectId { if (message) { - [self _sanitizedMIMEPart: [message body] - performed: &b]; + [self _sanitizedMIMEPart: message performed: &b]; if (b) { diff --git a/ActiveSync/common.make b/ActiveSync/common.make index 15024630c..bf77fa3be 100644 --- a/ActiveSync/common.make +++ b/ActiveSync/common.make @@ -20,7 +20,7 @@ ADDITIONAL_LIB_DIRS += \ -L../SoObjects/SOGo/$(GNUSTEP_OBJ_DIR)/ \ -L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \ -L/usr/local/lib \ - -Wl,-rpath,$(GNUSTEP_SYSTEM_LIBRARIES)/sogo + -Wl,-rpath,$(SOGO_SYSLIBDIR)/sogo BUNDLE_LIBS += \ -lSOGo \ diff --git a/ActiveSync/iCalEvent+ActiveSync.m b/ActiveSync/iCalEvent+ActiveSync.m index 6d4d4ad4d..dc996f381 100644 --- a/ActiveSync/iCalEvent+ActiveSync.m +++ b/ActiveSync/iCalEvent+ActiveSync.m @@ -76,13 +76,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (NSString *) activeSyncRepresentationInContext: (WOContext *) context { NSMutableString *s; - NSArray *attendees; + NSArray *attendees, *categories; iCalPerson *organizer, *attendee; iCalTimeZone *tz; id o; - int v; + int v, i, meetingStatus; NSTimeZone *userTimeZone; userTimeZone = [[[context activeUser] userDefaults] timeZone]; @@ -134,6 +134,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Organizer and other invitations related properties if ((organizer = [self organizer])) { + meetingStatus = 1; // meeting and the user is the meeting organizer. o = [organizer rfc822Email]; if ([o length]) { @@ -159,7 +160,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [s appendString: @""]; attendee = [attendees objectAtIndex: i]; - [s appendFormat: @"%@", [attendee rfc822Email]]; + [s appendFormat: @"%@", [[attendee rfc822Email] activeSyncRepresentationInContext: context]]; [s appendFormat: @"%@", [[attendee cn] activeSyncRepresentationInContext: context]]; attendee_status = [self _attendeeStatus: attendee]; @@ -177,6 +178,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } [s appendString: @""]; } + else + { + meetingStatus = 0; // appointment + } // This depends on the 'NEEDS-ACTION' parameter. // This will trigger the SendMail command @@ -186,18 +191,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. int attendee_status; + meetingStatus = 3; // event is a meeting, and the user is not the meeting organizer + attendee = [self userAsAttendee: [context activeUser]]; attendee_status = [self _attendeeStatus: attendee]; [s appendFormat: @"%d", 1]; [s appendFormat: @"%d", attendee_status]; - [s appendFormat: @"%d", 3]; [s appendFormat: @"%d", 1]; // BusyStatus -- http://msdn.microsoft.com/en-us/library/ee202290(v=exchg.80).aspx [s appendFormat: @"%d", 2]; } + [s appendFormat: @"%d", meetingStatus]; + // Subject -- http://msdn.microsoft.com/en-us/library/ee157192(v=exchg.80).aspx if ([[self summary] length]) [s appendFormat: @"%@", [[self summary] activeSyncRepresentationInContext: context]]; @@ -223,12 +231,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Sensitivity if ([[self accessClass] isEqualToString: @"PRIVATE"]) v = 2; - if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"]) + else if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"]) v = 3; else v = 0; [s appendFormat: @"%d", v]; + + categories = [self categories]; + + if ([categories count]) + { + [s appendFormat: @""]; + for (i = 0; i < [categories count]; i++) + { + [s appendFormat: @"%@", [[categories objectAtIndex: i] activeSyncRepresentationInContext: context]]; + } + [s appendFormat: @""]; + } + // Reminder -- http://msdn.microsoft.com/en-us/library/ee219691(v=exchg.80).aspx // TODO: improve this to handle more alarm types @@ -250,11 +271,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. o = [self comment]; if ([o length]) { + // It is very important here to NOT set 0 in the response, + // otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details. o = [o activeSyncRepresentationInContext: context]; [s appendString: @""]; [s appendFormat: @"%d", 1]; [s appendFormat: @"%d", [o length]]; - [s appendFormat: @"%d", 0]; [s appendFormat: @"%@", o]; [s appendString: @""]; } @@ -337,7 +359,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // 0- normal, 1- personal, 2- private and 3-confidential // - if ((o = [theValues objectForKey: @"Sensitivy"])) + if ((o = [theValues objectForKey: @"Sensitivity"])) { switch ([o intValue]) { @@ -353,7 +375,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [self setAccessClass: @"PUBLIC"]; } } - + + // Categories + if ((o = [theValues objectForKey: @"Categories"]) && [o length]) + [self setCategories: o]; + // We ignore TimeZone sent by mobile devices for now. // Some Windows devices don't send during event updates. //if ((o = [theValues objectForKey: @"TimeZone"])) diff --git a/ActiveSync/iCalToDo+ActiveSync.m b/ActiveSync/iCalToDo+ActiveSync.m index d09fda688..58b0d4c04 100644 --- a/ActiveSync/iCalToDo+ActiveSync.m +++ b/ActiveSync/iCalToDo+ActiveSync.m @@ -54,9 +54,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - (NSString *) activeSyncRepresentationInContext: (WOContext *) context { NSMutableString *s; + NSArray *categories; id o; - int v; + int v, i; s = [NSMutableString string]; @@ -96,9 +97,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Reminder - FIXME [s appendFormat: @"%d", 0]; - // Sensitivity - FIXME - [s appendFormat: @"%d", 0]; - + if ([[self accessClass] isEqualToString: @"PRIVATE"]) + v = 2; + else if ([[self accessClass] isEqualToString: @"CONFIDENTIAL"]) + v = 3; + else + v = 0; + + categories = [self categories]; + + if ([categories count]) + { + [s appendFormat: @""]; + for (i = 0; i < [categories count]; i++) + { + [s appendFormat: @"%@", [[categories objectAtIndex: i] activeSyncRepresentationInContext: context]]; + } + [s appendFormat: @""]; + } + // Subject o = [self summary]; if ([o length]) @@ -106,11 +123,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if ((o = [self comment])) { + // It is very important here to NOT set 0 in the response, + // otherwise it'll prevent WP8 phones from sync'ing. See #3028 for details. o = [o activeSyncRepresentationInContext: context]; [s appendString: @""]; [s appendFormat: @"%d", 1]; [s appendFormat: @"%d", [o length]]; - [s appendFormat: @"%d", 0]; [s appendFormat: @"%@", o]; [s appendString: @""]; } @@ -182,6 +200,29 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [self setPriority: @"9"]; } + // + // 0- normal, 1- personal, 2- private and 3-confidential + // + if ((o = [theValues objectForKey: @"Sensitivity"])) + { + switch ([o intValue]) + { + case 2: + [self setAccessClass: @"PRIVATE"]; + break; + case 3: + [self setAccessClass: @"CONFIDENTIAL"]; + break; + case 0: + case 1: + default: + [self setAccessClass: @"PUBLIC"]; + } + } + + // Categories + if ((o = [theValues objectForKey: @"Categories"]) && [o length]) + [self setCategories: o]; if ((o = [theValues objectForKey: @"ReminderTime"])) { diff --git a/ChangeLog b/ChangeLog index 35fe46f96..66b979fc8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,321 @@ +commit fcddb37a0dd76f75bb16f9380e8b8a01865d0c4c +Author: Ludovic Marcotte +Date: Thu Dec 18 08:53:31 2014 -0500 + + Fixed typo and bumped for release + +M NEWS + +commit 8e79d4f88ba2b9603f589e1ee300164647217c8d +Author: Francis Lachapelle +Date: Thu Dec 18 08:51:50 2014 -0500 + + Update translations + +M NEWS +M UI/PreferencesUI/Czech.lproj/Localizable.strings +M UI/PreferencesUI/Dutch.lproj/Localizable.strings +M UI/PreferencesUI/Finnish.lproj/Localizable.strings +M UI/PreferencesUI/German.lproj/Localizable.strings +M UI/PreferencesUI/Polish.lproj/Localizable.strings +M UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings + +commit f8ab3d5a427c17d647286bda441d63a98481fdbe +Author: Francis Lachapelle +Date: Thu Dec 18 08:45:27 2014 -0500 + + Update NEWS file + +M NEWS + +commit 8aea5f232e049024047726305a55580807d5e54b +Author: Ludovic Marcotte +Date: Wed Dec 17 16:18:39 2014 -0500 + + Fix for bug #3028 + +M ActiveSync/iCalEvent+ActiveSync.m +M ActiveSync/iCalToDo+ActiveSync.m +M NEWS + +commit 493c366ac42481d99c014eb9746b38bd43d726a2 +Author: Ludovic Marcotte +Date: Wed Dec 17 15:26:02 2014 -0500 + + Fix for bug #3003 + +M ActiveSync/SOGoMailObject+ActiveSync.m +M NEWS + +commit cd70bec9faf8f29159eafb3ae1e63ab9d7a76919 +Author: Ludovic Marcotte +Date: Wed Dec 17 13:57:49 2014 -0500 + + Fix for bug #3008 + +M NEWS +M SoObjects/Appointments/SOGoAppointmentFolder.m + +commit 6027cb50d4d1d351935ef924d957d5876e73d026 +Author: Francis Lachapelle +Date: Tue Dec 16 16:13:59 2014 -0500 + + Fix CSS and templates of component/apptmt editor + +M UI/Templates/SchedulerUI/UIxComponentEditor.wox +M UI/WebServerResources/UIxAppointmentEditor.css +M UI/WebServerResources/UIxComponentEditor.css + +commit eb4d20e50f109e20d4d040b56fecf27513c46c2c +Author: Ludovic Marcotte +Date: Tue Dec 16 13:27:01 2014 -0500 + + Privacy and categories support for tasks + +M ActiveSync/iCalToDo+ActiveSync.m + +commit 70dbcf0418d24f68232383186cb8443ffc269c8b +Author: Ludovic Marcotte +Date: Tue Dec 16 13:24:10 2014 -0500 + + Fix for MeetingStatus applied yesterday + +M ActiveSync/iCalEvent+ActiveSync.m + +commit bfaaf6eb3436cbfca5e8ede5963cd53012eff92a +Author: Ludovic Marcotte +Date: Tue Dec 16 13:17:59 2014 -0500 + + Revert back to using GNUSTEP_SYSTEM_LIBRARIES for teststrings + +M Tests/Integration/GNUmakefile.preamble + +commit 68655deabd942718c318feeb29e407a4c5b499c2 +Author: Ludovic Marcotte +Date: Tue Dec 16 12:36:08 2014 -0500 + + Improved rpath handling (#2996) + +M ActiveSync/common.make +M Main/GNUmakefile +M Tests/Integration/GNUmakefile.preamble +M Tools/GNUmakefile +M configure + +commit 7380b25b7596e58775afb4203dfa35bd5dd29c4f +Author: Ludovic Marcotte +Date: Tue Dec 16 10:55:12 2014 -0500 + + Be a bit more verbose regarding IMAP requirements for EAS + +M Documentation/SOGoInstallationGuide.asciidoc + +commit cbfb927ec91677ed75f3307d8e41b92a59ec45dd +Author: Ludovic Marcotte +Date: Tue Dec 16 10:42:18 2014 -0500 + + Properly escape Attendee's email address in case it contains crap + +M ActiveSync/iCalEvent+ActiveSync.m + +commit 0f2798f017c340cdf1000b771e5e74c2372562fc +Author: Ludovic Marcotte +Date: Tue Dec 16 10:35:32 2014 -0500 + + Lucid fixes for EAS + +M ActiveSync/SOGoActiveSyncDispatcher.m +M NEWS +M Tools/SOGoToolManageEAS.m + +commit e040805e0d56458d825102fb5920f381026bf09d +Author: Francis Lachapelle +Date: Tue Dec 16 10:07:08 2014 -0500 + + Fix display of dialog in RO appointment editor + +M UI/Templates/SchedulerUI/UIxComponentEditor.wox +M UI/WebServerResources/UIxAppointmentEditor.css + +commit fb6ef3aa8a8d00db1cb00dbdb51b43c51ae3a438 +Author: Ludovic Marcotte +Date: Tue Dec 16 09:20:27 2014 -0500 + + Now possible to set alarms on event invitations + +M ActiveSync/SOGoActiveSyncDispatcher.m +M NEWS +M SoObjects/Appointments/GNUmakefile +M SoObjects/Appointments/SOGoAppointmentObject.h +M SoObjects/Appointments/SOGoAppointmentObject.m +M SoObjects/Appointments/SOGoComponentOccurence.h +M SoObjects/Appointments/SOGoComponentOccurence.m +A SoObjects/Appointments/iCalAlarm+SOGo.h +A SoObjects/Appointments/iCalAlarm+SOGo.m +M UI/MailPartViewers/UIxMailPartICalActions.m +M UI/Scheduler/GNUmakefile +D UI/Scheduler/Toolbars/SOGoAppointmentObjectAccept.toolbar +D UI/Scheduler/Toolbars/SOGoAppointmentObjectAcceptOrDecline.toolbar +D UI/Scheduler/Toolbars/SOGoAppointmentObjectDecline.toolbar +M UI/Scheduler/UIxAppointmentEditor.m +M UI/Scheduler/UIxComponentEditor.h +M UI/Scheduler/UIxComponentEditor.m +M UI/Scheduler/product.plist +M UI/Templates/SchedulerUI/UIxAppointmentEditor.wox +M UI/Templates/SchedulerUI/UIxComponentEditor.wox +M UI/WebServerResources/SchedulerUI.js +M UI/WebServerResources/UIxAppointmentEditor.js +M UI/WebServerResources/UIxComponentEditor.js + +commit 3eac0f5261bc4bc4aa4c65547d93d29771fe816e +Author: Ludovic Marcotte +Date: Mon Dec 15 19:37:04 2014 -0500 + + Improved NEWS file + +M NEWS + +commit f29a74f82f437ffce96ac15f1ab604218f0a22d6 +Author: Ludovic Marcotte +Date: Mon Dec 15 19:34:51 2014 -0500 + + avoid testing for IMAP ANNOTATION when X-GUID is available + +M NEWS +M SoObjects/Mailer/SOGoMailAccount.m + +commit 20b3d4882d1675be4a7cbed58f99c2f4d9c96f8f +Author: Ludovic Marcotte +Date: Mon Dec 15 19:27:21 2014 -0500 + + MeetingStatus fix affecting iOS devices + +M ActiveSync/iCalEvent+ActiveSync.m +M NEWS + +commit 175b99b9ee69c08a279fa28f585a6bfd040556dd +Author: Ludovic Marcotte +Date: Mon Dec 15 19:25:38 2014 -0500 + + Updated NEWS regarding previous commit + +M NEWS + +commit e79ea7583415b8892e772d92abb55c8ebf688285 +Author: Ludovic Marcotte +Date: Mon Dec 15 19:24:10 2014 -0500 + + Categories and privacy fixes for bug #3022 + +M ActiveSync/iCalEvent+ActiveSync.m + +commit 9cb1f8097c5e195152e729c5052b88e80ceb064a +Author: Ludovic Marcotte +Date: Mon Dec 15 19:23:04 2014 -0500 + + Fix sogo-tool when cleaning up devices + +M Tools/SOGoToolManageEAS.m + +commit 54dabb6861ff32509b084ddcfdd3e9ebabf034fb +Author: Francis Lachapelle +Date: Mon Dec 15 10:52:04 2014 -0500 + + Update CKEditor to version 4.4.6 (+ sourcearea) + + Added an enabled the 'Source Area' plugin to be able to edit the HTML + code directly. + +M NEWS +M UI/WebServerResources/UIxPreferences.js +M UI/WebServerResources/ckeditor/build-config.js +M UI/WebServerResources/ckeditor/ckeditor.js +M UI/WebServerResources/ckeditor/config.js +M UI/WebServerResources/ckeditor/lang/ar.js +M UI/WebServerResources/ckeditor/lang/ca.js +M UI/WebServerResources/ckeditor/lang/cs.js +M UI/WebServerResources/ckeditor/lang/cy.js +M UI/WebServerResources/ckeditor/lang/da.js +M UI/WebServerResources/ckeditor/lang/de.js +M UI/WebServerResources/ckeditor/lang/en.js +M UI/WebServerResources/ckeditor/lang/es.js +M UI/WebServerResources/ckeditor/lang/fi.js +M UI/WebServerResources/ckeditor/lang/fr.js +M UI/WebServerResources/ckeditor/lang/hu.js +M UI/WebServerResources/ckeditor/lang/is.js +M UI/WebServerResources/ckeditor/lang/it.js +M UI/WebServerResources/ckeditor/lang/nb.js +M UI/WebServerResources/ckeditor/lang/nl.js +M UI/WebServerResources/ckeditor/lang/no.js +M UI/WebServerResources/ckeditor/lang/pl.js +M UI/WebServerResources/ckeditor/lang/pt-br.js +M UI/WebServerResources/ckeditor/lang/ru.js +M UI/WebServerResources/ckeditor/lang/sk.js +M UI/WebServerResources/ckeditor/lang/sv.js +M UI/WebServerResources/ckeditor/lang/uk.js +M UI/WebServerResources/ckeditor/plugins/icons.png +M UI/WebServerResources/ckeditor/plugins/icons_hidpi.png +M UI/WebServerResources/ckeditor/plugins/scayt/dialogs/options.js +M UI/WebServerResources/ckeditor/plugins/wsc/dialogs/wsc.js +M UI/WebServerResources/ckeditor/skins/moono/editor.css +M UI/WebServerResources/ckeditor/skins/moono/editor_gecko.css +M UI/WebServerResources/ckeditor/skins/moono/editor_ie.css +M UI/WebServerResources/ckeditor/skins/moono/editor_ie7.css +M UI/WebServerResources/ckeditor/skins/moono/editor_ie8.css +M UI/WebServerResources/ckeditor/skins/moono/editor_iequirks.css +M UI/WebServerResources/ckeditor/skins/moono/icons.png +M UI/WebServerResources/ckeditor/skins/moono/icons_hidpi.png + +commit 6526d587f2726d6087b15e893e619bdcec7412bf +Author: Francis Lachapelle +Date: Thu Dec 11 14:13:56 2014 -0500 + + Localization + +M UI/PreferencesUI/French.lproj/Localizable.strings + +commit 9dd0d6c427a44f089cedef256899fed367899733 +Author: Ludovic Marcotte +Date: Thu Dec 11 13:31:32 2014 -0500 + + Draft autossave feature + +M NEWS +M SoObjects/Mailer/SOGoDraftObject.m +M SoObjects/SOGo/SOGoDefaults.plist +M SoObjects/SOGo/SOGoDomainDefaults.h +M SoObjects/SOGo/SOGoDomainDefaults.m +M SoObjects/SOGo/SOGoUserDefaults.h +M SoObjects/SOGo/SOGoUserDefaults.m +M UI/MailerUI/UIxMailMainFrame.m +M UI/PreferencesUI/English.lproj/Localizable.strings +M UI/PreferencesUI/UIxPreferences.m +M UI/Templates/MailerUI/UIxMailEditor.wox +M UI/Templates/PreferencesUI/UIxPreferences.wox +M UI/WebServerResources/UIxMailEditor.js + +commit 990f782b627ed956755b727ef3b043d5d66cb14c +Author: Ludovic Marcotte +Date: Thu Dec 11 10:01:21 2014 -0500 + + Allow including or not freebusy info from subscribed calendars + +M NEWS +M SoObjects/Appointments/SOGoAppointmentFolder.m +M SoObjects/Appointments/SOGoFreeBusyObject.h +M SoObjects/Appointments/SOGoFreeBusyObject.m +M UI/Scheduler/UIxCalendarProperties.h +M UI/Scheduler/UIxCalendarProperties.m +M UI/Templates/SchedulerUI/UIxCalendarProperties.wox + +commit c8b74686cc0c656b2a0330c28d299a793417035e +Author: Ludovic Marcotte +Date: Wed Dec 10 15:22:32 2014 -0500 + + Update ChangeLog + +M ChangeLog + commit 16bf299b3b9011d151a598ee06e02e6a96f2249d Author: Ludovic Marcotte Date: Wed Dec 10 15:22:01 2014 -0500 diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index 45c4fc943..36e6e65ba 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -80,6 +80,12 @@ and others)  * SMTP server (Postfix, Sendmail and others) * IMAP server (Courier, Cyrus IMAP Server, Dovecot and others) +If you plan to use ActiveSync, an IMAP server supporting the ACL, +UIDPLUS, QRESYNC, ANNOTATE (or X-GUID) IMAP extensions is required, +such as Cyrus IMAP version 2.4 or later, or Dovecot version +2.1 or later. If your current IMAP server does not support these +extensions, you can use Dovecot's proxying capabilities. + In this guide, we assume that all those components are running on the same server (i.e., `localhost` or `127.0.0.1`) that SOGo will be installed on. diff --git a/Documentation/docinfo.xml b/Documentation/docinfo.xml index 6b2307a19..247cd8a9b 100644 --- a/Documentation/docinfo.xml +++ b/Documentation/docinfo.xml @@ -1,7 +1,7 @@ -Version 2.2.11 - December 2014 -for version 2.2.11a -2014-12-10 +Version 2.2.12 - December 2014 +for version 2.2.12 +2014-12-18 Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License". diff --git a/Documentation/includes/global-attributes.asciidoc b/Documentation/includes/global-attributes.asciidoc index b6fb5b222..064ce5b49 100644 --- a/Documentation/includes/global-attributes.asciidoc +++ b/Documentation/includes/global-attributes.asciidoc @@ -13,6 +13,6 @@ // TODO have the build system take care of this -:release_version: 2.2.11a +:release_version: 2.2.12 // vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab: diff --git a/Main/GNUmakefile b/Main/GNUmakefile index 33a40230d..ed6b61051 100644 --- a/Main/GNUmakefile +++ b/Main/GNUmakefile @@ -6,7 +6,7 @@ include ../Version ADDITIONAL_OBJCFLAGS += -fPIE ADDITIONAL_INCLUDE_DIRS += -ADDITIONAL_LDFLAGS += -Wl,--no-as-needed -fPIE -pie -Wl,--rpath,$(GNUSTEP_SYSTEM_LIBRARIES)/sogo +ADDITIONAL_LDFLAGS += -Wl,--no-as-needed -fPIE -pie -Wl,--rpath,$(SOGO_SYSLIBDIR)/sogo SOGOD = sogod TOOL_NAME = $(SOGOD) diff --git a/NEWS b/NEWS index 2b9d9616b..47e378896 100644 --- a/NEWS +++ b/NEWS @@ -1,8 +1,29 @@ +2.2.12 (2014-12-18) +------------------- + +New features + - allow including or not freebusy info from subscribed calendars + - now possible to set an autosave timer for draft messages + - now possible to set alarms on event invitations (#76) + +Enhancements + - updated CKEditor to version 4.4.6 and added the 'Source Area' plugin + - avoid testing for IMAP ANNOTATION when X-GUID is available (#3018) + - updated Czech, Dutch, Finnish, French, German, Polish and Spanish (Spain) translations + +Bug fixes + - fix for privacy and categories for EAS (#3022) + - correctly set MeetingStatus for EAS on iOS devices + - Ubuntu Lucid fixes for EAS + - fix calendar reminders for future events (#3008) + - make sure all text parts are UTF-8 re-encoded for Outlook 2013 over EAS (#3003) + - fix task description truncation affecting WP8 phones over EAS (#3028) + 2.2.11a (2014-12-10) -------------------- Bug fixes - - Make sure all address books returned using EAS are GCS ones + - make sure all address books returned using EAS are GCS ones 2.2.11 (2014-12-09) ------------------- @@ -11,16 +32,16 @@ New features - sogo-tool can now be used to manage EAS metadata for all devices Enhancements - - Improved the SAML2 documentation - - Radically reduced AES memory usage + - improved the SAML2 documentation + - radically reduced AES memory usage Bug fixes - - Now possible to specify the username attribute for SAML2 (SOGoSAML2LoginAttribute) (#2381) - - Added support for IdP-initiated SAML2 logout (#2377) - - We now generate SAML2 metadata on the fly (#2378) - - We now handle correctly the SOGo logout when using SAML (#2376 and #2379) - - Fixed freebusy lookups going off bounds for resources (#3010) - - Fixed EAS clients moving mails between folders but disconnecting before receiving server's response (#2982) + - now possible to specify the username attribute for SAML2 (SOGoSAML2LoginAttribute) (#2381) + - added support for IdP-initiated SAML2 logout (#2377) + - we now generate SAML2 metadata on the fly (#2378) + - we now handle correctly the SOGo logout when using SAML (#2376 and #2379) + - fixed freebusy lookups going off bounds for resources (#3010) + - fixed EAS clients moving mails between folders but disconnecting before receiving server's response (#2982) 2.2.10 (2014-11-21) ------------------- diff --git a/SoObjects/Appointments/GNUmakefile b/SoObjects/Appointments/GNUmakefile index 5d0a40152..55c66a1bd 100644 --- a/SoObjects/Appointments/GNUmakefile +++ b/SoObjects/Appointments/GNUmakefile @@ -9,6 +9,7 @@ Appointments_PRINCIPAL_CLASS = SOGoAppointmentsProduct Appointments_OBJC_FILES = \ Product.m \ NSArray+Appointments.m \ + iCalAlarm+SOGo.m \ iCalCalendar+SOGo.m \ iCalEntityObject+SOGo.m \ iCalRepeatableEntityObject+SOGo.m \ diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 0b5399107..a01c10c62 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -487,15 +487,28 @@ static Class iCalEventK = nil; inCategory: @"FolderSynchronize"]; } +// +// If the user is the owner of the calendar, by default we include the freebusy information. +// +// If the user is NOT the owner of the calendar, by default we exclude the freebusy information. +// - (BOOL) includeInFreeBusy { NSNumber *excludeFromFreeBusy; - + NSString *userLogin; + BOOL is_owner; + + userLogin = [[context activeUser] login]; + is_owner = [userLogin isEqualToString: [self ownerInContext: context]]; + // Check if the owner (not the active user) has excluded the calendar from her/his free busy data. excludeFromFreeBusy = [self folderPropertyValueInCategory: @"FreeBusyExclusions" - forUser: [SOGoUser userWithLogin: [self ownerInContext: context]]]; + forUser: [SOGoUser userWithLogin: userLogin]]; + if (!excludeFromFreeBusy && !is_owner) + return NO; + return ![excludeFromFreeBusy boolValue]; } @@ -2818,8 +2831,8 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir return nil; } - sql = [NSString stringWithFormat: @"((c_nextalarm <= %u) AND (c_nextalarm >= %u)) OR ((c_nextalarm > 0) AND (c_enddate > %u))", - [_endUTCDate unsignedIntValue], [_startUTCDate unsignedIntValue], [_startUTCDate unsignedIntValue]]; + sql = [NSString stringWithFormat: @"((c_nextalarm <= %u) AND (c_nextalarm >= %u)) OR ((c_nextalarm > 0) AND (c_nextalarm <= %u) AND (c_enddate > %u))", + [_endUTCDate unsignedIntValue], [_startUTCDate unsignedIntValue], [_startUTCDate unsignedIntValue], [_startUTCDate unsignedIntValue]]; qualifier = [EOQualifier qualifierWithQualifierFormat: sql]; records = [folder fetchFields: nameFields matchingQualifier: qualifier]; diff --git a/SoObjects/Appointments/SOGoAppointmentObject.h b/SoObjects/Appointments/SOGoAppointmentObject.h index 6abfb6c0d..8ab78d0a0 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.h +++ b/SoObjects/Appointments/SOGoAppointmentObject.h @@ -30,6 +30,7 @@ @class WORequest; +@class iCalAlarm; @class iCalEvent; @class iCalCalendar; @@ -38,9 +39,12 @@ @interface SOGoAppointmentObject : SOGoCalendarComponent - (NSException *) changeParticipationStatus: (NSString *) status - withDelegate: (iCalPerson *) delegate; + withDelegate: (iCalPerson *) delegate + alarm: (iCalAlarm *) alarm; + - (NSException *) changeParticipationStatus: (NSString *) status withDelegate: (iCalPerson *) delegate + alarm: (iCalAlarm *) alarm forRecurrenceId: (NSCalendarDate *) _recurrenceId; // diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index b10ce0c59..f4ece14de 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -1309,6 +1309,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent return ex; } +// +// +// - (NSDictionary *) _caldavSuccessCodeWithRecipient: (NSString *) recipient { NSMutableArray *element; @@ -1391,9 +1394,11 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // - (NSException *) changeParticipationStatus: (NSString *) status withDelegate: (iCalPerson *) delegate + alarm: (iCalAlarm *) alarm { return [self changeParticipationStatus: status withDelegate: delegate + alarm: alarm forRecurrenceId: nil]; } @@ -1402,6 +1407,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // - (NSException *) changeParticipationStatus: (NSString *) _status withDelegate: (iCalPerson *) delegate + alarm: (iCalAlarm *) alarm forRecurrenceId: (NSCalendarDate *) _recurrenceId { iCalCalendar *calendar; @@ -1479,7 +1485,18 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // Over DAV, it'll be handled directly in PUTAction: if (![context request] || [[context request] handledByDefaultHandler] || [[[context request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"]) - ex = [self saveCalendar: [event parent]]; + { + // If an alarm was specified, let's use it. This would happen if an attendee accepts/declines/etc. an + // event invitation and also sets an alarm along the way. This would happen ONLY from the web interface. + [event removeAllAlarms]; + + if (alarm) + { + [event addToAlarms: alarm]; + } + + ex = [self saveCalendar: [event parent]]; + } } } else @@ -1512,17 +1529,17 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent [v caseInsensitiveCompare: @"CLIENT"] == NSOrderedSame) b = NO; } - - // - // If we have to deal with Thunderbird/Lightning, we always send invitation - // reponses, as Lightning v2.6 (at least this version) sets SCHEDULE-AGENT - // to NONE/CLIENT when responding to an external invitation received by - // SOGo - so no invitation responses are ever sent by Lightning. See - // https://bugzilla.mozilla.org/show_bug.cgi?id=865726 and - // https://bugzilla.mozilla.org/show_bug.cgi?id=997784 - // - userAgents = [[context request] headersForKey: @"User-Agent"]; - + + // + // If we have to deal with Thunderbird/Lightning, we always send invitation + // reponses, as Lightning v2.6 (at least this version) sets SCHEDULE-AGENT + // to NONE/CLIENT when responding to an external invitation received by + // SOGo - so no invitation responses are ever sent by Lightning. See + // https://bugzilla.mozilla.org/show_bug.cgi?id=865726 and + // https://bugzilla.mozilla.org/show_bug.cgi?id=997784 + // + userAgents = [[context request] headersForKey: @"User-Agent"]; + for (i = 0; i < [userAgents count]; i++) { if ([[userAgents objectAtIndex: i] rangeOfString: @"Thunderbird"].location != NSNotFound && @@ -1533,7 +1550,6 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent } } - return b; } @@ -1594,7 +1610,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent { // The current user deletes the occurence; let the organizer know that // the user has declined this occurence. - [self changeParticipationStatus: @"DECLINED" withDelegate: nil + [self changeParticipationStatus: @"DECLINED" + withDelegate: nil + alarm: nil forRecurrenceId: recurrenceId]; send_receipt = NO; } @@ -2161,12 +2179,14 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent { [self changeParticipationStatus: @"DECLINED" withDelegate: nil // FIXME (specify delegate?) + alarm: nil forRecurrenceId: [self _addedExDate: oldEvent newEvent: newEvent]]; } else if (attendee) { [self changeParticipationStatus: [attendee partStat] withDelegate: delegate + alarm: nil forRecurrenceId: recurrenceId]; } // All attendees and the organizer field were removed. Apple iCal does diff --git a/SoObjects/Appointments/SOGoComponentOccurence.h b/SoObjects/Appointments/SOGoComponentOccurence.h index 68b1d5369..ed77dd96c 100644 --- a/SoObjects/Appointments/SOGoComponentOccurence.h +++ b/SoObjects/Appointments/SOGoComponentOccurence.h @@ -25,6 +25,7 @@ @class NSException; +@class iCalAlarm; @class iCalCalendar; @class iCalPerson; @class iCalRepeatableEntityObject; @@ -37,7 +38,8 @@ - (BOOL) isNew; - (NSException *) changeParticipationStatus: (NSString *) newPartStat - withDelegate: (iCalPerson *) delegate; + withDelegate: (iCalPerson *) delegate + alarm: (iCalAlarm *) alarm; @end diff --git a/SoObjects/Appointments/SOGoComponentOccurence.m b/SoObjects/Appointments/SOGoComponentOccurence.m index 3b829d15e..f81a73f5a 100644 --- a/SoObjects/Appointments/SOGoComponentOccurence.m +++ b/SoObjects/Appointments/SOGoComponentOccurence.m @@ -215,6 +215,7 @@ they should be siblings or SOGoComponentOccurence the parent class of SOGoCalendarComponent... - (NSException *) changeParticipationStatus: (NSString *) newStatus withDelegate: (iCalPerson *) delegate + alarm: (iCalAlarm *) alarm { NSCalendarDate *date; @@ -222,6 +223,7 @@ return [container changeParticipationStatus: newStatus withDelegate: delegate + alarm: alarm forRecurrenceId: date]; } diff --git a/SoObjects/Appointments/SOGoFreeBusyObject.h b/SoObjects/Appointments/SOGoFreeBusyObject.h index 77b2f53ec..17f56dd51 100644 --- a/SoObjects/Appointments/SOGoFreeBusyObject.h +++ b/SoObjects/Appointments/SOGoFreeBusyObject.h @@ -1,6 +1,5 @@ /* - Copyright (C) 2007-2012 Inverse inc. - Copyright (C) 2000-2004 SKYRIX Software AG + Copyright (C) 2007-2014 Inverse inc. This file is part of SOGo diff --git a/SoObjects/Appointments/SOGoFreeBusyObject.m b/SoObjects/Appointments/SOGoFreeBusyObject.m index 18f08e10b..6624a2cfe 100644 --- a/SoObjects/Appointments/SOGoFreeBusyObject.m +++ b/SoObjects/Appointments/SOGoFreeBusyObject.m @@ -1,6 +1,5 @@ /* - Copyright (C) 2007-2012 Inverse inc. - Copyright (C) 2000-2004 SKYRIX Software AG + Copyright (C) 2007-2014 Inverse inc. This file is part of SOGo @@ -339,7 +338,7 @@ for (count = 0; count < max; count++) { calFolder = [folders objectAtIndex: count]; - if (![calFolder isSubscription] && [calFolder includeInFreeBusy]) + if ([calFolder includeInFreeBusy]) [infos addObjectsFromArray: [calFolder fetchFreeBusyInfosFrom: startDate to: endDate]]; } diff --git a/SoObjects/Appointments/iCalAlarm+SOGo.h b/SoObjects/Appointments/iCalAlarm+SOGo.h new file mode 100644 index 000000000..2980f7bc6 --- /dev/null +++ b/SoObjects/Appointments/iCalAlarm+SOGo.h @@ -0,0 +1,37 @@ +/* iCalAlarm+SOGo.h - this file is part of SOGo + * + * Copyright (C) 2014 Inverse inc. + * + * This file 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 2, or (at your option) + * any later version. + * + * This file 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import + +@class iCalRepeatableEntityObject; + +@interface iCalAlarm (SOGoExtensions) + ++ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity + owner: (NSString *) theOwner + action: (NSString *) reminderAction + unit: (NSString *) reminderUnit + quantity: (NSString *) reminderQuantity + reference: (NSString *) reminderReference + reminderRelation: (NSString *) reminderRelation + emailAttendees: (BOOL) reminderEmailAttendees + emailOrganizer: (BOOL) reminderEmailOrganizer; + +@end diff --git a/SoObjects/Appointments/iCalAlarm+SOGo.m b/SoObjects/Appointments/iCalAlarm+SOGo.m new file mode 100644 index 000000000..c98c316db --- /dev/null +++ b/SoObjects/Appointments/iCalAlarm+SOGo.m @@ -0,0 +1,127 @@ +/* iCalAlarm+SOGo.m - this file is part of SOGo + * + * Copyright (C) 2014 Inverse inc. + * + * This file 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 2, or (at your option) + * any later version. + * + * This file 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import "iCalAlarm+SOGo.h" + +#import +#import +#import + +#import + +#import +#import + +@implementation iCalAlarm (SOGoExtensions) + +- (void) _appendAttendees: (NSArray *) attendees + toEmailAlarm: (iCalAlarm *) alarm +{ + NSMutableArray *aAttendees; + int count, max; + iCalPerson *currentAttendee, *aAttendee; + + max = [attendees count]; + aAttendees = [NSMutableArray arrayWithCapacity: max]; + for (count = 0; count < max; count++) + { + currentAttendee = [attendees objectAtIndex: count]; + aAttendee = [iCalPerson elementWithTag: @"attendee"]; + [aAttendee setCn: [currentAttendee cn]]; + [aAttendee setEmail: [currentAttendee rfc822Email]]; + [aAttendees addObject: aAttendee]; + } + [alarm setAttendees: aAttendees]; +} + +- (void) _appendOrganizerToEmailAlarm: (iCalAlarm *) alarm + owner: (NSString *) uid +{ + NSDictionary *ownerIdentity; + iCalPerson *aAttendee; + + ownerIdentity = [[SOGoUser userWithLogin: uid roles: nil] + defaultIdentity]; + aAttendee = [iCalPerson elementWithTag: @"attendee"]; + [aAttendee setCn: [ownerIdentity objectForKey: @"fullName"]]; + [aAttendee setEmail: [ownerIdentity objectForKey: @"email"]]; + [alarm addChild: aAttendee]; +} + ++ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity + owner: (NSString *) theOwner + action: (NSString *) reminderAction + unit: (NSString *) reminderUnit + quantity: (NSString *) reminderQuantity + reference: (NSString *) reminderReference + reminderRelation: (NSString *) reminderRelation + emailAttendees: (BOOL) reminderEmailAttendees + emailOrganizer: (BOOL) reminderEmailOrganizer +{ + iCalTrigger *aTrigger; + iCalAlarm *anAlarm; + NSString *aValue; + + anAlarm = [[self alloc] init]; + + aTrigger = [iCalTrigger elementWithTag: @"TRIGGER"]; + [aTrigger setValueType: @"DURATION"]; + [anAlarm setTrigger: aTrigger]; + + if ([reminderAction length] > 0 && [reminderUnit length] > 0) + { + [anAlarm setAction: [reminderAction uppercaseString]]; + if ([reminderAction isEqualToString: @"email"]) + { + [anAlarm removeAllAttendees]; + if (reminderEmailAttendees) + [anAlarm _appendAttendees: [theEntity attendees] + toEmailAlarm: anAlarm]; + if (reminderEmailOrganizer) + [anAlarm _appendOrganizerToEmailAlarm: anAlarm owner: theOwner]; + [anAlarm setSummary: [theEntity summary]]; + [anAlarm setComment: [theEntity comment]]; + } + + if ([reminderReference caseInsensitiveCompare: @"BEFORE"] == NSOrderedSame) + aValue = [NSString stringWithString: @"-P"]; + else + aValue = [NSString stringWithString: @"P"]; + + if ([reminderUnit caseInsensitiveCompare: @"MINUTES"] == NSOrderedSame || + [reminderUnit caseInsensitiveCompare: @"HOURS"] == NSOrderedSame) + aValue = [aValue stringByAppendingString: @"T"]; + + aValue = [aValue stringByAppendingFormat: @"%i%@", + [reminderQuantity intValue], + [reminderUnit substringToIndex: 1]]; + [aTrigger setSingleValue: aValue forKey: @""]; + [aTrigger setRelationType: reminderRelation]; + } + else + { + [anAlarm release]; + anAlarm = nil; + } + + return AUTORELEASE(anAlarm); +} + +@end diff --git a/SoObjects/Mailer/SOGoDraftObject.m b/SoObjects/Mailer/SOGoDraftObject.m index 24b84e8bd..c70e56c09 100644 --- a/SoObjects/Mailer/SOGoDraftObject.m +++ b/SoObjects/Mailer/SOGoDraftObject.m @@ -629,7 +629,7 @@ static NSString *userAgent = nil; folder = [imap4 imap4FolderNameForURL: [container imap4URL]]; result = [client append: message toFolder: folder - withFlags: [NSArray arrayWithObjects: @"seen", @"draft", nil]]; + withFlags: [NSArray arrayWithObjects: @"draft", nil]]; if ([[result objectForKey: @"result"] boolValue]) { if (IMAP4ID > -1) diff --git a/SoObjects/Mailer/SOGoMailAccount.m b/SoObjects/Mailer/SOGoMailAccount.m index a7c492f20..772c49ecd 100644 --- a/SoObjects/Mailer/SOGoMailAccount.m +++ b/SoObjects/Mailer/SOGoMailAccount.m @@ -673,7 +673,7 @@ static NSString *inboxFolderName = @"INBOX"; NSString *guid; id object; - BOOL subscribedOnly; + BOOL subscribedOnly, hasAnnotatemore; ud = [[context activeUser] userDefaults]; subscribedOnly = [ud mailShowSubscribedFoldersOnly]; @@ -685,14 +685,21 @@ static NSString *inboxFolderName = @"INBOX"; client = [[self imap4Connection] client]; namespaceDict = [client namespace]; + hasAnnotatemore = [self hasCapability: @"annotatemore"]; - result = [client annotation: @"*" entryName: @"/comment" attributeName: @"value.priv"]; + if (hasAnnotatemore) + result = [client annotation: @"*" entryName: @"/comment" attributeName: @"value.priv"]; + else + result = [client lstatus: @"*" flags: [NSArray arrayWithObjects: @"x-guid", nil]]; e = [folderList objectEnumerator]; while ((object = [e nextObject])) { - guid = [[[[result objectForKey: @"FolderList"] objectForKey: [object substringFromIndex: 1]] objectForKey: @"/comment"] objectForKey: @"value.priv"]; + if (hasAnnotatemore) + guid = [[[[result objectForKey: @"FolderList"] objectForKey: [object substringFromIndex: 1]] objectForKey: @"/comment"] objectForKey: @"value.priv"]; + else + guid = [[[result objectForKey: @"status"] objectForKey: [object substringFromIndex: 1]] objectForKey: @"x-guid"]; if (!guid) { @@ -708,22 +715,13 @@ static NSString *inboxFolderName = @"INBOX"; nresult = [client annotation: [object substringFromIndex: 1] entryName: @"/comment" attributeName: @"value.priv" attributeValue: guid]; if (![[nresult objectForKey: @"result"] boolValue]) - { - // Need to implement X-GUID query for Dovecot - this requires modification in SOPE to support following command: - // 1 list "" "*" return (status (x-guid)) -> this would avoid firing a command per folder to IMAP - nresult = [client status: [object substringFromIndex: 1] flags: [NSArray arrayWithObject: @"x-guid"]]; - guid = [nresult objectForKey: @"x-guid"]; - if (!guid) - { - guid = [NSString stringWithFormat: @"%@", [object substringFromIndex: 1]]; - } - } + guid = [NSString stringWithFormat: @"%@", [object substringFromIndex: 1]]; } - + [folders setObject: [NSString stringWithFormat: @"folder%@", guid] forKey: [NSString stringWithFormat: @"folder%@", [object substringFromIndex: 1]]]; } - + return folders; } diff --git a/SoObjects/SOGo/SOGoDefaults.plist b/SoObjects/SOGo/SOGoDefaults.plist index 17388f261..d0d230c43 100644 --- a/SoObjects/SOGo/SOGoDefaults.plist +++ b/SoObjects/SOGo/SOGoDefaults.plist @@ -68,6 +68,8 @@ SOGoDraftsFolderName = "Drafts"; SOGoTrashFolderName = "Trash"; + SOGoMailAutoSave = "5"; + SOGoCalendarDefaultCategoryColor = "#aaa"; SOGoCalendarShouldDisplayWeekend = YES; SOGoCalendarEventsDefaultClassification = "PUBLIC"; diff --git a/SoObjects/SOGo/SOGoDomainDefaults.h b/SoObjects/SOGo/SOGoDomainDefaults.h index 00a79cfe9..4743917af 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.h +++ b/SoObjects/SOGo/SOGoDomainDefaults.h @@ -1,8 +1,6 @@ /* SOGoDomainDefaults.h - this file is part of SOGo * - * Copyright (C) 2009-2013 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2009-2014 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/SoObjects/SOGo/SOGoDomainDefaults.m b/SoObjects/SOGo/SOGoDomainDefaults.m index 1a577c3f6..35a078b3f 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.m +++ b/SoObjects/SOGo/SOGoDomainDefaults.m @@ -2,8 +2,6 @@ * * Copyright (C) 2009-2014 Inverse inc. * - * Author: Wolfgang Sourdeau - * * This file 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 2, or (at your option) diff --git a/SoObjects/SOGo/SOGoUserDefaults.h b/SoObjects/SOGo/SOGoUserDefaults.h index 4cb893b8d..66dbcf5fb 100644 --- a/SoObjects/SOGo/SOGoUserDefaults.h +++ b/SoObjects/SOGo/SOGoUserDefaults.h @@ -1,6 +1,6 @@ /* SOGoUserDefaults.h - this file is part of SOGo * - * Copyright (C) 2011-2013 Inverse inc. + * Copyright (C) 2011-2014 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -126,6 +126,9 @@ extern NSString *SOGoWeekStartFirstFullWeek; - (void) setMailDisplayRemoteInlineImages: (NSString *) newValue; - (NSString *) mailDisplayRemoteInlineImages; +- (void) setMailAutoSave: (NSString *) newValue; +- (NSString *) mailAutoSave; + - (void) setMailMessageForwarding: (NSString *) newValue; - (NSString *) mailMessageForwarding; diff --git a/SoObjects/SOGo/SOGoUserDefaults.m b/SoObjects/SOGo/SOGoUserDefaults.m index 031a3283a..5d975f146 100644 --- a/SoObjects/SOGo/SOGoUserDefaults.m +++ b/SoObjects/SOGo/SOGoUserDefaults.m @@ -1,6 +1,6 @@ /* SOGoUserDefaults.m - this file is part of SOGo * - * Copyright (C) 2009-2013 Inverse inc. + * Copyright (C) 2009-2014 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -508,7 +508,7 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek"; return [self stringForKey: @"SOGoMailComposeMessageType"]; } -- (void) setMailDisplayRemoteInlineImages: (NSString *) newValue; +- (void) setMailDisplayRemoteInlineImages: (NSString *) newValue { [self setObject: newValue forKey: @"SOGoMailDisplayRemoteInlineImages"]; } @@ -518,6 +518,23 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek"; return [self stringForKey: @"SOGoMailDisplayRemoteInlineImages"]; } +- (void) setMailAutoSave: (NSString *) newValue +{ + [self setObject: newValue forKey: @"SOGoMailAutoSave"]; +} + +- (NSString *) mailAutoSave +{ + NSString *s; + + s = [self stringForKey: @"SOGoMailAutoSave"]; + + if ([s intValue] == 0) + s = @"5"; + + return s; +} + - (void) setMailMessageForwarding: (NSString *) newValue { [self setObject: newValue forKey: @"SOGoMailMessageForwarding"]; diff --git a/Tools/GNUmakefile b/Tools/GNUmakefile index 5b1eed2e9..869fb52c6 100644 --- a/Tools/GNUmakefile +++ b/Tools/GNUmakefile @@ -4,7 +4,7 @@ include ../config.make include $(GNUSTEP_MAKEFILES)/common.make include ../Version -ADDITIONAL_LDFLAGS += -Wl,--rpath,$(GNUSTEP_SYSTEM_LIBRARIES)/sogo +ADDITIONAL_LDFLAGS += -Wl,--rpath,$(SOGO_SYSLIBDIR)/sogo ### SOGO_TOOL = sogo-tool diff --git a/Tools/SOGoToolManageEAS.m b/Tools/SOGoToolManageEAS.m index 6003adef3..0d8a7d772 100644 --- a/Tools/SOGoToolManageEAS.m +++ b/Tools/SOGoToolManageEAS.m @@ -104,17 +104,17 @@ typedef enum - (BOOL) run { - NSString *userId; - SOGoManageEASCommand cmd; + NSString *urlString, *deviceId, *userId; + NSMutableString *ocFSTableName; SOGoCacheGCSObject *oc, *foc; - NSString *urlString, *ocFSTableName, *deviceId; NSURL *folderTableURL; NSMutableArray *parts; NSArray *entries; id cacheEntry; - - BOOL rc; + + SOGoManageEASCommand cmd; int i, max; + BOOL rc; max = [sanitizedArguments count]; rc = NO; @@ -141,10 +141,12 @@ typedef enum /* If "OCSFolderInfoURL" is properly configured, we must have 5 parts in this url. We strip the '-' character in case we have this in the domain part - like foo@bar-zot.com */ - ocFSTableName = [NSString stringWithFormat: @"sogo_cache_folder_%@", - [[[user loginInDomain] asCSSIdentifier] - stringByReplacingOccurrencesOfString: @"-" - withString: @"_"]]; + ocFSTableName = [NSMutableString stringWithFormat: @"sogo_cache_folder_%@", + [[user loginInDomain] asCSSIdentifier]]; + [ocFSTableName replaceOccurrencesOfString: @"-" + withString: @"_" + options: 0 + range: NSMakeRange(0, [ocFSTableName length])]; [parts replaceObjectAtIndex: 4 withObject: ocFSTableName]; folderTableURL = [NSURL URLWithString: [parts componentsJoinedByString: @"/"]]; @@ -225,7 +227,7 @@ typedef enum NSMutableString *sql; - sql = [NSMutableString stringWithFormat: @"DELETE FROM %@" @" WHERE c_path like '/%@'", [oc tableName], deviceId]; + sql = [NSMutableString stringWithFormat: @"DELETE FROM %@" @" WHERE c_path like '/%@%'", [oc tableName], deviceId]; [oc performBatchSQLQueries: [NSArray arrayWithObject: sql]]; rc = YES; diff --git a/UI/MailPartViewers/UIxMailPartICalActions.m b/UI/MailPartViewers/UIxMailPartICalActions.m index be8c47e73..e068ebbe5 100644 --- a/UI/MailPartViewers/UIxMailPartICalActions.m +++ b/UI/MailPartViewers/UIxMailPartICalActions.m @@ -248,6 +248,7 @@ { response = (WOResponse*)[eventObject changeParticipationStatus: newStatus withDelegate: delegate + alarm: nil forRecurrenceId: [chosenEvent recurrenceId]]; // if (ex) // response = ex; //[self responseWithStatus: 500]; diff --git a/UI/MailerUI/UIxMailMainFrame.m b/UI/MailerUI/UIxMailMainFrame.m index d94f90aae..488449207 100644 --- a/UI/MailerUI/UIxMailMainFrame.m +++ b/UI/MailerUI/UIxMailMainFrame.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2007-2013 Inverse inc. + Copyright (C) 2007-2014 Inverse inc. This file is part of SOGo. diff --git a/UI/PreferencesUI/Czech.lproj/Localizable.strings b/UI/PreferencesUI/Czech.lproj/Localizable.strings index 4a4b15302..891be5277 100644 --- a/UI/PreferencesUI/Czech.lproj/Localizable.strings +++ b/UI/PreferencesUI/Czech.lproj/Localizable.strings @@ -153,6 +153,9 @@ "displayremoteinlineimages_never" = "Nikdy"; "displayremoteinlineimages_always" = "Vždy"; +"Auto save every" = "Automaticky uložit každých"; +"minutes" = "minut"; + /* Contact */ "Personal Address Book" = "Osobní kontakty"; "Collected Address Book" = "Sebrané kontakty"; diff --git a/UI/PreferencesUI/Dutch.lproj/Localizable.strings b/UI/PreferencesUI/Dutch.lproj/Localizable.strings index ad1a94ffd..f4fe086f4 100644 --- a/UI/PreferencesUI/Dutch.lproj/Localizable.strings +++ b/UI/PreferencesUI/Dutch.lproj/Localizable.strings @@ -153,6 +153,9 @@ "displayremoteinlineimages_never" = "Nooit"; "displayremoteinlineimages_always" = "Altijd"; +"Auto save every" = "Automatisch opslaan elke"; +"minutes" = "minuten"; + /* Contact */ "Personal Address Book" = "Persoonlijk adresboek"; "Collected Address Book" = "Verzameld adresboek"; diff --git a/UI/PreferencesUI/English.lproj/Localizable.strings b/UI/PreferencesUI/English.lproj/Localizable.strings index ebc9ace4e..538bd3d9b 100644 --- a/UI/PreferencesUI/English.lproj/Localizable.strings +++ b/UI/PreferencesUI/English.lproj/Localizable.strings @@ -153,6 +153,9 @@ "displayremoteinlineimages_never" = "Never"; "displayremoteinlineimages_always" = "Always"; +"Auto save every" = "Auto save every"; +"minutes" = "minutes"; + /* Contact */ "Personal Address Book" = "Personal Address Book"; "Collected Address Book" = "Collected Address Book"; diff --git a/UI/PreferencesUI/Finnish.lproj/Localizable.strings b/UI/PreferencesUI/Finnish.lproj/Localizable.strings index c7ab17780..3df3e7718 100644 --- a/UI/PreferencesUI/Finnish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Finnish.lproj/Localizable.strings @@ -153,6 +153,9 @@ "displayremoteinlineimages_never" = "Ei koskaan"; "displayremoteinlineimages_always" = "Aina"; +"Auto save every" = "Tallenna automaattisesti"; +"minutes" = "minuutin välein"; + /* Contact */ "Personal Address Book" = "Henkilökohtainen osoitekirja"; "Collected Address Book" = "Keratty osoitekirja"; diff --git a/UI/PreferencesUI/French.lproj/Localizable.strings b/UI/PreferencesUI/French.lproj/Localizable.strings index c4a1c5e00..4caec8c78 100644 --- a/UI/PreferencesUI/French.lproj/Localizable.strings +++ b/UI/PreferencesUI/French.lproj/Localizable.strings @@ -153,6 +153,9 @@ "displayremoteinlineimages_never" = "Jamais"; "displayremoteinlineimages_always" = "Toujours"; +"Auto save every" = "Sauvegarde automatique toutes les"; +"minutes" = "minutes"; + /* Contact */ "Personal Address Book" = "Adresses personnelles"; "Collected Address Book" = "Adresses collectées"; diff --git a/UI/PreferencesUI/German.lproj/Localizable.strings b/UI/PreferencesUI/German.lproj/Localizable.strings index 71cea96b0..9b66de389 100644 --- a/UI/PreferencesUI/German.lproj/Localizable.strings +++ b/UI/PreferencesUI/German.lproj/Localizable.strings @@ -153,6 +153,9 @@ "displayremoteinlineimages_never" = "Niemals"; "displayremoteinlineimages_always" = "Immer"; +"Auto save every" = "Automatisch speichern alle"; +"minutes" = "Minuten"; + /* Contact */ "Personal Address Book" = "Persönliches Adressbuch"; "Collected Address Book" = "Gesammelte Adressen"; diff --git a/UI/PreferencesUI/Polish.lproj/Localizable.strings b/UI/PreferencesUI/Polish.lproj/Localizable.strings index 7bf185c5e..9936e5a4c 100644 --- a/UI/PreferencesUI/Polish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Polish.lproj/Localizable.strings @@ -153,6 +153,9 @@ "displayremoteinlineimages_never" = "Nigdy"; "displayremoteinlineimages_always" = "Zawsze"; +"Auto save every" = "Zapisuj automatycznie co"; +"minutes" = "min."; + /* Contact */ "Personal Address Book" = "Osobista książka adresowa"; "Collected Address Book" = "Książka zebranych adresów"; diff --git a/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings b/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings index cb59f9ee5..7a5eb414a 100644 --- a/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings +++ b/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings @@ -153,6 +153,9 @@ "displayremoteinlineimages_never" = "Nunca"; "displayremoteinlineimages_always" = "Siempre"; +"Auto save every" = "Guardar automáticamente cada"; +"minutes" = "minutos"; + /* Contact */ "Personal Address Book" = "Libreta de direcciones personal"; "Collected Address Book" = "Libreta de direcciones recogidas"; diff --git a/UI/PreferencesUI/UIxPreferences.m b/UI/PreferencesUI/UIxPreferences.m index 5017a4bec..a43a8e744 100644 --- a/UI/PreferencesUI/UIxPreferences.m +++ b/UI/PreferencesUI/UIxPreferences.m @@ -959,6 +959,16 @@ static NSArray *reminderValues = nil; [userDefaults setMailDisplayRemoteInlineImages: newType]; } +- (void) setAutoSave: (NSString *) theValue +{ + [userDefaults setMailAutoSave: theValue]; +} + +- (NSString *) autoSave +{ + return [userDefaults mailAutoSave]; +} + /* mail autoreply (vacation) */ - (BOOL) isSieveScriptsEnabled diff --git a/UI/Scheduler/GNUmakefile b/UI/Scheduler/GNUmakefile index c9c8a38b6..4c0765244 100644 --- a/UI/Scheduler/GNUmakefile +++ b/UI/Scheduler/GNUmakefile @@ -52,9 +52,6 @@ SchedulerUI_RESOURCE_FILES += \ SchedulerUI_RESOURCE_FILES += \ Toolbars/SOGoAppointmentFolders.toolbar \ Toolbars/SOGoAppointmentObject.toolbar \ - Toolbars/SOGoAppointmentObjectAccept.toolbar \ - Toolbars/SOGoAppointmentObjectDecline.toolbar \ - Toolbars/SOGoAppointmentObjectAcceptOrDecline.toolbar \ Toolbars/SOGoTaskObject.toolbar \ Toolbars/SOGoComponentClose.toolbar \ Toolbars/SOGoEmpty.toolbar diff --git a/UI/Scheduler/Toolbars/SOGoAppointmentObjectAccept.toolbar b/UI/Scheduler/Toolbars/SOGoAppointmentObjectAccept.toolbar deleted file mode 100644 index e3913afc1..000000000 --- a/UI/Scheduler/Toolbars/SOGoAppointmentObjectAccept.toolbar +++ /dev/null @@ -1,7 +0,0 @@ -( /* the toolbar groups -*-cperl-*- */ - ( { link = "#"; - isSafe = NO; - label = "accept"; - onclick = "return modifyEvent(this, 'accept');"; - image = "tb-ab-properties-flat-24x24.png"; } ) -) diff --git a/UI/Scheduler/Toolbars/SOGoAppointmentObjectAcceptOrDecline.toolbar b/UI/Scheduler/Toolbars/SOGoAppointmentObjectAcceptOrDecline.toolbar deleted file mode 100644 index bb967d8ea..000000000 --- a/UI/Scheduler/Toolbars/SOGoAppointmentObjectAcceptOrDecline.toolbar +++ /dev/null @@ -1,12 +0,0 @@ -( /* the toolbar groups -*-cperl-*- */ - ( { link = "#"; - isSafe = NO; - label = "accept"; - onclick = "return modifyEvent(this, 'accept');"; - image = "tb-ab-properties-flat-24x24.png"; }, - { link = "#"; - isSafe = NO; - label = "decline"; - onclick = "return modifyEvent(this, 'decline');"; - image = "tb-mail-stop-flat-24x24.png"; } ) -) diff --git a/UI/Scheduler/Toolbars/SOGoAppointmentObjectDecline.toolbar b/UI/Scheduler/Toolbars/SOGoAppointmentObjectDecline.toolbar deleted file mode 100644 index 347b8f8cf..000000000 --- a/UI/Scheduler/Toolbars/SOGoAppointmentObjectDecline.toolbar +++ /dev/null @@ -1,7 +0,0 @@ -( /* the toolbar groups -*-cperl-*- */ - ( { link = "#"; - isSafe = NO; - label = "decline"; - onclick = "return modifyEvent(this, 'decline');"; - image = "tb-mail-stop-flat-24x24.png"; } ) -) diff --git a/UI/Scheduler/UIxAppointmentEditor.m b/UI/Scheduler/UIxAppointmentEditor.m index 86c26153b..653226628 100644 --- a/UI/Scheduler/UIxAppointmentEditor.m +++ b/UI/Scheduler/UIxAppointmentEditor.m @@ -51,6 +51,7 @@ #import #import #import +#import #import #import #import @@ -80,7 +81,7 @@ isTransparent = NO; sendAppointmentNotifications = YES; componentCalendar = nil; - + user = [[self context] activeUser]; ASSIGN (dateFormatter, [user dateFormatterInContext: context]); } @@ -111,6 +112,11 @@ return event; } +- (NSString *) rsvpURL +{ + return [NSString stringWithFormat: @"%@/rsvpAppointment", + [[self clientObject] baseURL]]; +} - (NSString *) saveURL { return [NSString stringWithFormat: @"%@/saveAsAppointment", @@ -390,6 +396,137 @@ } } +// +// +// +- (id ) rsvpAction +{ + iCalPerson *delegatedAttendee; + NSDictionary *message; + WOResponse *response; + WORequest *request; + iCalAlarm *anAlarm; + NSString *status; + + int replyList, reminderList; + + request = [context request]; + message = [[request contentAsString] objectFromJSONString]; + + delegatedAttendee = nil; + anAlarm = nil; + status = nil; + + replyList = [[message objectForKey: @"replyList"] intValue]; + + switch (replyList) + { + case 0: + status = @"ACCEPTED"; + break; + + case 1: + status = @"DECLINED"; + break; + + case 2: + status = @"NEEDS-ACTION"; + break; + + case 3: + status = @"TENTATIVE"; + break; + + case 4: + default: + { + NSString *delegatedEmail, *delegatedUid; + SOGoUser *user; + + status = @"DELEGATED"; + delegatedEmail = [[message objectForKey: @"delegatedTo"] stringByTrimmingSpaces]; + + if ([delegatedEmail length]) + { + user = [context activeUser]; + delegatedAttendee = [iCalPerson new]; + [delegatedAttendee autorelease]; + [delegatedAttendee setEmail: delegatedEmail]; + delegatedUid = [delegatedAttendee uid]; + if (delegatedUid) + { + SOGoUser *delegatedUser; + delegatedUser = [SOGoUser userWithLogin: delegatedUid]; + [delegatedAttendee setCn: [delegatedUser cn]]; + } + + [delegatedAttendee setRole: @"REQ-PARTICIPANT"]; + [delegatedAttendee setRsvp: @"TRUE"]; + [delegatedAttendee setParticipationStatus: iCalPersonPartStatNeedsAction]; + [delegatedAttendee setDelegatedFrom: + [NSString stringWithFormat: @"mailto:%@", [[user allEmails] objectAtIndex: 0]]]; + } + else + return [NSException exceptionWithHTTPStatus: 400 + reason: @"missing 'to' parameter"]; + } + break; + } + + // Extract the user alarm, if any + reminderList = [[message objectForKey: @"reminderList"] intValue]; + + if ([[message objectForKey: @"reminderList"] isEqualToString: @"WONoSelectionString"] || reminderList == 5 || reminderList == 10 || reminderList == 14) + { + // No selection, wipe alarm which will be done in changeParticipationStatus... + } + else if (reminderList == 15) + { + // Custom + anAlarm = [iCalAlarm alarmForEvent: [self event] + owner: [[self clientObject] ownerInContext: context] + action: [message objectForKey: @"reminderAction"] + unit: [message objectForKey: @"reminderUnit"] + quantity: [message objectForKey: @"reminderQuantity"] + reference: [message objectForKey: @"reminderReference"] + reminderRelation: [message objectForKey: @"reminderRelation"] + emailAttendees: [[message objectForKey: @"reminderEmailAttendees"] boolValue] + emailOrganizer: [[message objectForKey: @"reminderEmailOrganizer"] boolValue]]; + } + else + { + // Standard + NSString *aValue; + + aValue = [[UIxComponentEditor reminderValues] objectAtIndex: reminderList]; + + // Predefined alarm + if ([aValue length]) + { + iCalTrigger *aTrigger; + + anAlarm = [[[iCalAlarm alloc] init] autorelease]; + aTrigger = [iCalTrigger elementWithTag: @"TRIGGER"]; + [aTrigger setValueType: @"DURATION"]; + [anAlarm setTrigger: aTrigger]; + [anAlarm setAction: @"DISPLAY"]; + [aTrigger setSingleValue: aValue forKey: @""]; + } + } + + response = (WOResponse *)[[self clientObject] changeParticipationStatus: status + withDelegate: delegatedAttendee + alarm: anAlarm]; + + if (!response) + response = [self responseWith204]; + + return response; +} + +// +// +// - (id ) saveAction { SOGoAppointmentFolder *previousCalendar; @@ -560,7 +697,7 @@ actionName = [[request requestHandlerPath] lastPathComponent]; return ([[self clientObject] conformsToProtocol: @protocol (SOGoComponentOccurence)] - && [actionName hasPrefix: @"save"]); + && ([actionName hasPrefix: @"save"] || [actionName hasPrefix: @"rsvp"])); } - (void) takeValuesFromRequest: (WORequest *) _rq @@ -637,81 +774,4 @@ } -- (id) _statusChangeAction: (NSString *) newStatus -{ - [[self clientObject] changeParticipationStatus: newStatus - withDelegate: nil]; - - return [self responseWith204]; -} - -- (id) acceptAction -{ - return [self _statusChangeAction: @"ACCEPTED"]; -} - -- (id) declineAction -{ - return [self _statusChangeAction: @"DECLINED"]; -} - -- (id) needsActionAction -{ - return [self _statusChangeAction: @"NEEDS-ACTION"]; -} - -- (id) tentativeAction -{ - return [self _statusChangeAction: @"TENTATIVE"]; -} - -- (id) delegateAction -{ -// BOOL receiveUpdates; - NSString *delegatedEmail, *delegatedUid; - iCalPerson *delegatedAttendee; - SOGoUser *user; - WORequest *request; - WOResponse *response; - - response = nil; - request = [context request]; - delegatedEmail = [request formValueForKey: @"to"]; - if ([delegatedEmail length]) - { - user = [context activeUser]; - delegatedAttendee = [iCalPerson new]; - [delegatedAttendee autorelease]; - [delegatedAttendee setEmail: delegatedEmail]; - delegatedUid = [delegatedAttendee uid]; - if (delegatedUid) - { - SOGoUser *delegatedUser; - delegatedUser = [SOGoUser userWithLogin: delegatedUid]; - [delegatedAttendee setCn: [delegatedUser cn]]; - } - - [delegatedAttendee setRole: @"REQ-PARTICIPANT"]; - [delegatedAttendee setRsvp: @"TRUE"]; - [delegatedAttendee setParticipationStatus: iCalPersonPartStatNeedsAction]; - [delegatedAttendee setDelegatedFrom: - [NSString stringWithFormat: @"mailto:%@", [[user allEmails] objectAtIndex: 0]]]; - -// receiveUpdates = [[request formValueForKey: @"receiveUpdates"] boolValue]; -// if (receiveUpdates) -// [delegatedAttendee setRole: @"NON-PARTICIPANT"]; - - response = (WOResponse*)[[self clientObject] changeParticipationStatus: @"DELEGATED" - withDelegate: delegatedAttendee]; - } - else - response = [NSException exceptionWithHTTPStatus: 400 - reason: @"missing 'to' parameter"]; - - if (!response) - response = [self responseWith204]; - - return response; -} - @end diff --git a/UI/Scheduler/UIxCalendarProperties.h b/UI/Scheduler/UIxCalendarProperties.h index 157653dc2..de17b6117 100644 --- a/UI/Scheduler/UIxCalendarProperties.h +++ b/UI/Scheduler/UIxCalendarProperties.h @@ -1,9 +1,6 @@ /* UIxCalendarProperties.m - this file is part of SOGo * - * Copyright (C) 2008-2012 Inverse inc. - * - * Author: Wolfgang Sourdeau - * Ludovic Marcotte + * Copyright (C) 2008-2014 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/UI/Scheduler/UIxCalendarProperties.m b/UI/Scheduler/UIxCalendarProperties.m index 3817ad48d..8f0ec4e81 100644 --- a/UI/Scheduler/UIxCalendarProperties.m +++ b/UI/Scheduler/UIxCalendarProperties.m @@ -1,9 +1,6 @@ /* UIxCalendarProperties.m - this file is part of SOGo * - * Copyright (C) 2008-2012 Inverse inc. - * - * Author: Wolfgang Sourdeau - * Ludovic Marcotte + * Copyright (C) 2008-2014 Inverse inc. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -181,15 +178,6 @@ [calendar setShowCalendarTasks: new]; } -- (BOOL) userIsOwner -{ - NSString *userLogin; - - userLogin = [[context activeUser] login]; - - return ([userLogin isEqualToString: [calendar ownerInContext: context]]); -} - - (BOOL) isPublicAccessEnabled { // NOTE: This method is the same found in Common/UIxAclEditor.m diff --git a/UI/Scheduler/UIxComponentEditor.h b/UI/Scheduler/UIxComponentEditor.h index 6120901d8..201065c11 100644 --- a/UI/Scheduler/UIxComponentEditor.h +++ b/UI/Scheduler/UIxComponentEditor.h @@ -38,6 +38,7 @@ id item; id attendee; + NSString *rsvpURL; NSString *saveURL; NSMutableArray *calendarList; NSDictionary *organizerProfile; @@ -62,7 +63,7 @@ NSString *dateFormat; NSMutableDictionary *jsonAttendees; - + NSString *reminder; NSString *reminderQuantity; NSString *reminderUnit; @@ -184,6 +185,8 @@ - (BOOL) isWriteableClientObject; - (NSException *) validateObjectForStatusChange; ++ (NSArray *) reminderValues; + @end #endif /* UIXCOMPONENTEDITOR_H */ diff --git a/UI/Scheduler/UIxComponentEditor.m b/UI/Scheduler/UIxComponentEditor.m index 77a4d032a..60ee34c12 100644 --- a/UI/Scheduler/UIxComponentEditor.m +++ b/UI/Scheduler/UIxComponentEditor.m @@ -51,6 +51,7 @@ #import #import +#import #import #import #import @@ -225,7 +226,7 @@ iRANGE(2); [attendee release]; [jsonAttendees release]; [calendarList release]; - + [reminder release]; [reminderQuantity release]; [reminderUnit release]; @@ -683,12 +684,16 @@ iRANGE(2); ASSIGN (ownerAsAttendee, [component findAttendeeWithEmail: (id)ownerEmail]); } } -// /* cycles */ -// if ([component isRecurrent]) -// { -// rrule = [[component recurrenceRules] objectAtIndex: 0]; -// [self adjustCycleControlsForRRule: rrule]; -// } +} + +- (void) setRSVPURL: (NSString *) theURL +{ + rsvpURL = theURL; +} + +- (NSString *) rsvpURL +{ + return rsvpURL; } - (void) setSaveURL: (NSString *) newSaveURL @@ -2168,40 +2173,7 @@ RANGE(2); } } -- (void) _appendAttendees: (NSArray *) attendees - toEmailAlarm: (iCalAlarm *) alarm -{ - NSMutableArray *aAttendees; - int count, max; - iCalPerson *currentAttendee, *aAttendee; - max = [attendees count]; - aAttendees = [NSMutableArray arrayWithCapacity: max]; - for (count = 0; count < max; count++) - { - currentAttendee = [attendees objectAtIndex: count]; - aAttendee = [iCalPerson elementWithTag: @"attendee"]; - [aAttendee setCn: [currentAttendee cn]]; - [aAttendee setEmail: [currentAttendee rfc822Email]]; - [aAttendees addObject: aAttendee]; - } - [alarm setAttendees: aAttendees]; -} - -- (void) _appendOrganizerToEmailAlarm: (iCalAlarm *) alarm -{ - NSString *uid; - NSDictionary *ownerIdentity; - iCalPerson *aAttendee; - - uid = [[self clientObject] ownerInContext: context]; - ownerIdentity = [[SOGoUser userWithLogin: uid roles: nil] - defaultIdentity]; - aAttendee = [iCalPerson elementWithTag: @"attendee"]; - [aAttendee setCn: [ownerIdentity objectForKey: @"fullName"]]; - [aAttendee setEmail: [ownerIdentity objectForKey: @"email"]]; - [alarm addChild: aAttendee]; -} - (void) takeValuesFromRequest: (WORequest *) _rq inContext: (WOContext *) _ctx @@ -2236,68 +2208,43 @@ RANGE(2); [component removeAllAlarms]; else { - iCalTrigger *aTrigger; iCalAlarm *anAlarm; NSString *aValue; NSUInteger index; - anAlarm = [iCalAlarm new]; - index = [reminderItems indexOfObject: reminder]; - - aTrigger = [iCalTrigger elementWithTag: @"TRIGGER"]; - [aTrigger setValueType: @"DURATION"]; - [anAlarm setTrigger: aTrigger]; - aValue = [reminderValues objectAtIndex: index]; - if ([aValue length]) { - // Predefined alarm - [anAlarm setAction: @"DISPLAY"]; - [aTrigger setSingleValue: aValue forKey: @""]; - } - else { - // Custom alarm - if ([reminderAction length] > 0 && [reminderUnit length] > 0) - { - [anAlarm setAction: [reminderAction uppercaseString]]; - if ([reminderAction isEqualToString: @"email"]) - { - [anAlarm removeAllAttendees]; - if (reminderEmailAttendees) - [self _appendAttendees: [component attendees] - toEmailAlarm: anAlarm]; - if (reminderEmailOrganizer) - [self _appendOrganizerToEmailAlarm: anAlarm]; - [anAlarm setSummary: [component summary]]; - [anAlarm setComment: [component comment]]; - } - - if ([reminderReference caseInsensitiveCompare: @"BEFORE"] == NSOrderedSame) - aValue = [NSString stringWithString: @"-P"]; - else - aValue = [NSString stringWithString: @"P"]; - - if ([reminderUnit caseInsensitiveCompare: @"MINUTES"] == NSOrderedSame || - [reminderUnit caseInsensitiveCompare: @"HOURS"] == NSOrderedSame) - aValue = [aValue stringByAppendingString: @"T"]; - - aValue = [aValue stringByAppendingFormat: @"%i%@", - [reminderQuantity intValue], - [reminderUnit substringToIndex: 1]]; - [aTrigger setSingleValue: aValue forKey: @""]; - [aTrigger setRelationType: reminderRelation]; - } - else - { - [anAlarm release]; - anAlarm = nil; - } - } + + // Predefined alarm + if ([aValue length]) + { + iCalTrigger *aTrigger; + + anAlarm = [[[iCalAlarm alloc] init] autorelease]; + aTrigger = [iCalTrigger elementWithTag: @"TRIGGER"]; + [aTrigger setValueType: @"DURATION"]; + [anAlarm setTrigger: aTrigger]; + [anAlarm setAction: @"DISPLAY"]; + [aTrigger setSingleValue: aValue forKey: @""]; + } + else + { + // Custom alarm + anAlarm = [iCalAlarm alarmForEvent: component + owner: [[self clientObject] ownerInContext: context] + action: reminderAction + unit: reminderUnit + quantity: reminderQuantity + reference: reminderReference + reminderRelation: reminderRelation + emailAttendees: reminderEmailAttendees + emailOrganizer: reminderEmailOrganizer]; + } + if (anAlarm) { [component removeAllAlarms]; [component addToAlarms: anAlarm]; - [anAlarm release]; } } @@ -2597,4 +2544,9 @@ RANGE(2); return response; } ++ (NSArray *) reminderValues +{ + return reminderValues; +} + @end diff --git a/UI/Scheduler/product.plist b/UI/Scheduler/product.plist index c13980130..da074912e 100644 --- a/UI/Scheduler/product.plist +++ b/UI/Scheduler/product.plist @@ -241,6 +241,11 @@ pageName = "UIxAppointmentEditor"; actionName = "save"; }; + rsvpAppointment = { + protectedBy = "RespondToComponent"; + pageName = "UIxAppointmentEditor"; + actionName = "rsvp"; + }; saveAsAppointment = { protectedBy = "ModifyComponent"; pageName = "UIxAppointmentEditor"; @@ -256,31 +261,6 @@ actionClass = "UIxComponentEditor"; actionName = "raw"; }; - accept = { - protectedBy = "RespondToComponent"; - pageName = "UIxAppointmentEditor"; - actionName = "accept"; - }; - decline = { - protectedBy = "RespondToComponent"; - pageName = "UIxAppointmentEditor"; - actionName = "decline"; - }; - delegate = { - protectedBy = "RespondToComponent"; - pageName = "UIxAppointmentEditor"; - actionName = "delegate"; - }; - tentative = { - protectedBy = "RespondToComponent"; - pageName = "UIxAppointmentEditor"; - actionName = "tentative"; - }; - needsaction = { - protectedBy = "RespondToComponent"; - pageName = "UIxAppointmentEditor"; - actionName = "needsAction"; - }; adjust = { protectedBy = "ModifyComponent"; actionClass = "UIxAppointmentActions"; diff --git a/UI/Templates/MailerUI/UIxMailEditor.wox b/UI/Templates/MailerUI/UIxMailEditor.wox index ea18d9c4f..51a3e0afc 100644 --- a/UI/Templates/MailerUI/UIxMailEditor.wox +++ b/UI/Templates/MailerUI/UIxMailEditor.wox @@ -10,7 +10,7 @@ className="UIxPageFrame" title="panelTitle" const:popup="YES" - const:userDefaultsKeys="SOGoMailComposeMessageType,SOGoMailReplyPlacement,SOGoMailSignature" + const:userDefaultsKeys="SOGoMailComposeMessageType,SOGoMailReplyPlacement,SOGoMailSignature,SOGoMailAutoSave,SOGoDraftsFolderName" const:jsFiles="UIxMailToSelection.js,ckeditor/ckeditor.js,SOGoAutoCompletion.js,ContactsUI.js,jquery-ui.js,jquery.fileupload.js,jquery.iframe-transport.js" const:cssFiles="jquery.fileupload.css">