diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m index 82c1868fc..0d5287a8a 100644 --- a/ActiveSync/SOGoActiveSyncDispatcher.m +++ b/ActiveSync/SOGoActiveSyncDispatcher.m @@ -457,13 +457,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. NSData *d; // - // We mark the cache object as deleted + // We destroy the cache object // key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], [folderToDelete nameInContainer]]; o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; [o setTableUrl: [self folderTableURL]]; - [o reloadIfNeeded]; - [o delete]; + [o destroy]; // // We update the FolderSync's synckey @@ -697,7 +696,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if (![imapGUIDs allKeysForObject: cKey]) { - // Delete folders cache content to avoid stale data if a new folder gets created with the same name + // Destroy folders cache content to avoid stale data if a new folder gets created with the same name key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], [cachedGUIDs objectForKey: cKey]]; o = [SOGoCacheGCSObject objectWithName: key inContainer: nil]; [o setObjectType: ActiveSyncFolderCacheObject]; @@ -711,8 +710,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. command_count++; } - [[o properties] removeAllObjects]; - [o save]; + [o destroy]; } } diff --git a/ActiveSync/common.make b/ActiveSync/common.make index d53fcef8e..505f53f88 100644 --- a/ActiveSync/common.make +++ b/ActiveSync/common.make @@ -18,7 +18,6 @@ ADDITIONAL_INCLUDE_DIRS += \ ADDITIONAL_LIB_DIRS += \ -L../SoObjects/SOGo/SOGo.framework/ \ -L../SoObjects/SOGo/$(GNUSTEP_OBJ_DIR)/ \ - -L../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ \ -L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \ -L/usr/local/lib diff --git a/Apache/SOGo-apple-ab.conf b/Apache/SOGo-apple-ab.conf index 9c42bf1cd..936fb98cc 100644 --- a/Apache/SOGo-apple-ab.conf +++ b/Apache/SOGo-apple-ab.conf @@ -28,8 +28,13 @@ ProxyPass /SOGo http://127.0.0.1:20000/SOGo interpolate ProxyPass / http://127.0.0.1:20000/SOGo/dav/ interpolate - Order allow,deny - Allow from all + + Order deny,allow + Allow from all + + = 2.4> + Require all granted + diff --git a/Apache/SOGo.conf b/Apache/SOGo.conf index f36fbbe8f..bfe7560ba 100644 --- a/Apache/SOGo.conf +++ b/Apache/SOGo.conf @@ -5,8 +5,14 @@ Alias /SOGo/WebServerResources/ \ AllowOverride None - Order deny,allow - Allow from all + + + Order deny,allow + Allow from all + + = 2.4> + Require all granted + # Explicitly allow caching of static content to avoid browser specific behavior. # A resource's URL MUST change in order to have the client load the new version. diff --git a/ChangeLog b/ChangeLog index 7c75ed5a7..ebfb6b9e2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,1499 @@ +commit 69c8aa074364395a8bdf3705d74ee6a93c11d54a +Author: Alexandre Cloutier +Date: Wed Sep 10 13:22:36 2014 -0400 + + quickfix for IE for searchInbox + +M UI/WebServerResources/UIxMailSearch.js + +commit 36b833ccb58353d36085a9fc8ca087f9684b7efb +Author: Alexandre Cloutier +Date: Wed Sep 10 13:02:13 2014 -0400 + + bugFix InboxSearch IMAP query keywords + +M UI/WebServerResources/UIxMailSearch.js + +commit 9d6bcb00bcbae81d55fa2d5c27d3e6458d5a2bb9 +Author: Francis Lachapelle +Date: Wed Sep 10 11:58:47 2014 -0400 + + Fix compilation warning + +M UI/MailerUI/UIxMailEditor.m + +commit 1dd3dbacb50a03a6e59508d3f504754ded0e1e5b +Author: Francis Lachapelle +Date: Wed Sep 10 11:37:52 2014 -0400 + + Various fixes and improvements to the Mail module + +M UI/MailerUI/English.lproj/Localizable.strings +M UI/MailerUI/French.lproj/Localizable.strings +M UI/MailerUI/Toolbars/SOGoMailFolder.toolbar +M UI/Templates/MailerUI/UIxMailSearch.wox +M UI/WebServerResources/MailerUI.js +M UI/WebServerResources/UIxMailSearch.css +M UI/WebServerResources/UIxMailSearch.js +M UI/WebServerResources/generic.css + +commit c441fa5b802a7920315c927479621de92937846c +Author: Francis Lachapelle +Date: Wed Sep 10 10:47:18 2014 -0400 + + Various fix and improvements to Scheduler module + +M UI/Scheduler/French.lproj/Localizable.strings +M UI/Templates/SchedulerUI/UIxCalViewPrint.wox +M UI/WebServerResources/SchedulerUI.js + +commit 7e297ad56c76d3c3477d66dcf0407889d1b40479 +Author: Francis Lachapelle +Date: Wed Sep 10 10:58:46 2014 -0400 + + Remove spaces + +M UI/WebServerResources/SchedulerUI.js + +commit 8eb4212f5ddc8b97c8a1cee7b5b08e2652f2b04f +Author: Francis Lachapelle +Date: Wed Sep 10 10:49:54 2014 -0400 + + Various fix and improvements to Mail module + +M UI/WebServerResources/MailerUI.js +M UI/WebServerResources/UIxMailSearch.css +M UI/WebServerResources/UIxMailSearch.js +M UI/WebServerResources/generic.css + +commit 3ad9327d171dc48f100eb490029999e3704068ac +Author: Luc Charland +Date: Wed Sep 10 10:25:42 2014 -0400 + + putting test-prevent-invitations back in + +M Tests/Integration/config.py.in +D Tests/Integration/dont-test-prevent-invitations.py +A Tests/Integration/test-prevent-invitations.py + +commit 0483794f451bf07ec6fc9b32db18b82eb962219b +Author: Ludovic Marcotte +Date: Wed Sep 10 09:20:47 2014 -0400 + + Updated the documentation for the release + +M Documentation/SOGo Installation Guide.odt +M Documentation/SOGo Mozilla Thunderbird Configuration.odt +M Documentation/SOGo Native Microsoft Outlook Configuration.odt +M NEWS +M Version + +commit 3c1d6d7d898a663f826cb0a32b013f01e02656d0 +Author: Alexandre Cloutier +Date: Tue Sep 9 16:03:44 2014 -0400 + + bugfix; UserSettings was not refresh properly after toggling threads + +M UI/WebServerResources/MailerUI.js + +commit 1dcf631ad6e78174a43b076c804b6026b45e532a +Author: Francis Lachapelle +Date: Tue Sep 9 15:49:16 2014 -0400 + + Update translations + +M NEWS +M SoObjects/Appointments/Catalan.lproj/Localizable.strings +M SoObjects/Appointments/Czech.lproj/Localizable.strings +M SoObjects/Appointments/Dutch.lproj/Localizable.strings +M SoObjects/Appointments/Hungarian.lproj/Localizable.strings +M SoObjects/Appointments/NorwegianBokmal.lproj/Localizable.strings +M SoObjects/Appointments/Polish.lproj/Localizable.strings +M SoObjects/Contacts/Catalan.lproj/Localizable.strings +M SoObjects/Contacts/NorwegianBokmal.lproj/Localizable.strings +M SoObjects/Mailer/Catalan.lproj/Localizable.strings +M SoObjects/Mailer/NorwegianBokmal.lproj/Localizable.strings +M UI/Common/Catalan.lproj/Localizable.strings +M UI/Common/Czech.lproj/Localizable.strings +M UI/Common/Dutch.lproj/Localizable.strings +M UI/Common/Finnish.lproj/Localizable.strings +M UI/Common/NorwegianBokmal.lproj/Localizable.strings +M UI/Common/Polish.lproj/Localizable.strings +M UI/Common/SpanishSpain.lproj/Localizable.strings +M UI/Contacts/Catalan.lproj/Localizable.strings +M UI/Contacts/NorwegianBokmal.lproj/Localizable.strings +M UI/MailPartViewers/Catalan.lproj/Localizable.strings +M UI/MailPartViewers/NorwegianBokmal.lproj/Localizable.strings +M UI/MailerUI/Catalan.lproj/Localizable.strings +M UI/MailerUI/NorwegianBokmal.lproj/Localizable.strings +M UI/PreferencesUI/Catalan.lproj/Localizable.strings +M UI/PreferencesUI/Czech.lproj/Localizable.strings +M UI/PreferencesUI/Dutch.lproj/Localizable.strings +M UI/PreferencesUI/French.lproj/Localizable.strings +M UI/PreferencesUI/Hungarian.lproj/Localizable.strings +M UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings +M UI/PreferencesUI/Polish.lproj/Localizable.strings +M UI/Scheduler/Catalan.lproj/Localizable.strings +M UI/Scheduler/Czech.lproj/Localizable.strings +M UI/Scheduler/Dutch.lproj/Localizable.strings +M UI/Scheduler/Hungarian.lproj/Localizable.strings +M UI/Scheduler/NorwegianBokmal.lproj/Localizable.strings +M UI/Scheduler/Polish.lproj/Localizable.strings + +commit cbc53b3a52f435f0e1ca49c532baf9954b2da82d +Author: Ludovic Marcotte +Date: Tue Sep 9 15:38:15 2014 -0400 + + Updated NEWS file + +M NEWS + +commit 574c2457580c24482cfd560512602b901971e53e +Author: Alexandre Cloutier +Date: Tue Sep 9 15:34:04 2014 -0400 + + bugfix PreventInvitation; fix object parsing on a blank user + +M UI/WebServerResources/UIxPreferences.js + +commit f3ca4ea7fca3e2f2d8434914fba0dc9e397d7e1f +Author: Alexandre Cloutier +Date: Tue Sep 9 15:29:08 2014 -0400 + + bugfix foldingThread; Renaming a foldername with a space was not correctly handled + +M UI/MailerUI/UIxMailFolderActions.m + +commit e542ff56465a22767751ff3a4259ce0c5d3f2bb8 +Author: Ludovic Marcotte +Date: Tue Sep 9 15:15:56 2014 -0400 + + Fix for bug #1240 + +M NEWS +M UI/MainUI/SOGoUserHomePage.m + +commit 9daab5a9a1de1c49b79deca6bda9cc667edd78bd +Author: Alexandre Cloutier +Date: Mon Sep 8 14:58:21 2014 -0400 + + update NEWS file + +M NEWS + +commit 1fb3198dbc379566e504d0423dd69f4968626564 +Author: Alexandre Cloutier +Date: Thu Sep 4 11:42:46 2014 -0400 + + applied comments + +M SoObjects/SOGo/SOGoUserDefaults.m + +commit d62c50476342d50bde7536ebcc2f1bd7ed83faed +Author: Alexandre Cloutier +Date: Fri Aug 29 13:16:30 2014 -0400 + + Changed the configurable variable SOGoMailMessageCheck to a general refresh associated to the current view + +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/PreferencesUI/Arabic.lproj/Localizable.strings +M UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings +M UI/PreferencesUI/Catalan.lproj/Localizable.strings +M UI/PreferencesUI/Czech.lproj/Localizable.strings +M UI/PreferencesUI/Danish.lproj/Localizable.strings +M UI/PreferencesUI/Dutch.lproj/Localizable.strings +M UI/PreferencesUI/English.lproj/Localizable.strings +M UI/PreferencesUI/Finnish.lproj/Localizable.strings +M UI/PreferencesUI/French.lproj/Localizable.strings +M UI/PreferencesUI/German.lproj/Localizable.strings +M UI/PreferencesUI/Hungarian.lproj/Localizable.strings +M UI/PreferencesUI/Icelandic.lproj/Localizable.strings +M UI/PreferencesUI/Italian.lproj/Localizable.strings +M UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings +M UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings +M UI/PreferencesUI/Polish.lproj/Localizable.strings +M UI/PreferencesUI/Russian.lproj/Localizable.strings +M UI/PreferencesUI/Slovak.lproj/Localizable.strings +M UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings +M UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings +M UI/PreferencesUI/Swedish.lproj/Localizable.strings +M UI/PreferencesUI/UIxPreferences.m +M UI/PreferencesUI/Ukrainian.lproj/Localizable.strings +M UI/PreferencesUI/Welsh.lproj/Localizable.strings +M UI/Templates/ContactsUI/UIxContactEditor.wox +M UI/Templates/MailerUI/UIxMailMainFrame.wox +M UI/Templates/PreferencesUI/UIxPreferences.wox +M UI/Templates/SchedulerUI/UIxCalMainView.wox +M UI/WebServerResources/ContactsUI.js +M UI/WebServerResources/MailerUI.js +M UI/WebServerResources/SchedulerUI.js +M UI/WebServerResources/UIxPreferences.js + +commit 7db526a5d968e0f0c7ad0782f8449220df722359 +Author: Alexandre Cloutier +Date: Tue Sep 9 13:50:42 2014 -0400 + + bugfix for new user without userSettings.mail + +M UI/MailerUI/UIxMailActions.m +M UI/WebServerResources/MailerUI.js + +commit 06f81a032cafe6017e328f8c8b4215119cc502a0 +Author: Alexandre Cloutier +Date: Tue Sep 9 12:44:23 2014 -0400 + + applied comments + +M UI/WebServerResources/MailerUI.js + +commit df42c31618611f41856bc5a2427e7b59cc899bda +Author: Alexandre Cloutier +Date: Mon Sep 8 12:43:29 2014 -0400 + + Update NEWS file + +M NEWS + +commit e6002c9b2a419dd12f543990fed588a07b0a88df +Author: Alexandre Cloutier +Date: Mon Sep 8 12:39:55 2014 -0400 + + applied comments + +M UI/MailerUI/UIxMailActions.m +M UI/MailerUI/UIxMailFolderActions.m +M UI/WebServerResources/MailerUI.js + +commit 0c88edc61b788c5a33de2ab85fda9680deb80bc2 +Author: Alexandre Cloutier +Date: Thu Sep 4 11:09:04 2014 -0400 + + bugfix where an exception was raised if threadsCollapsed is undefined + +M UI/WebServerResources/SOGoMailDataSource.js + +commit 267a689683dd435fde53dc7f442a9e9853b91305 +Author: Alexandre Cloutier +Date: Fri Aug 29 09:30:40 2014 -0400 + + Applied comments + +M UI/MailerUI/UIxMailFolderActions.m + +commit c9d1294a8aca4e66c8ef59595035e9f8b3dd35ac +Author: Alexandre Cloutier +Date: Thu Aug 28 17:55:35 2014 -0400 + + Manage the collapse UIDs save in the user settings + +M UI/MailerUI/UIxMailActions.m +M UI/MailerUI/UIxMailFolderActions.m + +commit 59023da7f00010c44ff026019d93009ff908d13e +Author: Alexandre Cloutier +Date: Wed Aug 13 18:39:33 2014 -0400 + + applied comments and fix bug where the folded threads were saved only after refreshing the entire page + +M UI/MailerUI/UIxMailActions.m +M UI/WebServerResources/MailerUI.js + +commit c5f9dd086b78319d5e8170b62ae854a8e9d625c0 +Author: Alexandre Cloutier +Date: Tue Aug 12 17:43:01 2014 -0400 + + Possibility to collapsed threads with persistance + +M UI/MailerUI/UIxMailActions.m +M UI/MailerUI/product.plist +M UI/WebServerResources/MailerUI.js +M UI/WebServerResources/SOGoDataTable.js +M UI/WebServerResources/SOGoMailDataSource.js + +commit 0bfaceb398e701379efa4be935d5c8d6ebd69313 +Author: Alexandre Cloutier +Date: Tue Sep 9 12:51:59 2014 -0400 + + applied comments + +M UI/MailerUI/UIxMailFolderActions.m + +commit 9a380f711c0b2bb9e7a15c561d8661a5aa975c2d +Author: Alexandre Cloutier +Date: Tue Sep 9 10:28:59 2014 -0400 + + bugfix PreventInvitation + +M SoObjects/Appointments/SOGoAppointmentObject.m +M UI/PreferencesUI/UIxPreferences.m +M UI/WebServerResources/UIxPreferences.js + +commit 8c8e7d24a76d82cf37b19d02983e140fff177540 +Author: Ludovic Marcotte +Date: Mon Sep 8 15:25:21 2014 -0400 + + Modified Apache's config file to handle Apache > 2.3 + +M Apache/SOGo-apple-ab.conf +M Apache/SOGo.conf +M NEWS + +commit 77c50e523250ccdceafa3fe1b1a3439e630a407e +Author: Alexandre Cloutier +Date: Mon Sep 8 14:11:42 2014 -0400 + + applied comments; fix errors from merge and update NEWS file + +M NEWS +M UI/WebServerResources/MailerUI.js + +commit b639c2d251c48a5ab6498a81e8a40fb3f153ef84 +Author: Francis Lachapelle +Date: Fri Sep 5 16:04:29 2014 -0400 + + Fixes for IE + +M UI/Templates/MailerUI/UIxMailSearch.wox +M UI/WebServerResources/UIxMailSearch.css +M UI/WebServerResources/UIxMailSearch.js + +commit 984e208dd938911cdb091514e8da2a3f328bedce +Author: Alexandre Cloutier +Date: Fri Sep 5 11:01:57 2014 -0400 + + IE bugfixes + +M UI/Templates/MailerUI/UIxMailSearch.wox +M UI/WebServerResources/UIxMailSearch.css +M UI/WebServerResources/UIxMailSearch.js +M UI/WebServerResources/generic.css +M UI/WebServerResources/iefixes.css + +commit 606ee9780e8f8edaecfbf0f245ee114a2c31874e +Author: Luc Charland +Date: Thu Sep 4 22:36:02 2014 -0400 + + dont test prevent invitations + +A Tests/Integration/dont-test-prevent-invitations.py +D Tests/Integration/test-prevent-invitations.py + +commit c6f82dac01545789528c524d91ac532ba04d5290 +Author: Luc Charland +Date: Thu Sep 4 22:24:32 2014 -0400 + + catch specific exception + +M Tests/Integration/webdavlib.py + +commit e37a603632e532d3f36588b3212575e02939a702 +Author: Luc Charland +Date: Thu Sep 4 21:59:36 2014 -0400 + + should help some builds that might timeout + +M Tests/Integration/webdavlib.py + +commit ca8351d1f9097793ed816c099683c3b97e87a13d +Author: Alexandre Cloutier +Date: Thu Sep 4 10:50:03 2014 -0400 + + fix IE7, IE8, IE9, IE10, IE11 GUI + +M UI/MailerUI/English.lproj/Localizable.strings +M UI/Templates/MailerUI/UIxMailSearch.wox +M UI/WebServerResources/MailerUI.js +M UI/WebServerResources/UIxMailSearch.css +M UI/WebServerResources/UIxMailSearch.js +M UI/WebServerResources/generic.css +M UI/WebServerResources/iefixes.css + +commit 031e4c9409abd0ce46a634d5e05db417e785faa7 +Author: Luc Charland +Date: Wed Sep 3 22:43:49 2014 -0400 + + more of the same + +M Tests/Integration/test-preferences.py +M Tests/Integration/test-sieve.py + +commit c621bbcd563a8b4306cbf59d9390f12a9a260e7c +Author: Luc Charland +Date: Wed Sep 3 22:32:46 2014 -0400 + + Those need the same adjustments + +M Tests/Integration/test-preferences.py +M Tests/Integration/test-sieve.py + +commit 4b94a183be2887ef958931bb4848dec51ad41cbb +Author: Luc Charland +Date: Wed Sep 3 13:50:13 2014 -0400 + + urllib.quote needs a string + +M Tests/Integration/test-prevent-invitations.py + +commit 32476b16cc79f113be494f6bc755d47b89a2ab85 +Author: Alexandre Cloutier +Date: Tue Sep 2 14:31:46 2014 -0400 + + applied comments and fix bug when tagging msgs already tagged + +M UI/MailerUI/UIxMailFolderActions.m +M UI/WebServerResources/MailerUI.js + +commit 3b65eaea0b177374abe7389a42935ccd08629910 +Author: Alexandre Cloutier +Date: Tue Sep 2 13:42:04 2014 -0400 + + Tagging labels with 1 AJAX request + +M UI/MailerUI/UIxMailFolderActions.m +M UI/MailerUI/product.plist +M UI/WebServerResources/MailerUI.js + +commit 7e26d80c77316c0869470b045a29786efccfdeb2 +Author: Alexandre Cloutier +Date: Tue Sep 2 10:11:27 2014 -0400 + + merge conflicts + +M UI/MailerUI/UIxMailActions.m +M UI/MailerUI/UIxMailFolderActions.m +M UI/MailerUI/product.plist +M UI/WebServerResources/MailerUI.js + +commit 1af52e6d6b93bc68dc7c038ce740851fb56c88b3 +Author: Francis Lachapelle +Date: Fri Aug 29 15:33:56 2014 -0400 + + Fix message labels handling + + Selecting "none" from the label submenu on a multiple selection of + messages is now working. When changing the label of multiple messages, + the display is now properly updated. + + Fixes #2902 + +M NEWS +M UI/WebServerResources/MailerUI.js + +commit eaf815eae62c8c7311158a34eb0b67bda2d3f9e6 +Author: Alexandre Cloutier +Date: Fri Aug 29 14:55:21 2014 -0400 + + update NEWS file + +M NEWS + +commit 9bd09cc55d08ed34745f4fa51413dc760c7114d1 +Author: Alexandre Cloutier +Date: Thu Aug 28 13:20:43 2014 -0400 + + add the location column + +M UI/Templates/MailerUI/UIxMailSearch.wox +M UI/WebServerResources/UIxMailSearch.js + +commit ced09628940cde92c3bb70ce96e31dd5cc973b08 +Author: Luc Charland +Date: Thu Aug 28 10:43:32 2014 -0400 + + Added white_listed_attendee to the config file + +M Tests/Integration/config.py.in + +commit dadad3df2a755ffcd73198d633a8eb368412b2b3 +Author: Francis Lachapelle +Date: Thu Aug 28 09:37:49 2014 -0400 + + Fix some compilation warnings + +M UI/Scheduler/UIxAppointmentActions.m +M UI/Scheduler/UIxCalMainView.m + +commit da184f8459a43bbe0c7f740e366cdf60baedb0ef +Author: Alexandre Cloutier +Date: Wed Aug 27 16:50:45 2014 -0400 + + bugfix#2864 + +M UI/WebServerResources/SchedulerUI.js + +commit 03152ea3d4cbf39ef8a5340e865d038bc899877f +Author: Alexandre Cloutier +Date: Wed Aug 27 16:01:35 2014 -0400 + + bugfix with DND from calendar view to calendar list + +M UI/WebServerResources/SchedulerUIDnD.js + +commit 84a08659ecc263f1962aa138bb025308aaf41870 +Author: Alexandre Cloutier +Date: Wed Aug 27 13:50:56 2014 -0400 + + bugfix where it is not possible to delete msg when mailbox is full + +M UI/WebServerResources/MailerUI.js + +commit d749d5c2fb9a41697732eaef3f17e27e807fff55 +Author: Luc Charland +Date: Wed Aug 27 13:31:49 2014 -0400 + + Added tests for prevent invitation and whitelist + +M Tests/Integration/preferences.py +M Tests/Integration/test-preferences.py +A Tests/Integration/test-prevent-invitations.py + +commit 07a138e0d62ebaaea722dc91ae0be162385d74ed +Author: Alexandre Cloutier +Date: Wed Aug 27 12:30:33 2014 -0400 + + applied comments and add the possibility to resize the search inbox window + +M UI/Templates/MailerUI/UIxMailSearch.wox +M UI/WebServerResources/MailerUI.js +M UI/WebServerResources/UIxMailSearch.css +M UI/WebServerResources/UIxMailSearch.js +M UI/WebServerResources/generic.css + +commit 76c9b45fd077759c888cde2941e710a9e5925577 +Author: Alexandre Cloutier +Date: Tue Aug 26 14:25:27 2014 -0400 + + update Localizable.strings + + Update the print window options strings + +M UI/Scheduler/English.lproj/Localizable.strings + +commit 38a3b7bb4f29aa676c7af0dccde3d87973b1007e +Author: Alexandre Cloutier +Date: Tue Aug 26 13:36:42 2014 -0400 + + print calendars events&Tasks with colors - either borders or backgrounds + +M UI/Scheduler/UIxCalListingActions.m +M UI/Scheduler/UIxCalViewPrint.h +M UI/Scheduler/UIxCalViewPrint.m +M UI/Templates/SchedulerUI/UIxCalViewPrint.wox +M UI/WebServerResources/SchedulerUI.js +M UI/WebServerResources/UIxCalViewPrint.css +M UI/WebServerResources/UIxCalViewPrint.js + +commit b2b77c65d3d35d7c96986eb0f01c53783e0c6b05 +Author: Francis Lachapelle +Date: Tue Aug 26 09:00:13 2014 -0400 + + Fix folders paths of "sogo-tool restore -l" + +M NEWS +M Tools/SOGoToolRestore.m + +commit 6aecccf6ca26231342af1f8542dfbe28347778fc +Author: Alexandre Cloutier +Date: Mon Aug 25 13:46:35 2014 -0400 + + indentations + +M UI/WebServerResources/UIxCalViewPrint.js + +commit e4a84d49202b2aa42251910dcbc1ffb8de6b498f +Author: Francis Lachapelle +Date: Mon Aug 25 12:15:05 2014 -0400 + + Fix usage of SOGoSubscriptionFolderFormat default + +M NEWS +M SoObjects/SOGo/SOGoGCSFolder.h +M SoObjects/SOGo/SOGoGCSFolder.m + +commit 28f26ec08e7994b857c0b51551b1d5625df822b3 +Author: Francis Lachapelle +Date: Mon Aug 25 10:16:10 2014 -0400 + + Fix display of users with no possible subscription + +M NEWS +M UI/WebServerResources/UIxContactsUserFolders.js + +commit 74bd96e5a635cefd963315db2fef5199eeba14c4 +Author: Francis Lachapelle +Date: Mon Aug 25 10:12:09 2014 -0400 + + Fix encoding of new calendars + new subscriptions + +M NEWS +M UI/WebServerResources/SchedulerUI.js + +commit e044476772b304e307f7853906996282f9b0eb93 +Author: Alexandre Cloutier +Date: Fri Aug 15 13:41:30 2014 -0400 + + bugFix #2827 + +M UI/WebServerResources/ContactsUI.js + +commit c76cb3646122e43e6b1cb2dfa6e64a88da85d605 +Author: Alexandre Cloutier +Date: Fri Aug 15 13:06:46 2014 -0400 + + Disable the search button on toolbar once it is opened + +M UI/WebServerResources/MailerUI.js +M UI/WebServerResources/UIxMailSearch.js + +commit 73fcdc230615da8ac22281397f4ba692a4a7cdc8 +Author: Alexandre Cloutier +Date: Fri Aug 15 11:41:37 2014 -0400 + + adjust css for other browser, fix display bug + +M UI/Templates/MailerUI/UIxMailSearch.wox +M UI/WebServerResources/UIxMailSearch.css +M UI/WebServerResources/UIxMailSearch.js +M UI/WebServerResources/tablekit.js + +commit b9dd1ddc5ae215b1b1c888ee9538b8036fbf9208 +Author: Ludovic Marcotte +Date: Fri Aug 15 09:57:19 2014 -0400 + + Fix for bug #2892 + +M NEWS +M UI/Common/UIxFolderActions.m + +commit 8783f09a4acca298fc3a9f533feb2dda5590db36 +Author: Alexandre Cloutier +Date: Thu Aug 14 15:01:40 2014 -0400 + + fix leak + +M UI/MailerUI/UIxMailSearch.m + +commit 52476251b82ed0c172bc143a0642ae223d306b6e +Author: Alexandre Cloutier +Date: Thu Aug 14 14:49:44 2014 -0400 + + applied comments, fix bug and add sortable results + +M UI/MailerUI/UIxMailListActions.m +M UI/Templates/MailerUI/UIxMailSearch.wox +M UI/WebServerResources/UIxMailSearch.css +M UI/WebServerResources/UIxMailSearch.js + +commit f6c7ca33797bfd0e00a6b1f4e85f6b7de1ae9889 +Author: Alexandre Cloutier +Date: Wed Aug 13 17:41:58 2014 -0400 + + applied comments + +M UI/MailerUI/English.lproj/Localizable.strings +M UI/MailerUI/UIxMailListActions.m +M UI/MailerUI/UIxMailSearch.m +M UI/WebServerResources/UIxMailSearch.js + +commit c3e46e4bafd6e26ec417927f10cf0c88180b7786 +Author: Alexandre Cloutier +Date: Mon Aug 11 09:25:09 2014 -0400 + + code refactoring and GUI improvement + +M UI/MailerUI/UIxMailListActions.m +M UI/Templates/MailerUI/UIxMailSearch.wox +M UI/WebServerResources/UIxMailSearch.css +M UI/WebServerResources/UIxMailSearch.js +M UI/WebServerResources/generic.css + +commit 7794d716b983992251492f26bcf3ff07edbd3b5a +Author: Alexandre Cloutier +Date: Wed Aug 6 14:49:36 2014 -0400 + + version 1 of the inbox search + +M UI/MailerUI/GNUmakefile +M UI/MailerUI/Toolbars/SOGoMailFolder.toolbar +M UI/MailerUI/UIxMailListActions.m +M UI/MailerUI/UIxMailMainFrame.m +A UI/MailerUI/UIxMailSearch.h +A UI/MailerUI/UIxMailSearch.m +M UI/MailerUI/product.plist +M UI/Templates/MailerUI/UIxMailMainFrame.wox +A UI/Templates/MailerUI/UIxMailSearch.wox +M UI/WebServerResources/MailerUI.js +M UI/WebServerResources/SOGoDataTable.js +M UI/WebServerResources/SOGoMailDataSource.js +A UI/WebServerResources/UIxMailSearch.css +A UI/WebServerResources/UIxMailSearch.js +M UI/WebServerResources/generic.css +M UI/WebServerResources/generic.js + +commit 5b886e5c4dab6bdf486f937df8bb234c4902f50c +Author: Francis Lachapelle +Date: Fri Aug 1 12:26:53 2014 -0400 + + Fix issue with DnD of tasks with FireFox + +M UI/WebServerResources/SchedulerUI.js + +commit cde1db93245c8bdfbf8e69ed8efe8a0f6a03027c +Author: Ludovic Marcotte +Date: Fri Aug 1 09:40:45 2014 -0400 + + Refactored GCSFolder: -writeContent and fixed sogo-tool restore + +M SOPE/GDLContentStore/GCSFolder.h +M SOPE/GDLContentStore/GCSFolder.m +M SoObjects/Contacts/SOGoContactGCSFolder.h +M SoObjects/SOGo/SOGoContentObject.m +M Tools/SOGoToolRestore.m + +commit f43341c1005d3d5813c23133071c253c753bb3e9 +Author: Ludovic Marcotte +Date: Thu Jul 31 16:19:49 2014 -0400 + + Fixed JSON'ification of the admin module + +M UI/WebServerResources/AdministrationUI.js + +commit b05fcc7d82395fe2d988567c36d213aa1c6cfeae +Author: Ludovic Marcotte +Date: Wed Jul 30 14:51:00 2014 -0400 + + Fat commit to refactor the quick record generation + +M ActiveSync/common.make +M GNUmakefile +M Main/GNUmakefile.preamble +M NEWS +D OGoContentStore/COPYING +D OGoContentStore/GNUmakefile +D OGoContentStore/GNUmakefile.postamble +D OGoContentStore/GNUmakefile.preamble +D OGoContentStore/OCSContactFieldExtractor.m +D OGoContentStore/OCSiCalFieldExtractor.h +D OGoContentStore/OCSiCalFieldExtractor.m +D OGoContentStore/README +D OGoContentStore/Version +D OGoContentStore/appointment-oracle.ocs +D OGoContentStore/appointment.ocs +D OGoContentStore/contact-oracle.ocs +D OGoContentStore/contact.ocs +D OGoContentStore/iCalRepeatableEntityObject+OCS.h +D OGoContentStore/iCalRepeatableEntityObject+OCS.m +D OGoContentStore/misc/iNetOrgPerson.txt +D OGoContentStore/misc/icaltest.ical +D OGoContentStore/misc/test1/GNUmakefile +D OGoContentStore/misc/test1/common.h +D OGoContentStore/misc/test1/test1.m +M OpenChange/GNUmakefile +M OpenChange/MAPIStoreContactsMessage.m +M OpenChange/MAPIStoreTasksMessage.m +D SOPE/GDLContentStore/GCSFieldExtractor.h +D SOPE/GDLContentStore/GCSFieldExtractor.m +M SOPE/GDLContentStore/GCSFolder.h +M SOPE/GDLContentStore/GCSFolder.m +M SOPE/GDLContentStore/GCSFolderManager.m +M SOPE/GDLContentStore/GCSFolderType.h +M SOPE/GDLContentStore/GCSFolderType.m +M SOPE/GDLContentStore/GNUmakefile +A SOPE/GDLContentStore/GNUmakefile.postamble +A SOPE/GDLContentStore/appointment-oracle.ocs +A SOPE/GDLContentStore/appointment.ocs +A SOPE/GDLContentStore/contact-oracle.ocs +A SOPE/GDLContentStore/contact.ocs +M SoObjects/Appointments/SOGoAppointmentFolder.m +M SoObjects/Appointments/SOGoAppointmentObject.h +M SoObjects/Appointments/SOGoAppointmentObject.m +M SoObjects/Appointments/SOGoCalendarComponent.h +M SoObjects/Appointments/SOGoCalendarComponent.m +M SoObjects/Appointments/SOGoComponentOccurence.m +M SoObjects/Appointments/SOGoTaskObject.h +M SoObjects/Appointments/SOGoTaskObject.m +M SoObjects/Appointments/SOGoWebAppointmentFolder.h +M SoObjects/Appointments/SOGoWebAppointmentFolder.m +M SoObjects/Appointments/iCalCalendar+SOGo.m +M SoObjects/Appointments/iCalEntityObject+SOGo.h +M SoObjects/Appointments/iCalEntityObject+SOGo.m +M SoObjects/Appointments/iCalEvent+SOGo.h +M SoObjects/Appointments/iCalEvent+SOGo.m +M SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.h +M SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m +M SoObjects/Appointments/iCalToDo+SOGo.h +M SoObjects/Appointments/iCalToDo+SOGo.m +M SoObjects/Contacts/NGVCard+SOGo.m +M SoObjects/Contacts/NGVList+SOGo.h +M SoObjects/Contacts/NGVList+SOGo.m +M SoObjects/Contacts/SOGoContactGCSEntry.m +M SoObjects/Contacts/SOGoContactGCSList.m +M SoObjects/Contacts/SOGoContactLDIFEntry.m +M SoObjects/Mailer/SOGoDraftObject.m +M SoObjects/SOGo/GNUmakefile.preamble +M SoObjects/SOGo/SOGoContentObject.h +M SoObjects/SOGo/SOGoContentObject.m +M SoObjects/common.make +M Tests/Integration/GNUmakefile.preamble +M Tests/Unit/GNUmakefile +M Tools/GNUmakefile.preamble +M Tools/SOGoToolRestore.m +M UI/Contacts/UIxContactFolderActions.m +M UI/Contacts/UIxListEditor.m +M UI/MailPartViewers/UIxMailPartICalActions.m +M UI/Scheduler/UIxComponentEditor.m +M UI/Scheduler/UIxTaskEditor.m +M UI/common.make +M packaging/debian-multiarch/sogo.overrides +M packaging/debian/sogo.overrides +M packaging/rhel/sogo.spec + +commit 07324e56a0ab257334d4d4a67ed52ce8f1a844d9 +Author: Francis Lachapelle +Date: Wed Jul 30 11:16:35 2014 -0400 + + Update ChangeLog + +M ChangeLog + +commit 397a36cc89f0160bc84ba19b2f7adc2b96a6a5c1 +Author: Francis Lachapelle +Date: Wed Jul 30 11:16:10 2014 -0400 + + Preparation for release 2.2.7 + +M Documentation/SOGo Installation Guide.odt +M Documentation/SOGo Mozilla Thunderbird Configuration.odt +M Documentation/SOGo Native Microsoft Outlook Configuration.odt +M Version + +commit c240931c54873943349d0c65c1fb9b1dadcbd9c1 +Author: Francis Lachapelle +Date: Wed Jul 30 11:15:05 2014 -0400 + + Update CKEditor to version 4.4.3 + +M NEWS +M UI/WebServerResources/ckeditor/build-config.js +M UI/WebServerResources/ckeditor/ckeditor.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/colordialog/dialogs/colordialog.js +M UI/WebServerResources/ckeditor/plugins/image/dialogs/image.js +M UI/WebServerResources/ckeditor/plugins/tabletools/dialogs/tableCell.js +M UI/WebServerResources/ckeditor/skins/moono/dialog.css +M UI/WebServerResources/ckeditor/skins/moono/dialog_ie.css +M UI/WebServerResources/ckeditor/skins/moono/dialog_ie7.css +M UI/WebServerResources/ckeditor/skins/moono/dialog_ie8.css +M UI/WebServerResources/ckeditor/skins/moono/dialog_iequirks.css +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 + +commit ef2432e10f396886f09134ff39c9b90cac5e8c2b +Author: Francis Lachapelle +Date: Wed Jul 30 10:36:31 2014 -0400 + + Update NEWS file + +M NEWS + +commit 571322958a875e53dc0c689caf3f24a1debdea54 +Author: Francis Lachapelle +Date: Wed Jul 30 10:33:15 2014 -0400 + + Update fi, fr, de, and es_ES translations + +M SoObjects/Appointments/Finnish.lproj/Localizable.strings +M SoObjects/Appointments/French.lproj/Localizable.strings +M SoObjects/Appointments/German.lproj/Localizable.strings +M SoObjects/Appointments/SpanishSpain.lproj/Localizable.strings +M UI/Common/German.lproj/Localizable.strings +M UI/PreferencesUI/Finnish.lproj/Localizable.strings +M UI/PreferencesUI/French.lproj/Localizable.strings +M UI/PreferencesUI/German.lproj/Localizable.strings +M UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings +M UI/Scheduler/Finnish.lproj/Localizable.strings +M UI/Scheduler/French.lproj/Localizable.strings +M UI/Scheduler/German.lproj/Localizable.strings +M UI/Scheduler/SpanishSpain.lproj/Localizable.strings + +commit bd5c05cca2aa9166198c9c6ea6e9c583da9359bd +Author: Francis Lachapelle +Date: Wed Jul 30 10:31:25 2014 -0400 + + Indentation in JavaScript files + +M UI/WebServerResources/AdministrationUI.js +M UI/WebServerResources/ContactsUI.js +M UI/WebServerResources/MailerUI.js +M UI/WebServerResources/SchedulerUI.js +M UI/WebServerResources/UIxPreferences.js +M UI/WebServerResources/generic.js + +commit ea44308aa3d95d150b6d6565cdf4dbdc79e5c64e +Author: Francis Lachapelle +Date: Tue Jul 29 16:05:46 2014 -0400 + + Fix all-day events display in IE + + It also fixes the display of the event categories for all-day events in + IE. + +M NEWS +M UI/WebServerResources/SchedulerUI.css +M UI/WebServerResources/SchedulerUI.js + +commit 94f3246658c151b55ca86404c607fe29de7b94b1 +Author: Ludovic Marcotte +Date: Tue Jul 29 10:36:20 2014 -0400 + + Improved error message when you cannot invite users + +M SoObjects/Appointments/English.lproj/Localizable.strings +M SoObjects/Appointments/SOGoAppointmentObject.m + +commit 1df7e989e012fbbb3a93293ed944b2c80f48299f +Author: Ludovic Marcotte +Date: Fri Jul 25 14:48:25 2014 -0400 + + Removed worthless declaration + +M UI/Scheduler/UIxCalListingActions.m + +commit d9ca1051b31f7efd35083dc87bd59b143cb3e593 +Author: Francis Lachapelle +Date: Fri Jul 25 11:34:33 2014 -0400 + + Improve headers of multicolumns view + +M UI/Templates/SchedulerUI/UIxCalDayTable.wox +M UI/Templates/SchedulerUI/UIxCalendarSelector.wox +M UI/WebServerResources/SchedulerUI.css + +commit 4eefa4c2697d4da8ba72674a81f74492e01a11dd +Author: Ludovic Marcotte +Date: Fri Jul 25 08:12:12 2014 -0400 + + Fixed test since we now return a 200 instead of a 204 + +M Tests/Integration/utilities.py + +commit 4c21a050d91e5969921db8f9971752863a7edd48 +Author: Ludovic Marcotte +Date: Thu Jul 24 14:00:57 2014 -0400 + + JSON interface to folder subscriptions + +M NEWS +M UI/MainUI/SOGoUserHomePage.m +M UI/WebServerResources/UIxContactsUserFolders.js + +commit b52289904f607121627a5cf3298f7124a5042d22 +Author: Alexandre Cloutier +Date: Thu Jul 24 13:48:41 2014 -0400 + + task.png for DnD taskList + +A UI/WebServerResources/task.png + +commit c389630d76b4d72e10552be32f8482b4807c1558 +Author: Alexandre Cloutier +Date: Thu Jul 24 13:35:14 2014 -0400 + + DnD tasksList + +M UI/WebServerResources/SchedulerUI.css +M UI/WebServerResources/SchedulerUI.js + +commit f6ef94a6314e3fe7d5b863d20a0df4580e8ca7bb +Author: Ludovic Marcotte +Date: Thu Jul 24 12:48:03 2014 -0400 + + improved handling of SOGoSubscriptionFolderFormat + +M NEWS +M SoObjects/Appointments/SOGoAppointmentFolders.m +M SoObjects/SOGo/SOGoGCSFolder.m +M SoObjects/SOGo/SOGoUserFolder.m +M UI/Common/UIxAclEditor.m +M UI/Common/UIxFolderActions.m +M UI/MainUI/SOGoUserHomePage.m +M UI/WebServerResources/UIxContactsUserFolders.js + +commit 5de51e8aa1facd6ddaf1162f31f9e42ca810d125 +Author: Francis Lachapelle +Date: Thu Jul 24 09:30:19 2014 -0400 + + Fix missing argument with new DnD controller + +M UI/WebServerResources/SchedulerUIDnD.js + +commit 006727a74b42ee0fe5fcb479a9ab8080db14602b +Author: Ludovic Marcotte +Date: Thu Jul 24 08:39:41 2014 -0400 + + Properly destroy cache objects of vanished folders + +M ActiveSync/SOGoActiveSyncDispatcher.m +M NEWS +M SoObjects/SOGo/SOGoCacheGCSObject.m +M SoObjects/SOGo/SOGoCacheObject.h + +commit 6619840f80dab04eacac6a7fe66436443796bfd8 +Author: Jeroen Dekkers +Date: Tue Jul 22 19:54:18 2014 +0200 + + Add Mailer and Appointments to product.list requires of MailPartViewers + +M UI/MailPartViewers/product.plist + +commit 08fd2fcfaa8488a8460c0b555b45f4950f1010a2 +Author: Francis Lachapelle +Date: Tue Jul 22 13:26:28 2014 -0400 + + Fix JavaScript after bad merge + +M UI/WebServerResources/SchedulerUI.js + +commit a43dd97fc939692f74f38dfcd2064147e8cafbdc +Author: Alexandre Cloutier +Date: Tue Jul 22 11:37:45 2014 -0400 + + Fix bug with DnD; leftPanelController.stopEvent() + +M UI/WebServerResources/SchedulerUIDnD.js + +commit 192659fdd9d64ddd63f8dcd014a14a3b4e7fad15 +Author: Francis Lachapelle +Date: Mon Jul 21 16:12:27 2014 -0400 + + Add missing localizable string + +M UI/PreferencesUI/English.lproj/Localizable.strings + +commit 06fe36fd75aaedd05ff7e92cad1320f4dff63160 +Author: Francis Lachapelle +Date: Mon Jul 21 16:07:13 2014 -0400 + + Add missing localizable strings + +M UI/PreferencesUI/English.lproj/Localizable.strings +M UI/Templates/PreferencesUI/UIxPreferences.wox + +commit 24f9d83ff7e8503118aabd02598f3300a07a646b +Author: Francis Lachapelle +Date: Mon Jul 21 16:06:24 2014 -0400 + + Fix JavaScript from merge leftover + +M UI/WebServerResources/SchedulerUI.js + +commit 8dd967d0a87a7ee560b854f667fb55c296b89245 +Author: Ludovic Marcotte +Date: Mon Jul 21 15:38:23 2014 -0400 + + Updated NEWS file for two merged pull requests + +M NEWS + +commit 08cd080d47f4ef0d0d262021e173b5d6c6519276 +Author: Ludovic Marcotte +Date: Mon Jul 21 15:34:56 2014 -0400 + + Fixed charset substitution in meta tags + +M NEWS +M UI/MailPartViewers/UIxMailPartHTMLViewer.m + +commit 28e6bb8b9d521dad8801206655c30d57ef3f9ff6 +Author: Alexandre Cloutier +Date: Fri Jul 18 15:03:28 2014 -0400 + + Applied comments + +M UI/Scheduler/English.lproj/Localizable.strings +M UI/Templates/SchedulerUI/UIxCalMainView.wox +M UI/WebServerResources/SchedulerUI.css +M UI/WebServerResources/SchedulerUI.js +M UI/WebServerResources/SchedulerUIDnD.js + +commit b892b3f7946e4466a51af901fb4b77da75872d40 +Author: Alexandre Cloutier +Date: Fri Jul 18 10:14:38 2014 -0400 + + Add RegEx + +M UI/Scheduler/English.lproj/Localizable.strings +M UI/WebServerResources/SchedulerUI.js + +commit cdf4cc1acb98f451bec00ab7d6e3858937c293a8 +Author: Alexandre Cloutier +Date: Fri Jul 18 09:46:31 2014 -0400 + + fix DnD from eventList to calendarList for repeated event + +M UI/WebServerResources/SchedulerUI.js + +commit 3cac794de5dbda0f005901cbb081b2ee5fa01e7f +Author: Alexandre Cloutier +Date: Fri Jul 18 09:33:51 2014 -0400 + + move line + +M UI/WebServerResources/SchedulerUIDnD.js + +commit 8f9b24a23f593eb2041cfaaca3e4fb611ddd7060 +Author: Alexandre Cloutier +Date: Fri Jul 18 09:23:07 2014 -0400 + + visual helper while dragging from calendar view to calendarList + +M UI/Templates/SchedulerUI/UIxCalMainView.wox +M UI/WebServerResources/SchedulerUI.css +M UI/WebServerResources/SchedulerUIDnD.js + +commit a2a7014140ce8abf97ce9b04b9eb7467deda192b +Author: Alexandre Cloutier +Date: Thu Jul 17 09:42:32 2014 -0400 + + fix dnd repeated event bug + applied comments + +M UI/Scheduler/UIxAppointmentActions.m +M UI/WebServerResources/SchedulerUI.js +M UI/WebServerResources/SchedulerUIDnD.js + +commit a5ecb2063f449505ab671e3952497f4484b12bec +Author: Alexandre Cloutier +Date: Wed Jul 16 10:30:48 2014 -0400 + + added event7.png and adjust javascript indentation + +M UI/WebServerResources/SchedulerUI.js +M UI/WebServerResources/SchedulerUIDnD.js +A UI/WebServerResources/event7.png + +commit 9bd7aeebfb5ea41c078c7e9691fe0c0e9513eb12 +Author: Alexandre Cloutier +Date: Tue Jul 15 17:29:39 2014 -0400 + + DnD from calendarView to CalendarList + +M UI/WebServerResources/SchedulerUIDnD.js + +commit 01af72cfe95e1970fa84405732690355ed72dcd8 +Author: Alexandre Cloutier +Date: Tue Jul 15 09:49:48 2014 -0400 + + DnD from eventList to the calendarList + +M UI/WebServerResources/SchedulerUI.css +M UI/WebServerResources/SchedulerUI.js + +commit 6dddb5c98ce402268205eaa4fc7eec3068380f9f +Author: Alexandre Cloutier +Date: Fri Jul 11 17:04:06 2014 -0400 + + dragNdrop from the events table to the calendars list + +M UI/Templates/SchedulerUI/UIxCalMainView.wox +M UI/WebServerResources/SchedulerUI.js +M UI/WebServerResources/UIxOccurenceDialog.js + +commit ccf181bcf95a8bebf6c4ab9bdb66c151548cc00d +Author: Alexandre Cloutier +Date: Thu Jul 10 13:34:47 2014 -0400 + + applied comments and fix repeated events dragNdrop between calendars + +M UI/Scheduler/UIxAppointmentActions.m +M UI/WebServerResources/SchedulerUI.js + +commit e478b29c2e40963041abf63c678a7e36d287ad2f +Author: Alexandre Cloutier +Date: Wed Jul 9 16:47:13 2014 -0400 + + creating and dragNDrop between calendars in multicolumndayview + +M UI/Scheduler/UIxAppointmentActions.m +M UI/Scheduler/UIxCalDayTable.m +M UI/Templates/SchedulerUI/UIxCalDayTable.wox +M UI/WebServerResources/SchedulerUI.js +M UI/WebServerResources/SchedulerUIDnD.js + +commit c2027f072d0b59944882ca39fc97f573a2157fd7 +Author: Alexandre Cloutier +Date: Fri Jul 18 13:57:18 2014 -0400 + + indentation + +M SoObjects/Appointments/SOGoAppointmentObject.m + +commit ce6a31dd38aa50736eae48eb073deb991b87173a +Author: Alexandre Cloutier +Date: Fri Jul 18 09:26:02 2014 -0400 + + Adjust line of code to make it easier to read + +M SoObjects/Appointments/SOGoAppointmentObject.m + +commit a71013eebf8e7dd088a55cd18554d4de9c69369b +Author: Alexandre Cloutier +Date: Wed Jul 16 14:26:30 2014 -0400 + + javascript indentation + +M UI/WebServerResources/UIxPreferences.js + +commit eaec5a614d713d397b032f58f6d56ac3034165c6 +Author: Alexandre Cloutier +Date: Wed Jul 16 14:08:39 2014 -0400 + + Change exception 403 to 409 and fix indentation in the javascript + +M SoObjects/Appointments/SOGoAppointmentObject.m +M UI/WebServerResources/UIxPreferences.js + +commit 5217bed5ae3e298adc48a4c2e8c94ca1ab0e4891 +Author: Alexandre Cloutier +Date: Wed Jul 16 14:01:50 2014 -0400 + + Organized the categories and appointments invitations inside a tabContainer + +M UI/Templates/PreferencesUI/UIxPreferences.wox +M UI/WebServerResources/UIxPreferences.css +M UI/WebServerResources/UIxPreferences.js +M UI/WebServerResources/generic.css + +commit 17319cc8dd39c46a3f0c7c48e1ef9d182d06a663 +Author: Alexandre Cloutier +Date: Wed Jul 16 10:37:27 2014 -0400 + + reverted UIxListEditor.wox + +M UI/Templates/ContactsUI/UIxListEditor.wox + +commit 89d116363ca6a27b08b3b3e6812669ebbbd69ea0 +Author: Alexandre Cloutier +Date: Wed Jul 16 10:03:13 2014 -0400 + + replace ampersands with commas + +M SoObjects/Appointments/SOGoAppointmentObject.m + +commit 7c3e85f517a56ca66f540a8468b75010a5943f5f +Author: Alexandre Cloutier +Date: Tue Jul 15 17:34:49 2014 -0400 + + applied comments + +M SoObjects/Appointments/SOGoAppointmentObject.m + +commit 05fb7ae0c88a1f9b47d636caabaab96bea426309 +Author: Alexandre Cloutier +Date: Thu Jul 10 11:13:07 2014 -0400 + + applied comments + +M SoObjects/Appointments/SOGoAppointmentObject.m +M UI/PreferencesUI/UIxPreferences.m +M UI/Templates/PreferencesUI/UIxPreferences.wox +M UI/WebServerResources/UIxPreferences.js + +commit ef20108e3297b5d644044307ba84fd2a6c6fbdbc +Author: Alexandre Cloutier +Date: Wed Jul 9 17:26:41 2014 -0400 + + fix bug where the user could enter any names in the whitelist + +M UI/WebServerResources/UIxPreferences.js + +commit b5c0363400a48610f782c06dd0fdba21cc7cc478 +Author: Alexandre Cloutier +Date: Mon Jul 7 17:42:43 2014 -0400 + + applied comment and refactor code + +M SoObjects/Appointments/SOGoAppointmentObject.m +M UI/PreferencesUI/UIxPreferences.h +M UI/PreferencesUI/UIxPreferences.m +M UI/WebServerResources/UIxPreferences.js + +commit f0b8eb8dfb6d19fdc82fe92a796c9e2b9a9c2aa3 +Author: Alexandre Cloutier +Date: Mon Jul 7 13:38:37 2014 -0400 + + server side code to integrate the whitelist + +M SoObjects/Appointments/English.lproj/Localizable.strings +M SoObjects/Appointments/SOGoAppointmentObject.m +M UI/PreferencesUI/UIxPreferences.m +M UI/WebServerResources/UIxPreferences.js + +commit 16d02455bdcb5e8d2e2c5b4dc160b294895da82c +Author: Alexandre Cloutier +Date: Mon Jul 7 10:00:16 2014 -0400 + + Serialization of the whiteList + +M UI/PreferencesUI/UIxPreferences.m +M UI/Templates/PreferencesUI/UIxPreferences.wox +M UI/WebServerResources/UIxPreferences.js + +commit 8ded5a8aaff1e7b4025b6ffe35e3e36d64ebd687 +Author: Alexandre Cloutier +Date: Fri Jul 4 09:51:41 2014 -0400 + + Prevent Invitations and whitelist GUI + +M SoObjects/Appointments/SOGoAppointmentObject.m +M UI/PreferencesUI/UIxPreferences.h +M UI/PreferencesUI/UIxPreferences.m +M UI/Templates/ContactsUI/UIxListEditor.wox +M UI/Templates/PreferencesUI/UIxPreferences.wox +M UI/WebServerResources/SOGoAutoCompletion.js +M UI/WebServerResources/UIxPreferences.css +M UI/WebServerResources/UIxPreferences.js + +commit 1cc93c700a0925f84262896ae6d28f90395a8b51 +Author: Ludovic Marcotte +Date: Wed Jul 9 14:08:31 2014 -0400 + + Updated NEWS file for changes occurred right after v2.2.6 was released + +M NEWS + +commit 44d0cb5c0d57de6548148ea204f49a5d697e8358 +Author: Francis Lachapelle +Date: Wed Jul 9 11:17:36 2014 -0400 + + Fix refresh of list when forwarding a message + + Fixes #2818 + +M NEWS +M SoObjects/Mailer/SOGoDraftObject.h +M SoObjects/Mailer/SOGoDraftObject.m +M UI/MailerUI/UIxMailEditor.m +M UI/WebServerResources/MailerUI.js +M UI/WebServerResources/UIxMailEditor.js + +commit dbdb7d90513aa54c2aa314013f236c434eeb0082 +Author: Ludovic Marcotte +Date: Wed Jul 9 10:04:44 2014 -0400 + + Fix sending of METHOD:REPLY for DAV clients + +M NEWS +M SoObjects/Appointments/SOGoAppointmentObject.m + +commit e343ed240b8de3b06da82a8777fe5d639cbf6a1c +Author: Francis Lachapelle +Date: Tue Jul 8 22:00:06 2014 -0400 + + Fix regression of word breaking + + Fixes #2845 + +M UI/WebServerResources/UIxCalViewPrint.css +M UI/WebServerResources/generic.css + +commit 45553be122c5dd12b0373e3e6fdc4bd2f1aeef4b +Author: Francis Lachapelle +Date: Tue Jul 8 17:19:55 2014 -0400 + + Fix rename of calendars, improve tasks count badge + +M NEWS +M UI/Scheduler/UIxCalendarSelector.m +M UI/Templates/ContactsUI/UIxContactFoldersView.wox +M UI/Templates/SchedulerUI/UIxCalendarSelector.wox +M UI/WebServerResources/ContactsUI.js +M UI/WebServerResources/SchedulerUI.css +M UI/WebServerResources/SchedulerUI.js +M UI/WebServerResources/UIxContactFolderProperties.js +M UI/WebServerResources/generic.css +M UI/WebServerResources/generic.js + +commit 1cf4841477dea27261c5654717cabab1ea6ac43e +Author: Alexandre Cloutier +Date: Fri Jul 4 11:23:59 2014 -0400 + + fix display glitch + +M UI/WebServerResources/SchedulerUI.js + +commit 017a85b7f2c91827615ab1adfbaab6dc4fef9707 +Author: Alexandre Cloutier +Date: Fri Jul 4 10:46:49 2014 -0400 + + fix month events categories display + +M UI/WebServerResources/SchedulerUI.js + +commit 8c8e47daf632ce5146df347c407cbdc5ba858681 +Author: Alexandre Cloutier +Date: Fri Jul 4 10:37:35 2014 -0400 + + fix spacing between events and categorie display + +M UI/WebServerResources/SchedulerUI.js + +commit 8a172467253db8e129a0926d949b86410e50d811 +Author: Ludovic Marcotte +Date: Fri Jul 4 10:18:56 2014 -0400 + + Cleaned up code and fixed the translation of the weekdays in the datepicker + +M UI/Scheduler/UIxDatePicker.h +M UI/Scheduler/UIxDatePicker.m +M UI/WebServerResources/datepicker.js + +commit 4843c6b6d7f05e32047a27e1c6cd28a1912e615d +Author: Ludovic Marcotte +Date: Fri Jul 4 08:55:35 2014 -0400 + + Renamed Multi-Column to Multicolumn and refactored some code + +M UI/Scheduler/English.lproj/Localizable.strings +M UI/Scheduler/Toolbars/SOGoAppointmentFolders.toolbar +M UI/Scheduler/UIxCalViewPrint.m + +commit 6e13f1547719c5f3bfc6149d9a038de90c284cb2 +Author: Ludovic Marcotte +Date: Wed Jul 2 11:32:18 2014 -0400 + + Update ChangeLog + +M ChangeLog + commit 5dd2088d42388b397e8cc3fde35455b2455f7f3e Author: Ludovic Marcotte Date: Wed Jul 2 11:32:06 2014 -0400 diff --git a/Documentation/SOGo Installation Guide.odt b/Documentation/SOGo Installation Guide.odt index 81fbe0883..64513c61f 100644 Binary files a/Documentation/SOGo Installation Guide.odt and b/Documentation/SOGo Installation Guide.odt differ diff --git a/Documentation/SOGo Mozilla Thunderbird Configuration.odt b/Documentation/SOGo Mozilla Thunderbird Configuration.odt index 97588c202..01702b8e5 100644 Binary files a/Documentation/SOGo Mozilla Thunderbird Configuration.odt and b/Documentation/SOGo Mozilla Thunderbird Configuration.odt differ diff --git a/Documentation/SOGo Native Microsoft Outlook Configuration.odt b/Documentation/SOGo Native Microsoft Outlook Configuration.odt index 7651f52ed..ed89ec8c0 100644 Binary files a/Documentation/SOGo Native Microsoft Outlook Configuration.odt and b/Documentation/SOGo Native Microsoft Outlook Configuration.odt differ diff --git a/GNUmakefile b/GNUmakefile index 957160740..fdff4d793 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -6,7 +6,6 @@ include $(GNUSTEP_MAKEFILES)/common.make SUBPROJECTS = \ SOPE/NGCards \ SOPE/GDLContentStore \ - OGoContentStore \ SoObjects \ Main \ UI \ diff --git a/Main/GNUmakefile.preamble b/Main/GNUmakefile.preamble index 675b5916c..022e3d1e2 100644 --- a/Main/GNUmakefile.preamble +++ b/Main/GNUmakefile.preamble @@ -10,14 +10,12 @@ ADDITIONAL_INCLUDE_DIRS += \ ADDITIONAL_LIB_DIRS += \ -L../SoObjects/SOGo/SOGo.framework \ - -L../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ \ -L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib $(SOGOD)_TOOL_LIBS += \ -lSOGo \ - -lOGoContentStore \ -lGDLContentStore \ -lGDLAccess \ -lWEExtensions \ diff --git a/NEWS b/NEWS index 4b233c364..88c22f938 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,59 @@ +2.2.8 (2014-09-10) +------------------ + +New features + - new user settings for threads collapsing + - IMAP global search support (#2670) + +Enchancements + - major refactoring of the GCS component saving code (dropped OGoContentStore) + - printing calendars in colors is now possible in all views; list, daily, weekly and multicolumns + - new option to print calendars events and tasks with a background color or with a border color + - labels tagging only make one AJAX call for all the selected messages instead of one AJAX call per message + - new option to print calendars events and tasks with a background color or with a border color + - all modules can now be automatically refreshed + - new configurable user defaults variables; SOGoRefreshViewCheck & SOGoRefreshViewIntervals. SOGoMailMessageCheck has + been replaced by SOGoRefreshViewCheck and SOGoMailPollingIntervals has been replaced by SOGoRefreshViewIntervals + - updated Catalan, Czech, Dutch, Finnish, French, Hungarian, Norwegian, and Polish translations + +Bug fixes + - fixed crasher when subscribing users to resources (#2892) + - fixed encoding of new calendars and new subscriptions (JavaScript only) + - fixed display of users with no possible subscription + - fixed usage of SOGoSubscriptionFolderFormat domain default when the folder's name hasn't been changed + - fixed "sogo-tool restore -l" that was returning incorrect folder IDs + - fixed Can not delete mail when over quota (#2812) + - fixed Events and tasks cannot be moved to other calendars using drag&drop (#2759) + - fixed In "Multicolumn Day View" mouse position is not honored when creating an event (#2864) + - fixed handling of messages labels (#2902) + - fixed Apache > 2.3 configuration + - fixed freebusy retrieval during timezone changes (#1240) + +2.2.7 (2014-07-30) +------------------ + +New features + - new user preference to prevent event invitations + +Enhancements + - improved badges of active tasks count + - refresh draft folder after sending a message + - now possible to DnD events in the calendar list + - improved handling of SOGoSubscriptionFolderFormat + - JSON'ified folder subscription interface + - updated Finnish, French, German, and Spanish (Spain) translations + - updated CKEditor to version 4.4.3 + +Bug fixes + - fixed weekdays translation in the datepicker + - fixed event categories display + - fixed all-day events display in IE + - fixed rename of calendars + - we now correctly add the "METHOD:REPLY" when sending out ITIP messages from DAV clients + - fixed refresh of message headers when forwarding a message (#2818) + - we now correctly escape all charset= in tags, not only in the + - we now destroy cache objects of vanished folders + 2.2.6 (2014-07-02) ------------------ @@ -9,7 +65,7 @@ Enhancements - implemented the GetAttachment ActiveSync command (#2808) - implemented the Ping ActiveSync command - added "soft deletes" support for ActiveSync (#2734) - - now display the active tasks next to calendar names (#2760) + - now display the active tasks count next to calendar names (#2760) Bug fixes - better handling of empty "Flag" messages over ActiveSync (#2806) diff --git a/OGoContentStore/COPYING b/OGoContentStore/COPYING deleted file mode 100644 index 161a3d1d4..000000000 --- a/OGoContentStore/COPYING +++ /dev/null @@ -1,482 +0,0 @@ - GNU LIBRARY GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1991 Free Software Foundation, Inc. - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the library GPL. It is - numbered 2 because it goes with version 2 of the ordinary GPL.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Library General Public License, applies to some -specially designated Free Software Foundation software, and to any -other libraries whose authors decide to use it. You can use it for -your libraries, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if -you distribute copies of the library, or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link a program with the library, you must provide -complete object files to the recipients so that they can relink them -with the library, after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - Our method of protecting your rights has two steps: (1) copyright -the library, and (2) offer you this license which gives you legal -permission to copy, distribute and/or modify the library. - - Also, for each distributor's protection, we want to make certain -that everyone understands that there is no warranty for this free -library. If the library is modified by someone else and passed on, we -want its recipients to know that what they have is not the original -version, so that any problems introduced by others will not reflect on -the original authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that companies distributing free -software will individually obtain patent licenses, thus in effect -transforming the program into proprietary software. To prevent this, -we have made it clear that any patent must be licensed for everyone's -free use or not licensed at all. - - Most GNU software, including some libraries, is covered by the ordinary -GNU General Public License, which was designed for utility programs. This -license, the GNU Library General Public License, applies to certain -designated libraries. This license is quite different from the ordinary -one; be sure to read it in full, and don't assume that anything in it is -the same as in the ordinary license. - - The reason we have a separate public license for some libraries is that -they blur the distinction we usually make between modifying or adding to a -program and simply using it. Linking a program with a library, without -changing the library, is in some sense simply using the library, and is -analogous to running a utility program or application program. However, in -a textual and legal sense, the linked executable is a combined work, a -derivative of the original library, and the ordinary General Public License -treats it as such. - - Because of this blurred distinction, using the ordinary General -Public License for libraries did not effectively promote software -sharing, because most developers did not use the libraries. We -concluded that weaker conditions might promote sharing better. - - However, unrestricted linking of non-free programs would deprive the -users of those programs of all benefit from the free status of the -libraries themselves. This Library General Public License is intended to -permit developers of non-free programs to use free libraries, while -preserving your freedom as a user of such programs to change the free -libraries that are incorporated in them. (We have not seen how to achieve -this as regards changes in header files, but we have achieved it as regards -changes in the actual functions of the Library.) The hope is that this -will lead to faster development of free libraries. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, while the latter only -works together with the library. - - Note that it is possible for a library to be covered by the ordinary -General Public License rather than by this special one. - - GNU LIBRARY GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library which -contains a notice placed by the copyright holder or other authorized -party saying it may be distributed under the terms of this Library -General Public License (also called "this License"). Each licensee is -addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also compile or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - c) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - d) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the source code distributed need not include anything that is normally -distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Library General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - Appendix: How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/OGoContentStore/GNUmakefile b/OGoContentStore/GNUmakefile deleted file mode 100644 index 3b7a48a62..000000000 --- a/OGoContentStore/GNUmakefile +++ /dev/null @@ -1,34 +0,0 @@ -# GNUstep makefile - -include ../config.make -include $(GNUSTEP_MAKEFILES)/common.make -include ./Version - -LIBRARY_NAME = libOGoContentStore - -TYPEMODELS_DIR = $(SOGO_TYPEMODELS) - -libOGoContentStore_HEADER_FILES_DIR = . -libOGoContentStore_HEADER_FILES_INSTALL_DIR = /OGoContentStore -libOGoContentStore_INTERFACE_VERSION=$(MAJOR_VERSION).$(MINOR_VERSION) - -ADDITIONAL_INCLUDE_DIRS += -I../SOPE -ADDITIONAL_LIB_DIRS += -L../SOPE/GDLContentStore/obj - -libOGoContentStore_OBJC_FILES += \ - OCSiCalFieldExtractor.m \ - OCSContactFieldExtractor.m \ - -libOGoContentStore_TYPEMODELS += \ - appointment.ocs \ - contact.ocs \ - appointment-oracle.ocs \ - contact-oracle.ocs - --include GNUmakefile.preamble -ifneq ($(FHS_INSTALL_ROOT),) -GNUSTEP_HEADERS=$(DESTDIR)$(FHS_INSTALL_ROOT)/include -endif -include $(GNUSTEP_MAKEFILES)/library.make -include $(GNUSTEP_MAKEFILES)/tool.make --include GNUmakefile.postamble diff --git a/OGoContentStore/GNUmakefile.preamble b/OGoContentStore/GNUmakefile.preamble deleted file mode 100644 index 3a458580d..000000000 --- a/OGoContentStore/GNUmakefile.preamble +++ /dev/null @@ -1,32 +0,0 @@ -# compilation settings - -libOGoContentStore_LIBRARIES_DEPEND_UPON += \ - -lGDLContentStore \ - -lGDLAccess \ - -lNGCards \ - -lNGExtensions \ - -lEOControl \ - -lSaxObjC \ - $(BASE_LIBS) - -ADDITIONAL_INCLUDE_DIRS += -I. -I.. -I../SOPE -I../SoObjects - -ADDITIONAL_LIB_DIRS += -L./$(GNUSTEP_OBJ_DIR) -L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ -L../SoObjects/SOGo/SOGo.framework/ - -SYSTEM_LIB_DIR += -L/usr/local/lib -L/usr/lib - -ifeq ($(FOUNDATION_LIB),apple) -libOGoContentStore_PREBIND_ADDR="0xC7700000" -libOGoContentStore_LDFLAGS += -seg1addr $(libOGoContentStore_PREBIND_ADDR) -endif - -test_quick_extract_TOOL_LIBS += \ - -lEOControl \ - -lSaxObjC \ - -lDOM \ - -lNGExtensions \ - -lNGCards \ - -lGDLContentStore \ - -lGDLAccess \ - -lOGoContentStore \ - $(BASE_LIBS) diff --git a/OGoContentStore/OCSContactFieldExtractor.m b/OGoContentStore/OCSContactFieldExtractor.m deleted file mode 100644 index 8b1445316..000000000 --- a/OGoContentStore/OCSContactFieldExtractor.m +++ /dev/null @@ -1,136 +0,0 @@ -/* - Copyright (C) 2004-2005 SKYRIX Software AG - - This file is part of OpenGroupware.org. - - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - OGo 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 Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -#import -#import -#import -#import - -#import -#import -#import - -#import - -@interface OCSContactFieldExtractor : GCSFieldExtractor -@end - -@implementation OCSContactFieldExtractor - -- (NSMutableDictionary *) extractQuickFieldsFromVCard: (NGVCard *) vCard -{ - NSMutableDictionary *fields; - NSArray *values; - CardElement *element; - NSString *value; - - fields = [NSMutableDictionary dictionaryWithCapacity: 16]; - - value = [vCard fn]; - if (value) - [fields setObject: value forKey: @"c_cn"]; - element = [vCard n]; - [fields setObject: [element flattenedValueAtIndex: 0 forKey: @""] - forKey: @"c_sn"]; - [fields setObject: [element flattenedValueAtIndex: 1 forKey: @""] - forKey: @"c_givenName"]; - value = [vCard preferredTel]; - if (value) - [fields setObject: value forKey: @"c_telephonenumber"]; - value = [vCard preferredEMail]; - if (![value isNotNull]) - value = @""; - [fields setObject: value forKey: @"c_mail"]; - element = [vCard org]; - [fields setObject: [element flattenedValueAtIndex: 0 forKey: @""] - forKey: @"c_o"]; - [fields setObject: [element flattenedValueAtIndex: 1 forKey: @""] - forKey: @"c_ou"]; - element = [vCard preferredAdr]; - if (element && ![element isVoid]) - [fields setObject: [element flattenedValueAtIndex: 3 - forKey: @""] - forKey: @"c_l"]; - value = [[vCard uniqueChildWithTag: @"X-AIM"] flattenedValuesForKey: @""]; - [fields setObject: value forKey: @"c_screenname"]; - values = [[vCard categories] trimmedComponents]; - if ([values count] > 0) - [fields setObject: [values componentsJoinedByString: @","] - forKey: @"c_categories"]; - else - [fields setObject: [NSNull null] forKey: @"c_categories"]; - [fields setObject: @"vcard" forKey: @"c_component"]; - - return fields; -} - -- (NSMutableDictionary *) extractQuickFieldsFromVList: (NGVList *) vList -{ - NSMutableDictionary *fields; - NSString *value; - - fields = [NSMutableDictionary dictionaryWithCapacity: 1]; - - value = [vList fn]; - if (value) - [fields setObject: value forKey: @"c_cn"]; - [fields setObject: @"vlist" forKey: @"c_component"]; - - return fields; -} - -- (NSMutableDictionary *) extractQuickFieldsFromContent: (NSString *) content -{ - NSMutableDictionary *fields; - NGVCard *vCard; - NGVList *vList; - NSString *upperContent; - - fields = nil; - if ([content length] > 0) - { - upperContent = [content uppercaseString]; - if ([upperContent hasPrefix: @"BEGIN:VCARD"]) - { - vCard = [NGVCard parseSingleFromSource: content]; - if (vCard) - fields = [self extractQuickFieldsFromVCard: vCard]; - else - [self errorWithFormat: @"Could not parse VCARD content."]; - } - else if ([upperContent hasPrefix: @"BEGIN:VLIST"]) - { - vList = [NGVList parseSingleFromSource: content]; - if (vList) - fields = [self extractQuickFieldsFromVList: vList]; - else - [self errorWithFormat: @"Could not parse VLIST content."]; - } - else - [self errorWithFormat: @"Content is unknown."]; - } - else - [self errorWithFormat: @"Content is empty."]; - - return fields; -} - -@end /* OCSContactFieldExtractor */ diff --git a/OGoContentStore/OCSiCalFieldExtractor.h b/OGoContentStore/OCSiCalFieldExtractor.h deleted file mode 100644 index 504d2842c..000000000 --- a/OGoContentStore/OCSiCalFieldExtractor.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - Copyright (C) 2004-2005 SKYRIX Software AG - - This file is part of OpenGroupware.org. - - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - OGo 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 Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -#ifndef __OGoContentStore_OCSiCalFieldExtractor_H__ -#define __OGoContentStore_OCSiCalFieldExtractor_H__ - -#include - -@interface OCSiCalFieldExtractor : GCSFieldExtractor -{ -} - -+ (id)sharedICalFieldExtractor; - -@end - -#endif /* __OGoContentStore_OCSiCalFieldExtractor_H__ */ diff --git a/OGoContentStore/OCSiCalFieldExtractor.m b/OGoContentStore/OCSiCalFieldExtractor.m deleted file mode 100644 index 3bd604720..000000000 --- a/OGoContentStore/OCSiCalFieldExtractor.m +++ /dev/null @@ -1,96 +0,0 @@ -/* - Copyright (C) 2004 SKYRIX Software AG - - This file is part of OpenGroupware.org. - - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - OGo 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 Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with SOGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -#import -#import -#import -#import - -#import -#import -#import - -#import - -#import "OCSiCalFieldExtractor.h" - -@implementation OCSiCalFieldExtractor - -+ (id) sharedICalFieldExtractor -{ - static OCSiCalFieldExtractor *extractor = nil; - - if (!extractor) - extractor = [self new]; - - return extractor; -} - -/* operations */ - -- (CardGroup *) firstElementFromCalendar: (iCalCalendar *) ical -{ - NSArray *elements; - CardGroup *element; - unsigned int count; - - elements = [ical allObjects]; - count = [elements count]; - if (count) - element = [elements objectAtIndex: 0]; - else - { - [self logWithFormat: @"ERROR: given calendar contains no elements: %@", ical]; - element = nil; - } - - return element; -} - -- (NSMutableDictionary *) extractQuickFieldsFromContent: (NSString *) _content -{ - NSMutableDictionary *fields; - id cal; - - fields = nil; - - if ([_content length]) - { - cal = [iCalCalendar parseSingleFromSource: _content]; - if (cal) - { - if ([cal isKindOfClass: [iCalCalendar class]]) - cal = [self firstElementFromCalendar: cal]; - - if ([cal isKindOfClass: [iCalRepeatableEntityObject class]]) - fields = [cal quickRecord]; - else if ([cal isNotNull]) - [self logWithFormat: @"ERROR: unexpected iCalendar parse result: %@", - cal]; - } - else - [self logWithFormat: @"ERROR: parsing source didn't return anything"]; - } - - return fields; -} - -@end /* OCSiCalFieldExtractor */ diff --git a/OGoContentStore/README b/OGoContentStore/README deleted file mode 100644 index 4c2b1d3b6..000000000 --- a/OGoContentStore/README +++ /dev/null @@ -1,144 +0,0 @@ -Storage Backend -=============== - -The storage backend implements the "low level" folder abstraction, which is -basically an arbitary "BLOB" containing some document. The feature is that -we extract "quick access" / "searchable" attributes from the document content. - -Further it contains the "folder management" API, as named folders can be stored -in different databases. -Note: we need a way to tell where "new" folders should be created -Note: to sync with LDAP we need to periodically delete or archive old folders - -Folders have associated a type (like 'calendar') which defines the query -attributes and serialization format. - -TODO -==== -- hierarchies deeper than 4 (properly filter on path in OCS) - -Open Questions -============== - -System-meta-data in the blob-table or in the quick-table? -- master data belongs into the blob table -- could be regular 'NSxxx' keys to differentiate meta data from - -Class Hierarchy -=============== - - [NSObject] - OCSContext - tracking context - OCSFolder - represents a single folder - OCSFolderManager - manages folders - OCSFolderType - the mapping info for a specific folder-type - OCSFieldInfo - mapping info for one 'quick field' - OCSChannelManager - maintains EOAdaptorChannel objects - - TBD: - - field 'extractor' - - field 'value' (eg array values for participants?) - - BLOB archiver/unarchiver - -Defaults -======== - - OCSFolderInfoURL - the DB URL where the folder-info table is located - eg: http://OGo:OGo@localhost/test/folder_info - - OCSFolderManagerDebugEnabled - enable folder-manager debug logs - OCSFolderManagerSQLDebugEnabled - enable folder-manager SQL gen debug logs - - OCSChannelManagerDebugEnabled - enable channel debug pooling logs - OCSChannelManagerPoolDebugEnabled - debug pool handle allocation - - OCSChannelExpireAge - if that age in seconds is exceeded, a channel - will be removed from the pool - OCSChannelCollectionTimer - time in seconds. each n-seconds the pool will be - checked for channels too old - - [PGDebugEnabled] - enable PostgreSQL adaptor debugging - -URLs -==== - - "Database URLs" - - We use the schema: - postgresql://[user]:[password]@[host]:[port]/[dbname]/[tablename] - -BLOB Formats -============ - -- TBD -- iCal, XML -- problem with iCal is that a complete and valid iCal file is different from - just the vevent. so we basically need to parse those files in any case. -- XML: the iCal SaxDriver reports XML events, so we can easily store that as - XML -- we need to parse the BLOB for different clients anyway (iCal != iCal ...) -- XML: we could use some XML query extension to PG in the future? - -Update: we now have OCSiCalFieldExtractor - - it parses the BLOB as an iCalendar file and extracts a set of fixed - keys: - - title - plain copy of "summary" - - uid - plain copy - - startdate - date as utime - - enddate - date as utime - - participants - CNs of attendees separated by comma ", " - - location - - partmails - - sequence - - TBD: iscyclic - - TBD: isallday - - TBD: cycles - I guess the client should fetch the BLOB to resolve - - the field extractor is accessed by OCSFolder using the folderinfo: - extractor = [self->folderInfo quickExtractor]; - quickRow = [extractor extractQuickFieldsFromContent:_content]; - -Support Tools -============= - -- tools we need: - - one to recreate a quick table based on the blob table - -Notes -===== - -- need to use http:// URLs for connect info, until generic URLs in - libFoundation are fixed (the parses breaks on the login/password parts) - -QA -== - -Q: Why do we use two tables, we could store the quick columns in the blob? -== -They could be in the same table. I considered using separate tables since the -quick table is likely to be recreated now and then if BLOB indexing -requirements change. -Actually one could even use different _quick tables which share a common BLOB -table. -(a quick table is nothing more than a database index and like with DB indexes - multiple ones for different requirements can make sense). - -Further it might improve caching behaviour for row based caches (the quick -table is going to be queried much more often) - not sure whether this is -relevant with PostgreSQL, probably not? - -Q: Can we use a VARCHAR primary key? -== -I asked in the postgres IRC channel and apparently the performance penalty of -string primary keys isn't big. -We could also use an 'internal' int sequence in addition (might be useful for -supporting ZideLook) -Motivation: the 'iCalendar' ID is a string and usually looks like a GUID. - -Q: Why using VARCHAR instead of TEXT in the BLOB? -== -To quote PostgreSQL documentation: -"There are no performance differences between these three types, apart from - the increased storage size when using the blank-padded type." -So varchar(xx) is just a large TEXT. Since we intend to store mostly small -snippets of data (tiny XML fragments), I considered VARCHAR the more -appropriate type. diff --git a/OGoContentStore/Version b/OGoContentStore/Version deleted file mode 100644 index 0d15a8243..000000000 --- a/OGoContentStore/Version +++ /dev/null @@ -1,14 +0,0 @@ -# Version file - -MAJOR_VERSION=0 -MINOR_VERSION=9 -SUBMINOR_VERSION:=33 - -# v0.9.30 requires libNGiCal v4.5.48 -# v0.9.26 requires libGDLContentStore v4.5.26 -# v0.9.19 requires libNGiCal v4.5.40 -# v0.9.18 requires libNGiCal v4.5.38 -# v0.9.17 requires libNGiCal v4.5.37 -# v0.9.11 requires libFoundation v1.0.63 -# v0.9.11 requires libNGExtensions v4.3.125 -# v0.9.7 requires libGDLAccess v1.1.35 diff --git a/OGoContentStore/iCalRepeatableEntityObject+OCS.h b/OGoContentStore/iCalRepeatableEntityObject+OCS.h deleted file mode 100644 index 36371ede4..000000000 --- a/OGoContentStore/iCalRepeatableEntityObject+OCS.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - Copyright (C) 2004-2005 SKYRIX Software AG - - This file is part of OpenGroupware.org. - - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - OGo 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 Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ -// $Id: iCalRepeatableEntityObject+OCS.h 617 2005-03-01 17:08:11Z znek $ - - -#ifndef __OGoContentStore_iCalRepeatableEntityObject_OCS_H_ -#define __OGoContentStore_iCalRepeatableEntityObject_OCS_H_ - -#include - -@interface iCalRepeatableEntityObject (OCS) - -- (NSString *)cycleInfo; - -@end - -#endif /* __OGoContentStore_iCalRepeatableEntityObject_OCS_H_ */ diff --git a/OGoContentStore/iCalRepeatableEntityObject+OCS.m b/OGoContentStore/iCalRepeatableEntityObject+OCS.m deleted file mode 100644 index 6e31876dc..000000000 --- a/OGoContentStore/iCalRepeatableEntityObject+OCS.m +++ /dev/null @@ -1,29 +0,0 @@ -/* - Copyright (C) 2004-2005 SKYRIX Software AG - - This file is part of OpenGroupware.org. - - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - OGo 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 Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -#import -#import - -#import "iCalRepeatableEntityObject+OCS.h" - -@implementation iCalRepeatableEntityObject (OCS) - -@end diff --git a/OGoContentStore/misc/iNetOrgPerson.txt b/OGoContentStore/misc/iNetOrgPerson.txt deleted file mode 100644 index eff00af8b..000000000 --- a/OGoContentStore/misc/iNetOrgPerson.txt +++ /dev/null @@ -1,108 +0,0 @@ -http://www.faqs.org/rfcs/rfc2798.html - -Class - NAME 'inetOrgPerson' - SUP organizationalPerson - STRUCTURAL - MAY ( - audio $ businessCategory $ carLicense $ departmentNumber $ - displayName $ employeeNumber $ employeeType $ givenName $ - homePhone $ homePostalAddress $ initials $ jpegPhoto $ - labeledURI $ mail $ manager $ mobile $ o $ pager $ - photo $ roomNumber $ secretary $ uid $ userCertificate $ - x500uniqueIdentifier $ preferredLanguage $ - userSMIMECertificate $ userPKCS12 - ) - MUST ( - cn $ objectClass $ sn - ) - MAY ( - description $ destinationIndicator $ facsimileTelephoneNumber $ - internationaliSDNNumber $ l $ ou $ physicalDeliveryOfficeName $ - postalAddress $ postalCode $ postOfficeBox $ - preferredDeliveryMethod $ registeredAddress $ seeAlso $ - st $ street $ telephoneNumber $ teletexTerminalIdentifier $ - telexNumber $ title $ userPassword $ x121Address - ) - -Properties (inetOrgPerson + organizationalPerson) - businessCategory - carLicense - departmentNumber - destinationIndicator - employeeNumber - employeeType - facsimileTelephoneNumber - givenName - homePostalAddress - initials - jpegPhoto - l - mail - manager - mobile - o - objectClass - ou - pager - physicalDeliveryOfficeName - postOfficeBox - postalCode - preferredLanguage - registeredAddress - roomNumber - secretary - seeAlso - sn - street - telephoneNumber - teletexTerminalIdentifier - title - uid - userCertificate - userPKCS12 - userPassword - x121Address - audio - cn - description - displayName - homePhone - internationaliSDNNumber - labeledURI - photo - postalAddress - preferredDeliveryMethod - st - telexNumber - userSMIMECertificate - x500uniqueIdentifier - -Sample: - version: 1 - dn: cn=Barbara Jensen,ou=Product Development,dc=siroe,dc=com - objectClass: top - objectClass: person - objectClass: organizationalPerson - objectClass: inetOrgPerson - cn: Barbara Jensen - cn: Babs Jensen - displayName: Babs Jensen - sn: Jensen - givenName: Barbara - initials: BJJ - title: manager, product development - uid: bjensen - mail: bjensen@siroe.com - telephoneNumber: +1 408 555 1862 - facsimileTelephoneNumber: +1 408 555 1992 - mobile: +1 408 555 1941 - roomNumber: 0209 - carLicense: 6ABC246 - o: Siroe - ou: Product Development - departmentNumber: 2604 - employeeNumber: 42 - employeeType: full time - preferredLanguage: fr, en-gb;q=0.8, en;q=0.7 - labeledURI: http://www.siroe.com/users/bjensen My Home Page diff --git a/OGoContentStore/misc/icaltest.ical b/OGoContentStore/misc/icaltest.ical deleted file mode 100644 index d048821f7..000000000 --- a/OGoContentStore/misc/icaltest.ical +++ /dev/null @@ -1,30 +0,0 @@ -BEGIN:VEVENT -DURATION:PT1H -ATTENDEE;CN="Elke Bethke";DIR="addressbook://B156F3F0-9CFD-11D8-8561-000 - D93C1A604:ABPerson":mailto:E.Bethke@Sachsen-Anhalt-Lotto.de -ATTENDEE;CN="Erik Doernenburg";DIR="addressbook://B15FCB0F-9CFD-11D8-8561 - -000D93C1A604:ABPerson":mailto:erik@x101.net -ATTENDEE;CN="Christian Schnelle";DIR="addressbook://B1418D4E-9CFD-11D8-8 - 561-000D93C1A604:ABPerson":mailto:cs@enervation.de -ATTENDEE;CN="Chris Herrenberger";DIR="addressbook://B14A390C-9CFD-11D8-8 - 561-000D93C1A604:ABPerson":invalid:nomail -ATTENDEE;CN="Horst Parplies";DIR="addressbook://B19B47E5-9CFD-11D8-8561- - 000D93C1A604:ABPerson":mailto:horst.parplies@freenet.de -ATTENDEE;CN="Imdat Solak";DIR="addressbook://B19EDB62-9CFD-11D8-8561-000 - D93C1A604:ABPerson":mailto:imdat@solak.de -ATTENDEE;CN="Jens Enders";DIR="addressbook://B1B6819F-9CFD-11D8-8561-000 - D93C1A604:ABPerson":mailto:jens.enders@skyrix.com -ATTENDEE;CN="Jens Muenster";DIR="addressbook://B1BBA42E-9CFD-11D8-8561-00 - 0D93C1A604:ABPerson":mailto:jens.muenster@skyrix.com -ATTENDEE;CN="Laurent Pierre";DIR="addressbook://9337C270-A825-11D8-B930- - 000D93C1A604:ABPerson":mailto:laurent.pierre@linagora.com -ATTENDEE;CN="Marcel Weiher";DIR="addressbook://B1F9BB12-9CFD-11D8-8561-0 - 00D93C1A604:ABPerson":mailto:marcel@metaobject.co -DTSTAMP:20040520T140002Z -UID:BD91C454-AA65-11D8-84CA-000D93C1A604 -SEQUENCE:3 -STATUS:CONFIRMED -DTSTART;TZID=Europe/Berlin:20040618T160000 -SUMMARY:SIZE EVENT -X-WR-ITIPSTATUSML:UNCLEAN -END:VEVENT diff --git a/OGoContentStore/misc/test1/GNUmakefile b/OGoContentStore/misc/test1/GNUmakefile deleted file mode 100644 index d92ce9b29..000000000 --- a/OGoContentStore/misc/test1/GNUmakefile +++ /dev/null @@ -1,15 +0,0 @@ -# $Id$ - -include $(GNUSTEP_MAKEFILES)/common.make - -TOOL_NAME = test - -test_OBJC_FILES += test1.m - -ADDITIONAL_TOOL_LIBS += -lOGoContentStore - -ADDITIONAL_LIB_DIRS += -L../../$(GNUSTEP_OBJ_DIR) - --include GNUmakefile.preamble -include $(GNUSTEP_MAKEFILES)/tool.make --include GNUmakefile.postamble diff --git a/OGoContentStore/misc/test1/common.h b/OGoContentStore/misc/test1/common.h deleted file mode 100644 index f771957e7..000000000 --- a/OGoContentStore/misc/test1/common.h +++ /dev/null @@ -1,3 +0,0 @@ -// $Id$ - -#import diff --git a/OGoContentStore/misc/test1/test1.m b/OGoContentStore/misc/test1/test1.m deleted file mode 100644 index 7724b19d1..000000000 --- a/OGoContentStore/misc/test1/test1.m +++ /dev/null @@ -1,31 +0,0 @@ -// $Id$ - -#include "common.h" - -static void testurl(NSString *s) { - NSURL *url; - - url = [NSURL URLWithString:s]; - NSLog(@"url: %@", url); - NSLog(@" login: %@", [url user]); - NSLog(@" pwd: %@", [url password]); -} - -static void test(void) { - testurl(@"http://OGoUser:OGoPwd@localhost/OGo"); - testurl(@"postgresql://OGoUser:OGoPwd@localhost/OGo"); -} - -int main(int argc, char **argv, char **env) { - NSAutoreleasePool *pool; - - pool = [[NSAutoreleasePool alloc] init]; -#if LIB_FOUNDATION_LIBRARY - [NSProcessInfo initializeWithArguments:argv count:argc environment:env]; -#endif - - test(); - - [pool release]; - return 0; -} diff --git a/OpenChange/GNUmakefile b/OpenChange/GNUmakefile index 2a7bac557..3e323a300 100644 --- a/OpenChange/GNUmakefile +++ b/OpenChange/GNUmakefile @@ -137,7 +137,6 @@ $(DBMSGREADER_TOOL)_OBJC_FILES += \ $(DBMSGREADER_TOOL)_LIB_DIRS += \ -L../SoObjects/SOGo/SOGo.framework/ -lSOGo \ - -L../OGoContentStore/obj/ -lOGoContentStore \ -L../SOPE/GDLContentStore/obj/ -lGDLContentStore \ -L../SOPE/NGCards/obj/ -lNGCards \ -lNGObjWeb @@ -166,7 +165,6 @@ $(MAPISTORESOGO)_LIB_DIRS += \ $(LIBMAPISTORE_LIBS) $(SOGOBACKEND)_LIB_DIRS += \ - -L../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ -lOGoContentStore \ -L../SoObjects/SOGo/SOGo.framework/ -lSOGo \ $(LIBMAPI_LIBS) \ $(LIBMAPISTORE_LIBS) diff --git a/OpenChange/MAPIStoreContactsMessage.m b/OpenChange/MAPIStoreContactsMessage.m index b2a5ce660..3b35f9c34 100644 --- a/OpenChange/MAPIStoreContactsMessage.m +++ b/OpenChange/MAPIStoreContactsMessage.m @@ -1200,7 +1200,7 @@ fromProperties: (NSDictionary *) attachmentProps // // we save the new/modified card // - [sogoObject saveContentString: [newCard versitString]]; + [sogoObject saveComponent: newCard]; [self updateVersions]; } diff --git a/OpenChange/MAPIStoreTasksMessage.m b/OpenChange/MAPIStoreTasksMessage.m index 2a7958f19..9215bc409 100644 --- a/OpenChange/MAPIStoreTasksMessage.m +++ b/OpenChange/MAPIStoreTasksMessage.m @@ -513,7 +513,7 @@ } [vToDo setTimeStampAsDate: now]; - [sogoObject saveContentString: [vCalendar versitString]]; + [sogoObject saveComponent: vCalendar]; [self updateVersions]; } diff --git a/SOPE/GDLContentStore/GCSFieldExtractor.h b/SOPE/GDLContentStore/GCSFieldExtractor.h deleted file mode 100644 index 1b1e64af4..000000000 --- a/SOPE/GDLContentStore/GCSFieldExtractor.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (C) 2004-2005 SKYRIX Software AG - - This file is part of OpenGroupware.org. - - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - OGo 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 Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -#ifndef __GDLContentStore_GCSFieldExtractor_H__ -#define __GDLContentStore_GCSFieldExtractor_H__ - -#import - -@class NSString, NSMutableDictionary; - -@interface GCSFieldExtractor : NSObject -{ -} - -- (NSMutableDictionary *)extractQuickFieldsFromContent:(NSString *)_content; - -@end - -#endif /* __GDLContentStore_GCSFieldExtractor_H__ */ diff --git a/SOPE/GDLContentStore/GCSFieldExtractor.m b/SOPE/GDLContentStore/GCSFieldExtractor.m deleted file mode 100644 index 134a40343..000000000 --- a/SOPE/GDLContentStore/GCSFieldExtractor.m +++ /dev/null @@ -1,30 +0,0 @@ -/* - Copyright (C) 2004-2005 SKYRIX Software AG - - This file is part of OpenGroupware.org. - - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - OGo 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 Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -#import "GCSFieldExtractor.h" - -@implementation GCSFieldExtractor - -- (NSMutableDictionary *)extractQuickFieldsFromContent:(NSString *)_content { - return nil; -} - -@end /* GCSFieldExtractor */ diff --git a/SOPE/GDLContentStore/GCSFolder.h b/SOPE/GDLContentStore/GCSFolder.h index ff940fd40..296933b2c 100644 --- a/SOPE/GDLContentStore/GCSFolder.h +++ b/SOPE/GDLContentStore/GCSFolder.h @@ -117,10 +117,11 @@ - (NSDictionary *) recordOfEntryWithName: (NSString *) name; - (NSException *) writeContent: (NSString *) _content + fromComponent: (id) theComponent + container: (id) theContainer toName: (NSString *) _name baseVersion: (unsigned int *) _baseVersion; -- (NSException *) writeContent: (NSString *) _content - toName: (NSString *) _name; + - (NSException *) deleteContentWithName: (NSString *) _name; - (NSException *) deleteAllContent; diff --git a/SOPE/GDLContentStore/GCSFolder.m b/SOPE/GDLContentStore/GCSFolder.m index ac5efa58e..2b05ef67e 100644 --- a/SOPE/GDLContentStore/GCSFolder.m +++ b/SOPE/GDLContentStore/GCSFolder.m @@ -1,22 +1,22 @@ /* Copyright (C) 2004-2007 SKYRIX Software AG Copyright (C) 2007 Helge Hess - Copyright (c) 2008-2011 Inverse inc. + Copyright (c) 2008-2014 Inverse inc. - This file is part of OpenGroupware.org. + This file is part of SOGo. - OGo is free software; you can redistribute it and/or modify it under + SOGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - OGo is distributed in the hope that it will be useful, but WITHOUT ANY + SOGo 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the + License along with SOGo; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -38,7 +38,6 @@ #import "GCSFolderManager.h" #import "GCSFolderType.h" #import "GCSChannelManager.h" -#import "GCSFieldExtractor.h" #import "NSURL+GCS.h" #import "EOAdaptorChannel+GCS.h" #import "EOQualifier+GCS.h" @@ -882,13 +881,14 @@ andAttribute: (EOAttribute *)_attribute } - (NSException *) writeContent: (NSString *) _content - toName: (NSString *) _name + fromComponent: (id) theComponent + container: (id) theContainer + toName: (NSString *) _name baseVersion: (unsigned int *) _baseVersion { EOAdaptorChannel *storeChannel, *quickChannel; NSMutableDictionary *quickRow, *contentRow; NSDictionary *currentRow; - GCSFieldExtractor *extractor; NSNumber *storedVersion; BOOL isNewRecord, hasInsertDelegate, hasUpdateDelegate; NSCalendarDate *nowDate; @@ -944,8 +944,7 @@ andAttribute: (EOAttribute *)_attribute || *_baseVersion == [storedVersion unsignedIntValue]) { /* extract quick info */ - extractor = [folderInfo quickExtractor]; - quickRow = [extractor extractQuickFieldsFromContent:_content]; + quickRow = [theComponent performSelector: @selector(quickRecordForContainer:) withObject: theContainer]; if (quickRow) { [quickRow setObject:_name forKey:@"c_name"]; @@ -1080,8 +1079,8 @@ andAttribute: (EOAttribute *)_attribute __PRETTY_FUNCTION__]; } else - error = [self errorExtractorReturnedNoQuickRow:extractor - forContent:_content]; + [self errorWithFormat:@"%s: could not extract quick row information", + __PRETTY_FUNCTION__]; } else /* version mismatch (concurrent update) */ error = [self errorVersionMismatchBetweenStoredVersion: @@ -1101,13 +1100,6 @@ andAttribute: (EOAttribute *)_attribute return error; } - -- (NSException *)writeContent:(NSString *)_content toName:(NSString *)_name { - /* this method does not check for concurrent writes */ - unsigned int v = 0; - return [self writeContent:_content toName:_name baseVersion:&v]; -} - - (NSException *)deleteContentWithName:(NSString *)_name { EOAdaptorChannel *storeChannel, *quickChannel; EOAdaptorContext *adaptorCtx; diff --git a/SOPE/GDLContentStore/GCSFolderManager.m b/SOPE/GDLContentStore/GCSFolderManager.m index f333c9951..91c59c268 100644 --- a/SOPE/GDLContentStore/GCSFolderManager.m +++ b/SOPE/GDLContentStore/GCSFolderManager.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2006-2011 Inverse inc. + Copyright (C) 2006-2014 Inverse inc. Copyright (C) 2004-2007 SKYRIX Software AG This file is part of SOGo. diff --git a/SOPE/GDLContentStore/GCSFolderType.h b/SOPE/GDLContentStore/GCSFolderType.h index 8a2e843ce..5784780bd 100644 --- a/SOPE/GDLContentStore/GCSFolderType.h +++ b/SOPE/GDLContentStore/GCSFolderType.h @@ -41,7 +41,7 @@ @class NSString, NSArray, NSDictionary; @class EOQualifier; @class NGResourceLocator; -@class GCSFolder, GCSFieldExtractor; +@class GCSFolder; @interface GCSFolderType : NSObject { @@ -50,7 +50,6 @@ NSArray *fields, *quickFields; // GCSFieldInfo objects EOQualifier *folderQualifier; // to further limit the table set NSString *extractorClassName; - GCSFieldExtractor *extractor; } + (id)folderTypeWithName:(NSString *)_type; @@ -67,10 +66,6 @@ - (NSString *)sqlQuickCreateWithTableName:(NSString *)_tabName; -/* quick support */ - -- (GCSFieldExtractor *)quickExtractor; - /* locator used to find .ocs files */ + (NGResourceLocator *)resourceLocator; diff --git a/SOPE/GDLContentStore/GCSFolderType.m b/SOPE/GDLContentStore/GCSFolderType.m index da051c7a3..bb99051e6 100644 --- a/SOPE/GDLContentStore/GCSFolderType.m +++ b/SOPE/GDLContentStore/GCSFolderType.m @@ -1,20 +1,20 @@ /* Copyright (C) 2004-2005 SKYRIX Software AG - This file is part of OpenGroupware.org. + This file is part of SOGo. - OGo is free software; you can redistribute it and/or modify it under + SOGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - OGo is distributed in the hope that it will be useful, but WITHOUT ANY + SOGo 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the + License along with SOGo; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -35,7 +35,6 @@ #import "GCSFolderType.h" #import "GCSFolder.h" #import "GCSFieldInfo.h" -#import "GCSFieldExtractor.h" @implementation GCSFolderType @@ -116,7 +115,6 @@ - (void) dealloc { - [extractor release]; [extractorClassName release]; [blobTablePattern release]; [quickTablePattern release]; @@ -173,36 +171,6 @@ return sql; } -/* quick support */ - -- (GCSFieldExtractor *) quickExtractor -{ - Class clazz; - GCSFieldExtractor *quickExtractor; - - if (!extractor) - { - clazz = (extractorClassName - ? NSClassFromString (extractorClassName) - : [GCSFieldExtractor class]); - if (clazz) - { - extractor = [clazz new]; - if (!extractor) - [self logWithFormat:@"ERROR: could not create field extractor of class %@", - clazz]; - } - else - [self logWithFormat:@"ERROR: did not find field extractor class (%@)", extractorClassName]; - } - if ([extractor isNotNull]) - quickExtractor = extractor; - else - quickExtractor = nil; - - return quickExtractor; -} - - (NSArray *) quickFields { return quickFields; diff --git a/SOPE/GDLContentStore/GNUmakefile b/SOPE/GDLContentStore/GNUmakefile index e0e50cab2..84f24d0c0 100644 --- a/SOPE/GDLContentStore/GNUmakefile +++ b/SOPE/GDLContentStore/GNUmakefile @@ -31,7 +31,6 @@ libGDLContentStore_HEADER_FILES += \ GCSFolderManager.h \ GCSFolderType.h \ GCSChannelManager.h \ - GCSFieldExtractor.h \ GCSSessionsFolder.h \ GCSSpecialQueries.h \ GCSStringFormatter.h \ @@ -48,7 +47,6 @@ libGDLContentStore_OBJC_FILES += \ GCSFolderManager.m \ GCSFolderType.m \ GCSChannelManager.m \ - GCSFieldExtractor.m \ GCSSessionsFolder.m \ GCSSpecialQueries.m \ GCSStringFormatter.m \ @@ -60,6 +58,14 @@ GDLContentStore_HEADER_FILES = $(libGDLContentStore_HEADER_FILES) GDLContentStore_OBJC_FILES = $(libGDLContentStore_OBJC_FILES) GDLContentStore_SUBPROJECTS = $(libGDLContentStore_SUBPROJECTS) +# Type models +TYPEMODELS_DIR = $(SOGO_TYPEMODELS) + +libGDLContentStore_TYPEMODELS += \ + appointment.ocs \ + contact.ocs \ + appointment-oracle.ocs \ + contact-oracle.ocs # building diff --git a/OGoContentStore/GNUmakefile.postamble b/SOPE/GDLContentStore/GNUmakefile.postamble similarity index 55% rename from OGoContentStore/GNUmakefile.postamble rename to SOPE/GDLContentStore/GNUmakefile.postamble index 834af222e..2de398a10 100644 --- a/OGoContentStore/GNUmakefile.postamble +++ b/SOPE/GDLContentStore/GNUmakefile.postamble @@ -3,10 +3,10 @@ $(TYPEMODELS_DIR) : $(MKDIRS) $(TYPEMODELS_DIR) -ocs-typemodels : $(TYPEMODELS_DIR) $(libOGoContentStore_TYPEMODELS) - @(if [ "$(libOGoContentStore_TYPEMODELS)" != "" ]; then \ +ocs-typemodels : $(TYPEMODELS_DIR) $(libGDLContentStore_TYPEMODELS) + @(if [ "$(libGDLContentStore_TYPEMODELS)" != "" ]; then \ echo "Copying type models into install path ..."; \ - for ff in $(libOGoContentStore_TYPEMODELS); do \ + for ff in $(libGDLContentStore_TYPEMODELS); do \ cp $$ff $(TYPEMODELS_DIR)/$$ff; \ done; \ fi) diff --git a/OGoContentStore/appointment-oracle.ocs b/SOPE/GDLContentStore/appointment-oracle.ocs similarity index 100% rename from OGoContentStore/appointment-oracle.ocs rename to SOPE/GDLContentStore/appointment-oracle.ocs diff --git a/OGoContentStore/appointment.ocs b/SOPE/GDLContentStore/appointment.ocs similarity index 100% rename from OGoContentStore/appointment.ocs rename to SOPE/GDLContentStore/appointment.ocs diff --git a/OGoContentStore/contact-oracle.ocs b/SOPE/GDLContentStore/contact-oracle.ocs similarity index 100% rename from OGoContentStore/contact-oracle.ocs rename to SOPE/GDLContentStore/contact-oracle.ocs diff --git a/OGoContentStore/contact.ocs b/SOPE/GDLContentStore/contact.ocs similarity index 100% rename from OGoContentStore/contact.ocs rename to SOPE/GDLContentStore/contact.ocs diff --git a/SoObjects/Appointments/Catalan.lproj/Localizable.strings b/SoObjects/Appointments/Catalan.lproj/Localizable.strings index 8d89ffe89..33861d079 100644 --- a/SoObjects/Appointments/Catalan.lproj/Localizable.strings +++ b/SoObjects/Appointments/Catalan.lproj/Localizable.strings @@ -1,3 +1,4 @@ +"Inviting the following persons is prohibited:" = "Està prohibit Convidar a les següents persones:"; "Personal Calendar" = "Calendari personal"; vevent_class0 = "(Esdeveniment públic)"; vevent_class1 = "(Esdeveniment privat)"; diff --git a/SoObjects/Appointments/Czech.lproj/Localizable.strings b/SoObjects/Appointments/Czech.lproj/Localizable.strings index 33667b7f7..fde0f1775 100644 --- a/SoObjects/Appointments/Czech.lproj/Localizable.strings +++ b/SoObjects/Appointments/Czech.lproj/Localizable.strings @@ -1,3 +1,4 @@ +"Inviting the following persons is prohibited:" = "Pozvání následujících osob je zakázáno:"; "Personal Calendar" = "Osobní kalendář"; vevent_class0 = "(Veřejná událost)"; vevent_class1 = "(Soukromá událost)"; diff --git a/SoObjects/Appointments/Dutch.lproj/Localizable.strings b/SoObjects/Appointments/Dutch.lproj/Localizable.strings index 857875a0e..f554e976e 100644 --- a/SoObjects/Appointments/Dutch.lproj/Localizable.strings +++ b/SoObjects/Appointments/Dutch.lproj/Localizable.strings @@ -1,3 +1,4 @@ +"Inviting the following persons is prohibited:" = "De volgende personen uitnodigen is niet toegestaan:"; "Personal Calendar" = "Persoonlijke agenda"; vevent_class0 = "(Publieke afspraak)"; vevent_class1 = "(Privé-afspraak)"; diff --git a/SoObjects/Appointments/English.lproj/Localizable.strings b/SoObjects/Appointments/English.lproj/Localizable.strings index ba3b942f1..45a038dfa 100644 --- a/SoObjects/Appointments/English.lproj/Localizable.strings +++ b/SoObjects/Appointments/English.lproj/Localizable.strings @@ -1,3 +1,4 @@ +"Inviting the following persons is prohibited:" = "Inviting the following persons is prohibited:"; "Personal Calendar" = "Personal Calendar"; vevent_class0 = "(Public event)"; vevent_class1 = "(Private event)"; diff --git a/SoObjects/Appointments/Finnish.lproj/Localizable.strings b/SoObjects/Appointments/Finnish.lproj/Localizable.strings index f184ddab3..2574c7110 100644 --- a/SoObjects/Appointments/Finnish.lproj/Localizable.strings +++ b/SoObjects/Appointments/Finnish.lproj/Localizable.strings @@ -1,3 +1,4 @@ +"Inviting the following persons is prohibited:" = "Seuraavien henkilöiden kutsuminen on estetty:"; "Personal Calendar" = "Henkilökohtainen kalenteri"; vevent_class0 = "(Julkinen tapahtuma)"; vevent_class1 = "(Yksityinen tapahtuma)"; diff --git a/SoObjects/Appointments/French.lproj/Localizable.strings b/SoObjects/Appointments/French.lproj/Localizable.strings index 7b39d0cf1..690738b77 100644 --- a/SoObjects/Appointments/French.lproj/Localizable.strings +++ b/SoObjects/Appointments/French.lproj/Localizable.strings @@ -1,3 +1,4 @@ +"Inviting the following persons is prohibited:" = "L'invitation des personnes suivantes est interdite :"; "Personal Calendar" = "Agenda personnel"; vevent_class0 = "(Événement public)"; vevent_class1 = "(Événement privé)"; diff --git a/SoObjects/Appointments/German.lproj/Localizable.strings b/SoObjects/Appointments/German.lproj/Localizable.strings index 4cd49d8c5..a9e975288 100644 --- a/SoObjects/Appointments/German.lproj/Localizable.strings +++ b/SoObjects/Appointments/German.lproj/Localizable.strings @@ -1,3 +1,4 @@ +"Inviting the following persons is prohibited:" = "Das Einladen von folgenden Personen ist verboten:"; "Personal Calendar" = "Persönlicher Kalender"; vevent_class0 = "(Öffentlicher Termin)"; vevent_class1 = "(Privater Termin)"; diff --git a/SoObjects/Appointments/Hungarian.lproj/Localizable.strings b/SoObjects/Appointments/Hungarian.lproj/Localizable.strings index 0a53c00ca..ab163b121 100644 --- a/SoObjects/Appointments/Hungarian.lproj/Localizable.strings +++ b/SoObjects/Appointments/Hungarian.lproj/Localizable.strings @@ -1,3 +1,4 @@ +"Inviting the following persons is prohibited:" = "A következő személyek meghívása tilos:"; "Personal Calendar" = "Személyes naptár"; vevent_class0 = "(Nyilvános esemény)"; vevent_class1 = "(Magán esemény)"; diff --git a/SoObjects/Appointments/NorwegianBokmal.lproj/Localizable.strings b/SoObjects/Appointments/NorwegianBokmal.lproj/Localizable.strings index 3a5b2227e..cbabb272b 100644 --- a/SoObjects/Appointments/NorwegianBokmal.lproj/Localizable.strings +++ b/SoObjects/Appointments/NorwegianBokmal.lproj/Localizable.strings @@ -1,3 +1,4 @@ +"Inviting the following persons is prohibited:" = "Invitering av følgende personer er forbudt."; "Personal Calendar" = "Personlig kalender"; vevent_class0 = "(Offentlig hendelse)"; vevent_class1 = "(Privat hendelse)"; diff --git a/SoObjects/Appointments/Polish.lproj/Localizable.strings b/SoObjects/Appointments/Polish.lproj/Localizable.strings index 409dd5916..092525208 100644 --- a/SoObjects/Appointments/Polish.lproj/Localizable.strings +++ b/SoObjects/Appointments/Polish.lproj/Localizable.strings @@ -1,3 +1,4 @@ +"Inviting the following persons is prohibited:" = "Zapraszanie następujących osób jest zakazane:"; "Personal Calendar" = "Kalendarz osobisty"; vevent_class0 = "(Wydarzenie publiczne)"; vevent_class1 = "(Wydarzenie prywatne)"; diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 96a14d536..6845a3d23 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -983,7 +983,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir { if ([dateRange containsDate: [component startDate]]) { - newRecord = [self fixupRecord: [component quickRecord]]; + newRecord = [self fixupRecord: [component quickRecordForContainer: self]]; [ma replaceObjectAtIndex: recordIndex withObject: newRecord]; } else @@ -999,7 +999,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir { // The recurrence id of the exception is outside the date range; // simply add the exception to the records array - newRecord = [self fixupRecord: [component quickRecord]]; + newRecord = [self fixupRecord: [component quickRecordForContainer: self]]; newRecordRange = [NGCalendarDateRange calendarDateRangeWithStartDate: [newRecord objectForKey: @"startDate"] endDate: [newRecord objectForKey: @"endDate"]]; @@ -3034,7 +3034,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir [content appendFormat: @"%@\n", [timezone versitString]]; [content appendFormat: @"%@\nEND:VCALENDAR", [event versitString]]; - return ([object saveContentString: content] == nil) ? uid : nil; + return ([object saveCalendar: [iCalCalendar parseSingleFromSource: content]] == nil) ? uid : nil; } /** @@ -3047,7 +3047,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir NSArray *vtimezones; NSMutableArray *components; NSMutableDictionary *timezones, *uids; - NSString *tzId, *uid, *originalUid, *content; + NSString *tzId, *uid, *originalUid; iCalEntityObject *element; iCalDateTime *startDate; iCalTimeZone *timezone; @@ -3175,8 +3175,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir [masterCalendar addToEvents: event]; if (timezone) [masterCalendar addTimeZone: timezone]; - content = [masterCalendar versitString]; - [master saveContentString: content]; + [master saveCalendar: masterCalendar]; continue; } } diff --git a/SoObjects/Appointments/SOGoAppointmentFolders.m b/SoObjects/Appointments/SOGoAppointmentFolders.m index 99b73a636..f5ae1b23c 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolders.m +++ b/SoObjects/Appointments/SOGoAppointmentFolders.m @@ -750,7 +750,8 @@ static SoSecurityManager *sm = nil; for (userCount = 0; userCount < userMax; userCount++) [currentFolder subscribeUserOrGroup: [proxySubscribers objectAtIndex: userCount] - reallyDo: YES]; + reallyDo: YES + response: nil]; } } } @@ -775,7 +776,8 @@ static SoSecurityManager *sm = nil; for (userCount = 0; userCount < userMax; userCount++) [currentFolder subscribeUserOrGroup: [proxySubscribers objectAtIndex: userCount] - reallyDo: NO]; + reallyDo: NO + response: nil]; } } } diff --git a/SoObjects/Appointments/SOGoAppointmentObject.h b/SoObjects/Appointments/SOGoAppointmentObject.h index 6c0aebd01..6abfb6c0d 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.h +++ b/SoObjects/Appointments/SOGoAppointmentObject.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2007-2011 Inverse inc. + Copyright (C) 2007-2014 Inverse inc. This file is part of SOGo diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 0e397928f..986978878 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -25,6 +25,7 @@ #import #import #import +#import #import #import @@ -52,6 +53,7 @@ #import #import #import +#import #import #import #import @@ -126,13 +128,12 @@ } - (iCalRepeatableEntityObject *) lookupOccurrence: (NSString *) recID - { return [[self calendar: NO secure: NO] eventWithRecurrenceID: recID]; } - (SOGoAppointmentObject *) _lookupEvent: (NSString *) eventUID - forUID: (NSString *) uid + forUID: (NSString *) uid { SOGoAppointmentFolder *folder; SOGoAppointmentObject *object; @@ -146,30 +147,30 @@ while ( object == nil && (folder = [e nextObject]) ) { object = [folder lookupName: nameInContainer - inContext: context - acquire: NO]; + inContext: context + acquire: NO]; if ([object isKindOfClass: [NSException class]] || [object isNew]) - { - possibleName = [folder resourceNameForEventUID: eventUID]; - if (possibleName) - { - object = [folder lookupName: possibleName - inContext: context acquire: NO]; - if ([object isKindOfClass: [NSException class]] || [object isNew]) - object = nil; - } - else - object = nil; - } + { + possibleName = [folder resourceNameForEventUID: eventUID]; + if (possibleName) + { + object = [folder lookupName: possibleName + inContext: context acquire: NO]; + if ([object isKindOfClass: [NSException class]] || [object isNew]) + object = nil; + } + else + object = nil; + } } - + if (!object) { // Create the event in the user's personal calendar. folder = [[SOGoUser userWithLogin: uid] personalCalendarFolderInContext: context]; object = [SOGoAppointmentObject objectWithName: nameInContainer - inContainer: folder]; + inContainer: folder]; [object setIsNew: YES]; } @@ -180,60 +181,60 @@ // // - (void) _addOrUpdateEvent: (iCalEvent *) theEvent - forUID: (NSString *) theUID - owner: (NSString *) theOwner + forUID: (NSString *) theUID + owner: (NSString *) theOwner { if (![theUID isEqualToString: theOwner]) { SOGoAppointmentObject *attendeeObject; - NSString *iCalString; - - iCalString = nil; + iCalCalendar *iCalendarToSave; + + iCalendarToSave = nil; attendeeObject = [self _lookupEvent: [theEvent uid] forUID: theUID]; // We must add an occurence to a non-existing event. We have // to handle this with care, as in the postCalDAVEventRequestTo:from: if ([attendeeObject isNew] && [theEvent recurrenceId]) - { - iCalEvent *ownerEvent; - iCalPerson *person; - SOGoUser *user; - - // We check if the attendee that was added to a single occurence is - // present in the master component. If not, we add it with a participation - // status set to "DECLINED". - ownerEvent = [[[theEvent parent] events] objectAtIndex: 0]; - user = [SOGoUser userWithLogin: theUID]; - if (![ownerEvent userAsAttendee: user]) - { - // Update the master event in the owner's calendar with the - // status of the new attendee set as "DECLINED". + { + iCalEvent *ownerEvent; + iCalPerson *person; + SOGoUser *user; + + // We check if the attendee that was added to a single occurence is + // present in the master component. If not, we add it with a participation + // status set to "DECLINED". + ownerEvent = [[[theEvent parent] events] objectAtIndex: 0]; + user = [SOGoUser userWithLogin: theUID]; + if (![ownerEvent userAsAttendee: user]) + { + // Update the master event in the owner's calendar with the + // status of the new attendee set as "DECLINED". person = [iCalPerson elementWithTag: @"attendee"]; [person setCn: [user cn]]; [person setEmail: [[user allEmails] objectAtIndex: 0]]; [person setParticipationStatus: iCalPersonPartStatDeclined]; [person setRsvp: @"TRUE"]; [person setRole: @"REQ-PARTICIPANT"]; - [ownerEvent addToAttendees: person]; - - iCalString = [[ownerEvent parent] versitString]; - } - } + [ownerEvent addToAttendees: person]; + + iCalendarToSave = [ownerEvent parent]; + } + } else - { - // TODO : if [theEvent recurrenceId], only update this occurrence - // in attendee's calendar - - // TODO : when updating the master event, handle exception dates - // in attendee's calendar (add exception dates and remove matching - // occurrences) -- see _updateRecurrenceIDsWithEvent: - - iCalString = [[theEvent parent] versitString]; - } + { + // TODO : if [theEvent recurrenceId], only update this occurrence + // in attendee's calendar + + // TODO : when updating the master event, handle exception dates + // in attendee's calendar (add exception dates and remove matching + // occurrences) -- see _updateRecurrenceIDsWithEvent: + + iCalendarToSave = [theEvent parent]; + } // Save the event in the attendee's calendar - if (iCalString) - [attendeeObject saveContentString: iCalString]; + if (iCalendarToSave) + [attendeeObject saveCalendar: iCalendarToSave]; } } @@ -242,7 +243,7 @@ // - (void) _removeEventFromUID: (NSString *) theUID owner: (NSString *) theOwner - withRecurrenceId: (NSCalendarDate *) recurrenceId + withRecurrenceId: (NSCalendarDate *) recurrenceId { if (![theUID isEqualToString: theOwner]) { @@ -252,53 +253,50 @@ iCalRepeatableEntityObject *event; iCalCalendar *calendar; NSCalendarDate *currentId; - NSString *calendarContent; NSArray *occurences; int max, count; // Invitations are always written to the personal folder; it's not necessay // to look into all folders of the user folder = [[SOGoUser userWithLogin: theUID] - personalCalendarFolderInContext: context]; + personalCalendarFolderInContext: context]; object = [folder lookupName: nameInContainer inContext: context acquire: NO]; if (![object isKindOfClass: [NSException class]]) - { - if (recurrenceId == nil) - [object delete]; - else - { - calendar = [object calendar: NO secure: NO]; - - // If recurrenceId is defined, remove the occurence from - // the repeating event. - occurences = [calendar events]; - max = [occurences count]; - count = 1; - while (count < max) - { - currentOccurence = [occurences objectAtIndex: count]; - currentId = [currentOccurence recurrenceId]; - if ([currentId compare: recurrenceId] == NSOrderedSame) - { - [[calendar children] removeObject: currentOccurence]; - break; - } - count++; - } - - // Add an date exception. - event = (iCalRepeatableEntityObject*)[calendar firstChildWithTag: [object componentTag]]; - [event addToExceptionDates: recurrenceId]; - - [event increaseSequence]; - - // We generate the updated iCalendar file and we save it - // in the database. - calendarContent = [calendar versitString]; - [object saveContentString: calendarContent]; - } - } + { + if (recurrenceId == nil) + [object delete]; + else + { + calendar = [object calendar: NO secure: NO]; + + // If recurrenceId is defined, remove the occurence from + // the repeating event. + occurences = [calendar events]; + max = [occurences count]; + count = 1; + while (count < max) + { + currentOccurence = [occurences objectAtIndex: count]; + currentId = [currentOccurence recurrenceId]; + if ([currentId compare: recurrenceId] == NSOrderedSame) + { + [[calendar children] removeObject: currentOccurence]; + break; + } + count++; + } + + // Add an date exception. + event = (iCalRepeatableEntityObject*)[calendar firstChildWithTag: [object componentTag]]; + [event addToExceptionDates: recurrenceId]; + + [event increaseSequence]; + + // We save the updated iCalendar in the database. + [object saveCalendar: calendar]; + } + } } } @@ -317,8 +315,8 @@ { currentUID = [currentAttendee uid]; if (currentUID) - [self _removeEventFromUID: currentUID - owner: owner + [self _removeEventFromUID: currentUID + owner: owner withRecurrenceId: recurrenceId]; } } @@ -343,9 +341,7 @@ [event removeFromAttendees: delegate]; } else - [self errorWithFormat: - @"broken chain: delegate with email '%@' was not found", - mailTo]; + [self errorWithFormat:@"broken chain: delegate with email '%@' was not found", mailTo]; } } @@ -400,9 +396,9 @@ { currentUID = [currentAttendee uid]; if (currentUID) - [self _addOrUpdateEvent: newEvent - forUID: currentUID - owner: owner]; + [self _addOrUpdateEvent: newEvent + forUID: currentUID + owner: owner]; } [self sendEMailUsingTemplateNamed: @"Update" @@ -412,6 +408,70 @@ withType: @"calendar:invitation-update"]; } +// This method scans the list of attendees. +- (NSException *) _handleAttendeeAvailability: (NSArray *) theAttendees + forEvent: (iCalEvent *) theEvent +{ + iCalPerson *currentAttendee; + SOGoUser *user; + SOGoUserSettings *us; + NSMutableArray *unavailableAttendees; + NSEnumerator *enumerator; + NSString *currentUID, *ownerUID, *whiteListString; + NSMutableString *reason; + NSDictionary *values; + NSMutableDictionary *value, *moduleSettings, *whiteList; + int i, count; + i = count = 0; + + // Build list of the attendees uids without ressources + unavailableAttendees = [[NSMutableArray alloc] init]; + enumerator = [theAttendees objectEnumerator]; + ownerUID = [[[self context] activeUser] login]; + + while ((currentAttendee = [enumerator nextObject])) + { + currentUID = [currentAttendee uid]; + if (currentUID) + { + user = [SOGoUser userWithLogin: currentUID]; + us = [user userSettings]; + moduleSettings = [us objectForKey:@"Calendar"]; + // Check if the user prevented his account from beeing invited to events + if (![user isResource] && [[moduleSettings objectForKey:@"PreventInvitations"] boolValue]) + { + // Check if the user have a whiteList + whiteListString = [moduleSettings objectForKey:@"PreventInvitationsWhitelist"]; + whiteList = [whiteListString objectFromJSONString]; + // If the filter have a hit, do not add the currentUID to the unavailableAttendees array + if (![whiteList objectForKey:ownerUID]) + { + values = [NSDictionary dictionaryWithObject:[user cn] forKey:@"Cn"]; + [unavailableAttendees addObject:values]; + } + } + } + } + + count = [unavailableAttendees count]; + if (count > 0) + { + reason = [NSMutableString stringWithString:[self labelForKey: @"Inviting the following persons is prohibited:"]]; + // Add all the unavailable users in the warning message + for (i = 0; i < count; i++) + { + value = [unavailableAttendees objectAtIndex:i]; + [reason appendString:[value keysWithFormat: @"\n %{Cn}"]]; + if (i < count-2) + [reason appendString:@", "]; + } + [unavailableAttendees release]; + return [NSException exceptionWithHTTPStatus:409 reason: reason]; + } + [unavailableAttendees release]; + return nil; +} + // // This methods scans the list of attendees. If they are // considered as resource, it checks for conflicting @@ -434,8 +494,8 @@ NSMutableArray *attendees; NSEnumerator *enumerator; NSString *currentUID; - SOGoUser *user, *currentUser, *ownerUser; - + SOGoUser *user, *currentUser; + // Build a list of the attendees uids attendees = [NSMutableArray arrayWithCapacity: [theAttendees count]]; enumerator = [theAttendees objectEnumerator]; @@ -447,7 +507,7 @@ [attendees addObject: currentUID]; } } - + // If the active user is not the owner of the calendar, check possible conflict when // the owner is a resource currentUser = [context activeUser]; @@ -455,152 +515,152 @@ { [attendees addObject: owner]; } - + enumerator = [attendees objectEnumerator]; while ((currentUID = [enumerator nextObject])) { - user = [SOGoUser userWithLogin: currentUID]; - - if ([user isResource]) - { - SOGoAppointmentFolder *folder; - NSCalendarDate *start, *end; - NGCalendarDateRange *range; - NSMutableArray *fbInfo; - NSArray *allOccurences; - - BOOL must_delete; - int i, j; - - // We get the start/end date for our conflict range. If the event to be added is recurring, we - // check for at least a year to start with. - start = [[theEvent startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: 1]; - end = [[theEvent endDate] dateByAddingYears: ([theEvent isRecurrent] ? 1 : 0) months: 0 days: 0 hours: 0 minutes: 0 seconds: -1]; - - folder = [user personalCalendarFolderInContext: context]; - - // Deny access to the resource if the ACLs don't allow the user - if (![folder aclSQLListingFilter]) - { - NSDictionary *values; - NSString *reason; - - values = [NSDictionary dictionaryWithObjectsAndKeys: - [user cn], @"Cn", - [user systemEmail], @"SystemEmail"]; - reason = [values keysWithFormat: [self labelForKey: @"Cannot access resource: \"%{Cn} %{SystemEmail}\""]]; - return [NSException exceptionWithHTTPStatus:403 reason: reason]; - } - - fbInfo = [NSMutableArray arrayWithArray: [folder fetchFreeBusyInfosFrom: start - to: end]]; - - // We first remove any occurences in the freebusy that corresponds to the - // current event. We do this to avoid raising a conflict if we move a 1 hour - // meeting from 12:00-13:00 to 12:15-13:15. We would overlap on ourself otherwise. - // - // We must also check here for repetitive events that don't overlap our event. - // We remove all events that don't overlap. The events here are already - // decomposed. - // - if ([theEvent isRecurrent]) - allOccurences = [theEvent recurrenceRangesWithinCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: start - endDate: end] - firstInstanceCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: [theEvent startDate] - endDate: [theEvent endDate]]]; - else - allOccurences = nil; - - for (i = [fbInfo count]-1; i >= 0; i--) - { - range = [NGCalendarDateRange calendarDateRangeWithStartDate: [[fbInfo objectAtIndex: i] objectForKey: @"startDate"] - endDate: [[fbInfo objectAtIndex: i] objectForKey: @"endDate"]]; - - if ([[[fbInfo objectAtIndex: i] objectForKey: @"c_uid"] compare: [theEvent uid]] == NSOrderedSame) - { - [fbInfo removeObjectAtIndex: i]; - continue; - } - - // No need to check if the event isn't recurrent here as it's handled correctly - // when we compute the "end" date. - if ([allOccurences count]) - { - must_delete = YES; - - for (j = 0; j < [allOccurences count]; j++) - { - if ([range doesIntersectWithDateRange: [allOccurences objectAtIndex: j]]) - { - must_delete = NO; - break; - } - } - - if (must_delete) - [fbInfo removeObjectAtIndex: i]; - } - } - - // Find the attendee associated to the current UID - for (i = 0; i < [theAttendees count]; i++) + user = [SOGoUser userWithLogin: currentUID]; + + if ([user isResource]) + { + SOGoAppointmentFolder *folder; + NSCalendarDate *start, *end; + NGCalendarDateRange *range; + NSMutableArray *fbInfo; + NSArray *allOccurences; + + BOOL must_delete; + int i, j; + + // We get the start/end date for our conflict range. If the event to be added is recurring, we + // check for at least a year to start with. + start = [[theEvent startDate] dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: 1]; + end = [[theEvent endDate] dateByAddingYears: ([theEvent isRecurrent] ? 1 : 0) months: 0 days: 0 hours: 0 minutes: 0 seconds: -1]; + + folder = [user personalCalendarFolderInContext: context]; + + // Deny access to the resource if the ACLs don't allow the user + if (![folder aclSQLListingFilter]) + { + NSDictionary *values; + NSString *reason; + + values = [NSDictionary dictionaryWithObjectsAndKeys: + [user cn], @"Cn", + [user systemEmail], @"SystemEmail"]; + reason = [values keysWithFormat: [self labelForKey: @"Cannot access resource: \"%{Cn} %{SystemEmail}\""]]; + return [NSException exceptionWithHTTPStatus:403 reason: reason]; + } + + fbInfo = [NSMutableArray arrayWithArray: [folder fetchFreeBusyInfosFrom: start + to: end]]; + + // We first remove any occurences in the freebusy that corresponds to the + // current event. We do this to avoid raising a conflict if we move a 1 hour + // meeting from 12:00-13:00 to 12:15-13:15. We would overlap on ourself otherwise. + // + // We must also check here for repetitive events that don't overlap our event. + // We remove all events that don't overlap. The events here are already + // decomposed. + // + if ([theEvent isRecurrent]) + allOccurences = [theEvent recurrenceRangesWithinCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: start + endDate: end] + firstInstanceCalendarDateRange: [NGCalendarDateRange calendarDateRangeWithStartDate: [theEvent startDate] + endDate: [theEvent endDate]]]; + else + allOccurences = nil; + + for (i = [fbInfo count]-1; i >= 0; i--) + { + range = [NGCalendarDateRange calendarDateRangeWithStartDate: [[fbInfo objectAtIndex: i] objectForKey: @"startDate"] + endDate: [[fbInfo objectAtIndex: i] objectForKey: @"endDate"]]; + + if ([[[fbInfo objectAtIndex: i] objectForKey: @"c_uid"] compare: [theEvent uid]] == NSOrderedSame) { - currentAttendee = [theAttendees objectAtIndex: i]; - if ([[currentAttendee uid] isEqualToString: currentUID]) - break; - else - currentAttendee = nil; + [fbInfo removeObjectAtIndex: i]; + continue; } - - if ([fbInfo count]) + + // No need to check if the event isn't recurrent here as it's handled correctly + // when we compute the "end" date. + if ([allOccurences count]) { - // If we always force the auto-accept if numberOfSimultaneousBookings == 0 (ie., no limit - // is imposed) or if numberOfSimultaneousBookings is greater than the number of - // overlapping events - if ([user numberOfSimultaneousBookings] == 0 || - [user numberOfSimultaneousBookings] > [fbInfo count]) + must_delete = YES; + + for (j = 0; j < [allOccurences count]; j++) { - if (currentAttendee) + if ([range doesIntersectWithDateRange: [allOccurences objectAtIndex: j]]) { - [[currentAttendee attributes] removeObjectForKey: @"RSVP"]; - [currentAttendee setParticipationStatus: iCalPersonPartStatAccepted]; + must_delete = NO; + break; } } - else - { - iCalCalendar *calendar; - NSDictionary *values; - NSString *reason; - iCalEvent *event; - - calendar = [iCalCalendar parseSingleFromSource: [[fbInfo objectAtIndex: 0] objectForKey: @"c_content"]]; - event = [[calendar events] lastObject]; - - values = [NSDictionary dictionaryWithObjectsAndKeys: - [NSString stringWithFormat: @"%d", [user numberOfSimultaneousBookings]], @"NumberOfSimultaneousBookings", - [user cn], @"Cn", - [user systemEmail], @"SystemEmail", - ([event summary] ? [event summary] : @""), @"EventTitle", - [[fbInfo objectAtIndex: 0] objectForKey: @"startDate"], @"StartDate", - nil]; - - reason = [values keysWithFormat: [self labelForKey: @"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}."]]; - - return [NSException exceptionWithHTTPStatus: 403 - reason: reason]; - } - } - else if (currentAttendee) - { - // No conflict, we auto-accept. We do this for resources automatically if no - // double-booking is observed. If it's not the desired behavior, just don't - // set the resource as one! - [[currentAttendee attributes] removeObjectForKey: @"RSVP"]; - [currentAttendee setParticipationStatus: iCalPersonPartStatAccepted]; + + if (must_delete) + [fbInfo removeObjectAtIndex: i]; } } + + // Find the attendee associated to the current UID + for (i = 0; i < [theAttendees count]; i++) + { + currentAttendee = [theAttendees objectAtIndex: i]; + if ([[currentAttendee uid] isEqualToString: currentUID]) + break; + else + currentAttendee = nil; + } + + if ([fbInfo count]) + { + // If we always force the auto-accept if numberOfSimultaneousBookings == 0 (ie., no limit + // is imposed) or if numberOfSimultaneousBookings is greater than the number of + // overlapping events + if ([user numberOfSimultaneousBookings] == 0 || + [user numberOfSimultaneousBookings] > [fbInfo count]) + { + if (currentAttendee) + { + [[currentAttendee attributes] removeObjectForKey: @"RSVP"]; + [currentAttendee setParticipationStatus: iCalPersonPartStatAccepted]; + } + } + else + { + iCalCalendar *calendar; + NSDictionary *values; + NSString *reason; + iCalEvent *event; + + calendar = [iCalCalendar parseSingleFromSource: [[fbInfo objectAtIndex: 0] objectForKey: @"c_content"]]; + event = [[calendar events] lastObject]; + + values = [NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithFormat: @"%d", [user numberOfSimultaneousBookings]], @"NumberOfSimultaneousBookings", + [user cn], @"Cn", + [user systemEmail], @"SystemEmail", + ([event summary] ? [event summary] : @""), @"EventTitle", + [[fbInfo objectAtIndex: 0] objectForKey: @"startDate"], @"StartDate", + nil]; + + reason = [values keysWithFormat: [self labelForKey: @"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}."]]; + + return [NSException exceptionWithHTTPStatus: 403 + reason: reason]; + } + } + else if (currentAttendee) + { + // No conflict, we auto-accept. We do this for resources automatically if no + // double-booking is observed. If it's not the desired behavior, just don't + // set the resource as one! + [[currentAttendee attributes] removeObjectForKey: @"RSVP"]; + [currentAttendee setParticipationStatus: iCalPersonPartStatAccepted]; + } + } } - + return nil; } @@ -608,27 +668,29 @@ // // - (NSException *) _handleAddedUsers: (NSArray *) attendees - fromEvent: (iCalEvent *) newEvent + fromEvent: (iCalEvent *) newEvent { iCalPerson *currentAttendee; NSEnumerator *enumerator; NSString *currentUID; NSException *e; - + // We check for conflicts if ((e = [self _handleResourcesConflicts: attendees forEvent: newEvent])) return e; - + if ((e = [self _handleAttendeeAvailability: attendees forEvent: newEvent])) + return e; + enumerator = [attendees objectEnumerator]; while ((currentAttendee = [enumerator nextObject])) { currentUID = [currentAttendee uid]; if (currentUID) - [self _addOrUpdateEvent: newEvent - forUID: currentUID - owner: owner]; + [self _addOrUpdateEvent: newEvent + forUID: currentUID + owner: owner]; } - + return nil; } @@ -656,12 +718,11 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent { e = [events objectAtIndex: i]; if ([e recurrenceId]) - for (j = 0; j < [theAttendees count]; j++) - if (shouldAdd) - [e addToAttendees: [theAttendees objectAtIndex: j]]; - else - [e removeFromAttendees: [theAttendees objectAtIndex: j]]; - + for (j = 0; j < [theAttendees count]; j++) + if (shouldAdd) + [e addToAttendees: [theAttendees objectAtIndex: j]]; + else + [e removeFromAttendees: [theAttendees objectAtIndex: j]]; } } @@ -709,8 +770,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent withType: @"calendar:cancellation"]; } - if ((ex = [self _handleResourcesConflicts: [newEvent attendees] - forEvent: newEvent])) + if ((ex = [self _handleResourcesConflicts: [newEvent attendees] forEvent: newEvent])) + return ex; + if ((ex = [self _handleAttendeeAvailability: [newEvent attendees] forEvent: newEvent])) return ex; addedAttendees = [changes insertedAttendees]; @@ -719,7 +781,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // the attendees were added to the master event. [self _addOrDeleteAttendees: addedAttendees inRecurrenceExceptionsForEvent: newEvent - add: YES]; + add: YES]; if ([changes sequenceShouldBeIncreased]) { @@ -735,29 +797,29 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // If other attributes have changed, update the event // in each attendee's calendar if ([[changes updatedProperties] count]) - { - NSEnumerator *enumerator; - iCalPerson *currentAttendee; - NSString *currentUID; - + { + NSEnumerator *enumerator; + iCalPerson *currentAttendee; + NSString *currentUID; + updatedAttendees = [newEvent attendees]; - enumerator = [updatedAttendees objectEnumerator]; - while ((currentAttendee = [enumerator nextObject])) - { - currentUID = [currentAttendee uid]; - if (currentUID) - [self _addOrUpdateEvent: newEvent - forUID: currentUID - owner: owner]; - } - } + enumerator = [updatedAttendees objectEnumerator]; + while ((currentAttendee = [enumerator nextObject])) + { + currentUID = [currentAttendee uid]; + if (currentUID) + [self _addOrUpdateEvent: newEvent + forUID: currentUID + owner: owner]; + } + } } if ([addedAttendees count]) { // Send an invitation to new attendees if ((ex = [self _handleAddedUsers: addedAttendees fromEvent: newEvent])) - return ex; + return ex; [self sendEMailUsingTemplateNamed: @"Invitation" forObject: [newEvent itipEntryWithMethod: @"request"] @@ -765,12 +827,12 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent toAttendees: addedAttendees withType: @"calendar:invitation"]; } - - [self sendReceiptEmailForObject: newEvent - addedAttendees: addedAttendees - deletedAttendees: deletedAttendees - updatedAttendees: updatedAttendees - operation: EventUpdated]; + + [self sendReceiptEmailForObject: newEvent + addedAttendees: addedAttendees + deletedAttendees: deletedAttendees + updatedAttendees: updatedAttendees + operation: EventUpdated]; return nil; } @@ -783,7 +845,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // +------------> _handleUpdatedEvent:fromOldEvent: ---> _addOrUpdateEvent:forUID:owner: <-----------+ // | | ^ | // v v | | -// _handleRemovedUsers:withRecurrenceId: _handleSequenceUpdateInEvent:ignoringAttendees:fromOldEvent: | +// _handleRemovedUsers:withRecurrenceId: _handleSequenceUpdateInEvent:ignoringAttendees:fromOldEvent: | // | | // | [DELETEAction:] | // | | {_handleAdded/Updated...}<--+ | @@ -822,57 +884,56 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent { // New event -- send invitation to all attendees attendees = [newEvent attendeesWithoutUser: ownerUser]; - + // We catch conflicts and abort the save process immediately // in case of one with resources if ((ex = [self _handleAddedUsers: attendees fromEvent: newEvent])) return ex; - + if ([attendees count]) - { - [self sendEMailUsingTemplateNamed: @"Invitation" - forObject: [newEvent itipEntryWithMethod: @"request"] - previousObject: nil - toAttendees: attendees + { + [self sendEMailUsingTemplateNamed: @"Invitation" + forObject: [newEvent itipEntryWithMethod: @"request"] + previousObject: nil + toAttendees: attendees withType: @"calendar:invitation"]; - } - + } + [self sendReceiptEmailForObject: newEvent - addedAttendees: attendees - deletedAttendees: nil - updatedAttendees: nil - operation: EventCreated]; + addedAttendees: attendees + deletedAttendees: nil + updatedAttendees: nil + operation: EventCreated]; } - else + else { BOOL hasOrganizer; - + // Event is modified -- sent update status to all attendees // and modify their calendars. recurrenceId = [newEvent recurrenceId]; if (recurrenceId == nil) - oldEvent = [self component: NO secure: NO]; + oldEvent = [self component: NO secure: NO]; else - { - // If recurrenceId is defined, find the specified occurence - // within the repeating vEvent. - recurrenceTime = [NSString stringWithFormat: @"%f", [recurrenceId timeIntervalSince1970]]; - oldEvent = (iCalEvent*)[self lookupOccurrence: recurrenceTime]; - if (oldEvent == nil) - // If no occurence found, create one - oldEvent = (iCalEvent *)[self newOccurenceWithID: recurrenceTime]; - } - + { + // If recurrenceId is defined, find the specified occurence + // within the repeating vEvent. + recurrenceTime = [NSString stringWithFormat: @"%f", [recurrenceId timeIntervalSince1970]]; + oldEvent = (iCalEvent*)[self lookupOccurrence: recurrenceTime]; + if (oldEvent == nil) // If no occurence found, create one + oldEvent = (iCalEvent *)[self newOccurenceWithID: recurrenceTime]; + } + oldMasterEvent = (iCalEvent *)[[oldEvent parent] firstChildWithTag: [self componentTag]]; hasOrganizer = [[[oldMasterEvent organizer] email] length]; - + if (!hasOrganizer || [oldMasterEvent userIsOrganizer: ownerUser]) - // The owner is the organizer of the event; handle the modifications. We aslo - // catch conflicts just like when the events are created - if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent])) - return ex; + // The owner is the organizer of the event; handle the modifications. We aslo + // catch conflicts just like when the events are created + if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent])) + return ex; } - + [super saveComponent: newEvent]; [self flush]; @@ -894,17 +955,17 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent - (NSException *) _updateAttendee: (iCalPerson *) attendee withDelegate: (iCalPerson *) delegate ownerUser: (SOGoUser *) theOwnerUser - forEventUID: (NSString *) eventUID - withRecurrenceId: (NSCalendarDate *) recurrenceId - withSequence: (NSNumber *) sequence - forUID: (NSString *) uid - shouldAddSentBy: (BOOL) b + forEventUID: (NSString *) eventUID + withRecurrenceId: (NSCalendarDate *) recurrenceId + withSequence: (NSNumber *) sequence + forUID: (NSString *) uid + shouldAddSentBy: (BOOL) b { SOGoAppointmentObject *eventObject; iCalCalendar *calendar; iCalEntityObject *event; iCalPerson *otherAttendee, *otherDelegate; - NSString *iCalString, *recurrenceTime, *delegateEmail; + NSString *recurrenceTime, *delegateEmail; NSException *error; BOOL addDelegate, removeDelegate; @@ -914,30 +975,30 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent if (![eventObject isNew]) { if (recurrenceId == nil) - { - // We must update main event and all its occurences (if any). - calendar = [eventObject calendar: NO secure: NO]; - event = (iCalEntityObject*)[calendar firstChildWithTag: [self componentTag]]; - } + { + // We must update main event and all its occurences (if any). + calendar = [eventObject calendar: NO secure: NO]; + event = (iCalEntityObject*)[calendar firstChildWithTag: [self componentTag]]; + } else - { - // If recurrenceId is defined, find the specified occurence - // within the repeating vEvent. - recurrenceTime = [NSString stringWithFormat: @"%f", [recurrenceId timeIntervalSince1970]]; - event = [eventObject lookupOccurrence: recurrenceTime]; - - if (event == nil) - // If no occurence found, create one - event = [eventObject newOccurenceWithID: recurrenceTime]; - } + { + // If recurrenceId is defined, find the specified occurence + // within the repeating vEvent. + recurrenceTime = [NSString stringWithFormat: @"%f", [recurrenceId timeIntervalSince1970]]; + event = [eventObject lookupOccurrence: recurrenceTime]; + + if (event == nil) + // If no occurence found, create one + event = [eventObject newOccurenceWithID: recurrenceTime]; + } if ([[event sequence] intValue] <= [sequence intValue]) - { - SOGoUser *currentUser; - - currentUser = [context activeUser]; - otherAttendee = [event userAsAttendee: theOwnerUser]; - + { + SOGoUser *currentUser; + + currentUser = [context activeUser]; + otherAttendee = [event userAsAttendee: theOwnerUser]; + delegateEmail = [otherAttendee delegatedTo]; if ([delegateEmail length]) delegateEmail = [delegateEmail rfc822Email]; @@ -945,7 +1006,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent otherDelegate = [event findAttendeeWithEmail: delegateEmail]; else otherDelegate = NO; - + /* we handle the addition/deletion of delegate users */ addDelegate = NO; removeDelegate = NO; @@ -960,68 +1021,65 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent } } else - addDelegate = YES; + addDelegate = YES; } else { if (otherDelegate) - removeDelegate = YES; + removeDelegate = YES; } - + if (removeDelegate) - { - while (otherDelegate) - { - [event removeFromAttendees: otherDelegate]; - - // Verify if the delegate was already delegate - delegateEmail = [otherDelegate delegatedTo]; - if ([delegateEmail length]) - delegateEmail = [delegateEmail rfc822Email]; - - if ([delegateEmail length]) - otherDelegate = [event findAttendeeWithEmail: delegateEmail]; - else - otherDelegate = NO; - } - } + { + while (otherDelegate) + { + [event removeFromAttendees: otherDelegate]; + + // Verify if the delegate was already delegate + delegateEmail = [otherDelegate delegatedTo]; + if ([delegateEmail length]) + delegateEmail = [delegateEmail rfc822Email]; + + if ([delegateEmail length]) + otherDelegate = [event findAttendeeWithEmail: delegateEmail]; + else + otherDelegate = NO; + } + } if (addDelegate) [event addToAttendees: delegate]; - - [otherAttendee setPartStat: [attendee partStat]]; + + [otherAttendee setPartStat: [attendee partStat]]; [otherAttendee setDelegatedTo: [attendee delegatedTo]]; [otherAttendee setDelegatedFrom: [attendee delegatedFrom]]; - + // Remove the RSVP attribute, as an action from the attendee // was actually performed, and this confuses iCal (bug #1850) [[otherAttendee attributes] removeObjectForKey: @"RSVP"]; - - // If one has accepted / declined an invitation on behalf of - // the attendee, we add the user to the SENT-BY attribute. - if (b && ![[currentUser login] isEqualToString: [theOwnerUser login]]) - { - NSString *currentEmail, *quotedEmail; - currentEmail = [[currentUser allEmails] objectAtIndex: 0]; - quotedEmail = [NSString stringWithFormat: @"\"MAILTO:%@\"", - currentEmail]; - [otherAttendee setValue: 0 ofAttribute: @"SENT-BY" + + // If one has accepted / declined an invitation on behalf of + // the attendee, we add the user to the SENT-BY attribute. + if (b && ![[currentUser login] isEqualToString: [theOwnerUser login]]) + { + NSString *currentEmail, *quotedEmail; + currentEmail = [[currentUser allEmails] objectAtIndex: 0]; + quotedEmail = [NSString stringWithFormat: @"\"MAILTO:%@\"", currentEmail]; + [otherAttendee setValue: 0 ofAttribute: @"SENT-BY" to: quotedEmail]; - } - else - { - // We must REMOVE any SENT-BY here. This is important since if A accepted - // the event for B and then, B changes by himself his participation status, - // we don't want to keep the previous SENT-BY attribute there. - [(NSMutableDictionary *)[otherAttendee attributes] removeObjectForKey: @"SENT-BY"]; - } - } + } + else + { + // We must REMOVE any SENT-BY here. This is important since if A accepted + // the event for B and then, B changes by himself his participation status, + // we don't want to keep the previous SENT-BY attribute there. + [(NSMutableDictionary *)[otherAttendee attributes] removeObjectForKey: @"SENT-BY"]; + } + } - // We generate the updated iCalendar file and we save it - // in the database. - iCalString = [[event parent] versitString]; - error = [eventObject saveContentString: iCalString]; + // We save the updated iCalendar in the database. + error = [eventObject saveCalendar: [event parent]]; } - + return error; } @@ -1035,8 +1093,8 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent - (NSException *) _handleAttendee: (iCalPerson *) attendee withDelegate: (iCalPerson *) delegate ownerUser: (SOGoUser *) theOwnerUser - statusChange: (NSString *) newStatus - inEvent: (iCalEvent *) event + statusChange: (NSString *) newStatus + inEvent: (iCalEvent *) event { NSString *currentStatus, *organizerUID; SOGoUser *ownerUser, *currentUser; @@ -1067,29 +1125,28 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent if (delegate) { if (otherDelegate) - { - // There was already a delegate - if (![delegate hasSameEmailAddress: otherDelegate]) - { - // The delegate has changed - removeDelegate = YES; - addDelegate = YES; - } - } + { + // There was already a delegate + if (![delegate hasSameEmailAddress: otherDelegate]) + { + // The delegate has changed + removeDelegate = YES; + addDelegate = YES; + } + } else - // There was no previous delegate - addDelegate = YES; + // There was no previous delegate + addDelegate = YES; } else { if (otherDelegate) - // The user has removed the delegate - removeDelegate = YES; + // The user has removed the delegate + removeDelegate = YES; } if (addDelegate || removeDelegate - || [currentStatus caseInsensitiveCompare: newStatus] - != NSOrderedSame) + || [currentStatus caseInsensitiveCompare: newStatus] != NSOrderedSame) { NSMutableArray *delegates; NSString *delegatedUID; @@ -1101,106 +1158,105 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // the attendee, we add the user to the SENT-BY attribute. currentUser = [context activeUser]; if (![[currentUser login] isEqualToString: [theOwnerUser login]]) - { + { NSString *currentEmail, *quotedEmail; currentEmail = [[currentUser allEmails] objectAtIndex: 0]; - quotedEmail = [NSString stringWithFormat: @"\"MAILTO:%@\"", - currentEmail]; + quotedEmail = [NSString stringWithFormat: @"\"MAILTO:%@\"", currentEmail]; [attendee setValue: 0 ofAttribute: @"SENT-BY" to: quotedEmail]; - } + } else - { - // We must REMOVE any SENT-BY here. This is important since if A accepted - // the event for B and then, B changes by himself his participation status, - // we don't want to keep the previous SENT-BY attribute there. - [(NSMutableDictionary *)[attendee attributes] removeObjectForKey: @"SENT-BY"]; - } - + { + // We must REMOVE any SENT-BY here. This is important since if A accepted + // the event for B and then, B changes by himself his participation status, + // we don't want to keep the previous SENT-BY attribute there. + [(NSMutableDictionary *)[attendee attributes] removeObjectForKey: @"SENT-BY"]; + } + [attendee setDelegatedTo: [delegate email]]; - + if (removeDelegate) - { - delegates = [NSMutableArray array]; - - while (otherDelegate) - { - [delegates addObject: otherDelegate]; - - delegatedUID = [otherDelegate uid]; - if (delegatedUID) - // Delegate attendee is a local user; remove event from his calendar - [self _removeEventFromUID: delegatedUID - owner: [theOwnerUser login] - withRecurrenceId: [event recurrenceId]]; - - [event removeFromAttendees: otherDelegate]; - - // Verify if the delegate was already delegated - delegateEmail = [otherDelegate delegatedTo]; - if ([delegateEmail length]) - delegateEmail = [delegateEmail rfc822Email]; - - if ([delegateEmail length]) - otherDelegate = [event findAttendeeWithEmail: delegateEmail]; - else - otherDelegate = NO; - } - - [self sendEMailUsingTemplateNamed: @"Deletion" - forObject: [event itipEntryWithMethod: @"cancel"] - previousObject: nil - toAttendees: delegates + { + delegates = [NSMutableArray array]; + + while (otherDelegate) + { + [delegates addObject: otherDelegate]; + + delegatedUID = [otherDelegate uid]; + if (delegatedUID) + // Delegate attendee is a local user; remove event from his calendar + [self _removeEventFromUID: delegatedUID + owner: [theOwnerUser login] + withRecurrenceId: [event recurrenceId]]; + + [event removeFromAttendees: otherDelegate]; + + // Verify if the delegate was already delegated + delegateEmail = [otherDelegate delegatedTo]; + if ([delegateEmail length]) + delegateEmail = [delegateEmail rfc822Email]; + + if ([delegateEmail length]) + otherDelegate = [event findAttendeeWithEmail: delegateEmail]; + else + otherDelegate = NO; + } + + [self sendEMailUsingTemplateNamed: @"Deletion" + forObject: [event itipEntryWithMethod: @"cancel"] + previousObject: nil + toAttendees: delegates withType: @"calendar:cancellation"]; - } // if (removeDelegate) - + } // if (removeDelegate) + if (addDelegate) - { - delegatedUID = [delegate uid]; - delegates = [NSArray arrayWithObject: delegate]; - [event addToAttendees: delegate]; - - if (delegatedUID) - // Delegate attendee is a local user; add event to his calendar - [self _addOrUpdateEvent: event - forUID: delegatedUID - owner: [theOwnerUser login]]; - - [self sendEMailUsingTemplateNamed: @"Invitation" - forObject: [event itipEntryWithMethod: @"request"] - previousObject: nil - toAttendees: delegates + { + delegatedUID = [delegate uid]; + delegates = [NSArray arrayWithObject: delegate]; + [event addToAttendees: delegate]; + + if (delegatedUID) + // Delegate attendee is a local user; add event to his calendar + [self _addOrUpdateEvent: event + forUID: delegatedUID + owner: [theOwnerUser login]]; + + [self sendEMailUsingTemplateNamed: @"Invitation" + forObject: [event itipEntryWithMethod: @"request"] + previousObject: nil + toAttendees: delegates withType: @"calendar:invitation"]; - } // if (addDelegate) + } // if (addDelegate) // If the current user isn't the organizer of the event // that has just been updated, we update the event and // send a notification ownerUser = [SOGoUser userWithLogin: owner]; if (!(ex || [event userIsOrganizer: ownerUser])) - { - if ([event isStillRelevant]) - [self sendResponseToOrganizer: event + { + if ([event isStillRelevant]) + [self sendResponseToOrganizer: event from: ownerUser]; - - organizerUID = [[event organizer] uid]; - - // Event is an exception to a recurring event; retrieve organizer from master event - if (!organizerUID) - organizerUID = [[(iCalEntityObject*)[[event parent] firstChildWithTag: [self componentTag]] organizer] uid]; - - if (organizerUID) - // Update the attendee in organizer's calendar. - ex = [self _updateAttendee: attendee + + organizerUID = [[event organizer] uid]; + + // Event is an exception to a recurring event; retrieve organizer from master event + if (!organizerUID) + organizerUID = [[(iCalEntityObject*)[[event parent] firstChildWithTag: [self componentTag]] organizer] uid]; + + if (organizerUID) + // Update the attendee in organizer's calendar. + ex = [self _updateAttendee: attendee withDelegate: delegate ownerUser: theOwnerUser forEventUID: [event uid] withRecurrenceId: [event recurrenceId] withSequence: [event sequence] forUID: organizerUID - shouldAddSentBy: YES]; - } - + shouldAddSentBy: YES]; + } + // We update the calendar of all attendees that are // local to the system. This is useful in case user A accepts // invitation from organizer B and users C, D, E who are also @@ -1209,15 +1265,13 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent iCalPerson *att; NSString *uid; int i; - + attendees = [event attendees]; for (i = 0; i < [attendees count]; i++) - { - att = [attendees objectAtIndex: i]; - uid = [att uid]; - if (uid - && att != attendee - && ![uid isEqualToString: delegatedUID]) + { + att = [attendees objectAtIndex: i]; + uid = [att uid]; + if (uid && att != attendee && ![uid isEqualToString: delegatedUID]) [self _updateAttendee: attendee withDelegate: delegate ownerUser: theOwnerUser @@ -1226,9 +1280,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent withSequence: [event sequence] forUID: uid shouldAddSentBy: YES]; - } + } } - + return ex; } @@ -1238,13 +1292,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent NSDictionary *code; element = [NSMutableArray array]; - [element addObject: davElementWithContent (@"recipient", XMLNS_CALDAV, - recipient)]; - [element addObject: davElementWithContent (@"request-status", - XMLNS_CALDAV, - @"2.0;Success")]; - code = davElementWithContent (@"response", XMLNS_CALDAV, - element); + [element addObject: davElementWithContent (@"recipient", XMLNS_CALDAV, recipient)]; + [element addObject: davElementWithContent (@"request-status", XMLNS_CALDAV, @"2.0;Success")]; + code = davElementWithContent (@"response", XMLNS_CALDAV, element); return code; } @@ -1254,7 +1304,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // advertise for its support but we do everything within the calendar-auto-scheduling code // - (NSArray *) postCalDAVEventRequestTo: (NSArray *) recipients - from: (NSString *) originator + from: (NSString *) originator { NSEnumerator *recipientsEnum; NSMutableArray *elements; @@ -1267,14 +1317,14 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent while ((recipient = [recipientsEnum nextObject])) if ([[recipient lowercaseString] hasPrefix: @"mailto:"]) { - [elements addObject: [self _caldavSuccessCodeWithRecipient: recipient]]; + [elements addObject: [self _caldavSuccessCodeWithRecipient: recipient]]; } return elements; } - (NSArray *) postCalDAVEventCancelTo: (NSArray *) recipients - from: (NSString *) originator + from: (NSString *) originator { NSEnumerator *recipientsEnum; NSMutableArray *elements; @@ -1288,14 +1338,14 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent while ((recipient = [recipientsEnum nextObject])) if ([[recipient lowercaseString] hasPrefix: @"mailto:"]) { - [elements addObject: [self _caldavSuccessCodeWithRecipient: recipient]]; + [elements addObject: [self _caldavSuccessCodeWithRecipient: recipient]]; } return elements; } - (NSArray *) postCalDAVEventReplyTo: (NSArray *) recipients - from: (NSString *) originator + from: (NSString *) originator { NSEnumerator *recipientsEnum; NSMutableArray *elements; @@ -1307,7 +1357,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent while ((recipient = [recipientsEnum nextObject])) if ([[recipient lowercaseString] hasPrefix: @"mailto:"]) { - [elements addObject: [self _caldavSuccessCodeWithRecipient: recipient]]; + [elements addObject: [self _caldavSuccessCodeWithRecipient: recipient]]; } return elements; @@ -1320,7 +1370,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent withDelegate: (iCalPerson *) delegate { return [self changeParticipationStatus: status - withDelegate: delegate + withDelegate: delegate forRecurrenceId: nil]; } @@ -1371,15 +1421,14 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent attendee = [event userAsAttendee: ownerUser]; if (attendee) { - if (delegate - && ![[delegate email] isEqualToString: [attendee delegatedTo]]) + if (delegate && ![[delegate email] isEqualToString: [attendee delegatedTo]]) { delegatedUid = [delegate uid]; if (delegatedUid) delegatedUser = [SOGoUser userWithLogin: delegatedUid]; if (delegatedUser != nil && [event userIsOrganizer: delegatedUser]) ex = [NSException exceptionWithHTTPStatus: 403 - reason: @"delegate is organizer"]; + reason: @"delegate is organizer"]; if ([event isAttendee: [[delegate email] rfc822Email]]) ex = [NSException exceptionWithHTTPStatus: 403 reason: @"delegate is a participant"]; @@ -1405,19 +1454,18 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // the database. We do this ONLY when using SOGo from the // Web interface or over ActiveSync. // Over DAV, it'll be handled directly in PUTAction: - if (![context request] - || [[context request] handledByDefaultHandler] - || [[[context request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"]) - ex = [self saveContentString: [[event parent] versitString]]; + if (![context request] || [[context request] handledByDefaultHandler] + || [[[context request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"]) + ex = [self saveCalendar: [event parent]]; } } else ex = [NSException exceptionWithHTTPStatus: 404 // Not Found reason: @"user does not participate in this calendar event"]; } - else - ex = [NSException exceptionWithHTTPStatus: 500 // Server Error - reason: @"unable to parse event record"]; + else + ex = [NSException exceptionWithHTTPStatus: 500 // Server Error + reason: @"unable to parse event record"]; return ex; } @@ -1438,20 +1486,20 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent if (theOrganizer && (v = [theOrganizer value: 0 ofAttribute: @"SCHEDULE-AGENT"])) { if ([v caseInsensitiveCompare: @"NONE"] == NSOrderedSame || - [v caseInsensitiveCompare: @"CLIENT"] == NSOrderedSame) - b = NO; + [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 && @@ -1504,36 +1552,36 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent #warning Make sure this is correct .. if (![attendees count] && event != occurence) - attendees = [event attendeesWithoutUser: currentUser]; + attendees = [event attendeesWithoutUser: currentUser]; if ([attendees count]) - { - // Remove the event from all attendees calendars - // and send them an email. - [self _handleRemovedUsers: attendees - withRecurrenceId: recurrenceId]; - [self sendEMailUsingTemplateNamed: @"Deletion" + { + // Remove the event from all attendees calendars + // and send them an email. + [self _handleRemovedUsers: attendees + withRecurrenceId: recurrenceId]; + [self sendEMailUsingTemplateNamed: @"Deletion" forObject: [occurence itipEntryWithMethod: @"cancel"] previousObject: nil toAttendees: attendees withType: @"calendar:cancellation"]; - } + } } else if ([occurence userIsAttendee: ownerUser]) { // The current user deletes the occurence; let the organizer know that // the user has declined this occurence. [self changeParticipationStatus: @"DECLINED" withDelegate: nil - forRecurrenceId: recurrenceId]; + forRecurrenceId: recurrenceId]; send_receipt = NO; } - - if (send_receipt) - [self sendReceiptEmailForObject: event - addedAttendees: nil - deletedAttendees: nil - updatedAttendees: nil - operation: EventDeleted]; + + if (send_receipt) + [self sendReceiptEmailForObject: event + addedAttendees: nil + deletedAttendees: nil + updatedAttendees: nil + operation: EventDeleted]; } - (NSException *) prepareDelete @@ -1616,9 +1664,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent { event = [allEvents objectAtIndex: i]; if ([event isAllDay] && [event isOpaque]) - { - [event setTransparency: @"TRANSPARENT"]; - } + [event setTransparency: @"TRANSPARENT"]; } } @@ -1690,10 +1736,10 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent event = [allEvents objectAtIndex: i]; if ([event recurrenceId] && !theRecurrenceId) - return event; - + return event; + if ([event recurrenceId] && [[event recurrenceId] compare: theRecurrenceId] == NSOrderedSame) - return event; + return event; } return nil; @@ -1744,12 +1790,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent NSArray *roles; SOGoUser *ownerUser; - if (calendar == fullCalendar - || calendar == safeCalendar - || calendar == originalCalendar) - [NSException raise: NSInvalidArgumentException - format: @"the 'calendar' argument must be a distinct instance" - @" from the original object"]; + if (calendar == fullCalendar || calendar == safeCalendar + || calendar == originalCalendar) + [NSException raise: NSInvalidArgumentException format: @"the 'calendar' argument must be a distinct instance" @" from the original object"]; ownerUser = [SOGoUser userWithLogin: owner]; @@ -1760,24 +1803,21 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // responding to one of our invitation. In this case, _setupResponseCalendarInRequest // will only take the new attendee status and actually discard any other modifications. // - if ([roles containsObject: @"ComponentResponder"] - && ![roles containsObject: @"ComponentModifier"]) + if ([roles containsObject: @"ComponentResponder"] && ![roles containsObject: @"ComponentModifier"]) calendar = [self _setupResponseInRequestCalendar: calendar]; else { - if (![[rq headersForKey: @"X-SOGo"] - containsObject: @"NoGroupsDecomposition"]) + if (![[rq headersForKey: @"X-SOGo"] containsObject: @"NoGroupsDecomposition"]) [self _decomposeGroupsInRequestCalendar: calendar]; - - if ([[ownerUser domainDefaults] iPhoneForceAllDayTransparency] - && [rq isIPhone]) - { - [self _adjustTransparencyInRequestCalendar: calendar]; - } - + + if ([[ownerUser domainDefaults] iPhoneForceAllDayTransparency] && [rq isIPhone]) + { + [self _adjustTransparencyInRequestCalendar: calendar]; + } + [self _adjustEventsInRequestCalendar: calendar]; } - + // // We first check if it's a new event // @@ -1798,47 +1838,46 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // TODO: send out a no-uid-conflict (DAV:href) xml element (rfc4791 section 5.3.2.1) if ([container resourceNameForEventUID: eventUID]) { - return [NSException exceptionWithHTTPStatus: 403 - reason: [NSString stringWithFormat: @"Event UID already in use. (%s)", eventUID]]; + return [NSException exceptionWithHTTPStatus: 403 + reason: [NSString stringWithFormat: @"Event UID already in use. (%s)", eventUID]]; } // // New event and we're the organizer -- send invitation to all attendees // if (scheduling && [event userIsOrganizer: ownerUser]) - { - attendees = [event attendeesWithoutUser: ownerUser]; - if ([attendees count]) - { - if ((ex = [self _handleAddedUsers: attendees fromEvent: event])) - return ex; - else - { - // We might have auto-accepted resources here. If that's the - // case, let's regenerate the versitstring and replace the - // one from the request. - [rq setContent: [[[event parent] versitString] dataUsingEncoding: [rq contentEncoding]]]; - } - - [self sendEMailUsingTemplateNamed: @"Invitation" - forObject: [event itipEntryWithMethod: @"request"] - previousObject: nil - toAttendees: attendees - withType: @"calendar:invitation"]; - } - } + { + attendees = [event attendeesWithoutUser: ownerUser]; + if ([attendees count]) + { + if ((ex = [self _handleAddedUsers: attendees fromEvent: event])) + return ex; + else + { + // We might have auto-accepted resources here. If that's the + // case, let's regenerate the versitstring and replace the + // one from the request. + [rq setContent: [[[event parent] versitString] dataUsingEncoding: [rq contentEncoding]]]; + } + + [self sendEMailUsingTemplateNamed: @"Invitation" + forObject: [event itipEntryWithMethod: @"request"] + previousObject: nil + toAttendees: attendees + withType: @"calendar:invitation"]; + } + } // // We aren't the organizer but we're an attendee. That can happen when // we receive an external invitation (IMIP/ITIP) and we accept it // from a CUA - it gets added to a specific CalDAV calendar using a PUT // else if (scheduling && [event userIsAttendee: ownerUser]) - { - [self sendIMIPReplyForEvent: event - from: ownerUser - to: [event organizer]]; - } - + { + [self sendIMIPReplyForEvent: event + from: ownerUser + to: [event organizer]]; + } [self sendReceiptEmailForObject: event addedAttendees: attendees @@ -1865,198 +1904,197 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent recurrenceId = nil; for (i = [newEvents count]-1; i >= 0; i--) - { - newEvent = [newEvents objectAtIndex: i]; - - if ([newEvent recurrenceId]) - { - // Find the corresponding RECURRENCE-ID in the old calendar - // If not present, we assume it was created before the PUT - oldEvent = [self _eventFromRecurrenceId: [newEvent recurrenceId] - events: oldEvents]; - - if (oldEvent == nil) - { - NSString *recurrenceTime; - recurrenceTime = [NSString stringWithFormat: @"%f", [[newEvent recurrenceId] timeIntervalSince1970]]; - oldEvent = (iCalEvent *)[self newOccurenceWithID: recurrenceTime]; - } - - // If present, we look for changes - changes = [iCalEventChanges changesFromEvent: oldEvent toEvent: newEvent]; - - if ([changes sequenceShouldBeIncreased] | [changes hasAttendeeChanges]) - { - // We found a RECURRENCE-ID with changes, we consider it - recurrenceId = [newEvent recurrenceId]; - break; - } - else - { + { + newEvent = [newEvents objectAtIndex: i]; + + if ([newEvent recurrenceId]) + { + // Find the corresponding RECURRENCE-ID in the old calendar + // If not present, we assume it was created before the PUT + oldEvent = [self _eventFromRecurrenceId: [newEvent recurrenceId] + events: oldEvents]; + + if (oldEvent == nil) + { + NSString *recurrenceTime; + recurrenceTime = [NSString stringWithFormat: @"%f", [[newEvent recurrenceId] timeIntervalSince1970]]; + oldEvent = (iCalEvent *)[self newOccurenceWithID: recurrenceTime]; + } + + // If present, we look for changes + changes = [iCalEventChanges changesFromEvent: oldEvent toEvent: newEvent]; + + if ([changes sequenceShouldBeIncreased] | [changes hasAttendeeChanges]) + { + // We found a RECURRENCE-ID with changes, we consider it + recurrenceId = [newEvent recurrenceId]; + break; + } + else + { [newEvents removeObject: newEvent]; [oldEvents removeObject: oldEvent]; - } - } - - oldEvent = nil; - newEvent = nil; - } + } + } + + oldEvent = nil; + newEvent = nil; + } // If no changes were observed, let's see if we have any left overs // in the oldEvents or in the newEvents array if (!oldEvent && !newEvent) - { - // We check if we only have to deal with the MASTER event - if ([newEvents count] == [oldEvents count]) - { - oldEvent = [oldEvents objectAtIndex: 0]; - newEvent = [newEvents objectAtIndex: 0]; - } - // A RECURRENCE-ID was added - else if ([newEvents count] > [oldEvents count]) - { - oldEvent = nil; - newEvent = [self _eventFromRecurrenceId: nil events: newEvents]; - recurrenceId = [newEvent recurrenceId]; - } - // A RECURRENCE-ID was removed - else - { - oldEvent = [self _eventFromRecurrenceId: nil events: oldEvents]; - newEvent = nil; - recurrenceId = [oldEvent recurrenceId]; - } - } - + { + // We check if we only have to deal with the MASTER event + if ([newEvents count] == [oldEvents count]) + { + oldEvent = [oldEvents objectAtIndex: 0]; + newEvent = [newEvents objectAtIndex: 0]; + } + // A RECURRENCE-ID was added + else if ([newEvents count] > [oldEvents count]) + { + oldEvent = nil; + newEvent = [self _eventFromRecurrenceId: nil events: newEvents]; + recurrenceId = [newEvent recurrenceId]; + } + // A RECURRENCE-ID was removed + else + { + oldEvent = [self _eventFromRecurrenceId: nil events: oldEvents]; + newEvent = nil; + recurrenceId = [oldEvent recurrenceId]; + } + } + // We check if the PUT call is actually an PART-STATE change // from one of the attendees - here's the logic : // - // if owner == organizer + // if owner == organizer // // if [context activeUser] == organizer // [send the invitation update] // else // [react on SENT-BY as someone else is acting for the organizer] - // - // + // + // if ([[newEvent attendees] count] || [[oldEvent attendees] count]) - { - NSString *uid; + { + NSString *uid; // newEvent might be nil here, if we're deleting a RECURRENCE-ID with attendees // If that's the case, we use the oldEvent for now just to obtain the organizer // and we'll swap it back to nil once we're done. if (!newEvent) newEvent = oldEvent; - - // We fetch the organizer's uid. Sometimes, the recurrence-id will - // have it, sometimes not. If it doesn't, we fetch it from the master event. - uid = [[newEvent organizer] uid]; - - if (!uid && !recurrenceId) - uid = [[[[[newEvent parent] events] objectAtIndex: 0] organizer] uid]; - - // With Thunderbird 10, if you create a recurring event with an exception - // occurence, and invite someone, the PUT will have the organizer in the - // recurrence-id and not in the master event. We must fix this, otherwise - // SOGo will break. - if (!recurrenceId && ![[[[[newEvent parent] events] objectAtIndex: 0] organizer] uid]) - [[[[newEvent parent] events] objectAtIndex: 0] - setOrganizer: [newEvent organizer]]; - + + // We fetch the organizer's uid. Sometimes, the recurrence-id will + // have it, sometimes not. If it doesn't, we fetch it from the master event. + uid = [[newEvent organizer] uid]; + + if (!uid && !recurrenceId) + uid = [[[[[newEvent parent] events] objectAtIndex: 0] organizer] uid]; + + // With Thunderbird 10, if you create a recurring event with an exception + // occurence, and invite someone, the PUT will have the organizer in the + // recurrence-id and not in the master event. We must fix this, otherwise + // SOGo will break. + if (!recurrenceId && ![[[[[newEvent parent] events] objectAtIndex: 0] organizer] uid]) + [[[[newEvent parent] events] objectAtIndex: 0] setOrganizer: [newEvent organizer]]; + if (newEvent == oldEvent) newEvent = nil; - - if (uid && [uid caseInsensitiveCompare: owner] == NSOrderedSame) - { + + if (uid && [uid caseInsensitiveCompare: owner] == NSOrderedSame) + { // A RECCURENCE-ID was removed if (!newEvent && oldEvent) [self prepareDeleteOccurence: oldEvent]; // The master event was changed, A RECCURENCE-ID was added or modified else if ((ex = [self _handleUpdatedEvent: newEvent fromOldEvent: oldEvent])) - return ex; - } - // - // else => attendee is responding - // - // if [context activeUser] == attendee - // [we change the PART-STATE] - // else - // [react on SENT-BY as someone else is acting for the attendee] - else - { - iCalPerson *attendee, *delegate; - NSString *delegateEmail; - - attendee = [newEvent userAsAttendee: [SOGoUser userWithLogin: owner]]; - - // We first check of the sequences are alright. We don't accept attendees - // accepting "old" invitations. If that's the case, we return a 403 + return ex; + } + // + // else => attendee is responding + // + // if [context activeUser] == attendee + // [we change the PART-STATE] + // else + // [react on SENT-BY as someone else is acting for the attendee] + else + { + iCalPerson *attendee, *delegate; + NSString *delegateEmail; + + attendee = [newEvent userAsAttendee: [SOGoUser userWithLogin: owner]]; + + // We first check of the sequences are alright. We don't accept attendees + // accepting "old" invitations. If that's the case, we return a 403 if ([[newEvent sequence] intValue] < [[oldEvent sequence] intValue]) - return [NSException exceptionWithHTTPStatus:403 - reason: @"sequences don't match"]; - + return [NSException exceptionWithHTTPStatus:403 + reason: @"sequences don't match"]; + // Remove the RSVP attribute, as an action from the attendee // was actually performed, and this confuses iCal (bug #1850) - [[attendee attributes] removeObjectForKey: @"RSVP"]; - - delegate = nil; - delegateEmail = [attendee delegatedTo]; - - if ([delegateEmail length]) - { - delegateEmail = [delegateEmail substringFromIndex: 7]; - if ([delegateEmail length]) - delegate = [newEvent findAttendeeWithEmail: delegateEmail]; - } - - changes = [iCalEventChanges changesFromEvent: oldEvent toEvent: newEvent]; - - // The current user deletes the occurence; let the organizer know that - // the user has declined this occurence. - if ([[changes updatedProperties] containsObject: @"exdate"]) - { - [self changeParticipationStatus: @"DECLINED" - withDelegate: nil // FIXME (specify delegate?) - forRecurrenceId: [self _addedExDate: oldEvent newEvent: newEvent]]; - } - else if (attendee) - { - [self changeParticipationStatus: [attendee partStat] - withDelegate: delegate - forRecurrenceId: recurrenceId]; - } - // All attendees and the organizer field were removed. Apple iCal does - // that when we remove the last attendee of an event. - // - // We must update previous's attendees' calendars to actually - // remove the event in each of them. - else - { - [self _handleRemovedUsers: [changes deletedAttendees] - withRecurrenceId: recurrenceId]; - } - } - } // if ([[newEvent attendees] count] || [[oldEvent attendees] count]) + [[attendee attributes] removeObjectForKey: @"RSVP"]; + + delegate = nil; + delegateEmail = [attendee delegatedTo]; + + if ([delegateEmail length]) + { + delegateEmail = [delegateEmail substringFromIndex: 7]; + if ([delegateEmail length]) + delegate = [newEvent findAttendeeWithEmail: delegateEmail]; + } + + changes = [iCalEventChanges changesFromEvent: oldEvent toEvent: newEvent]; + + // The current user deletes the occurence; let the organizer know that + // the user has declined this occurence. + if ([[changes updatedProperties] containsObject: @"exdate"]) + { + [self changeParticipationStatus: @"DECLINED" + withDelegate: nil // FIXME (specify delegate?) + forRecurrenceId: [self _addedExDate: oldEvent newEvent: newEvent]]; + } + else if (attendee) + { + [self changeParticipationStatus: [attendee partStat] + withDelegate: delegate + forRecurrenceId: recurrenceId]; + } + // All attendees and the organizer field were removed. Apple iCal does + // that when we remove the last attendee of an event. + // + // We must update previous's attendees' calendars to actually + // remove the event in each of them. + else + { + [self _handleRemovedUsers: [changes deletedAttendees] + withRecurrenceId: recurrenceId]; + } + } + } // if ([[newEvent attendees] count] || [[oldEvent attendees] count]) else - { - [self sendReceiptEmailForObject: newEvent - addedAttendees: nil - deletedAttendees: nil - updatedAttendees: nil - operation: EventUpdated]; - } + { + [self sendReceiptEmailForObject: newEvent + addedAttendees: nil + deletedAttendees: nil + updatedAttendees: nil + operation: EventUpdated]; + } } // else of if (isNew) ... - + unsigned int baseVersion; // We must NOT invoke [super PUTAction:] here as it'll resave // the content string and we could have etag mismatches. baseVersion = (isNew ? 0 : version); - - ex = [self saveContentString: [calendar versitString] - baseVersion: baseVersion]; - - return ex; + + ex = [self saveComponent: calendar + baseVersion: baseVersion]; + + return ex; } // @@ -2086,24 +2124,24 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent // ex = [self matchesRequestConditionInContext: context]; if (ex) - return ex; + return ex; } - - ex = [self updateContentWithCalendar: rqCalendar fromRequest: rq]; - if (ex) - response = (WOResponse *) ex; + + ex = [self updateContentWithCalendar: rqCalendar fromRequest: rq]; + if (ex) + response = (WOResponse *) ex; else { response = [_ctx response]; if (isNew) - [response setStatus: 201 /* Created */]; + [response setStatus: 201 /* Created */]; else - [response setStatus: 204 /* No Content */]; + [response setStatus: 204 /* No Content */]; etag = [self davEntityTag]; if (etag) - [response setHeader: etag forKey: @"etag"]; + [response setHeader: etag forKey: @"etag"]; } - + return response; } diff --git a/SoObjects/Appointments/SOGoCalendarComponent.h b/SoObjects/Appointments/SOGoCalendarComponent.h index 61aca5c3d..e1e2bc7bd 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.h +++ b/SoObjects/Appointments/SOGoCalendarComponent.h @@ -1,6 +1,6 @@ /* SOGoCalendarComponent.h - this file is part of SOGo * - * Copyright (C) 2006-2013 Inverse inc. + * Copyright (C) 2006-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/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index d595bdba0..f8822c061 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -169,6 +169,11 @@ DESTROY(originalCalendar); } +- (Class *) parsingClass +{ + return [iCalCalendar class]; +} + - (NSString *) davContentType { return @"text/calendar"; @@ -662,7 +667,7 @@ - (NSException *) saveCalendar: (iCalCalendar *) newCalendar { - [self saveContentString: [newCalendar versitString]]; + [super saveComponent: newCalendar]; return nil; } @@ -1193,7 +1198,19 @@ [NSString stringWithFormat: @"%@.ics", newUID] inContainer: newFolder]; - return [newComponent saveContentString: [calendar versitString]]; + return [newComponent saveCalendar: calendar]; +} + +- (NSException *) moveToFolder: (SOGoGCSFolder *) newFolder +{ + NSException *ex; + + ex = [self copyToFolder: newFolder]; + + if (!ex) + ex = [self delete]; + + return ex; } #warning Should we not remove the concept of Organizer and Participant roles? diff --git a/SoObjects/Appointments/SOGoComponentOccurence.m b/SoObjects/Appointments/SOGoComponentOccurence.m index 97c9b1138..0609720b3 100644 --- a/SoObjects/Appointments/SOGoComponentOccurence.m +++ b/SoObjects/Appointments/SOGoComponentOccurence.m @@ -1,9 +1,6 @@ /* SOGoComponentOccurence.m - this file is part of SOGo * - * Copyright (C) 2008-2011 Inverse inc. - * - * Author: Wolfgang Sourdeau - * Francis Lachapelle + * 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 @@ -152,7 +149,6 @@ NSArray *occurences; NSCalendarDate *recurrenceId, *currentId; NSException *error; - NSString *newContent; NSTimeZone *timeZone; iCalCalendar *calendar; iCalEntityObject *currentOccurence; @@ -197,10 +193,8 @@ [master addToExceptionDates: recurrenceId]; [master increaseSequence]; - // We generate the updated iCalendar file and we save it - // in the database. - newContent = [calendar versitString]; - error = [container saveContentString: newContent]; + // We save the updated iCalendar in the database. + error = [container saveComponent: calendar]; } return error; diff --git a/SoObjects/Appointments/SOGoTaskObject.h b/SoObjects/Appointments/SOGoTaskObject.h index 6b5d27d60..5d15c856a 100644 --- a/SoObjects/Appointments/SOGoTaskObject.h +++ b/SoObjects/Appointments/SOGoTaskObject.h @@ -1,6 +1,6 @@ /* - Copyright (C) 2006-2013 Inverse inc. + Copyright (C) 2006-2014 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG This file is part of SOGo. diff --git a/SoObjects/Appointments/SOGoTaskObject.m b/SoObjects/Appointments/SOGoTaskObject.m index 5d70dabd0..a94ca3a38 100644 --- a/SoObjects/Appointments/SOGoTaskObject.m +++ b/SoObjects/Appointments/SOGoTaskObject.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2006-2012 Inverse inc. + Copyright (C) 2006-2014 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG This file is part of SOGo. @@ -65,12 +65,12 @@ #warning this code should be put in SOGoCalendarComponent once the UID hack\ in SOGoAppointmentObject is resolved -- (NSException *) saveContentString: (NSString *) newContent - baseVersion: (unsigned int) newVersion +- (NSException *) saveComponent: (id) theComponent + baseVersion: (unsigned int) newVersion { NSException *ex; - ex = [super saveContentString: newContent baseVersion: newVersion]; + ex = [super saveComponent: theComponent baseVersion: newVersion]; [fullCalendar release]; fullCalendar = nil; [safeCalendar release]; diff --git a/SoObjects/Appointments/SOGoWebAppointmentFolder.h b/SoObjects/Appointments/SOGoWebAppointmentFolder.h index 45e5da636..736a6b461 100644 --- a/SoObjects/Appointments/SOGoWebAppointmentFolder.h +++ b/SoObjects/Appointments/SOGoWebAppointmentFolder.h @@ -1,9 +1,6 @@ /* SOGoWebAppointmentFolder.h - this file is part of SOGo * - * Copyright (C) 2009-2012 Inverse inc. - * - * Author: Cyril Robert - * Ludovic Marcotte + * 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/Appointments/SOGoWebAppointmentFolder.m b/SoObjects/Appointments/SOGoWebAppointmentFolder.m index f5c004c84..3bd6c4206 100644 --- a/SoObjects/Appointments/SOGoWebAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoWebAppointmentFolder.m @@ -1,9 +1,6 @@ /* SOGoWebAppointmentFolder.m - this file is part of SOGo * - * Copyright (C) 2009-2013 Inverse inc. - * - * Author: Cyril Robert - * Ludovic Marcotte + * 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/Appointments/SpanishSpain.lproj/Localizable.strings b/SoObjects/Appointments/SpanishSpain.lproj/Localizable.strings index c855a96bf..5add824eb 100644 --- a/SoObjects/Appointments/SpanishSpain.lproj/Localizable.strings +++ b/SoObjects/Appointments/SpanishSpain.lproj/Localizable.strings @@ -1,3 +1,4 @@ +"Inviting the following persons is prohibited:" = "Esta prohibido invitar las siguientes personas:"; "Personal Calendar" = "Calendario personal"; vevent_class0 = "(Evento público)"; vevent_class1 = "(Evento privado)"; diff --git a/SoObjects/Appointments/iCalCalendar+SOGo.m b/SoObjects/Appointments/iCalCalendar+SOGo.m index 466c47291..e3af84d15 100644 --- a/SoObjects/Appointments/iCalCalendar+SOGo.m +++ b/SoObjects/Appointments/iCalCalendar+SOGo.m @@ -1,8 +1,6 @@ /* iCalCalendar+SOGo.m - this file is part of SOGo * - * Copyright (C) 2012 Inverse inc - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2012-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 @@ -80,4 +78,24 @@ return [self _occurrence: recID inArray: [self todos]]; } +- (NSMutableDictionary *) quickRecordForContainer: (id) theContainer +{ + CardGroup *element; + NSArray *elements; + + unsigned int count; + + elements = [self allObjects]; + count = [elements count]; + if (count) + element = [elements objectAtIndex: 0]; + else + { + [self logWithFormat: @"ERROR: given calendar contains no elements: %@", self]; + element = nil; + } + + return [element quickRecordForContainer: theContainer]; +} + @end diff --git a/SoObjects/Appointments/iCalEntityObject+SOGo.h b/SoObjects/Appointments/iCalEntityObject+SOGo.h index dee22f353..a37c727d5 100644 --- a/SoObjects/Appointments/iCalEntityObject+SOGo.h +++ b/SoObjects/Appointments/iCalEntityObject+SOGo.h @@ -43,7 +43,7 @@ extern NSNumber *iCalDistantFutureNumber; - (id) itipEntryWithMethod: (NSString *) method; - (NSArray *) attendeesWithoutUser: (SOGoUser *) user; -- (NSMutableDictionary *) quickRecord; +- (NSMutableDictionary *) quickRecordForContainer: (id) theContainer; - (int) priorityNumber; - (NSString *) createdBy; diff --git a/SoObjects/Appointments/iCalEntityObject+SOGo.m b/SoObjects/Appointments/iCalEntityObject+SOGo.m index 97f9ce336..cdb32a582 100644 --- a/SoObjects/Appointments/iCalEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalEntityObject+SOGo.m @@ -213,7 +213,7 @@ NSNumber *iCalDistantFutureNumber = nil; return dateNumber; } -- (NSMutableDictionary *) quickRecord +- (NSMutableDictionary *) quickRecordForContainer: (id) theContainer { [self subclassResponsibility: _cmd]; diff --git a/SoObjects/Appointments/iCalEvent+SOGo.h b/SoObjects/Appointments/iCalEvent+SOGo.h index 9d2ee9dd4..fd4869132 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.h +++ b/SoObjects/Appointments/iCalEvent+SOGo.h @@ -1,9 +1,6 @@ /* iCalEvent+SOGo.h - this file is part of SOGo * - * Copyright (C) 2007-2011 Inverse inc. - * - * Author: Wolfgang Sourdeau - * Francis Lachapelle + * Copyright (C) 2007-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 @@ -32,7 +29,7 @@ - (BOOL) isStillRelevant; - (unsigned int) occurenceInterval; -- (NSMutableDictionary *) quickRecord; +- (NSMutableDictionary *) quickRecordForContainer: (id) theContainer; - (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate; @end diff --git a/SoObjects/Appointments/iCalEvent+SOGo.m b/SoObjects/Appointments/iCalEvent+SOGo.m index 6856d89fb..f811ceeca 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.m +++ b/SoObjects/Appointments/iCalEvent+SOGo.m @@ -1,9 +1,6 @@ /* iCalEvent+SOGo.m - this file is part of SOGo * - * Copyright (C) 2007-2013 Inverse inc. - * - * Author: Wolfgang Sourdeau - * Francis Lachapelle + * Copyright (C) 2007-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 @@ -67,7 +64,7 @@ // // // -- (NSMutableDictionary *) quickRecord +- (NSMutableDictionary *) quickRecordForContainer: (id) theContainer { NSMutableDictionary *row; NSCalendarDate *startDate, *endDate, *nextAlarmDate; diff --git a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.h b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.h index 2d5233666..845167967 100644 --- a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.h +++ b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.h @@ -1,24 +1,22 @@ /* iCalRepeatableEntityObject+SOGo.h - this file is part of SOGo - Copyright (C) 2004-2005 SKYRIX Software AG - Copyright (C) 2008-2013 Inverse inc. - - This file is part of OpenGroupware.org. - - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - OGo 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 Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ + * + * Copyright (C) 2007-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 diff --git a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m index 4bbc30e25..128249e88 100644 --- a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m @@ -1,24 +1,22 @@ -/* - Copyright (C) 2008-2013 Inverse inc. - Copyright (C) 2004-2005 SKYRIX Software AG - - This file is part of OpenGroupware.org. - - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - OGo 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 Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ +/* iCalRepeatableEntityObject+SOGo.m - this file is part of SOGo + * + * Copyright (C) 2007-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 #import diff --git a/SoObjects/Appointments/iCalToDo+SOGo.h b/SoObjects/Appointments/iCalToDo+SOGo.h index 81c16f80d..8da587395 100644 --- a/SoObjects/Appointments/iCalToDo+SOGo.h +++ b/SoObjects/Appointments/iCalToDo+SOGo.h @@ -1,8 +1,6 @@ /* iCalToDo+SOGo.h - this file is part of SOGo * - * Copyright (C) 2007 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2007-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 @@ -29,7 +27,7 @@ @interface iCalToDo (SOGoExtensions) -- (NSMutableDictionary *) quickRecord; +- (NSMutableDictionary *) quickRecordForContainer: (id) theContainer; @end diff --git a/SoObjects/Appointments/iCalToDo+SOGo.m b/SoObjects/Appointments/iCalToDo+SOGo.m index fd3487198..f46f05ba5 100644 --- a/SoObjects/Appointments/iCalToDo+SOGo.m +++ b/SoObjects/Appointments/iCalToDo+SOGo.m @@ -1,9 +1,6 @@ -/* iCalEvent+SOGo.m - this file is part of SOGo +/* iCalToDot+SOGo.m - this file is part of SOGo * - * Copyright (C) 2008 Inverse inc. - * Copyright (C) 2004-2005 SKYRIX Software AG - * - * Author: Wolfgang Sourdeau + * 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 @@ -41,7 +38,7 @@ @implementation iCalToDo (SOGoExtensions) -- (NSMutableDictionary *) quickRecord +- (NSMutableDictionary *) quickRecordForContainer: (id) theContainer { NSMutableDictionary *row; NSCalendarDate *startDate, *dueDate, *nextAlarmDate; diff --git a/SoObjects/Contacts/Catalan.lproj/Localizable.strings b/SoObjects/Contacts/Catalan.lproj/Localizable.strings index 4be5199c9..368f3b89b 100644 --- a/SoObjects/Contacts/Catalan.lproj/Localizable.strings +++ b/SoObjects/Contacts/Catalan.lproj/Localizable.strings @@ -1 +1,2 @@ "Personal Address Book" = "Llibreta personal d'adreces"; +"Collected Address Book" = "Llibreta d'adreces recopilades"; diff --git a/SoObjects/Contacts/NGVCard+SOGo.m b/SoObjects/Contacts/NGVCard+SOGo.m index e13cbfffe..5f94a8a87 100644 --- a/SoObjects/Contacts/NGVCard+SOGo.m +++ b/SoObjects/Contacts/NGVCard+SOGo.m @@ -24,9 +24,12 @@ #import #import +#import + #import #import +#import #import #import "NSDictionary+LDIF.h" @@ -788,4 +791,51 @@ convention: return date; } +- (NSMutableDictionary *) quickRecordForContainer: (id) theContainer +{ + NSMutableDictionary *fields; + CardElement *element; + NSString *value; + NSArray *v; + + fields = [NSMutableDictionary dictionaryWithCapacity: 16]; + + value = [self fn]; + if (value) + [fields setObject: value forKey: @"c_cn"]; + element = [self n]; + [fields setObject: [element flattenedValueAtIndex: 0 forKey: @""] + forKey: @"c_sn"]; + [fields setObject: [element flattenedValueAtIndex: 1 forKey: @""] + forKey: @"c_givenName"]; + value = [self preferredTel]; + if (value) + [fields setObject: value forKey: @"c_telephonenumber"]; + value = [self preferredEMail]; + if (![value isNotNull]) + value = @""; + [fields setObject: value forKey: @"c_mail"]; + element = [self org]; + [fields setObject: [element flattenedValueAtIndex: 0 forKey: @""] + forKey: @"c_o"]; + [fields setObject: [element flattenedValueAtIndex: 1 forKey: @""] + forKey: @"c_ou"]; + element = [self preferredAdr]; + if (element && ![element isVoid]) + [fields setObject: [element flattenedValueAtIndex: 3 + forKey: @""] + forKey: @"c_l"]; + value = [[self uniqueChildWithTag: @"X-AIM"] flattenedValuesForKey: @""]; + [fields setObject: value forKey: @"c_screenname"]; + v = [[self categories] trimmedComponents]; + if ([v count] > 0) + [fields setObject: [v componentsJoinedByString: @","] + forKey: @"c_categories"]; + else + [fields setObject: [NSNull null] forKey: @"c_categories"]; + [fields setObject: @"vcard" forKey: @"c_component"]; + + return fields; +} + @end /* NGVCard */ diff --git a/SoObjects/Contacts/NGVList+SOGo.h b/SoObjects/Contacts/NGVList+SOGo.h index 04601ae1c..286a65faa 100644 --- a/SoObjects/Contacts/NGVList+SOGo.h +++ b/SoObjects/Contacts/NGVList+SOGo.h @@ -1,8 +1,6 @@ /* NGVCard+SOGo.h - this file is part of SOGo * - * Copyright (C) 2009 Inverse inc. - * - * Author: Cyril Robert + * 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/Contacts/NGVList+SOGo.m b/SoObjects/Contacts/NGVList+SOGo.m index 4e6fd7c78..7c7bebbe7 100644 --- a/SoObjects/Contacts/NGVList+SOGo.m +++ b/SoObjects/Contacts/NGVList+SOGo.m @@ -1,8 +1,6 @@ /* NGVCard+SOGo.m - this file is part of SOGo * - * Copyright (C) 2009 Inverse inc. - * - * Author: Cyril Robert + * 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 @@ -71,4 +69,19 @@ return rc; } +- (NSMutableDictionary *) quickRecordForContainer: (id) theContainer +{ + NSMutableDictionary *fields; + NSString *value; + + fields = [NSMutableDictionary dictionaryWithCapacity: 1]; + + value = [self fn]; + if (value) + [fields setObject: value forKey: @"c_cn"]; + [fields setObject: @"vlist" forKey: @"c_component"]; + + return fields; +} + @end /* NGVList */ diff --git a/SoObjects/Contacts/NorwegianBokmal.lproj/Localizable.strings b/SoObjects/Contacts/NorwegianBokmal.lproj/Localizable.strings index ab6ee40e1..85fade2b6 100644 --- a/SoObjects/Contacts/NorwegianBokmal.lproj/Localizable.strings +++ b/SoObjects/Contacts/NorwegianBokmal.lproj/Localizable.strings @@ -1 +1,2 @@ "Personal Address Book" = "Personlig adressebok"; +"Collected Address Book" = "Samlet addressebok"; diff --git a/SoObjects/Contacts/SOGoContactGCSEntry.m b/SoObjects/Contacts/SOGoContactGCSEntry.m index 6e3365d4e..4b5d5bd21 100644 --- a/SoObjects/Contacts/SOGoContactGCSEntry.m +++ b/SoObjects/Contacts/SOGoContactGCSEntry.m @@ -48,6 +48,11 @@ [super dealloc]; } +- (Class *) parsingClass +{ + return [NGVCard class]; +} + /* content */ - (NGVCard *) vCard @@ -121,11 +126,25 @@ newContact = [[self class] objectWithName: [NSString stringWithFormat: @"%@.vcf", newUID] - inContainer: newFolder]; + inContainer: newFolder]; - return [newContact saveContentString: [newCard versitString]]; + return [newContact saveComponent: newCard]; } + +- (NSException *) moveToFolder: (SOGoGCSFolder *) newFolder +{ + NSException *ex; + + ex = [self copyToFolder: newFolder]; + + if (!ex) + ex = [self delete]; + + return ex; +} + + - (NSString *) displayName { return [[self vCard] fn]; @@ -150,7 +169,7 @@ NSException *result; if (card) - result = [self saveContentString: [card versitString]]; + result = [super saveComponent: card]; else result = nil; /* TODO: we should probably return an exception instead */ @@ -160,15 +179,15 @@ - (NSException *) saveComponent: (NGVCard *) newCard { ASSIGN(card, newCard); - return [self save]; + return [super saveComponent: newCard]; } -- (NSException *) saveContentString: (NSString *) newContent - baseVersion: (unsigned int) newVersion +- (NSException *) saveComponent: (NGVCard *) newCard + baseVersion: (unsigned int) newVersion { NSException *ex; - ex = [super saveContentString: newContent baseVersion: newVersion]; + ex = [super saveComponent: newCard baseVersion: newVersion]; [card release]; card = nil; diff --git a/SoObjects/Contacts/SOGoContactGCSFolder.h b/SoObjects/Contacts/SOGoContactGCSFolder.h index 643491278..15c823542 100644 --- a/SoObjects/Contacts/SOGoContactGCSFolder.h +++ b/SoObjects/Contacts/SOGoContactGCSFolder.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2006-2011 Inverse inc. + Copyright (C) 2006-2014 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG This file is part of SOGo. diff --git a/SoObjects/Contacts/SOGoContactGCSList.m b/SoObjects/Contacts/SOGoContactGCSList.m index 11d75a752..61e68bf85 100644 --- a/SoObjects/Contacts/SOGoContactGCSList.m +++ b/SoObjects/Contacts/SOGoContactGCSList.m @@ -1,8 +1,6 @@ /* SOGoContactGCSList.m - this file is part of SOGo * - * Copyright (C) 2008-2009 Inverse inc. - * - * Author: Wolfgang Sourdeau + * 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 @@ -45,6 +43,12 @@ [super dealloc]; } +- (Class *) parsingClass +{ + return [NGVList class]; +} + + /* content */ - (NGVList *) vList @@ -77,7 +81,19 @@ [NSString stringWithFormat: @"%@.vlf", newUID] inContainer: newFolder]; - return [newGList saveContentString: [newList versitString]]; + return [newGList saveComponent: newList]; +} + +- (NSException *) moveToFolder: (SOGoGCSFolder *) newFolder +{ + NSException *ex; + + ex = [self copyToFolder: newFolder]; + + if (!ex) + ex = [self delete]; + + return ex; } /* DAV */ @@ -100,7 +116,7 @@ vlist = [self vList]; - [self saveContentString: [vlist versitString]]; + [self saveComponent: vlist]; } @end diff --git a/SoObjects/Contacts/SOGoContactLDIFEntry.m b/SoObjects/Contacts/SOGoContactLDIFEntry.m index dc8ec3431..4e456cbed 100644 --- a/SoObjects/Contacts/SOGoContactLDIFEntry.m +++ b/SoObjects/Contacts/SOGoContactLDIFEntry.m @@ -1,9 +1,6 @@ /* SOGoContactLDIFEntry.m - this file is part of SOGo * - * Copyright (C) 2006-2012 Inverse inc. - * - * Author: Wolfgang Sourdeau - * Ludovic Marcotte + * Copyright (C) 2006-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 @@ -231,7 +228,7 @@ [NSString stringWithFormat: @"%@.vcf", newUID] inContainer: newFolder]; - return [newContact saveContentString: [newCard versitString]]; + return [newContact saveComponent: newCard]; } @end diff --git a/SoObjects/Mailer/Catalan.lproj/Localizable.strings b/SoObjects/Mailer/Catalan.lproj/Localizable.strings index b58d9f585..e2cedc99a 100644 --- a/SoObjects/Mailer/Catalan.lproj/Localizable.strings +++ b/SoObjects/Mailer/Catalan.lproj/Localizable.strings @@ -1,2 +1,2 @@ -"SieveFolderName" = "Filtres"; "OtherUsersFolderName" = "Altres usuaris"; +"SharedFoldersName" = "Carpetes compartides"; diff --git a/SoObjects/Mailer/NorwegianBokmal.lproj/Localizable.strings b/SoObjects/Mailer/NorwegianBokmal.lproj/Localizable.strings index 37a85656e..bcd8b0460 100644 --- a/SoObjects/Mailer/NorwegianBokmal.lproj/Localizable.strings +++ b/SoObjects/Mailer/NorwegianBokmal.lproj/Localizable.strings @@ -1,2 +1,2 @@ -"SieveFolderName" = "Filter"; "OtherUsersFolderName" = "Andre brukere"; +"SharedFoldersName" = "Delte mapper"; diff --git a/SoObjects/Mailer/SOGoDraftObject.h b/SoObjects/Mailer/SOGoDraftObject.h index 0405a8ebe..cf4e06d27 100644 --- a/SoObjects/Mailer/SOGoDraftObject.h +++ b/SoObjects/Mailer/SOGoDraftObject.h @@ -53,6 +53,7 @@ { NSString *path; int IMAP4ID; + int sourceIMAP4ID; NSMutableDictionary *headers; NSString *inReplyTo; NSString *text; @@ -87,6 +88,9 @@ - (void) setSourceFolder: (NSString *) newSourceFolder; - (NSString *) sourceFolder; +- (void) setSourceIMAP4ID: (int) newSourceIMAPID; +- (int) sourceIMAP4ID; + - (void) setIMAP4ID: (int) newIMAPID; - (int) IMAP4ID; diff --git a/SoObjects/Mailer/SOGoDraftObject.m b/SoObjects/Mailer/SOGoDraftObject.m index 4d0f9aa38..b1cb2a2cc 100644 --- a/SoObjects/Mailer/SOGoDraftObject.m +++ b/SoObjects/Mailer/SOGoDraftObject.m @@ -197,6 +197,7 @@ static NSString *userAgent = nil; { if ((self = [super init])) { + sourceIMAP4ID = -1; IMAP4ID = -1; headers = [NSMutableDictionary new]; text = @""; @@ -234,8 +235,7 @@ static NSString *userAgent = nil; { if (!path) { - path = [[self userSpoolFolderPath] stringByAppendingPathComponent: - nameInContainer]; + path = [[self userSpoolFolderPath] stringByAppendingPathComponent: nameInContainer]; [path retain]; } @@ -466,6 +466,9 @@ static NSString *userAgent = nil; forKey: @"isHTML"]; if (inReplyTo) [infos setObject: inReplyTo forKey: @"inReplyTo"]; + if (sourceIMAP4ID > -1) + [infos setObject: [NSString stringWithFormat: @"%i", sourceIMAP4ID] + forKey: @"sourceIMAP4ID"]; if (IMAP4ID > -1) [infos setObject: [NSString stringWithFormat: @"%i", IMAP4ID] forKey: @"IMAP4ID"]; @@ -513,6 +516,10 @@ static NSString *userAgent = nil; [self setText: value]; isHTML = [[infoDict objectForKey: @"isHTML"] boolValue]; + value = [infoDict objectForKey: @"sourceIMAP4ID"]; + if (value) + [self setSourceIMAP4ID: [value intValue]]; + value = [infoDict objectForKey: @"IMAP4ID"]; if (value) [self setIMAP4ID: [value intValue]]; @@ -564,6 +571,22 @@ static NSString *userAgent = nil; [self debugWithFormat: @"Note: info object does not yet exist: %@", p]; } +// +// +// +- (void) setSourceIMAP4ID: (int) newSourceIMAP4ID +{ + sourceIMAP4ID = newSourceIMAP4ID; +} + +// +// +// +- (int) sourceIMAP4ID +{ + return sourceIMAP4ID; +} + // // // @@ -610,7 +633,7 @@ static NSString *userAgent = nil; { if (IMAP4ID > -1) error = [imap4 markURLDeleted: [self imap4URL]]; - IMAP4ID = [self IMAP4IDFromAppendResult: result]; + [self setIMAP4ID: [self IMAP4IDFromAppendResult: result]]; if (imap4URL) { // Invalidate the IMAP message URL since the message ID has changed @@ -902,9 +925,7 @@ static NSString *userAgent = nil; [self setHeaders: info]; [self setText: [sourceMail contentForEditing]]; - [self setSourceURL: [sourceMail imap4URLString]]; [self setIMAP4ID: [[sourceMail nameInContainer] intValue]]; - [self setSourceFolderWithMailObject: sourceMail]; [self storeInfo]; } @@ -934,7 +955,7 @@ static NSString *userAgent = nil; [self setHeaders: info]; [self setSourceURL: [sourceMail imap4URLString]]; [self setSourceFlag: @"Answered"]; - [self setIMAP4ID: [[sourceMail nameInContainer] intValue]]; + [self setSourceIMAP4ID: [[sourceMail nameInContainer] intValue]]; [self setSourceFolderWithMailObject: sourceMail]; [self storeInfo]; @@ -957,7 +978,7 @@ static NSString *userAgent = nil; [self setSourceURL: [sourceMail imap4URLString]]; [self setSourceFlag: @"$Forwarded"]; - [self setIMAP4ID: [[sourceMail nameInContainer] intValue]]; + [self setSourceIMAP4ID: [[sourceMail nameInContainer] intValue]]; [self setSourceFolderWithMailObject: sourceMail]; /* attach message */ @@ -1812,7 +1833,7 @@ static NSString *userAgent = nil; newContact = [contactGCSEntry objectWithName: uid inContainer: folder]; [newContact setIsNew: YES]; - [newContact saveContentString: [card versitString]]; + [newContact saveComponent: card]; } } } diff --git a/SoObjects/SOGo/GNUmakefile.preamble b/SoObjects/SOGo/GNUmakefile.preamble index 925df35a0..a03047c38 100644 --- a/SoObjects/SOGo/GNUmakefile.preamble +++ b/SoObjects/SOGo/GNUmakefile.preamble @@ -15,7 +15,6 @@ ADDITIONAL_CPPFLAGS += \ SOGo_LIBRARIES_DEPEND_UPON += \ -Wl,--no-as-needed \ - -L../../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ -lOGoContentStore \ -L../../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \ -lmemcached \ -lGDLAccess \ diff --git a/SoObjects/SOGo/SOGoCacheGCSObject.m b/SoObjects/SOGo/SOGoCacheGCSObject.m index 03269d9d7..0b3ba25e8 100644 --- a/SoObjects/SOGo/SOGoCacheGCSObject.m +++ b/SoObjects/SOGo/SOGoCacheGCSObject.m @@ -456,6 +456,40 @@ static EOAttribute *textColumn = nil; return nil; } +- (NSException *) destroy +{ + NSString *tableName, *pathValue, *sql; + EOAdaptorChannel *channel; + GCSChannelManager *cm; + NSException *result; + EOAdaptor *adaptor; + + cm = [GCSChannelManager defaultChannelManager]; + channel = [cm acquireOpenChannelForURL: [self tableUrl]]; + tableName = [self tableName]; + + adaptor = [[channel adaptorContext] adaptor]; + pathValue = [adaptor formatValue: [self path] + forAttribute: textColumn]; + result = nil; + + sql = [NSString stringWithFormat: + (@"DELETE FROM %@" + @" WHERE c_path = %@"), + tableName, + pathValue]; + + result = [channel evaluateExpressionX: sql]; + + if (result) + [self errorWithFormat: @"could not delete record %@" + @" in %@: %@", pathValue, tableName, result]; + + [cm releaseChannel: channel]; + + return result; +} + - (void) save { NSString *sql; diff --git a/SoObjects/SOGo/SOGoCacheObject.h b/SoObjects/SOGo/SOGoCacheObject.h index 0d0a78492..d8b01c468 100644 --- a/SoObjects/SOGo/SOGoCacheObject.h +++ b/SoObjects/SOGo/SOGoCacheObject.h @@ -42,6 +42,8 @@ - (NSCalendarDate *) creationDate; - (NSCalendarDate *) lastModified; +- (NSException *) destroy; + @end #endif /* SOGOCACHEOBJECT_H */ diff --git a/SoObjects/SOGo/SOGoContentObject.h b/SoObjects/SOGo/SOGoContentObject.h index 866e2dfb2..fbd0e7365 100644 --- a/SoObjects/SOGo/SOGoContentObject.h +++ b/SoObjects/SOGo/SOGoContentObject.h @@ -1,20 +1,21 @@ /* Copyright (C) 2004 SKYRIX Software AG + Copyright (C) 2005-2014 Inverse inc. - This file is part of OpenGroupware.org. + This file is part of SOGo. - OGo is free software; you can redistribute it and/or modify it under + SOGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - OGo is distributed in the hope that it will be useful, but WITHOUT ANY + SOGo 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the + License along with SOGo; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -50,6 +51,7 @@ - (id) initWithName: (NSString *) newName andContent: (NSString *) newContent inContainer: (SOGoGCSFolder *) newContainer; +- (Class *) parsingClass; /* content */ @@ -62,9 +64,9 @@ - (NSCalendarDate *) lastModified; - (NSString *) contentAsString; -- (NSException *) saveContentString: (NSString *) _str - baseVersion: (unsigned int) _baseVersion; -- (NSException *) saveContentString: (NSString *)_str; +- (NSException *) saveComponent: (id) theComponent + baseVersion: (unsigned int) _baseVersion; +- (NSException *) saveComponent: (id) theComponent; - (id) PUTAction: (WOContext *) _ctx; diff --git a/SoObjects/SOGo/SOGoContentObject.m b/SoObjects/SOGo/SOGoContentObject.m index b1afaa699..088fe522e 100644 --- a/SoObjects/SOGo/SOGoContentObject.m +++ b/SoObjects/SOGo/SOGoContentObject.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2006-2013 Inverse inc. + Copyright (C) 2006-2014 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG This file is part of SOGo. @@ -32,6 +32,9 @@ #import #import +#import +#import + #import "NSCalendarDate+SOGo.h" #import "SOGoCache.h" #import "SOGoGCSFolder.h" @@ -87,6 +90,13 @@ return self; } +- (Class *) parsingClass +{ + [self subclassResponsibility: _cmd]; + + return nil; +} + - (void) _setRecord: (NSDictionary *) objectRecord { id data; @@ -187,16 +197,18 @@ return content; } -- (NSException *) saveContentString: (NSString *) newContent - baseVersion: (unsigned int) newVersion +- (NSException *) saveComponent: (id) theComponent + baseVersion: (unsigned int) newVersion { /* Note: "iCal multifolder saves" are implemented in the apt subclass! */ GCSFolder *folder; NSException *ex; NSCalendarDate *now; + NSString *newContent; ex = nil; - + + newContent = [theComponent versitString]; now = [NSCalendarDate calendarDate]; if (!content) ASSIGN (creationDate, now); @@ -208,8 +220,10 @@ if (folder) { ex = [folder writeContent: newContent - toName: nameInContainer - baseVersion: &version]; + fromComponent: theComponent + container: container + toName: nameInContainer + baseVersion: &version]; if (ex) [self errorWithFormat:@"write failed: %@", ex]; } @@ -223,9 +237,9 @@ return ex; } -- (NSException *) saveContentString: (NSString *) newContent +- (NSException *) saveComponent: (id) theComponent { - return [self saveContentString: newContent baseVersion: version]; + return [self saveComponent: theComponent baseVersion: version]; } /* actions */ @@ -239,17 +253,7 @@ - (NSException *) moveToFolder: (SOGoGCSFolder *) newFolder { - SOGoContentObject *newObject; - NSException *ex; - - newObject = [[self class] objectWithName: nameInContainer - inContainer: newFolder]; - [newObject setIsNew: YES]; - ex = [newObject saveContentString: content]; - if (!ex) - ex = [self delete]; - - return ex; + [self subclassResponsibility: _cmd]; } - (NSException *) delete @@ -341,8 +345,8 @@ /* attempt a save */ - error = [self saveContentString: [rq contentAsString] - baseVersion: baseVersion]; + error = [self saveComponent: [[self parsingClass] parseSingleFromSource: [rq contentAsString]] + baseVersion: baseVersion]; if (error) response = (WOResponse *) error; else diff --git a/SoObjects/SOGo/SOGoDefaults.plist b/SoObjects/SOGo/SOGoDefaults.plist index d23fa971a..17388f261 100644 --- a/SoObjects/SOGo/SOGoDefaults.plist +++ b/SoObjects/SOGo/SOGoDefaults.plist @@ -55,11 +55,11 @@ SOGoIMAPServer = "localhost"; SOGoMailDomain = "localhost"; SOGoSelectedAddressBook = "collected"; - SOGoMailMessageCheck = "manually"; + SOGoRefreshViewCheck = "manually"; SOGoMailMessageForwarding = "inline"; SOGoMailReplyPlacement = "below"; SOGoMailSignaturePlacement = "below"; - SOGoMailPollingIntervals = ( 1, 2, 5, 10, 20, 30, 60 ); + SOGoRefreshViewIntervals = ( 1, 2, 5, 10, 20, 30, 60 ); SOGoMailComposeMessageType = "text"; SOGoMailListViewColumnsOrder = ( "Thread", "Flagged", "Attachment", "Subject", "From", "Unread", "Date", "Priority", diff --git a/SoObjects/SOGo/SOGoDomainDefaults.h b/SoObjects/SOGo/SOGoDomainDefaults.h index a55bb572c..00a79cfe9 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.h +++ b/SoObjects/SOGo/SOGoDomainDefaults.h @@ -66,7 +66,7 @@ - (BOOL) foldersSendEMailNotifications; - (NSArray *) calendarDefaultRoles; - (NSArray *) contactsDefaultRoles; -- (NSArray *) mailPollingIntervals; +- (NSArray *) refreshViewIntervals; - (NSString *) subscriptionFolderFormat; - (NSString *) calendarDefaultCategoryColor; diff --git a/SoObjects/SOGo/SOGoDomainDefaults.m b/SoObjects/SOGo/SOGoDomainDefaults.m index b602e8a9a..1a577c3f6 100644 --- a/SoObjects/SOGo/SOGoDomainDefaults.m +++ b/SoObjects/SOGo/SOGoDomainDefaults.m @@ -225,9 +225,9 @@ return [mailingMechanism lowercaseString]; } -- (NSArray *) mailPollingIntervals +- (NSArray *) refreshViewIntervals { - return [self arrayForKey: @"SOGoMailPollingIntervals"]; + return [self arrayForKey: @"SOGoRefreshViewIntervals"]; } - (NSString *) smtpServer diff --git a/SoObjects/SOGo/SOGoGCSFolder.h b/SoObjects/SOGo/SOGoGCSFolder.h index 794ebc9ac..806be9105 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.h +++ b/SoObjects/SOGo/SOGoGCSFolder.h @@ -1,6 +1,6 @@ /* Copyright (C) 2004-2005 SKYRIX Software AG - Copyright (C) 2006-2013 Inverse inc. + Copyright (C) 2006-2014 Inverse inc. This file is part of SOGo. @@ -53,7 +53,6 @@ NSString *ocsPath; GCSFolder *ocsFolder; NSMutableDictionary *childRecords; - NSMutableDictionary *folderSubscriptionValues; BOOL userCanAccessAllObjects; /* i.e. user obtains 'Access Object' on subobjects */ } @@ -102,7 +101,8 @@ withReference: (NSString *) reference; - (BOOL) subscribeUserOrGroup: (NSString *) theIdentifier - reallyDo: (BOOL) reallyDo; + reallyDo: (BOOL) reallyDo + response: (WOResponse *) theResponse; - (BOOL) userIsSubscriber: (NSString *) subscribingUser; - (void) initializeQuickTablesAclsInContext: (WOContext *) localContext; diff --git a/SoObjects/SOGo/SOGoGCSFolder.m b/SoObjects/SOGo/SOGoGCSFolder.m index b0110f58d..e128f7424 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -196,7 +196,6 @@ static NSArray *childRecordFields = nil; ocsPath = nil; ocsFolder = nil; childRecords = [NSMutableDictionary new]; - folderSubscriptionValues = nil; userCanAccessAllObjects = NO; } @@ -208,7 +207,6 @@ static NSArray *childRecordFields = nil; [ocsFolder release]; [ocsPath release]; [childRecords release]; - [folderSubscriptionValues release]; [super dealloc]; } @@ -216,18 +214,17 @@ static NSArray *childRecordFields = nil; - (void) setFolderPropertyValue: (id) theValue inCategory: (NSString *) theKey + settings: (SOGoUserSettings *) theSettings { - SOGoUserSettings *settings; NSMutableDictionary *folderSettings, *values; NSString *module; - settings = [[context activeUser] userSettings]; module = [container nameInContainer]; - folderSettings = [settings objectForKey: module]; + folderSettings = [theSettings objectForKey: module]; if (!folderSettings) { folderSettings = [NSMutableDictionary dictionary]; - [settings setObject: folderSettings forKey: module]; + [theSettings setObject: folderSettings forKey: module]; } values = [folderSettings objectForKey: theKey]; if (theValue) @@ -249,7 +246,19 @@ static NSArray *childRecordFields = nil; [folderSettings removeObjectForKey: theKey]; } - [settings synchronize]; + [theSettings synchronize]; +} + +- (void) setFolderPropertyValue: (id) theValue + inCategory: (NSString *) theKey +{ + SOGoUserSettings *settings; + + settings = [[context activeUser] userSettings]; + + [self setFolderPropertyValue: theValue + inCategory: theKey + settings: settings]; } - (id) folderPropertyValueInCategory: (NSString *) theKey @@ -276,39 +285,21 @@ static NSArray *childRecordFields = nil; - (void) _setDisplayNameFromRow: (NSDictionary *) row { NSString *primaryDN; - NSDictionary *ownerIdentity; - NSString *subjectFormat; - SOGoDomainDefaults *dd; primaryDN = [row objectForKey: @"c_foldername"]; + if ([primaryDN length]) - { - displayName = [NSMutableString new]; - if ([primaryDN isEqualToString: [container defaultFolderName]]) - [displayName appendString: [self labelForKey: primaryDN - inContext: context]]; - else - [displayName appendString: primaryDN]; - - if (!activeUserIsOwner) { - // We MUST NOT use SOGoUser instances here (by calling -primaryIdentity) - // as it'll load user defaults and user settings which is _very costly_ - // since it involves JSON parsing and database requests - ownerIdentity = [[SOGoUserManager sharedUserManager] - contactInfosForUserWithUIDorEmail: owner]; - - folderSubscriptionValues = [[NSMutableDictionary alloc] initWithObjectsAndKeys: displayName, @"FolderName", - [ownerIdentity objectForKey: @"cn"], @"UserName", - [ownerIdentity objectForKey: @"c_email"], @"Email", nil]; + DESTROY(displayName); - dd = [[context activeUser] domainDefaults]; - subjectFormat = [dd subscriptionFolderFormat]; + if ([primaryDN isEqualToString: [container defaultFolderName]]) + displayName = [self labelForKey: primaryDN + inContext: context]; + else + displayName = primaryDN; - displayName = [folderSubscriptionValues keysWithFormat: subjectFormat]; - [displayName retain]; + RETAIN(displayName); } - } } /* This method fetches the display name defined by the owner, but is also the @@ -352,7 +343,31 @@ static NSArray *childRecordFields = nil; - (void) _fetchDisplayNameFromSubscriber { + NSDictionary *ownerIdentity, *folderSubscriptionValues; + NSString *displayNameFormat; + SOGoDomainDefaults *dd; + displayName = [self folderPropertyValueInCategory: @"FolderDisplayNames"]; + if (!displayName) + { + [self _fetchDisplayNameFromOwner]; + + // We MUST NOT use SOGoUser instances here (by calling -primaryIdentity) + // as it'll load user defaults and user settings which is _very costly_ + // since it involves JSON parsing and database requests + ownerIdentity = [[SOGoUserManager sharedUserManager] + contactInfosForUserWithUIDorEmail: owner]; + + folderSubscriptionValues = [[NSDictionary alloc] initWithObjectsAndKeys: displayName, @"FolderName", + [ownerIdentity objectForKey: @"cn"], @"UserName", + [ownerIdentity objectForKey: @"c_email"], @"Email", nil]; + + dd = [[context activeUser] domainDefaults]; + displayNameFormat = [dd subscriptionFolderFormat]; + + displayName = [folderSubscriptionValues keysWithFormat: displayNameFormat]; + } + [displayName retain]; } @@ -365,6 +380,7 @@ static NSArray *childRecordFields = nil; else { [self _fetchDisplayNameFromSubscriber]; + if (!displayName) [self _fetchDisplayNameFromOwner]; } @@ -856,6 +872,7 @@ static NSArray *childRecordFields = nil; - (BOOL) subscribeUserOrGroup: (NSString *) theIdentifier reallyDo: (BOOL) reallyDo + response: (WOResponse *) theResponse { NSMutableDictionary *moduleSettings, *folderShowAlarms; NSMutableArray *folderSubscription; @@ -868,7 +885,7 @@ static NSArray *childRecordFields = nil; int i; dict = [[SOGoUserManager sharedUserManager] contactInfosForUserWithUIDorEmail: theIdentifier]; - + if ([[dict objectForKey: @"isGroup"] boolValue]) { SOGoGroup *aGroup; @@ -890,9 +907,13 @@ static NSArray *childRecordFields = nil; else allUsers = [NSArray array]; } - + rc = NO; + // This is consumed by SOGo Integrator during folder subscription since v24.0.6 + if (theResponse) + [theResponse appendContentString: [self displayName]]; + for (i = 0; i < [allUsers count]; i++) { sogoUser = [allUsers objectAtIndex: i]; @@ -905,8 +926,7 @@ static NSArray *childRecordFields = nil; [us setObject: moduleSettings forKey: [container nameInContainer]]; } - folderSubscription - = [moduleSettings objectForKey: @"SubscribedFolders"]; + folderSubscription = [moduleSettings objectForKey: @"SubscribedFolders"]; subscriptionPointer = [self folderReference]; folderShowAlarms = [moduleSettings objectForKey: @"FolderShowAlarms"]; @@ -929,6 +949,10 @@ static NSArray *childRecordFields = nil; forKey: @"FolderShowAlarms"]; } + [self setFolderPropertyValue: [self displayName] + inCategory: @"FolderDisplayNames" + settings: us]; + [folderSubscription addObjectUniquely: subscriptionPointer]; // By default, we disable alarms on subscribed calendars @@ -987,7 +1011,7 @@ static NSArray *childRecordFields = nil; response = [context response]; [response setHeader: @"text/plain; charset=utf-8" forKey: @"Content-Type"]; - [response setStatus: 204]; + [response setStatus: 200]; currentUser = [context activeUser]; delegatedUsers = [self _parseDAVDelegatedUsers]; @@ -1002,7 +1026,8 @@ static NSArray *childRecordFields = nil; create contention on GDNC. */ for (count = 0; count < max; count++) [self subscribeUserOrGroup: [delegatedUsers objectAtIndex: count] - reallyDo: reallyDo]; + reallyDo: reallyDo + response: response]; } else { @@ -1021,7 +1046,9 @@ static NSArray *childRecordFields = nil; @"You cannot (un)subscribe to a folder that you own!"]; } else - [self subscribeUserOrGroup: userLogin reallyDo: reallyDo]; + [self subscribeUserOrGroup: userLogin + reallyDo: reallyDo + response: response]; } return response; diff --git a/SoObjects/SOGo/SOGoUserDefaults.h b/SoObjects/SOGo/SOGoUserDefaults.h index b8df5621b..4cb893b8d 100644 --- a/SoObjects/SOGo/SOGoUserDefaults.h +++ b/SoObjects/SOGo/SOGoUserDefaults.h @@ -117,8 +117,8 @@ extern NSString *SOGoWeekStartFirstFullWeek; - (void) setSelectedAddressBook: (NSString *) newValue; - (NSString *) selectedAddressBook; -- (void) setMailMessageCheck: (NSString *) newValue; -- (NSString *) mailMessageCheck; +- (void) setRefreshViewCheck: (NSString *) newValue; +- (NSString *) refreshViewCheck; - (void) setMailComposeMessageType: (NSString *) newValue; - (NSString *) mailComposeMessageType; diff --git a/SoObjects/SOGo/SOGoUserDefaults.m b/SoObjects/SOGo/SOGoUserDefaults.m index d6d1c3999..031a3283a 100644 --- a/SoObjects/SOGo/SOGoUserDefaults.m +++ b/SoObjects/SOGo/SOGoUserDefaults.m @@ -198,7 +198,7 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek"; @"SOGoLanguage", @"Language", @"SOGoMailComposeMessageType", @"ComposeMessagesType", @"SOGoSelectedAddressBook", @"SelectedAddressBook", - @"SOGoMailMessageCheck", @"MessageCheck", + @"SOGoRefreshViewCheck", @"RefreshViewCheck", @"SOGoMailMessageForwarding", @"MessageForwarding", @"SOGoMailSignature", @"MailSignature", @"SOGoMailSignaturePlacement", @"SignaturePlacement", @@ -488,14 +488,14 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek"; return [self stringForKey: @"SOGoSelectedAddressBook"]; } -- (void) setMailMessageCheck: (NSString *) newValue +- (void) setRefreshViewCheck: (NSString *) newValue { - [self setObject: newValue forKey: @"SOGoMailMessageCheck"]; + [self setObject: newValue forKey: @"SOGoRefreshViewCheck"]; } -- (NSString *) mailMessageCheck +- (NSString *) refreshViewCheck { - return [self stringForKey: @"SOGoMailMessageCheck"]; + return [self stringForKey: @"SOGoRefreshViewCheck"]; } - (void) setMailComposeMessageType: (NSString *) newValue diff --git a/SoObjects/SOGo/SOGoUserFolder.m b/SoObjects/SOGo/SOGoUserFolder.m index c450dc0cf..9292f9ffa 100644 --- a/SoObjects/SOGo/SOGoUserFolder.m +++ b/SoObjects/SOGo/SOGoUserFolder.m @@ -153,13 +153,15 @@ - (NSArray *) _subFoldersFromFolder: (SOGoParentFolder *) parentFolder { - NSMutableArray *folders; - NSEnumerator *subfolders; - SOGoFolder *currentFolder; - NSString *folderName, *folderOwner; - Class subfolderClass; + NSDictionary *folderSubscriptionValues, *ownerIdentity; + NSString *folderName, *folderOwner, *formattedName; NSMutableDictionary *currentDictionary; SoSecurityManager *securityManager; + SOGoFolder *currentFolder; + NSEnumerator *subfolders; + NSMutableArray *folders; + SOGoDomainDefaults *dd; + Class subfolderClass; folders = [NSMutableArray array]; @@ -190,6 +192,18 @@ forKey: @"owner"]; [currentDictionary setObject: [currentFolder folderType] forKey: @"type"]; + + dd = [[context activeUser] domainDefaults]; + ownerIdentity = [[SOGoUserManager sharedUserManager] + contactInfosForUserWithUIDorEmail: owner]; + folderSubscriptionValues = [[NSDictionary alloc] initWithObjectsAndKeys: [currentFolder displayName], @"FolderName", + [ownerIdentity objectForKey: @"cn"], @"UserName", + [ownerIdentity objectForKey: @"c_email"], @"Email", nil]; + + formattedName = [folderSubscriptionValues keysWithFormat: [dd subscriptionFolderFormat]]; + [currentDictionary setObject: formattedName + forKey: @"formattedName"]; + [folders addObject: currentDictionary]; } } diff --git a/SoObjects/common.make b/SoObjects/common.make index 8dc2d8c28..31f10e1ac 100644 --- a/SoObjects/common.make +++ b/SoObjects/common.make @@ -20,7 +20,6 @@ ADDITIONAL_INCLUDE_DIRS += \ ADDITIONAL_LIB_DIRS += \ -L../SOGo/SOGo.framework/ \ -L../../SOGo/$(GNUSTEP_OBJ_DIR)/ \ - -L../../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ \ -L../../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ \ -L/usr/local/lib diff --git a/Tests/Integration/GNUmakefile.preamble b/Tests/Integration/GNUmakefile.preamble index 90cc86a41..cd29c8e19 100644 --- a/Tests/Integration/GNUmakefile.preamble +++ b/Tests/Integration/GNUmakefile.preamble @@ -11,7 +11,6 @@ ADDITIONAL_INCLUDE_DIRS += \ ADDITIONAL_LIB_DIRS += \ -L../../SoObjects/SOGo/SOGo.framework -lSOGo \ - -L../../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ -lOGoContentStore \ -L../../SOPE/GDLContentStore/$(GNUSTEP_OBJ_DIR)/ -lGDLContentStore \ -L../../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ -lNGCards \ -L/usr/local/lib -L/usr/lib -lEOControl -lNGStreams -lNGMime -lNGExtensions diff --git a/Tests/Integration/config.py.in b/Tests/Integration/config.py.in index 3a7d79a55..7e5c1ca78 100644 --- a/Tests/Integration/config.py.in +++ b/Tests/Integration/config.py.in @@ -23,6 +23,8 @@ attendee1_delegate_password = "sogo" resource_no_overbook = "res" resource_can_overbook = "res-nolimit" +white_listed_attendee = '{"sogo1":"John Doe "}' + mailserver = "imaphost" testput_nbrdays = 30 diff --git a/Tests/Integration/preferences.py b/Tests/Integration/preferences.py index f66ac0dea..ab12f7894 100644 --- a/Tests/Integration/preferences.py +++ b/Tests/Integration/preferences.py @@ -46,7 +46,7 @@ class preferences: self.client = webdavlib.WebDAVClient(hostname, port) - authCookie = sogoLogin.getAuthCookie(hostname, port, username, password) + authCookie = sogoLogin.getAuthCookie(hostname, port, self.login, self.passw) self.cookie = authCookie # map between preferences/jsonDefaults and the webUI names @@ -56,17 +56,22 @@ class preferences: "SOGoTimeZone": "timezone", "SOGoSieveFilters": "sieveFilters", - # Vacation stuff - "Vacation": "enableVacation", # to disable, don't specify it - "autoReplyText": "autoReplyText", # string - "autoReplyEmailAddresses": "autoReplyEmailAddresses", # LIST - "daysBetweenResponse": "daysBetweenResponsesList", - "ignoreLists": "ignoreLists", #bool + # Vacation stuff + "Vacation": "enableVacation", # to disable, don't specify it + "autoReplyText": "autoReplyText", # string + "autoReplyEmailAddresses": "autoReplyEmailAddresses", # LIST + "daysBetweenResponse": "daysBetweenResponsesList", + "ignoreLists": "ignoreLists", #bool - # forward stuff - "Forward": "enableForward", # to disable, don't specify it - "forwardAddress": "forwardAddress", - "keepCopy": "forwardKeepCopy", + # forward stuff + "Forward": "enableForward", # to disable, don't specify it + "forwardAddress": "forwardAddress", + "keepCopy": "forwardKeepCopy", + + # Calendar stuff + "enablePreventInvitations": "preventInvitations", + "PreventInvitations": "PreventInvitations", + "whiteList": "whiteList", } def set(self, preference, value=None): @@ -74,11 +79,11 @@ class preferences: content="" if isinstance(preference, dict): for k,v in preference.items(): - content+="%s=%s&" % (self.preferencesMap[k], v) + content+="%s=%s&" % (self.preferencesMap[k], urllib.quote(v)) else: # assume it is a str formKey = self.preferencesMap[preference] - content = "%s=%s&hasChanged=1" % (formKey, value) + content = "%s=%s&hasChanged=1" % (formKey, urllib.quote(value)) url = "/SOGo/so/%s/preferences" % self.login @@ -94,7 +99,7 @@ class preferences: raise Exception ("failure setting prefs, (code = %d)" \ % post.response["status"]) - def get(self, preference): + def get(self, preference=None): url = "/SOGo/so/%s/preferences/jsonDefaults" % self.login get = HTTPPreferencesGET (url) get.cookie = self.cookie @@ -102,7 +107,26 @@ class preferences: content = simplejson.loads(get.response['body']) result = None try: - result = content[preference] + if preference: + result = content[preference] + else: + result = content + except: + pass + return result + + def get_settings(self, preference=None): + url = "/SOGo/so/%s/preferences/jsonSettings" % self.login + get = HTTPPreferencesGET (url) + get.cookie = self.cookie + self.client.execute (get) + content = simplejson.loads(get.response['body']) + result = None + try: + if preference: + result = content[preference] + else: + result = content except: pass return result diff --git a/Tests/Integration/test-preferences.py b/Tests/Integration/test-preferences.py index cd1866d39..e49e82dd2 100644 --- a/Tests/Integration/test-preferences.py +++ b/Tests/Integration/test-preferences.py @@ -1,5 +1,5 @@ #!/usr/bin/python -from config import hostname, port, username, password +from config import hostname, port, username, password, white_listed_attendee import preferences import simplejson @@ -35,5 +35,22 @@ class preferencesTest(unittest.TestCase): prefText = "weird data \ ' \"; ^" self._setTextPref(prefText) + def testSetPreventInvitation(self): + """ Set/get the PreventInvitation pref""" + self.prefs.set('PreventInvitations', '0') + notset = self.prefs.get_settings('')['Calendar']['PreventInvitations'] + self.assertEqual(notset, 0) + self.prefs.set('enablePreventInvitations', '0') + isset = self.prefs.get_settings('')['Calendar']['PreventInvitations'] + self.assertEqual(isset, 1) + + def testPreventInvitationsWhiteList(self): + """Add to the PreventInvitations Whitelist""" + self.prefs.set("whiteList", white_listed_attendee) + whitelist = self.prefs.get_settings('Calendar')['PreventInvitationsWhitelist'] + self.assertEqual(whitelist, white_listed_attendee) + + + if __name__ == "__main__": sogotests.runTests() diff --git a/Tests/Integration/test-prevent-invitations.py b/Tests/Integration/test-prevent-invitations.py new file mode 100755 index 000000000..a1ec81b1f --- /dev/null +++ b/Tests/Integration/test-prevent-invitations.py @@ -0,0 +1,209 @@ +#!/usr/bin/python +from config import hostname, port, username, password, \ + superuser, superuser_password, \ + attendee1, attendee1_username, \ + attendee1_password, \ + attendee1_delegate, attendee1_delegate_username, \ + attendee1_delegate_password, \ + resource_no_overbook, resource_can_overbook, \ + white_listed_attendee + +import preferences +import simplejson +import sogotests +import unittest +import utilities +import datetime +import dateutil.tz +import vobject +import vobject.base +import vobject.icalendar +import webdavlib +import StringIO + + +class preventInvitationsTest(unittest.TestCase): + def setUp(self): + self.prefs = preferences.preferences(attendee1, attendee1_password) + self.caldav = CalDAVSchedulingTest(self) + + def tearDown(self): + self.prefs.set("autoReplyText", "") + self.prefs.set('PreventInvitations', '0') + self.prefs.set("whiteList", "") + #- Manual Cleanup, not called because classs is not derived from unittest + self.caldav.tearDown() + + def testDontPreventInvitation(self): + """ Set/get the PreventInvitation pref""" + #- First accept the invitation + self.prefs.set('PreventInvitations', '0') + notset = self.prefs.get_settings('')['Calendar']['PreventInvitations'] + self.assertEqual(notset, 0) + self.caldav.AddAttendee() + self.caldav.VerifyEvent() + + def testPreventInvitation(self): + """ Set PreventInvitation and don't accept the Invitation""" + #- Second, enable PreventInviation and refuse it + self.prefs.set('enablePreventInvitations', '0') + isset = self.prefs.get_settings('')['Calendar']['PreventInvitations'] + self.assertEqual(isset, 1) + self.caldav.AddAttendee(409) + self.caldav.VerifyEvent(404) + + def testPreventInvitationWhiteList(self): + """ Set PreventInvitation add to WhiteList and accept the Invitation""" + #- First, add the Organiser to the Attendee's whitelist + self.prefs.set('enablePreventInvitations', '0') + self.prefs.set("whiteList", white_listed_attendee) + whitelist = self.prefs.get_settings('Calendar')['PreventInvitationsWhitelist'] + self.assertEqual(whitelist, white_listed_attendee) + + #- Second, try again to invite, it should work + self.prefs.set('enablePreventInvitations', '0') + isset = self.prefs.get_settings('')['Calendar']['PreventInvitations'] + self.assertEqual(isset, 1) + self.caldav.AddAttendee() + self.caldav.VerifyEvent() + + +class CalDAVSchedulingTest(object): + def __init__(self, parent_self): + self.test = parent_self # used for utilities + self.setUp() + + def setUp(self): + self.superuser_client = webdavlib.WebDAVClient(hostname, port, + superuser, superuser_password) + self.client = webdavlib.WebDAVClient(hostname, port, + username, password) + self.attendee1_client = webdavlib.WebDAVClient(hostname, port, + attendee1_username, attendee1_password) + self.attendee1_delegate_client = webdavlib.WebDAVClient(hostname, port, + attendee1_delegate_username, attendee1_delegate_password) + + utility = utilities.TestUtility(self.test, self.client) + (self.user_name, self.user_email) = utility.fetchUserInfo(username) + (self.attendee1_name, self.attendee1_email) = utility.fetchUserInfo(attendee1) + (self.attendee1_delegate_name, self.attendee1_delegate_email) = utility.fetchUserInfo(attendee1_delegate) + + self.user_calendar = "/SOGo/dav/%s/Calendar/personal/" % username + self.attendee1_calendar = "/SOGo/dav/%s/Calendar/personal/" % attendee1 + self.attendee1_delegate_calendar = "/SOGo/dav/%s/Calendar/personal/" % attendee1_delegate + + # fetch non existing event to let sogo create the calendars in the db + self._getEvent(self.client, "%snonexistent" % self.user_calendar, exp_status=404) + self._getEvent(self.attendee1_client, "%snonexistent" % self.attendee1_calendar, exp_status=404) + self._getEvent(self.attendee1_delegate_client, "%snonexistent" % + self.attendee1_delegate_calendar, exp_status=404) + + # list of ics used by the test. + # tearDown will loop over this and wipe them in all users' calendar + self.ics_list = [] + + + def tearDown(self): + # delete all created events from all users' calendar + for ics in self.ics_list: + self._deleteEvent(self.superuser_client, + "%s%s" % (self.user_calendar, ics), None) + self._deleteEvent(self.superuser_client, + "%s%s" % (self.attendee1_calendar, ics), None) + self._deleteEvent(self.superuser_client, + "%s%s" % (self.attendee1_delegate_calendar, ics), None) + + def _newEvent(self, summary="test event", uid="test", transp=0): + transparency = ("OPAQUE", "TRANSPARENT") + + newCal = vobject.iCalendar() + vevent = newCal.add('vevent') + vevent.add('summary').value = summary + vevent.add('transp').value = transparency[transp] + + now = datetime.datetime.now(dateutil.tz.gettz("America/Montreal")) + startdate = vevent.add('dtstart') + startdate.value = now + enddate = vevent.add('dtend') + enddate.value = now + datetime.timedelta(0, 3600) + vevent.add('uid').value = uid + vevent.add('dtstamp').value = now + vevent.add('last-modified').value = now + vevent.add('created').value = now + vevent.add('class').value = "PUBLIC" + vevent.add('sequence').value = "0" + + return newCal + + def _putEvent(self, client, filename, event, exp_status = 201): + put = webdavlib.HTTPPUT(filename, event.serialize()) + put.content_type = "text/calendar; charset=utf-8" + client.execute(put) + if exp_status is not None: + self.test.assertEquals(put.response["status"], exp_status) + + def _getEvent(self, client, filename, exp_status = 200): + get = webdavlib.HTTPGET(filename) + client.execute(get) + + if exp_status is not None: + self.test.assertEquals(get.response["status"], exp_status) + + if get.response["headers"]["content-type"].startswith("text/calendar"): + stream = StringIO.StringIO(get.response["body"]) + event = vobject.base.readComponents(stream).next() + else: + event = None + + return event + + def _deleteEvent(self, client, filename, exp_status = 204): + delete = webdavlib.WebDAVDELETE(filename) + client.execute(delete) + if exp_status is not None: + self.test.assertEquals(delete.response["status"], exp_status) + + def AddAttendee(self, exp_status=204): + """ add attendee after event creation """ + + # make sure the event doesn't exist + ics_name = "test-add-attendee.ics" + self.ics_list += [ics_name] + + self._deleteEvent(self.client, + "%s%s" % (self.user_calendar,ics_name), None) + self._deleteEvent(self.attendee1_client, + "%s%s" % (self.attendee1_calendar,ics_name), None) + + # 1. create an event in the organiser's calendar + event = self._newEvent(summary="Test add attendee", uid="Test add attendee") + organizer = event.vevent.add('organizer') + organizer.cn_param = self.user_name + organizer.value = self.user_email + self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event) + + # 2. add an attendee + event.add("method").value = "REQUEST" + attendee = event.vevent.add('attendee') + attendee.cn_param = self.attendee1_name + attendee.rsvp_param = "TRUE" + attendee.partstat_param = "NEEDS-ACTION" + attendee.value = self.attendee1_email + self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event, + exp_status=exp_status) + + #- save event for VerifyEvent + self.event = event + self.ics_name = ics_name + + def VerifyEvent(self, exp_status=200): + # 1. verify that the attendee has the event + attendee_event = self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, self.ics_name), exp_status) + + # 2. make sure the received event match the original one + if attendee_event: + self.test.assertEquals(self.event.vevent.uid, attendee_event.vevent.uid) + + +if __name__ == "__main__": + sogotests.runTests() diff --git a/Tests/Integration/test-sieve.py b/Tests/Integration/test-sieve.py index 54620a339..55a85a4c5 100644 --- a/Tests/Integration/test-sieve.py +++ b/Tests/Integration/test-sieve.py @@ -73,9 +73,9 @@ class sieveTest(unittest.TestCase): "days": preferences.daysBetweenResponseList[daysSelect], } - filterAdd = {"Vacation":1, + filterAdd = {"Vacation":"1", "autoReplyText": vacation_msg, - "daysBetweenResponse": daysSelect, + "daysBetweenResponse": "%d" % daysSelect, "autoReplyEmailAddresses": self.user_email, } @@ -94,11 +94,11 @@ class sieveTest(unittest.TestCase): "days": preferences.daysBetweenResponseList[daysSelect], } - filterAdd = {"Vacation":1, + filterAdd = {"Vacation":"1", "autoReplyText": vacation_msg, - "daysBetweenResponse": daysSelect, + "daysBetweenResponse": "%d" % daysSelect, "autoReplyEmailAddresses": self.user_email, - "ignoreLists": 1, + "ignoreLists": "1", } self.prefs.set(filterAdd) @@ -112,7 +112,7 @@ class sieveTest(unittest.TestCase): sieveScript = sieve_simple_forward % { "redirect_mailaddr": redirect_mailaddr } - filterAdd = { "Forward": 1, + filterAdd = { "Forward": "1", "forwardAddress": redirect_mailaddr, } @@ -126,9 +126,9 @@ class sieveTest(unittest.TestCase): sieveScript = sieve_forward_keep % { "redirect_mailaddr": redirect_mailaddr } - filterAdd = { "Forward": 1, + filterAdd = { "Forward": "1", "forwardAddress": redirect_mailaddr, - "keepCopy": 1, + "keepCopy": "1", } self.prefs.set(filterAdd) diff --git a/Tests/Integration/utilities.py b/Tests/Integration/utilities.py index 3b7f31441..850cb07a8 100644 --- a/Tests/Integration/utilities.py +++ b/Tests/Integration/utilities.py @@ -101,7 +101,7 @@ class TestACLUtility(TestUtility): post = webdavlib.HTTPPOST(self.resource, subscribeQuery) post.content_type = "application/xml; charset=\"utf-8\"" self.client.execute(post) - self.test.assertEquals(post.response["status"], 204, + self.test.assertEquals(post.response["status"], 200, "subscribtion failure to '%s' for '%s' (status: %d)" % (self.resource, "', '".join(subscribers), post.response["status"])) diff --git a/Tests/Integration/webdavlib.py b/Tests/Integration/webdavlib.py index 0e0f7c384..4d3d8311d 100644 --- a/Tests/Integration/webdavlib.py +++ b/Tests/Integration/webdavlib.py @@ -106,7 +106,12 @@ class WebDAVClient: query.start = time.time() self.conn.request(query.method, query.url, body, self.prepare_headers(query, body)) - query.set_response(self.conn.getresponse()); + try: + query.set_response(self.conn.getresponse()); + except httplib.BadStatusLine, e: + print e + time.sleep(3) + query.set_response(self.conn.getresponse()); query.duration = time.time() - query.start class HTTPSimpleQuery: diff --git a/Tests/Unit/GNUmakefile b/Tests/Unit/GNUmakefile index 316e5cf4e..6b99e05a5 100644 --- a/Tests/Unit/GNUmakefile +++ b/Tests/Unit/GNUmakefile @@ -35,7 +35,7 @@ $(TEST_TOOL)_CPPFLAGS += \ ADDITIONAL_LIB_DIRS += \ -L../../SoObjects/SOGo/SOGo.framework/Versions/Current -L../../SOPE/NGCards/obj -L../../SOPE/GDLContentStore/obj -lSOGo -lNGMime -lNGCards -lGDLContentStore -lNGExtensions -lSBJson -lobjc \ -L/usr/local/lib -lSaxObjC \ - -Wl,-rpath,../../SoObjects/SOGo/SOGo.framework/Versions/Current -Wl,-rpath,../../SOPE/NGCards/obj -Wl,-rpath,../../SOPE/GDLContentStore/obj -Wl,-rpath,../../OGoContentStore/obj + -Wl,-rpath,../../SoObjects/SOGo/SOGo.framework/Versions/Current -Wl,-rpath,../../SOPE/NGCards/obj -Wl,-rpath,../../SOPE/GDLContentStore/obj ADDITIONAL_LDFLAGS += -Wl,--no-as-needed -include GNUmakefile.preamble diff --git a/Tools/GNUmakefile.preamble b/Tools/GNUmakefile.preamble index 3c0700276..fc78b8b0a 100644 --- a/Tools/GNUmakefile.preamble +++ b/Tools/GNUmakefile.preamble @@ -11,7 +11,6 @@ ADDITIONAL_INCLUDE_DIRS += \ ADDITIONAL_LIB_DIRS += \ -L../SoObjects/SOGo/SOGo.framework -lSOGo \ - -L../OGoContentStore/$(GNUSTEP_OBJ_DIR)/ -lOGoContentStore \ -L../SOPE/GDLContentStore/$(GNUSTEP_OBJ_DIR)/ -lGDLContentStore \ -L../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ -lNGCards \ -L/usr/local/lib -L/usr/lib -lEOControl -lNGStreams -lNGMime -lNGExtensions -lNGObjWeb diff --git a/Tools/SOGoToolRestore.m b/Tools/SOGoToolRestore.m index 0cdda3b29..d3fb601f9 100644 --- a/Tools/SOGoToolRestore.m +++ b/Tools/SOGoToolRestore.m @@ -1,8 +1,6 @@ /* SOGoToolRestore.m - this file is part of SOGo * - * Copyright (C) 2009-2012 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 @@ -43,6 +41,10 @@ #import #import +#import +#import +#import + #import "SOGoTool.h" /* TODO: @@ -116,6 +118,29 @@ typedef enum SOGoToolRestoreMode { " sogo-tool restore -p /tmp/foo bob\n"); } +- (Class) parsingClassForContent: (NSString *) theContent +{ + CardGroup *cardEntry; + NSString *firstTag; + Class objectClass; + + objectClass = Nil; + + cardEntry = [CardGroup parseSingleFromSource: theContent]; + if (cardEntry) + { + firstTag = [[cardEntry tag] uppercaseString]; + if ([firstTag isEqualToString: @"VCARD"]) + objectClass = [NGVCard class]; + else if ([firstTag isEqualToString: @"VLIST"]) + objectClass = [NGVList class]; + else + objectClass = [iCalCalendar class]; + } + + return objectClass; +} + - (BOOL) checkDirectory { NSFileManager *fm; @@ -347,11 +372,12 @@ typedef enum SOGoToolRestoreMode { - (BOOL) restoreRecords: (NSArray *) records ofFolder: (GCSFolder *) gcsFolder { - NSAutoreleasePool *pool; NSDictionary *existingRecords, *currentRecord; NSString *cName, *cContent; - int count, max; + NSAutoreleasePool *pool; + unsigned int version; + int count, max; BOOL rc; if (records) @@ -374,7 +400,10 @@ typedef enum SOGoToolRestoreMode { { NSLog (@"restoring record '%@'", cName); cContent = [currentRecord objectForKey: @"c_content"]; - [gcsFolder writeContent: cContent toName: cName + [gcsFolder writeContent: cContent + fromComponent: [[self parsingClassForContent: cContent] parseSingleFromSource: cContent] + container: nil + toName: cName baseVersion: &version]; } } @@ -529,14 +558,14 @@ typedef enum SOGoToolRestoreMode { if (tables) { NSLog (@"Restorable folders:"); - folderPrefixLen = 1 + [userID length]; - tableKeys = [[tables allKeys] objectEnumerator]; + folderPrefixLen = 8 + [userID length]; // tables keys start with /Users/ + tableKeys = [[[tables allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectEnumerator]; while ((key = [tableKeys nextObject])) { currentFolder = [tables objectForKey: key]; folderKey = [key substringFromIndex: folderPrefixLen]; - NSLog (@" '%@': %@", - [currentFolder objectForKey: @"displayname"], folderKey); + NSLog (@" %@ (%@)", + folderKey, [currentFolder objectForKey: @"displayname"]); } } else diff --git a/UI/Common/Catalan.lproj/Localizable.strings b/UI/Common/Catalan.lproj/Localizable.strings index a29d35ddd..750183367 100644 --- a/UI/Common/Catalan.lproj/Localizable.strings +++ b/UI/Common/Catalan.lproj/Localizable.strings @@ -69,6 +69,7 @@ "You cannot create a list in a shared address book." = "No podeu crear una llista en una agenda compartida."; "Warning" = "Atenció"; +"Can't contact server" = "Ha ocorregut un error en contactar amb el servidor. Per favor, intenti-ho més tarda."; "You are not allowed to access this module or this system. Please contact your system administrator." = "No teniu permís per a accedir a aquest mòdul o sistema. Contacteu amb l'administrador."; @@ -101,10 +102,9 @@ "Due Date:" = "Data límit:"; "Location:" = "Lloc:"; -/* Mail labels */ +/* mail labels */ "Important" = "Important"; "Work" = "Feina"; -"Work" = "Feina"; "Personal" = "Personal"; "To Do" = "Per fer"; "Later" = "Més tard"; diff --git a/UI/Common/Czech.lproj/Localizable.strings b/UI/Common/Czech.lproj/Localizable.strings index 011219d31..ef60019d4 100644 --- a/UI/Common/Czech.lproj/Localizable.strings +++ b/UI/Common/Czech.lproj/Localizable.strings @@ -69,6 +69,7 @@ "You cannot create a list in a shared address book." = "Nemůžete vytvořit seznam ve sdíleném adresáři."; "Warning" = "Upozornění"; +"Can't contact server" = "Při připojení k serveru došlo k chybě. Prosím zkuste to později."; "You are not allowed to access this module or this system. Please contact your system administrator." = "Nemáte oprávnění pro přístup k tomuto modulu nebo systému. Kontaktujte prosím svého systémového administrátora."; @@ -101,10 +102,9 @@ "Due Date:" = "Do dne:"; "Location:" = "Místo:"; -/* Mail labels */ +/* mail labels */ "Important" = "Důležitý"; "Work" = "Pracovní"; -"Work" = "Pracovní"; "Personal" = "Osobní"; "To Do" = "Třeba udělat"; "Later" = "Později"; diff --git a/UI/Common/Dutch.lproj/Localizable.strings b/UI/Common/Dutch.lproj/Localizable.strings index 7b69510c0..b01954529 100644 --- a/UI/Common/Dutch.lproj/Localizable.strings +++ b/UI/Common/Dutch.lproj/Localizable.strings @@ -69,6 +69,7 @@ "You cannot create a list in a shared address book." = "U kunt geen lijst maken in een gedeeld adresboek."; "Warning" = "Waarschuwing"; +"Can't contact server" = "Bij het verbinden met de server is een fout opgetreden, probeer het alter opnieuw."; "You are not allowed to access this module or this system. Please contact your system administrator." = "U heeft geen toegang tot deze module of dit systeem. Neem contact op met uw systeembeheerder."; @@ -101,10 +102,9 @@ "Due Date:" = "Verloopdatum:"; "Location:" = "Plaats:"; -/* Mail labels */ +/* mail labels */ "Important" = "Belangrijk"; "Work" = "Werk"; -"Work" = "Werk"; "Personal" = "Persoonlijk"; "To Do" = "Te doen"; "Later" = "Later"; diff --git a/UI/Common/Finnish.lproj/Localizable.strings b/UI/Common/Finnish.lproj/Localizable.strings index dccd332d8..b4d89b9f4 100644 --- a/UI/Common/Finnish.lproj/Localizable.strings +++ b/UI/Common/Finnish.lproj/Localizable.strings @@ -69,6 +69,7 @@ "You cannot create a list in a shared address book." = "Et voi luoda listaa jaettuun osoitekirjaan."; "Warning" = "Varoitus"; +"Can't contact server" = "Virhe palvelinyhteydessä. Yritä myöhemmin uudelleen."; "You are not allowed to access this module or this system. Please contact your system administrator." = "Sinulla ei ole pääsyoikeuksia tähän moduliin tai järjestelmään. Ole hyvä ja ota yhteyttä järjestelmänvalvojaan. "; @@ -102,10 +103,8 @@ "Location:" = "Sijainti:"; /* mail labels */ -/* Mail labels */ "Important" = "Tärkeä"; "Work" = "Työ"; -"Work" = "Työ"; "Personal" = "Henkilökohtainen"; "To Do" = "Tehtävä"; "Later" = "Myöhemmin"; diff --git a/UI/Common/German.lproj/Localizable.strings b/UI/Common/German.lproj/Localizable.strings index a9f5e1458..85d47b658 100644 --- a/UI/Common/German.lproj/Localizable.strings +++ b/UI/Common/German.lproj/Localizable.strings @@ -13,7 +13,7 @@ "Administration" = "Administration"; "Disconnect" = "Beenden"; "Right Administration" = "Rechteadministration"; -"Log Console (dev.)" = "Journal (dev.)"; +"Log Console (dev.)" = "Journal (Entwickler)"; "User" = "Benutzer"; "Vacation message is enabled" = "Abwesenheitsmeldung ist eingeschaltet"; diff --git a/UI/Common/NorwegianBokmal.lproj/Localizable.strings b/UI/Common/NorwegianBokmal.lproj/Localizable.strings index d73850120..3789e7797 100644 --- a/UI/Common/NorwegianBokmal.lproj/Localizable.strings +++ b/UI/Common/NorwegianBokmal.lproj/Localizable.strings @@ -69,6 +69,7 @@ "You cannot create a list in a shared address book." = "Du kan ikke opprette en liste i en delt adressebok."; "Warning" = "Advarsel"; +"Can't contact server" = "Det oppstod en feil ved opprettelse av kontakt med serveren. Vennligst prøv igjen senere."; "You are not allowed to access this module or this system. Please contact your system administrator." = "Du har ikke rettigheter til modulen eller systemet. Kontakt din systemadministrator."; @@ -101,10 +102,9 @@ "Due Date:" = "Forfallsdato:"; "Location:" = "Lokasjon:"; -/* Mail labels */ +/* mail labels */ "Important" = "Viktig"; "Work" = "Arbeid"; -"Work" = "Arbeid"; "Personal" = "Personlig"; "To Do" = "Gjøremål"; "Later" = "Senere"; diff --git a/UI/Common/Polish.lproj/Localizable.strings b/UI/Common/Polish.lproj/Localizable.strings index a041733e5..23df39b8e 100644 --- a/UI/Common/Polish.lproj/Localizable.strings +++ b/UI/Common/Polish.lproj/Localizable.strings @@ -103,10 +103,8 @@ "Location:" = "Miejsce:"; /* mail labels */ -/* Mail labels */ "Important" = "Ważne"; "Work" = "Praca"; -"Work" = "Praca"; "Personal" = "Osobiste"; "To Do" = "Do zrobienia"; "Later" = "Później"; diff --git a/UI/Common/SpanishSpain.lproj/Localizable.strings b/UI/Common/SpanishSpain.lproj/Localizable.strings index 1be575659..8bf18963b 100644 --- a/UI/Common/SpanishSpain.lproj/Localizable.strings +++ b/UI/Common/SpanishSpain.lproj/Localizable.strings @@ -69,6 +69,7 @@ "You cannot create a list in a shared address book." = "No es posible crear una lista en una libreta de direcciones compartida."; "Warning" = "Aviso"; +"Can't contact server" = "Ha ocurrido un error al contactar al servidor. Prueba otra vez mas tarde."; "You are not allowed to access this module or this system. Please contact your system administrator." = "No esta permitido el acceso a este módulo o éste sistema. Por favor contacte con su administrador de sistemas."; @@ -101,10 +102,9 @@ "Due Date:" = "Vencimiento:"; "Location:" = "Lugar:"; -/* Mail labels */ +/* mail labels */ "Important" = "Importante"; "Work" = "Trabajo"; -"Work" = "Trabajo"; "Personal" = "Personal"; "To Do" = "Por hacer"; "Later" = "Más tarde"; diff --git a/UI/Common/UIxAclEditor.m b/UI/Common/UIxAclEditor.m index a50e0ff07..8c39a7ebd 100644 --- a/UI/Common/UIxAclEditor.m +++ b/UI/Common/UIxAclEditor.m @@ -1,8 +1,6 @@ /* UIxAclEditor.m - this file is part of SOGo * - * Copyright (C) 2006-2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2006-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 @@ -138,7 +136,7 @@ - (BOOL) canSubscribeUsers { return [[self clientObject] - respondsToSelector: @selector (subscribeUserOrGroup:reallyDo:)]; + respondsToSelector: @selector (subscribeUserOrGroup:reallyDo:response:)]; } - (BOOL) currentUserIsSubscribed diff --git a/UI/Common/UIxFolderActions.m b/UI/Common/UIxFolderActions.m index fb0a95eeb..374be4be8 100644 --- a/UI/Common/UIxFolderActions.m +++ b/UI/Common/UIxFolderActions.m @@ -89,7 +89,10 @@ } else { - [clientObject subscribeUserOrGroup: login reallyDo: reallyDo]; + [clientObject subscribeUserOrGroup: login + reallyDo: reallyDo + response: response]; + if (isMailInvitation) { mailInvitationURL @@ -373,7 +376,8 @@ max = [userIDs count]; for (count = 0; count < max; count++) [folder subscribeUserOrGroup: [userIDs objectAtIndex: count] - reallyDo: YES]; + reallyDo: YES + response: nil]; ex = nil; } else diff --git a/UI/Contacts/Catalan.lproj/Localizable.strings b/UI/Contacts/Catalan.lproj/Localizable.strings index 2249ab989..0371cfe93 100644 --- a/UI/Contacts/Catalan.lproj/Localizable.strings +++ b/UI/Contacts/Catalan.lproj/Localizable.strings @@ -36,7 +36,6 @@ "delete" = "esborrar"; "edit" = "modificar"; "invalidemailwarn" = "Adreça de correu no vàlida."; -"invaliddatewarn" = "Data no vàlida."; "new" = "nou"; "Preferred Phone" = "Telèfon preferit"; @@ -64,6 +63,7 @@ "New Card" = "Afegir contacte"; "New List" = "Crear llista"; +"Edit" = "Modificar"; "Properties" = "Propietats"; "Sharing..." = "Compartir..."; "Write" = "Escriure"; @@ -147,7 +147,7 @@ "You cannot delete the card of \"%{0}\"." = "No podeu esborrar el contacte \"%{0}\"."; -"Address Book Name" = "Nom de la llibreta"; + "You cannot subscribe to a folder that you own!" = "No us podeu subscriure a una carpeta que és vostra."; @@ -206,3 +206,10 @@ "A total of %{0} cards were imported in the addressbook." = "S'han importat a la llibreta %{0} contactes."; "Reload" = "Actualitzar"; + +/* Properties window */ +"Address Book Name:" = "Nom de la llibreta d'adreces"; +"Links to this Address Book" = "Enllaços a aquesta llibreta d'adreces"; +"Authenticated User Access" = "Accés autenticat"; +"CardDAV URL: " = "URL CardDAV:"; + diff --git a/UI/Contacts/NorwegianBokmal.lproj/Localizable.strings b/UI/Contacts/NorwegianBokmal.lproj/Localizable.strings index c4eb8bf81..c8405b9aa 100644 --- a/UI/Contacts/NorwegianBokmal.lproj/Localizable.strings +++ b/UI/Contacts/NorwegianBokmal.lproj/Localizable.strings @@ -36,7 +36,6 @@ "delete" = "slett"; "edit" = "endre"; "invalidemailwarn" = "Den spesifiserte e-posten er ikke gyldig"; -"invaliddatewarn" = "Den spesifiserte datoen er ikke gyldig."; "new" = "ny"; "Preferred Phone" = "Foretrukket telefon"; @@ -64,6 +63,7 @@ "New Card" = "Nytt adressekort"; "New List" = "Ny mailingliste"; +"Edit" = "Endre"; "Properties" = "Egenskaper"; "Sharing..." = "Deling..."; "Write" = "Skriv"; @@ -147,7 +147,7 @@ "You cannot delete the card of \"%{0}\"." = "Du kan ikke slette adressekortet \"%{0}\"."; -"Address Book Name" = "Navn på adressebok"; + "You cannot subscribe to a folder that you own!" = "Du kan ikke abonnere på en mappe som du selv eier."; @@ -206,3 +206,10 @@ "A total of %{0} cards were imported in the addressbook." = "Totalt %{0} av adressekortene ble importert til adresseboken."; "Reload" = "Last på nytt"; + +/* Properties window */ +"Address Book Name:" = "Addressebok navn:"; +"Links to this Address Book" = "Lenker til denne addresseboken"; +"Authenticated User Access" = "Autentisert brukertilgang"; +"CardDAV URL: " = "CardDAV URL:"; + diff --git a/UI/Contacts/UIxContactFolderActions.m b/UI/Contacts/UIxContactFolderActions.m index 887ce3b7b..974e7d5d0 100644 --- a/UI/Contacts/UIxContactFolderActions.m +++ b/UI/Contacts/UIxContactFolderActions.m @@ -1,6 +1,6 @@ /* Copyright (C) 2004-2005 SKYRIX Software AG - Copyright (C) 2006-2013 Inverse inc. + Copyright (C) 2006-2014 Inverse inc. This file is part of SOGo @@ -280,7 +280,7 @@ inContainer: folder]; [contact setIsNew: YES]; - [contact saveContentString: [card versitString]]; + [contact saveComponent: card]; rc = YES; } diff --git a/UI/Contacts/UIxListEditor.m b/UI/Contacts/UIxListEditor.m index d9478b9cc..69d63bd21 100644 --- a/UI/Contacts/UIxListEditor.m +++ b/UI/Contacts/UIxListEditor.m @@ -1,9 +1,6 @@ /* UIxListEditor.m - this file is part of SOGo * - * Copyright (C) 2008-2012 Inverse inc. - * - * Author: Wolfgang Sourdeau - * Francis Lachapelle + * 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 @@ -190,7 +187,7 @@ // Add vCard to current folder newContact = [SOGoContactGCSEntry objectWithName: newUID inContainer: folder]; - [newContact saveContentString: [newCard versitString]]; + [newContact saveComponent: newCard]; // Create card reference for the list cardReference = [NGVCardReference elementWithTag: @"card"]; diff --git a/UI/MailPartViewers/Catalan.lproj/Localizable.strings b/UI/MailPartViewers/Catalan.lproj/Localizable.strings index c55d613ec..350a38191 100644 --- a/UI/MailPartViewers/Catalan.lproj/Localizable.strings +++ b/UI/MailPartViewers/Catalan.lproj/Localizable.strings @@ -12,6 +12,8 @@ publish_info_text = "El remitent us informa de l'esdeveniment adjunt."; cancel_info_text = "La vostra invitació o l'esdeveniment han estat \ncancel·lats."; request_info_no_attendee = "Heu proposat un esdeveniment. Rebeu aquest correu com a notificació, però no hi figureu com a participant."; Appointment = "Cita"; +"Status Update" = "Canvi d'estat"; +was = "era"; Organizer = "Organitzador"; Time = "Hora"; diff --git a/UI/MailPartViewers/NorwegianBokmal.lproj/Localizable.strings b/UI/MailPartViewers/NorwegianBokmal.lproj/Localizable.strings index 2088ec87d..23ee2f68e 100644 --- a/UI/MailPartViewers/NorwegianBokmal.lproj/Localizable.strings +++ b/UI/MailPartViewers/NorwegianBokmal.lproj/Localizable.strings @@ -12,6 +12,8 @@ publish_info_text = "Avsenderen informerer deg om vedlagte arrangement."; cancel_info_text = "Din invitasjon eller hele arrangementet er avlyst."; request_info_no_attendee = "foreslår et møte til deltakerne. Du har fått informasjon, men er ikke planlagt som en deltaker."; Appointment = "Møte"; +"Status Update" = "Status oppdatering"; +was = "var"; Organizer = "Arrangør"; Time = "Tid"; diff --git a/UI/MailPartViewers/UIxMailPartHTMLViewer.m b/UI/MailPartViewers/UIxMailPartHTMLViewer.m index 7160f6ba2..09d48ae5a 100644 --- a/UI/MailPartViewers/UIxMailPartHTMLViewer.m +++ b/UI/MailPartViewers/UIxMailPartHTMLViewer.m @@ -146,34 +146,34 @@ static NSData* _sanitizeContent(NSData *theData) const char *bytes; char *buf; int i, j, len; - BOOL found_delimiter; + BOOL found_delimiter, in_meta; d = [NSMutableData dataWithData: theData]; bytes = [d bytes]; len = [d length]; i = 0; + in_meta = NO; + while (i < len) { - // We check if we see in which case, we don't do any kind - // of substitution there after. - if (i < len-6) + // We check if we see in which case, we substitute de charset= stuff. + if (i < len-5) { if ((*bytes == '<') && - (*(bytes+1) == '/') && - (*(bytes+2) == 'h' || *(bytes+2) == 'H') && - (*(bytes+3) == 'e' || *(bytes+3) == 'E') && - (*(bytes+4) == 'a' || *(bytes+4) == 'A') && - (*(bytes+5) == 'd' || *(bytes+5) == 'D') && - (*(bytes+6) == '>')) - break; + (*(bytes+1) == 'm' || *(bytes+2) == 'M') && + (*(bytes+2) == 'e' || *(bytes+3) == 'E') && + (*(bytes+3) == 't' || *(bytes+4) == 'T') && + (*(bytes+4) == 'a' || *(bytes+5) == 'A') && + (*(bytes+5) == ' ')) + in_meta = YES; } // We search for something like : // // // - if (i < len-9) + if (in_meta && i < len-9) { if ((*bytes == 'c' || *bytes == 'C') && (*(bytes+1) == 'h' || *(bytes+1) == 'H') && @@ -195,16 +195,18 @@ static NSData* _sanitizeContent(NSData *theData) // We haven't found anything, let's return the data untouched if ((i+j) >= len) { - found_delimiter = NO; + in_meta = found_delimiter = NO; break; } } if (found_delimiter) - [d replaceBytesInRange: NSMakeRange(i, j) - withBytes: NULL - length: 0]; - break; + { + [d replaceBytesInRange: NSMakeRange(i, j) + withBytes: NULL + length: 0]; + in_meta = found_delimiter = NO; + } } } diff --git a/UI/MailPartViewers/UIxMailPartICalActions.m b/UI/MailPartViewers/UIxMailPartICalActions.m index 281ee4bdc..be8c47e73 100644 --- a/UI/MailPartViewers/UIxMailPartICalActions.m +++ b/UI/MailPartViewers/UIxMailPartICalActions.m @@ -1,8 +1,6 @@ /* UIxMailPartICalActions.m - this file is part of SOGo * - * Copyright (C) 2007-2011 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2007-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 @@ -179,7 +177,7 @@ if ([*eventObject isNew]) { chosenEvent = emailEvent; - [*eventObject saveContentString: [[emailEvent parent] versitString]]; + [*eventObject saveCalendar: [emailEvent parent]]; } else { @@ -209,7 +207,7 @@ parent = [calendarEvent parent]; [parent removeChild: calendarEvent]; [parent addChild: emailEvent]; - [*eventObject saveContentString: [parent versitString]]; + [*eventObject saveCalendar: parent]; [*eventObject flush]; chosenEvent = emailEvent; } @@ -332,7 +330,6 @@ { iCalEvent *emailEvent; SOGoAppointmentObject *eventObject; - NSString *iCalString; WOResponse *response; emailEvent = [self _emailEvent]; @@ -341,8 +338,7 @@ eventObject = [self _eventObjectWithUID: [emailEvent uid]]; if ([eventObject isNew]) { - iCalString = [[emailEvent parent] versitString]; - [eventObject saveContentString: iCalString]; + [eventObject saveCalendar: [emailEvent parent]]; response = [self responseWith204]; } else diff --git a/UI/MailPartViewers/product.plist b/UI/MailPartViewers/product.plist index dd749362d..225c9e7f0 100644 --- a/UI/MailPartViewers/product.plist +++ b/UI/MailPartViewers/product.plist @@ -1,5 +1,5 @@ { /* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ - requires = ( MAIN ); + requires = ( MAIN, Mailer, Appointments ); publicResources = ( ); diff --git a/UI/MailerUI/Catalan.lproj/Localizable.strings b/UI/MailerUI/Catalan.lproj/Localizable.strings index c5c6f293c..c76770b66 100644 --- a/UI/MailerUI/Catalan.lproj/Localizable.strings +++ b/UI/MailerUI/Catalan.lproj/Localizable.strings @@ -79,7 +79,7 @@ "Remove this folder" = "Esborrar aquesta carpeta"; "Erase mails from this folder" = "Esborrar els correus d'aquesta carpeta"; "Expunge this folder" = "Compactar aquesta carpeta"; -"Archive This Folder" = "Arxivar aquesta carpeta"; +"Export This Folder" = "Exportar Aquesta Carpeta"; "Modify the acl of this folder" = "Modificar la llista de permisos d'aquesta carpeta"; "Saved Messages.zip" = "missatgesdesats.zip"; @@ -97,11 +97,12 @@ "Reply-To" = "Respondre a"; "Add address" = "Afegir adreça"; -"Attachments:" = "Adjunts:"; "Open" = "Obrir"; "Select All" = "Seleccionar tots"; "Attach Web Page..." = "Adjuntar pàgina Web..."; -"Attach File(s)..." = "Adjuntar fitxers..."; +"file" = "Fitxer"; +"files" = "Fitxers"; +"Save all" = "Desar Tot"; "to" = "per a"; "cc" = "cc"; @@ -252,7 +253,6 @@ "Please select only one message to print." = "Per imprimir, seleccioneu només un missatge."; "The message you have selected doesn't exist anymore." = "El missatge seleccionat ja no existeix."; - "The folder with name \"%{0}\" could not be created." = "La carpeta de nom \"%{0}\" no es pot crear."; "This folder could not be renamed to \"%{0}\"." @@ -279,11 +279,15 @@ "error_missingsubject" = "No heu indicat l'assumpte"; "error_missingrecipients" = "No heu indicat els destinataris"; "Send Anyway" = "Enviar igualment"; +"Error while saving the draft:" = "Error en guardar l'esborrany:"; +"Error while uploading the file \"%{0}\":" = "Error en carregar el fitxer \"%{0}\":"; +"There is an active file upload. Closing the window will interrupt it." = "Hi ha una càrrega de fitxer activa. S'interromprà si es tanca la finestra."; /* Message sending */ "cannot send message: (smtp) all recipients discarded" = "No s'ha pogut enviar el missatge: tots els destinataris són incorrectes."; "cannot send message (smtp) - recipients discarded:" = "No s'ha pogut enviar el missatge. Les següents adreces són incorrectes:"; "cannot send message: (smtp) error when connecting" = "No s'ha pogut enviar el missatge. Error al connectar amb el servidor SMTP."; -"Name" = "Nom"; +/* Contacts list in mail editor */ "Email" = "Correu electrònic"; +"Name" = "Nom"; diff --git a/UI/MailerUI/English.lproj/Localizable.strings b/UI/MailerUI/English.lproj/Localizable.strings index 345d4c09b..02466f5b5 100644 --- a/UI/MailerUI/English.lproj/Localizable.strings +++ b/UI/MailerUI/English.lproj/Localizable.strings @@ -13,6 +13,7 @@ "Print" = "Print"; "Stop" = "Stop"; "Write" = "Write"; +"Search" = "Search"; "Send" = "Send"; "Contacts" = "Contacts"; @@ -41,6 +42,7 @@ "Attachment" = "Attachment"; "Unread" = "Unread"; "Flagged" = "Flagged"; +"Search multiple mailboxes" = "Search multiple mailboxes"; /* Main Frame */ @@ -94,8 +96,9 @@ "To" = "To"; "Cc" = "Cc"; "Bcc" = "Bcc"; -"Reply-To" = "Reply-To"; +"Reply-To" = "Reply-To"; "Add address" = "Add address"; +"Body" = "Body"; "Open" = "Open"; "Select All" = "Select All"; @@ -237,6 +240,18 @@ "As Not Junk" = "As Not Junk"; "Run Junk Mail Controls" = "Run Junk Mail Controls"; +"Search messages in:" = "Search messages in:"; +"Search" = "Search"; +"Search subfolders" = "Search subfolders"; +"Match any of the following" = "Match any of the following"; +"Match all of the following" = "Match all of the following"; +"contains" = "contains"; +"does not contain" = "does not contain"; +"No matches found" = "No matches found"; +"results found" = "results found"; +"result found" = "result found"; +"Please specify at least one filter" = "Please specify at least one filter"; + /* Folder operations */ "Name :" = "Name :"; "Enter the new name of your folder :" diff --git a/UI/MailerUI/French.lproj/Localizable.strings b/UI/MailerUI/French.lproj/Localizable.strings index d0e64a0db..b136c0a99 100644 --- a/UI/MailerUI/French.lproj/Localizable.strings +++ b/UI/MailerUI/French.lproj/Localizable.strings @@ -13,6 +13,7 @@ "Print" = "Imprimer"; "Stop" = "Arrêter"; "Write" = "Écrire"; +"Search" = "Recherche"; "Send" = "Envoyer"; "Contacts" = "Contacts"; @@ -41,6 +42,7 @@ "Attachment" = "Documents joints"; "Unread" = "Non lus"; "Flagged" = "Marqués"; +"Search multiple mailboxes" = "Rechercher dans plusieurs boîtes"; /* Main Frame */ @@ -94,8 +96,9 @@ "To" = "Destinataire"; "Cc" = "Copie à"; "Bcc" = "Copie cachée à"; -"Reply-To" = "Répondre à"; +"Reply-To" = "Répondre à"; "Add address" = "Ajouter Adresse"; +"Body" = "Contenu"; "Open" = "Ouvrir"; "Select All" = "Tout sélectionner"; @@ -237,6 +240,18 @@ "As Not Junk" = "Comme acceptable"; "Run Junk Mail Controls" = "Lancer le contrôle des indésirables"; +"Search messages in:" = "Rechercher dans :"; +"Search" = "Recherche"; +"Search subfolders" = "Rechercher dans les sous-dossiers"; +"Match any of the following" = "Répondant à un des critères suivants"; +"Match all of the following" = "Répondant à tous les critères suivants"; +"contains" = "contient"; +"does not contain" = "ne contient pas"; +"No matches found" = "Aucun résultat"; +"results found" = "messages trouvés"; +"result found" = "message trouvé"; +"Please specify at least one filter" = "Veuillez définir au moins un critère."; + /* Folder operations */ "Name :" = "Nom:"; "Enter the new name of your folder :" diff --git a/UI/MailerUI/GNUmakefile b/UI/MailerUI/GNUmakefile index e354d983f..851eb9abf 100644 --- a/UI/MailerUI/GNUmakefile +++ b/UI/MailerUI/GNUmakefile @@ -23,6 +23,7 @@ MailerUI_OBJC_FILES += \ UIxMailPopupView.m \ UIxMailMoveToPopUp.m \ UIxMailFilterPanel.m \ + UIxMailSearch.m \ \ UIxMailAccountActions.m \ UIxMailFolderActions.m \ diff --git a/UI/MailerUI/NorwegianBokmal.lproj/Localizable.strings b/UI/MailerUI/NorwegianBokmal.lproj/Localizable.strings index 7904d6acc..b1632f685 100644 --- a/UI/MailerUI/NorwegianBokmal.lproj/Localizable.strings +++ b/UI/MailerUI/NorwegianBokmal.lproj/Localizable.strings @@ -79,7 +79,7 @@ "Remove this folder" = "Fjern mappen"; "Erase mails from this folder" = "Slett meldinger fra mappen"; "Expunge this folder" = "Fjern mappen"; -"Archive This Folder" = "Arkiver denne mappen"; +"Export This Folder" = "Eksporter denne mappen"; "Modify the acl of this folder" = "Endre adgangsrettigheter til mappen"; "Saved Messages.zip" = "Lagrede Meldinger.zip"; @@ -97,11 +97,12 @@ "Reply-To" = "Svar-til"; "Add address" = "Legg til adresse"; -"Attachments:" = "Vedlegg:"; "Open" = "Åpne"; "Select All" = "Velg alle"; "Attach Web Page..." = "Legg til url..."; -"Attach File(s)..." = "Legg til fil(er)..."; +"file" = "fil"; +"files" = "filer"; +"Save all" = "Lagre alle"; "to" = "Til"; "cc" = "Kopi"; @@ -278,6 +279,9 @@ "error_missingsubject" = "Emnefelt mangler. Vil du likevel sende meldingen?"; "error_missingrecipients" = "Ingen mottakere er angitt"; "Send Anyway" = "Send likevel"; +"Error while saving the draft:" = "Problem ved lagring av utkast:"; +"Error while uploading the file \"%{0}\":" = "Problem ved opplasting av fil \"%{0}\":"; +"There is an active file upload. Closing the window will interrupt it." = "Det er en aktiv fil opplasting. Lukk vinduet for å avbryte den."; /* Message sending */ "cannot send message: (smtp) all recipients discarded" = "Kan ikke sende melding: alle mottakeradresser er ugyldige."; diff --git a/UI/MailerUI/Toolbars/SOGoMailFolder.toolbar b/UI/MailerUI/Toolbars/SOGoMailFolder.toolbar index 1c9d1968d..119b2ac9b 100644 --- a/UI/MailerUI/Toolbars/SOGoMailFolder.toolbar +++ b/UI/MailerUI/Toolbars/SOGoMailFolder.toolbar @@ -63,5 +63,12 @@ image = "tb-mail-print-flat-24x24.png"; label = "Print"; tooltip = "Print this message"; }, + + { link = "#"; + onclick = "return onSearchMail(event);"; + cssClass = ""; + image = "search-messages.png"; + label = "Search"; + tooltip = "Search multiple mailboxes"; } ) ) diff --git a/UI/MailerUI/UIxMailActions.m b/UI/MailerUI/UIxMailActions.m index b21cd160f..691a75f26 100644 --- a/UI/MailerUI/UIxMailActions.m +++ b/UI/MailerUI/UIxMailActions.m @@ -34,6 +34,7 @@ #import #import #import +#import #import #import "../Common/WODirectAction+SOGo.h" @@ -145,6 +146,80 @@ return response; } +- (void) collapseAction: (BOOL) isCollapsing +{ + SOGoMailObject *co; + NSMutableDictionary *moduleSettings, *threadsCollapsed; + NSMutableArray *mailboxThreadsCollapsed; + NSString *msguid, *currentMailbox, *currentAccount, *keyForMsgUIDs; + SOGoUserSettings *us; + + co = [self clientObject]; + us = [[context activeUser] userSettings]; + if (!(moduleSettings = [us objectForKey: @"Mail"])) + [us setObject:[NSMutableDictionary dictionnary] forKey: @"Mail"]; + msguid = [co nameInContainer]; + currentMailbox = [[co container] nameInContainer]; + currentAccount = [[[co container] container] nameInContainer]; + keyForMsgUIDs = [NSString stringWithFormat:@"/%@/%@", currentAccount, currentMailbox]; + + if (isCollapsing) + { + // Check if the module threadsCollapsed is created in the userSettings + if ((threadsCollapsed = [moduleSettings objectForKey:@"threadsCollapsed"])) + { + // Check if the currentMailbox already have other threads saved and add the new collapsed thread + if ((mailboxThreadsCollapsed = [threadsCollapsed objectForKey:keyForMsgUIDs])) + { + if (![mailboxThreadsCollapsed containsObject:msguid]) + [mailboxThreadsCollapsed addObject:msguid]; + } + else + { + mailboxThreadsCollapsed = [NSMutableArray arrayWithObject:msguid]; + [threadsCollapsed setObject:mailboxThreadsCollapsed forKey:keyForMsgUIDs]; + } + } + else + { + // Created the module threadsCollapsed and add the new collapsed thread + mailboxThreadsCollapsed = [NSMutableArray arrayWithObject:msguid]; + threadsCollapsed = [NSMutableDictionary dictionaryWithObject:mailboxThreadsCollapsed forKey:keyForMsgUIDs]; + [moduleSettings setObject:threadsCollapsed forKey: @"threadsCollapsed"]; + } + } + else + { + // Check if the module threadsCollapsed is created in the userSettings + if ((threadsCollapsed = [moduleSettings objectForKey:@"threadsCollapsed"])) + { + // Check if the currentMailbox already have other threads saved and remove the uncollapsed thread + if ((mailboxThreadsCollapsed = [threadsCollapsed objectForKey:keyForMsgUIDs])) + { + [mailboxThreadsCollapsed removeObject:msguid]; + if ([mailboxThreadsCollapsed count] == 0) + [threadsCollapsed removeObjectForKey:keyForMsgUIDs]; + } + } + // TODO : Manage errors + } + [us synchronize]; +} + +- (id) markMessageCollapseAction +{ + [self collapseAction: YES]; + + return [self responseWith204]; +} + +- (id) markMessageUncollapseAction +{ + [self collapseAction: NO]; + + return [self responseWith204]; +} + /* SOGoDraftObject */ - (WOResponse *) editAction { @@ -203,74 +278,4 @@ return response; } -- (WOResponse *) addLabelAction -{ - WOResponse *response; - SOGoMailObject *co; - NSException *error; - NSArray *flags; - NSString *flag; - - flag = [[[self->context request] formValueForKey: @"flag"] fromCSSIdentifier]; - co = [self clientObject]; - flags = [NSArray arrayWithObject: flag]; - - error = [co addFlags: flags]; - if (error) - response = (WOResponse *) error; - else - response = [self responseWith204]; - - return response; -} - -- (WOResponse *) removeLabelAction -{ - WOResponse *response; - SOGoMailObject *co; - NSException *error; - NSArray *flags; - NSString *flag; - - flag = [[[self->context request] formValueForKey: @"flag"] fromCSSIdentifier]; - co = [self clientObject]; - flags = [NSArray arrayWithObject: flag]; - - error = [co removeFlags: flags]; - if (error) - response = (WOResponse *) error; - else - response = [self responseWith204]; - - return response; -} - -- (WOResponse *) removeAllLabelsAction -{ - NSMutableArray *flags; - WOResponse *response; - SOGoMailObject *co; - NSException *error; - NSDictionary *v; - - - co = [self clientObject]; - - v = [[[context activeUser] userDefaults] mailLabelsColors]; - - // We always unconditionally remove the Mozilla tags - flags = [NSMutableArray arrayWithObjects: @"$Label1", @"$Label2", @"$Label3", - @"$Label4", @"$Label5", nil]; - - [flags addObjectsFromArray: [v allKeys]]; - - error = [co removeFlags: flags]; - if (error) - response = (WOResponse *) error; - else - response = [self responseWith204]; - - return response; -} - @end diff --git a/UI/MailerUI/UIxMailEditor.m b/UI/MailerUI/UIxMailEditor.m index f78843b53..5c0a7a661 100644 --- a/UI/MailerUI/UIxMailEditor.m +++ b/UI/MailerUI/UIxMailEditor.m @@ -1,6 +1,6 @@ /* Copyright (C) 2004-2005 SKYRIX Software AG - Copyright (C) 2008-2011 Inverse inc. + Copyright (C) 2008-2014 Inverse inc. This file is part of SOGo. @@ -15,11 +15,12 @@ License for more details. You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the + License along with SOGo; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#import #import #import #import @@ -808,7 +809,7 @@ static NSArray *infoKeys = nil; jsonResponse = [NSDictionary dictionaryWithObjectsAndKeys: @"success", @"status", [co sourceFolder], @"sourceFolder", - [NSNumber numberWithInt: [co IMAP4ID]], @"messageID", + [NSNumber numberWithInt: [co sourceIMAP4ID]], @"sourceMessageID", nil]; recipients_count += [[co allRecipients] count]; diff --git a/UI/MailerUI/UIxMailFolderActions.m b/UI/MailerUI/UIxMailFolderActions.m index 0e1ca9123..e04bf50bc 100644 --- a/UI/MailerUI/UIxMailFolderActions.m +++ b/UI/MailerUI/UIxMailFolderActions.m @@ -89,22 +89,45 @@ - (WOResponse *) renameFolderAction { SOGoMailFolder *co; + SOGoUserSettings *us; WOResponse *response; NSException *error; - NSString *folderName; + NSString *newFolderName, *currentMailbox, *currentAccount, *keyForMsgUIDs, *newKeyForMsgUIDs; + NSMutableDictionary *moduleSettings, *threadsCollapsed; + NSArray *values; co = [self clientObject]; + //Prepare the variables need to verify if the current folder have any collapsed threads saved in userSettings + us = [[context activeUser] userSettings]; + moduleSettings = [us objectForKey: @"Mail"]; + threadsCollapsed = [moduleSettings objectForKey:@"threadsCollapsed"]; + currentMailbox = [co nameInContainer]; + currentAccount = [[co container] nameInContainer]; + keyForMsgUIDs = [NSString stringWithFormat:@"/%@/%@", currentAccount, currentMailbox]; - folderName = [[context request] formValueForKey: @"name"]; - error = [co renameTo: folderName]; + newFolderName = [[context request] formValueForKey: @"name"]; + newKeyForMsgUIDs = [[NSString stringWithFormat:@"/%@/folder%@", currentAccount, newFolderName] asCSSIdentifier]; + error = [co renameTo: newFolderName]; if (error) { response = [self responseWithStatus: 500]; [response appendContentString: @"Unable to rename folder."]; } else - response = [self responseWith204]; - + { + // Verify if the current folder have any collapsed threads save under it old name and adjust the folderName + if (threadsCollapsed) + { + if ([threadsCollapsed objectForKey:keyForMsgUIDs]) + { + values = [NSArray arrayWithArray:[threadsCollapsed objectForKey:keyForMsgUIDs]]; + [threadsCollapsed setObject:values forKey:newKeyForMsgUIDs]; + [threadsCollapsed removeObjectForKey:keyForMsgUIDs]; + [us synchronize]; + } + } + response = [self responseWith204]; + } return response; } @@ -149,10 +172,13 @@ - (WOResponse *) deleteAction { SOGoMailFolder *co, *inbox; + SOGoUserSettings *us; WOResponse *response; NGImap4Connection *connection; NSException *error; NSURL *srcURL, *destURL; + NSMutableDictionary *moduleSettings, *threadsCollapsed; + NSString *currentMailbox, *currentAccount, *keyForMsgUIDs; co = [self clientObject]; if ([co ensureTrashFolder]) @@ -174,7 +200,23 @@ // We unsubscribe to the old one, and subscribe back to the new one [[connection client] subscribe: [destURL path]]; [[connection client] unsubscribe: [srcURL path]]; - + + // Verify if the current folder have any collapsed threads save under it name and erase it + us = [[context activeUser] userSettings]; + moduleSettings = [us objectForKey: @"Mail"]; + threadsCollapsed = [moduleSettings objectForKey:@"threadsCollapsed"]; + currentMailbox = [co nameInContainer]; + currentAccount = [[co container] nameInContainer]; + keyForMsgUIDs = [NSString stringWithFormat:@"/%@/%@", currentAccount, currentMailbox]; + + if (threadsCollapsed) + { + if ([threadsCollapsed objectForKey:keyForMsgUIDs]) + { + [threadsCollapsed removeObjectForKey:keyForMsgUIDs]; + [us synchronize]; + } + } response = [self responseWith204]; } } @@ -191,11 +233,16 @@ { SOGoMailFolder *co; SOGoMailAccount *account; + SOGoUserSettings *us; WOResponse *response; NSArray *uids; NSString *value; NSDictionary *data; BOOL withTrash; + NSMutableDictionary *moduleSettings, *threadsCollapsed; + NSString *currentMailbox, *currentAccount, *keyForMsgUIDs; + NSMutableArray *mailboxThreadsCollapsed; + int i; co = [self clientObject]; value = [[context request] formValueForKey: @"uid"]; @@ -217,7 +264,26 @@ andString: [data jsonRepresentation]]; } else - response = [self responseWith204]; + { + // Verify if the message beeing delete is saved as the root of a collapsed thread + us = [[context activeUser] userSettings]; + moduleSettings = [us objectForKey: @"Mail"]; + threadsCollapsed = [moduleSettings objectForKey:@"threadsCollapsed"]; + currentMailbox = [co nameInContainer]; + currentAccount = [[co container] nameInContainer]; + keyForMsgUIDs = [NSString stringWithFormat:@"/%@/%@", currentAccount, currentMailbox]; + + if (threadsCollapsed) + { + if ((mailboxThreadsCollapsed = [threadsCollapsed objectForKey:keyForMsgUIDs])) + { + for (i = 0; i < [uids count]; i++) + [mailboxThreadsCollapsed removeObject:[uids objectAtIndex:i]]; + [us synchronize]; + } + } + response = [self responseWith204]; + } } } else @@ -317,9 +383,14 @@ - (WOResponse *) moveMessagesAction { SOGoMailFolder *co; + SOGoUserSettings *us; WOResponse *response; NSArray *uids; NSString *value, *destinationFolder; + NSMutableDictionary *moduleSettings, *threadsCollapsed; + NSString *currentMailbox, *currentAccount, *keyForMsgUIDs; + NSMutableArray *mailboxThreadsCollapsed; + int i; co = [self clientObject]; value = [[context request] formValueForKey: @"uid"]; @@ -331,6 +402,23 @@ uids = [value componentsSeparatedByString: @","]; response = [co moveUIDs: uids toFolder: destinationFolder inContext: context]; if (!response) + // Verify if the message beeing delete is saved as the root of a collapsed thread + us = [[context activeUser] userSettings]; + moduleSettings = [us objectForKey: @"Mail"]; + threadsCollapsed = [moduleSettings objectForKey:@"threadsCollapsed"]; + currentMailbox = [co nameInContainer]; + currentAccount = [[co container] nameInContainer]; + keyForMsgUIDs = [NSString stringWithFormat:@"/%@/%@", currentAccount, currentMailbox]; + + if (threadsCollapsed) + { + if ((mailboxThreadsCollapsed = [threadsCollapsed objectForKey:keyForMsgUIDs])) + { + for (i = 0; i < [uids count]; i++) + [mailboxThreadsCollapsed removeObject:[uids objectAtIndex:i]]; + [us synchronize]; + } + } response = [self responseWith204]; } else @@ -618,4 +706,70 @@ return response; } +- (WOResponse *) addOrRemoveLabelAction +{ + WOResponse *response; + WORequest *request; + SOGoMailFolder *co; + NSException *error; + NSArray *msgUIDs, *flags; + NSString *operation; + NSDictionary *content, *result; + BOOL addOrRemove; + NGImap4Client *client; + + request = [context request]; + content = [[request contentAsString] objectFromJSONString]; + flags = [NSArray arrayWithObject:[content objectForKey:@"flags"]]; + msgUIDs = [NSArray arrayWithArray:[content objectForKey:@"msgUIDs"]]; + operation = [content objectForKey:@"operation"]; + addOrRemove = ([operation isEqualToString:@"add"]? YES: NO); + + co = [self clientObject]; + client = [[co imap4Connection] client]; + [[co imap4Connection] selectFolder: [co imap4URL]]; + result = [client storeFlags:flags forUIDs:msgUIDs addOrRemove:addOrRemove]; + if ([[result valueForKey: @"result"] boolValue]) + response = [self responseWith204]; + else + response = [self responseWithStatus:500 andJSONRepresentation:result]; + + return response; +} + +- (WOResponse *) removeAllLabelsAction +{ + WOResponse *response; + WORequest *request; + SOGoMailFolder *co; + NGImap4Client *client; + NSArray *msgUIDs; + NSMutableArray *flags; + NSDictionary *v, *content, *result; + + request = [context request]; + content = [[request contentAsString] objectFromJSONString]; + msgUIDs = [NSArray arrayWithArray:[content objectForKey:@"msgUIDs"]]; + + // We always unconditionally remove the Mozilla tags + flags = [NSMutableArray arrayWithObjects: @"$Label1", @"$Label2", @"$Label3", + @"$Label4", @"$Label5", nil]; + + co = [self clientObject]; + v = [[[context activeUser] userDefaults] mailLabelsColors]; + [flags addObjectsFromArray: [v allKeys]]; + + client = [[co imap4Connection] client]; + [[co imap4Connection] selectFolder: [co imap4URL]]; + result = [client storeFlags:flags forUIDs:msgUIDs addOrRemove:NO]; + + if ([[result valueForKey: @"result"] boolValue]) + response = [self responseWith204]; + else + response = [self responseWithStatus:500 andJSONRepresentation:result]; + + return response; +} + + @end diff --git a/UI/MailerUI/UIxMailListActions.m b/UI/MailerUI/UIxMailListActions.m index 33585e932..163ead021 100644 --- a/UI/MailerUI/UIxMailListActions.m +++ b/UI/MailerUI/UIxMailListActions.m @@ -54,6 +54,7 @@ #import #import #import +#import #import #import #import @@ -322,27 +323,21 @@ return @"ARRIVAL"; } -- (NSString *) imap4SortKey -{ - NSString *sort; - - sort = [[context request] formValueForKey: @"sort"]; - - return [sort uppercaseString]; -} - - (NSString *) imap4SortOrdering { - NSString *sort, *ascending; - NSString *module; + WORequest *request; + NSString *sort, *module; NSMutableDictionary *moduleSettings; - BOOL asc; + NSDictionary *urlParams, *sortingAttributes; SOGoUser *activeUser; SOGoUserSettings *us; + BOOL asc; - sort = [self imap4SortKey]; - ascending = [[context request] formValueForKey: @"asc"]; - asc = [ascending boolValue]; + request = [context request]; + urlParams = [[request contentAsString] objectFromJSONString]; + sortingAttributes = [urlParams objectForKey:@"sortingAttributes"]; + sort = [[sortingAttributes objectForKey:@"sort"] uppercaseString]; + asc = [[sortingAttributes objectForKey:@"asc"] boolValue]; activeUser = [context activeUser]; module = @"Mail"; @@ -393,37 +388,47 @@ - (EOQualifier *) searchQualifier { - NSString *criteria, *value; - EOQualifier *qualifier; - WORequest *request; - - request = [context request]; - criteria = [request formValueForKey: @"search"]; - value = [request formValueForKey: @"value"]; - qualifier = nil; - if ([value length]) - { - if ([criteria isEqualToString: @"subject"]) - qualifier = [EOQualifier qualifierWithQualifierFormat: - @"(subject doesContain: %@)", value]; - else if ([criteria isEqualToString: @"sender"]) - qualifier = [EOQualifier qualifierWithQualifierFormat: - @"(from doesContain: %@)", value]; - else if ([criteria isEqualToString: @"subject_or_sender"]) - qualifier = [EOQualifier qualifierWithQualifierFormat: - @"((subject doesContain: %@)" - @" OR (from doesContain: %@))", - value, value]; - else if ([criteria isEqualToString: @"to_or_cc"]) - qualifier = [EOQualifier qualifierWithQualifierFormat: - @"((to doesContain: %@)" - @" OR (cc doesContain: %@))", - value, value]; - else if ([criteria isEqualToString: @"entire_message"]) - qualifier = [EOQualifier qualifierWithQualifierFormat: - @"(body doesContain: %@)", value]; - } + EOQualifier *qualifier, *searchQualifier; + WORequest *request; + NSDictionary *sortingAttributes, *content; + NSArray *filters; + NSString *searchBy, *searchArgument, *searchInput, *searchString, *match; + NSMutableArray *searchArray; + int nbFilters = 0, i; + request = [context request]; + content = [[request contentAsString] objectFromJSONString]; + qualifier = nil; + searchString = nil; + + if ([content objectForKey:@"filters"]) + { + filters = [content objectForKey:@"filters"]; + sortingAttributes = [content objectForKey:@"sortingAttributes"]; + nbFilters = [filters count]; + match = [NSString stringWithString:[sortingAttributes objectForKey:@"match"]]; // AND, OR + + searchArray = [NSMutableArray arrayWithCapacity:nbFilters]; + for (i = 0; i < nbFilters; i++) + { + searchBy = [NSString stringWithString:[[filters objectAtIndex:i] objectForKey:@"searchBy"]]; + searchArgument = [NSString stringWithString:[[filters objectAtIndex:i] objectForKey:@"searchArgument"]]; + searchInput = [NSString stringWithString:[[filters objectAtIndex:i] objectForKey:@"searchInput"]]; + + if ([[[filters objectAtIndex:i] objectForKey:@"negative"] boolValue]) + searchString = [NSString stringWithFormat:@"(not (%@ %@: '%@'))", searchBy, searchArgument, searchInput]; + else + searchString = [NSString stringWithFormat:@"(%@ %@: '%@')", searchBy, searchArgument, searchInput]; + + searchQualifier = [EOQualifier qualifierWithQualifierFormat:searchString]; + [searchArray addObject:searchQualifier]; + } + if ([match isEqualToString:@"OR"]) + qualifier = [[EOOrQualifier alloc] initWithQualifierArray: searchArray]; + else + qualifier = [[EOAndQualifier alloc] initWithQualifierArray: searchArray]; + } + return qualifier; } @@ -433,25 +438,20 @@ if (!sortedUIDs) { - notDeleted = [EOQualifier qualifierWithQualifierFormat: - @"(not (flags = %@))", - @"deleted"]; + notDeleted = [EOQualifier qualifierWithQualifierFormat: @"(not (flags = %@))", @"deleted"]; qualifier = [self searchQualifier]; if (qualifier) - { - fetchQualifier = [[EOAndQualifier alloc] initWithQualifiers: - notDeleted, qualifier, - nil]; - [fetchQualifier autorelease]; - } + { + fetchQualifier = [[EOAndQualifier alloc] initWithQualifiers: notDeleted, qualifier, nil]; + [fetchQualifier autorelease]; + } else - fetchQualifier = notDeleted; - - sortedUIDs - = [mailFolder fetchUIDsMatchingQualifier: fetchQualifier - sortOrdering: [self imap4SortOrdering] - threaded: sortByThread]; - + fetchQualifier = notDeleted; + + sortedUIDs = [mailFolder fetchUIDsMatchingQualifier: fetchQualifier + sortOrdering: [self imap4SortOrdering] + threaded: sortByThread]; + [sortedUIDs retain]; } @@ -459,7 +459,8 @@ } /** - * Returns a flatten representation of the messages threads as triples of + * Returns a + of the messages threads as triples of * metadata, including the message UID, thread level and root position. * @param _sortedUIDs the interleaved arrays representation of the messages UIDs * @return an flatten array representation of the messages UIDs @@ -632,11 +633,14 @@ // Retrieve messages UIDs using form parameters "sort" and "asc" uids = [self getSortedUIDsInFolder: folder]; + + // Get rid of the extra parenthesis + // uids = [[[[uids stringValue] stringByReplacingOccurrencesOfString:@"(" withString:@""] stringByReplacingOccurrencesOfString:@")" withString:@""] componentsSeparatedByString:@","]; if (includeHeaders) { // Also retrieve the first headers, up to 'headersPrefetchMaxSize' - count = [uids count]; + count = [[uids flattenedArray] count]; if (count > headersPrefetchMaxSize) count = headersPrefetchMaxSize; r = NSMakeRange(0, count); headers = [self getHeadersForUIDs: [[uids flattenedArray] subarrayWithRange: r] @@ -671,7 +675,7 @@ - (id ) getUIDsAction { - NSDictionary *data; + NSDictionary *data, *requestContent; NSString *noHeaders; SOGoMailFolder *folder; WORequest *request; @@ -679,11 +683,14 @@ request = [context request]; response = [context response]; + requestContent = [[request contentAsString] objectFromJSONString]; + [response setHeader: @"text/plain; charset=utf-8" - forKey: @"content-type"]; + forKey: @"content-type"]; + folder = [self clientObject]; - noHeaders = [request formValueForKey: @"no_headers"]; + noHeaders = [[requestContent objectForKey: @"sortingAttributes"] objectForKey:@"no_headers"]; data = [self getUIDsInFolder: folder withHeaders: ([noHeaders length] == 0)]; diff --git a/UI/MailerUI/UIxMailMainFrame.m b/UI/MailerUI/UIxMailMainFrame.m index d87fa20af..d94f90aae 100644 --- a/UI/MailerUI/UIxMailMainFrame.m +++ b/UI/MailerUI/UIxMailMainFrame.m @@ -119,6 +119,17 @@ return [names jsonRepresentation]; } +- (NSString *) userNames +{ + NSArray *accounts, *userNames; + + accounts = [[self clientObject] mailAccounts]; + userNames = [accounts objectsForKey: @"userName" notFoundMarker: nil]; + + return [userNames jsonRepresentation]; + +} + - (NSString *) pageFormURL { NSString *u; diff --git a/UI/MailerUI/UIxMailSearch.h b/UI/MailerUI/UIxMailSearch.h new file mode 100644 index 000000000..8eb7b03ba --- /dev/null +++ b/UI/MailerUI/UIxMailSearch.h @@ -0,0 +1,29 @@ +/* UIxMailSearch.h - this file is part of SOGo + * + * Copyright (C) 2006-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. + */ + +#include + +@interface UIxMailSearch : UIxComponent +{ + id item; + +} +@end \ No newline at end of file diff --git a/UI/MailerUI/UIxMailSearch.m b/UI/MailerUI/UIxMailSearch.m new file mode 100644 index 000000000..a89467df2 --- /dev/null +++ b/UI/MailerUI/UIxMailSearch.m @@ -0,0 +1,91 @@ +/* UIxMailSearch.m - this file is part of SOGo + * + * Copyright (C) 2006-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 +#import +#import + +#import +#import +#import + +#import + +@implementation UIxMailSearch + +- (id) init +{ + item = nil; + + return self; +} + +- (void) dealloc +{ + [item release]; +} + +- (void) setItem: (NSString *) newItem +{ + ASSIGN(item, newItem); +} + +- (NSString *) item +{ + return item; +} + +- (NSArray *) mailAccountsList +{ + SOGoMailAccount *mAccount; + SOGoMailAccounts *mAccounts; + NSString *userName, *option, *aString; + NSArray *accountFolders; + NSMutableArray *mailboxes; + NSDictionary *accountName; + int nbMailboxes, nbMailAccounts, i, j; + + // Number of accounts linked with the current user + mAccounts = [self clientObject]; + nbMailAccounts = [[mAccounts mailAccounts] count]; + + mailboxes = [NSMutableArray array]; + for (i = 0; i < nbMailAccounts; i++) + { + accountName = [[[mAccounts mailAccounts] objectAtIndex:i] objectForKey:@"name"]; // Keys on this account = (name, port, encryption, mailboxes, serverName, identities, userName) + userName = [[[mAccounts mailAccounts] objectAtIndex:i] objectForKey:@"userName"]; + + aString = [NSString stringWithFormat:@"%i", i]; + mAccount = [mAccounts lookupName:aString inContext: context acquire: NO]; + accountFolders = [mAccount allFoldersMetadata]; + + // Number of mailboxes inside the current account + nbMailboxes = [accountFolders count]; + [mailboxes addObject:accountName]; + for (j = 0; j < nbMailboxes; j++) + { + option = [NSString stringWithFormat:@"%@%@", userName, [[accountFolders objectAtIndex:j] objectForKey:@"displayName"]]; + [mailboxes addObject:option]; + } + } + return mailboxes; +} + +@end \ No newline at end of file diff --git a/UI/MailerUI/product.plist b/UI/MailerUI/product.plist index 8b9ed30cb..4ef063eca 100644 --- a/UI/MailerUI/product.plist +++ b/UI/MailerUI/product.plist @@ -181,6 +181,16 @@ pageName = "UIxMailUserRightsEditor"; actionName = "saveUserRights"; }; + addOrRemoveLabel = { + protectedBy = "View"; + actionClass = "UIxMailFolderActions"; + actionName = "addOrRemoveLabel"; + }; + removeAllLabels = { + protectedBy = "View"; + actionClass = "UIxMailFolderActions"; + actionName = "removeAllLabels"; + }; }; }; @@ -252,6 +262,16 @@ actionClass = "UIxMailActions"; actionName = "forward"; }; + markMessageUncollapse = { + protectedBy = "View"; + actionClass = "UIxMailActions"; + actionName = "markMessageUncollapse"; + }; + markMessageCollapse = { + protectedBy = "View"; + actionClass = "UIxMailActions"; + actionName = "markMessageCollapse"; + }; markMessageUnflagged = { protectedBy = "View"; actionClass = "UIxMailActions"; @@ -272,21 +292,6 @@ actionClass = "UIxMailActions"; actionName = "markMessageRead"; }; - addLabel = { - protectedBy = "View"; - actionClass = "UIxMailActions"; - actionName = "addLabel"; - }; - removeLabel = { - protectedBy = "View"; - actionClass = "UIxMailActions"; - actionName = "removeLabel"; - }; - removeAllLabels = { - protectedBy = "View"; - actionClass = "UIxMailActions"; - actionName = "removeAllLabels"; - }; }; }; @@ -327,6 +332,10 @@ pageName = "UIxMailMainFrame"; actionName = "saveColumnsState"; }; + search = { + protectedBy = "View"; + pageName = "UIxMailSearch"; + }; }; }; diff --git a/UI/MainUI/SOGoUserHomePage.m b/UI/MainUI/SOGoUserHomePage.m index 95d7a72de..39d96ec26 100644 --- a/UI/MainUI/SOGoUserHomePage.m +++ b/UI/MainUI/SOGoUserHomePage.m @@ -1,8 +1,6 @@ /* SOGoUserHomePage.m - this file is part of SOGo * - * Copyright (C) 2007-2010 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2007-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 @@ -47,6 +45,8 @@ #import #import +#import + #define intervalSeconds 900 /* 15 minutes */ @interface SOGoUserHomePage : UIxComponent @@ -91,7 +91,7 @@ NSDictionary *record; SOGoUser *user; - int recordCount, recordMax, count, startInterval, endInterval, i, type, maxBookings, isResource; + int recordCount, recordMax, count, startInterval, endInterval, i, type, maxBookings, isResource, delta; recordMax = [records count]; user = [SOGoUser userWithLogin: [[self clientObject] ownerInContext: context] roles: nil]; @@ -148,12 +148,18 @@ startInterval = ([currentDate timeIntervalSinceDate: startDate] / intervalSeconds); + delta = [[currentDate timeZoneDetail] timeZoneSecondsFromGMT] - [[startDate timeZoneDetail] timeZoneSecondsFromGMT]; + startInterval += (delta/60/15); + currentDate = [record objectForKey: @"endDate"]; if ([currentDate earlierDate: endDate] == endDate) endInterval = itemCount - 1; else endInterval = ([currentDate timeIntervalSinceDate: startDate] / intervalSeconds); + + delta = [[currentDate timeZoneDetail] timeZoneSecondsFromGMT] - [[startDate timeZoneDetail] timeZoneSecondsFromGMT]; + endInterval += (delta/60/15); // Update bit string representation // If the user is a resource, keep the sum of overlapping events @@ -183,11 +189,31 @@ forFreeBusy: (SOGoFreeBusyObject *) fb andContact: (NSString *) uid { + NSCalendarDate *start, *end; NSMutableArray *freeBusy; unsigned int *freeBusyItems; NSTimeInterval interval; unsigned int count, intervals; + // We "copy" the start/end date because -fetchFreeBusyInfosFrom will mess + // with the timezone and we don't want that to properly calculate the delta + // DO NOT USE -copy HERE - it'll simply return [self retain]. + start = [NSCalendarDate dateWithYear: [startDate yearOfCommonEra] + month: [startDate monthOfYear] + day: [startDate dayOfMonth] + hour: [startDate hourOfDay] + minute: [startDate minuteOfHour] + second: [startDate secondOfMinute] + timeZone: [startDate timeZone]]; + + end = [NSCalendarDate dateWithYear: [endDate yearOfCommonEra] + month: [endDate monthOfYear] + day: [endDate dayOfMonth] + hour: [endDate hourOfDay] + minute: [endDate minuteOfHour] + second: [endDate secondOfMinute] + timeZone: [endDate timeZone]]; + interval = [endDate timeIntervalSinceDate: startDate] + 60; intervals = interval / intervalSeconds; /* slices of 15 minutes */ @@ -195,7 +221,7 @@ freeBusyItems = NSZoneCalloc (NULL, intervals, sizeof (int)); [self _fillFreeBusyItems: freeBusyItems count: intervals - withRecords: [fb fetchFreeBusyInfosFrom: startDate to: endDate forContact: uid] + withRecords: [fb fetchFreeBusyInfosFrom: start to: end forContact: uid] fromStartDate: startDate toEndDate: endDate]; @@ -437,17 +463,13 @@ - (WOResponse *) _foldersResponseForResults: (NSArray *) folders { WOResponse *response; - NSEnumerator *foldersEnum; - NSDictionary *currentFolder; response = [context response]; [response setStatus: 200]; [response setHeader: @"text/plain; charset=utf-8" forKey: @"Content-Type"]; - foldersEnum = [folders objectEnumerator]; - while ((currentFolder = [foldersEnum nextObject])) - [response appendContentString: - [currentFolder keysWithFormat: @";%{displayName}:%{name}:%{type}"]]; + + [response appendContentString: [folders JSONRepresentation]]; return response; } diff --git a/UI/PreferencesUI/Arabic.lproj/Localizable.strings b/UI/PreferencesUI/Arabic.lproj/Localizable.strings index 3b526b597..855e12075 100644 --- a/UI/PreferencesUI/Arabic.lproj/Localizable.strings +++ b/UI/PreferencesUI/Arabic.lproj/Localizable.strings @@ -127,14 +127,14 @@ "Show subscribed mailboxes only" = "عرض صناديق البريد المشترك بها فقط"; "Sort messages by threads" = "رتب الرسائل حسب المواضيع"; "Check for new mail:" = "تحقق من وجود بريد جديد:"; -"messagecheck_manually" = "يدوي"; -"messagecheck_every_minute" = "كل دقيقة"; -"messagecheck_every_2_minutes" = "كل 2 دقيقة"; -"messagecheck_every_5_minutes" = "كل 5 دقائق"; -"messagecheck_every_10_minutes" = "كل 10 دقائق"; -"messagecheck_every_20_minutes" = "كل 20 دقيقة"; -"messagecheck_every_30_minutes" = "كل 30 دقيقة"; -"messagecheck_once_per_hour" = "مرة كل ساعة"; +"refreshview_manually" = "يدوي"; +"refreshview_every_minute" = "كل دقيقة"; +"refreshview_every_2_minutes" = "كل 2 دقيقة"; +"refreshview_every_5_minutes" = "كل 5 دقائق"; +"refreshview_every_10_minutes" = "كل 10 دقائق"; +"refreshview_every_20_minutes" = "كل 20 دقيقة"; +"refreshview_every_30_minutes" = "كل 30 دقيقة"; +"refreshview_once_per_hour" = "مرة كل ساعة"; "Forward messages:" = "أعد توجيه الرسائل:"; "messageforward_inline" = "مضمنة"; @@ -199,7 +199,7 @@ "Contacts" = "دفتر العناوين"; "Mail" = "بريد"; "Last" = "أخر إستخدام"; -"Default module :" = "وحدة نمطية افتراضية :"; +"Default Module :" = "وحدة نمطية افتراضية :"; "Language :" = "اللغة:"; "choose" = "اختيار ..."; diff --git a/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings b/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings index 933d6250f..eae77b19b 100644 --- a/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings @@ -127,14 +127,14 @@ "Show subscribed mailboxes only" = "Exibir somente caixas de correio inscritas"; "Sort messages by threads" = "Ordenar mensagens por tópicos"; "Check for new mail:" = "Checar novos emails:"; -"messagecheck_manually" = "Manualmente"; -"messagecheck_every_minute" = "A cada minuto"; -"messagecheck_every_2_minutes" = "A cada 2 minutos"; -"messagecheck_every_5_minutes" = "A cada 5 minutos"; -"messagecheck_every_10_minutes" = "A cada 10 minutos"; -"messagecheck_every_20_minutes" = "A cada 20 minutos"; -"messagecheck_every_30_minutes" = "A cada 30 minutos"; -"messagecheck_once_per_hour" = "Uma vez por hora"; +"refreshview_manually" = "Manualmente"; +"refreshview_every_minute" = "A cada minuto"; +"refreshview_every_2_minutes" = "A cada 2 minutos"; +"refreshview_every_5_minutes" = "A cada 5 minutos"; +"refreshview_every_10_minutes" = "A cada 10 minutos"; +"refreshview_every_20_minutes" = "A cada 20 minutos"; +"refreshview_every_30_minutes" = "A cada 30 minutos"; +"refreshview_once_per_hour" = "Uma vez por hora"; "Forward messages:" = "Encaminhar mensagens:"; "messageforward_inline" = "No corpo da mensagem"; @@ -199,7 +199,7 @@ "Contacts" = "Catálogo"; "Mail" = "Correio"; "Last" = "Último usado"; -"Default module :" = "Módulo Padrão:"; +"Default Module :" = "Módulo Padrão:"; "Language :" = "Idioma :"; "choose" = "Escolha ..."; diff --git a/UI/PreferencesUI/Catalan.lproj/Localizable.strings b/UI/PreferencesUI/Catalan.lproj/Localizable.strings index 2668c029b..459878dda 100644 --- a/UI/PreferencesUI/Catalan.lproj/Localizable.strings +++ b/UI/PreferencesUI/Catalan.lproj/Localizable.strings @@ -97,44 +97,52 @@ "Show time as busy outside working hours" = "Mostra el temps com ocupat fora de les hores de treball"; "First week of year :" = "Primera setmana de l'any: "; "Enable reminders for Calendar items" = "Habilitar recordatoris per a ítems del calendari"; -"Play a sound when a reminder comes due" -= "Usar senyal acústic per als recordatoris"; +"Play a sound when a reminder comes due" = "Usar senyal acústic per als recordatoris"; "Default reminder :" = "Recordatori per omissió: "; "firstWeekOfYear_January1" = "Comença l'1 de gener"; "firstWeekOfYear_First4DayWeek" = "Primera setmana de 4 dies"; "firstWeekOfYear_FirstFullWeek" = "Primera setmana completa"; +"Prevent from being invited to appointments" = "Impedeix ser convidat a cites"; +"White list for appointment invitations:" = "Llista blanca per a invitacions a cites:"; +"Contacts Names" = "Nom dels Contactes"; + /* Default Calendar */ "Default calendar :" = "Calendari per defecte"; "selectedCalendar" = "Calendari seleccionat"; "personalCalendar" = "Calendari personal"; "firstCalendar" = "Primer calendari habilitat"; +"reminder_NONE" = "Cap Recordatori"; "reminder_5_MINUTES_BEFORE" = "5 minuts"; "reminder_10_MINUTES_BEFORE" = "10 minuts"; "reminder_15_MINUTES_BEFORE" = "15 minuts"; "reminder_30_MINUTES_BEFORE" = "30 minuts"; +"reminder_45_MINUTES_BEFORE" = "45 minuts abans"; "reminder_1_HOUR_BEFORE" = "1 hora"; "reminder_2_HOURS_BEFORE" = "2 hores"; -"reminder_5_HOURS_BEFORE"= "5 hores"; -"reminder_15_HOURS_BEFORE"= "15 hores"; +"reminder_5_HOURS_BEFORE" = "5 hores"; +"reminder_15_HOURS_BEFORE" = "15 hores"; "reminder_1_DAY_BEFORE" = "1 dia"; "reminder_2_DAYS_BEFORE" = "2 dies"; +"reminder_1_WEEK_BEFORE" = "1 setmana abans"; /* Mailer */ +"Labels" = "Etiquetes"; "Label" = "Etiquetar"; "Show subscribed mailboxes only" = "Mostrar només les bústies subscrites"; "Sort messages by threads" = "Ordenar els missatges per temes"; +"When sending mail, add unknown recipients to my" = "En enviar un missatge, afegeix els destinataris desconeguts al meu"; "Check for new mail:" = "Comprovar nou correu: "; -"messagecheck_manually" = "Manualment"; -"messagecheck_every_minute" = "Cada minut"; -"messagecheck_every_2_minutes" = "Cada 2 minuts"; -"messagecheck_every_5_minutes" = "Cada 5 minuts"; -"messagecheck_every_10_minutes" = "Cada 10 minuts"; -"messagecheck_every_20_minutes" = "Cada 20 minuts"; -"messagecheck_every_30_minutes" = "Cada 30 minuts"; -"messagecheck_once_per_hour" = "Cada hora"; +"refreshview_manually" = "Manualment"; +"refreshview_every_minute" = "Cada minut"; +"refreshview_every_2_minutes" = "Cada 2 minuts"; +"refreshview_every_5_minutes" = "Cada 5 minuts"; +"refreshview_every_10_minutes" = "Cada 10 minuts"; +"refreshview_every_20_minutes" = "Cada 20 minuts"; +"refreshview_every_30_minutes" = "Cada 30 minuts"; +"refreshview_once_per_hour" = "Cada hora"; "Forward messages:" = "Reenviar missatges:"; "messageforward_inline" = "Incorporats"; @@ -153,6 +161,10 @@ "displayremoteinlineimages_never" = "Mai"; "displayremoteinlineimages_always" = "Sempre"; +/* Contact */ +"Personal Address Book" = "Llibreta personal d'adreces"; +"Collected Address Book" = "Llibreta d'adreces recopilades"; + /* IMAP Accounts */ "New Mail Account" = "Compte de correu nou"; @@ -199,7 +211,8 @@ "Contacts" = "Llibreta d'adreces"; "Mail" = "Correu"; "Last" = "Últim usat"; -"Default module :" = "Pàgina per defecte :"; +"Default Module :" = "Pàgina per defecte :"; +"SOGo Version :" = "Versió del SOGo:"; "Language :" = "Language :"; "choose" = "Choose ..."; @@ -244,6 +257,8 @@ "Active" = "Actiu"; "Move Up" = "Pujar"; "Move Down" = "Baixar"; +"Connection error" = "Error de connexió"; +"Service temporarily unavailable" = "Servei temporalment no disponible"; /* Filters - UIxFilterEditor */ "Filter name:" = "Nom del filtre:"; @@ -261,6 +276,7 @@ "To or Cc" = "A o Cc"; "Size (Kb)" = "Mida (Kb)"; "Header" = "Capçalera"; +"Body" = "Cos"; "Flag the message with:" = "Marca el missatge amb:"; "Discard the message" = "Descarta el missatge"; "File the message in:" = "Arxiva el missatge en:"; @@ -287,12 +303,8 @@ "Flagged" = "Marcat"; "Junk" = "Brossa"; "Not Junk" = "No brossa"; -"Label 1" = "Etiqueta 1"; -"Label 2" = "Etiqueta 2"; -"Label 3" = "Etiqueta 3"; -"Label 4" = "Etiqueta 4"; -"Label 5" = "Etiqueta 5"; +/* Password policy */ "The password was changed successfully." = "La contrasenya s'ha canviat correctament"; "Password must not be empty." = "La contrasenya no ha d'estar buida."; "The passwords do not match. Please try again." = "Les contrasenyes no coincideixen. Intenta-ho de nou."; diff --git a/UI/PreferencesUI/Czech.lproj/Localizable.strings b/UI/PreferencesUI/Czech.lproj/Localizable.strings index bcf32ab2c..47750fbb8 100644 --- a/UI/PreferencesUI/Czech.lproj/Localizable.strings +++ b/UI/PreferencesUI/Czech.lproj/Localizable.strings @@ -27,7 +27,7 @@ "Add default email addresses" = "Přidat základní emailové adresy"; "Days between responses :" = "Počet dnů mezi odpověďmi :"; "Do not send responses to mailing lists" = "Neposílat odpovědi do konferencí"; -"Disable auto reply on" = "Zakázat automatické odpovědi na"; +"Disable auto reply on" = "Automatické odpovědi vypnout dne"; "Please specify your message and your email addresses for which you want to enable auto reply." = "Prosím zadejte text zprávy a své emailové adresy, pro které chcete zasílání v nepřítomnosti použít."; "Your vacation message must not end with a single dot on a line." = "Vaše zpráva v nepřítomnosti nesmí končit samotnou tečkou na řádce."; @@ -104,6 +104,10 @@ "firstWeekOfYear_First4DayWeek" = "První 4-denní týden"; "firstWeekOfYear_FirstFullWeek" = "První celý týden"; +"Prevent from being invited to appointments" = "Blokovat budoucí pozvání na schůzky"; +"White list for appointment invitations:" = "Seznam povolených pro pozvání na schůzky:"; +"Contacts Names" = "Jména kontaktů"; + /* Default Calendar */ "Default calendar :" = "Výchozí kalendář :"; "selectedCalendar" = "Zvolený kalendář"; @@ -131,14 +135,14 @@ "Sort messages by threads" = "Třídit zprávy podle souvislostí"; "When sending mail, add unknown recipients to my" = "Automaticky přidávat odchozí e-mailovou adresu do složky:"; "Check for new mail:" = "Zkontrolovat novou poštu:"; -"messagecheck_manually" = "Manuálně"; -"messagecheck_every_minute" = "Každou minutu"; -"messagecheck_every_2_minutes" = "Každé 2 minuty"; -"messagecheck_every_5_minutes" = "Každých 5 minut"; -"messagecheck_every_10_minutes" = "Každých 10 minut"; -"messagecheck_every_20_minutes" = "Každých 20 minut"; -"messagecheck_every_30_minutes" = "Každých 30 minut"; -"messagecheck_once_per_hour" = "Jednou za hodinu"; +"refreshview_manually" = "Manuálně"; +"refreshview_every_minute" = "Každou minutu"; +"refreshview_every_2_minutes" = "Každé 2 minuty"; +"refreshview_every_5_minutes" = "Každých 5 minut"; +"refreshview_every_10_minutes" = "Každých 10 minut"; +"refreshview_every_20_minutes" = "Každých 20 minut"; +"refreshview_every_30_minutes" = "Každých 30 minut"; +"refreshview_once_per_hour" = "Jednou za hodinu"; "Forward messages:" = "Přeposlat zprávy:"; "messageforward_inline" = "Do řady"; @@ -207,8 +211,8 @@ "Contacts" = "Adresář"; "Mail" = "Pošta"; "Last" = "Naposledy použitý"; -"Default module :" = "Výchozí modul :"; -"SOGo version:" = "SOGo verze:"; +"Default Module :" = "Výchozí modul :"; +"SOGo Version :" = "SOGo verze:"; "Language :" = "Jazyk :"; "choose" = "Vybrat ..."; diff --git a/UI/PreferencesUI/Danish.lproj/Localizable.strings b/UI/PreferencesUI/Danish.lproj/Localizable.strings index ad2b8b98c..903abf10d 100644 --- a/UI/PreferencesUI/Danish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Danish.lproj/Localizable.strings @@ -127,14 +127,14 @@ "Show subscribed mailboxes only" = "Vis kun abonnerede postkasser"; "Sort messages by threads" = "Sortér beskeder efter tråd"; "Check for new mail:" = "Tjek for ny post:"; -"messagecheck_manually" = "Manuelt"; -"messagecheck_every_minute" = "Hvert minut"; -"messagecheck_every_2_minutes" = "Hvert 2. minut"; -"messagecheck_every_5_minutes" = "Hvert 5. minut"; -"messagecheck_every_10_minutes" = "Hvert 10. minut"; -"messagecheck_every_20_minutes" = "Hvert 20. minut"; -"messagecheck_every_30_minutes" = "Hvert 30. minut"; -"messagecheck_once_per_hour" = "Én gang i timen"; +"refreshview_manually" = "Manuelt"; +"refreshview_every_minute" = "Hvert minut"; +"refreshview_every_2_minutes" = "Hvert 2. minut"; +"refreshview_every_5_minutes" = "Hvert 5. minut"; +"refreshview_every_10_minutes" = "Hvert 10. minut"; +"refreshview_every_20_minutes" = "Hvert 20. minut"; +"refreshview_every_30_minutes" = "Hvert 30. minut"; +"refreshview_once_per_hour" = "Én gang i timen"; "Forward messages:" = "Videresend beskeder:"; "messageforward_inline" = "På linie"; @@ -199,7 +199,7 @@ "Contacts" = "Adressebog"; "Mail" = "Mail"; "Last" = "Sidst anvendt"; -"Default module :" = "Standard modul:"; +"Default Module :" = "Standard modul:"; "Language :" = "Sprog:"; "choose" = "Vælg ..."; diff --git a/UI/PreferencesUI/Dutch.lproj/Localizable.strings b/UI/PreferencesUI/Dutch.lproj/Localizable.strings index 5135fe739..12b996c4d 100644 --- a/UI/PreferencesUI/Dutch.lproj/Localizable.strings +++ b/UI/PreferencesUI/Dutch.lproj/Localizable.strings @@ -104,6 +104,10 @@ "firstWeekOfYear_First4DayWeek" = "Eerste week met 4 dagen"; "firstWeekOfYear_FirstFullWeek" = "Eerste volledige week"; +"Prevent from being invited to appointments" = "Voorkom uitgenodigingen voor gebeurtenissen"; +"White list for appointment invitations:" = "Whitelist voor uitnodigingen:"; +"Contacts Names" = "Contactnamen"; + /* Default Calendar */ "Default calendar :" = "Standaardagenda:"; "selectedCalendar" = "Geselecteerde agenda"; @@ -131,14 +135,14 @@ "Sort messages by threads" = "Berichten sorteren op threads"; "When sending mail, add unknown recipients to my" = "Bij het versturen van mail, voeg onbekende ontvangers toe aan mijn"; "Check for new mail:" = "Controleren op nieuwe mail:"; -"messagecheck_manually" = "Handmatig"; -"messagecheck_every_minute" = "Iedere minuut"; -"messagecheck_every_2_minutes" = "Iedere 2 minuten"; -"messagecheck_every_5_minutes" = "Iedere 5 minuten"; -"messagecheck_every_10_minutes" = "Iedere 10 minuten"; -"messagecheck_every_20_minutes" = "Iedere 20 minuten"; -"messagecheck_every_30_minutes" = "Iedere 30 minuten"; -"messagecheck_once_per_hour" = "Ieder uur"; +"refreshview_manually" = "Handmatig"; +"refreshview_every_minute" = "Iedere minuut"; +"refreshview_every_2_minutes" = "Iedere 2 minuten"; +"refreshview_every_5_minutes" = "Iedere 5 minuten"; +"refreshview_every_10_minutes" = "Iedere 10 minuten"; +"refreshview_every_20_minutes" = "Iedere 20 minuten"; +"refreshview_every_30_minutes" = "Iedere 30 minuten"; +"refreshview_once_per_hour" = "Ieder uur"; "Forward messages:" = "Berichten doorsturen:"; "messageforward_inline" = "In het bericht"; @@ -207,8 +211,8 @@ "Contacts" = "Adresboek"; "Mail" = "Mail"; "Last" = "Laatst gebruikt"; -"Default module :" = "Standaardmodule:"; -"SOGo version:" = "SOGo versie:"; +"Default Module :" = "Standaardmodule:"; +"SOGo Version :" = "SOGo versie:"; "Language :" = "Taal:"; "choose" = "Kies..."; diff --git a/UI/PreferencesUI/English.lproj/Localizable.strings b/UI/PreferencesUI/English.lproj/Localizable.strings index 66f8729e0..65219eec9 100644 --- a/UI/PreferencesUI/English.lproj/Localizable.strings +++ b/UI/PreferencesUI/English.lproj/Localizable.strings @@ -104,6 +104,10 @@ "firstWeekOfYear_First4DayWeek" = "First 4-day week"; "firstWeekOfYear_FirstFullWeek" = "First full week"; +"Prevent from being invited to appointments" = "Prevent from being invited to appointments"; +"White list for appointment invitations:" = "White list for appointment invitations:"; +"Contacts Names" = "Contacts Names"; + /* Default Calendar */ "Default calendar :" = "Default calendar:"; "selectedCalendar" = "Selected calendar"; @@ -130,15 +134,6 @@ "Show subscribed mailboxes only" = "Show subscribed mailboxes only"; "Sort messages by threads" = "Sort messages by threads"; "When sending mail, add unknown recipients to my" = "When sending mail, add unknown recipients to my"; -"Check for new mail:" = "Check for new mail:"; -"messagecheck_manually" = "Manually"; -"messagecheck_every_minute" = "Every minute"; -"messagecheck_every_2_minutes" = "Every 2 minutes"; -"messagecheck_every_5_minutes" = "Every 5 minutes"; -"messagecheck_every_10_minutes" = "Every 10 minutes"; -"messagecheck_every_20_minutes" = "Every 20 minutes"; -"messagecheck_every_30_minutes" = "Every 30 minutes"; -"messagecheck_once_per_hour" = "Once per hour"; "Forward messages:" = "Forward messages:"; "messageforward_inline" = "Inline"; @@ -207,8 +202,8 @@ "Contacts" = "Address Book"; "Mail" = "Mail"; "Last" = "Last used"; -"Default module :" = "Default module:"; -"SOGo version:" = "SOGo version:"; +"Default Module :" = "Default Module:"; +"SOGo Version :" = "SOGo Version:"; "Language :" = "Language:"; "choose" = "Choose ..."; @@ -236,6 +231,16 @@ "Ukrainian" = "Українська"; "Welsh" = "Cymraeg"; +"Refresh View :" = "Refresh View:"; +"refreshview_manually" = "Manually"; +"refreshview_every_minute" = "Every minute"; +"refreshview_every_2_minutes" = "Every 2 minutes"; +"refreshview_every_5_minutes" = "Every 5 minutes"; +"refreshview_every_10_minutes" = "Every 10 minutes"; +"refreshview_every_20_minutes" = "Every 20 minutes"; +"refreshview_every_30_minutes" = "Every 30 minutes"; +"refreshview_once_per_hour" = "Once per hour"; + /* Return receipts */ "When I receive a request for a return receipt:" = "When I receive a request for a return receipt:"; "Never send a return receipt" = "Never send a return receipt"; diff --git a/UI/PreferencesUI/Finnish.lproj/Localizable.strings b/UI/PreferencesUI/Finnish.lproj/Localizable.strings index 08a58f9d5..4769f69e0 100644 --- a/UI/PreferencesUI/Finnish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Finnish.lproj/Localizable.strings @@ -104,6 +104,10 @@ "firstWeekOfYear_First4DayWeek" = "Ensimmäinen nelipäiväinen viikko"; "firstWeekOfYear_FirstFullWeek" = "Ensimmäinen täysi viikko"; +"Prevent from being invited to appointments" = "Estä tapaamiskutsut"; +"White list for appointment invitations:" = "Valkoinen lista kalenterikutsuille:"; +"Contacts Names" = "Yhteystietojen Nimet"; + /* Default Calendar */ "Default calendar :" = "Oletuskalenteri"; "selectedCalendar" = "Valittu kalenteri"; @@ -131,14 +135,14 @@ "Sort messages by threads" = "Järjestä viestit ketjuiksi"; "When sending mail, add unknown recipients to my" = "Postia lähettäessä lisää tuntemattomat vastaanottajat"; "Check for new mail:" = "Hae uudet sähköpostit"; -"messagecheck_manually" = "Manuaalisesti"; -"messagecheck_every_minute" = "Joka minuutti"; -"messagecheck_every_2_minutes" = "Joka toinen minuutti"; -"messagecheck_every_5_minutes" = "Viiden minuutin välein"; -"messagecheck_every_10_minutes" = "10 minuutin välein"; -"messagecheck_every_20_minutes" = "20 minuutin välein"; -"messagecheck_every_30_minutes" = "Puolen tunnin välein"; -"messagecheck_once_per_hour" = "Kerran tunnissa "; +"refreshview_manually" = "Manuaalisesti"; +"refreshview_every_minute" = "Joka minuutti"; +"refreshview_every_2_minutes" = "Joka toinen minuutti"; +"refreshview_every_5_minutes" = "Viiden minuutin välein"; +"refreshview_every_10_minutes" = "10 minuutin välein"; +"refreshview_every_20_minutes" = "20 minuutin välein"; +"refreshview_every_30_minutes" = "Puolen tunnin välein"; +"refreshview_once_per_hour" = "Kerran tunnissa "; "Forward messages:" = "Edelleenlähetä viestit:"; "messageforward_inline" = "Tekstisisällössä"; @@ -207,8 +211,8 @@ "Contacts" = "Osoitekirja"; "Mail" = "Sähköposti"; "Last" = "Viimeksi käytetty"; -"Default module :" = "Oletusmoduli:"; -"SOGo version:" = "SOGo versio:"; +"Default Module :" = "Oletusmoduli:"; +"SOGo Version :" = "SOGo versio:"; "Language :" = "Kieli:"; "choose" = "Valitse..."; diff --git a/UI/PreferencesUI/French.lproj/Localizable.strings b/UI/PreferencesUI/French.lproj/Localizable.strings index b4d291e61..58218f0e6 100644 --- a/UI/PreferencesUI/French.lproj/Localizable.strings +++ b/UI/PreferencesUI/French.lproj/Localizable.strings @@ -104,6 +104,10 @@ "firstWeekOfYear_First4DayWeek" = "Première semaine de 4 jours"; "firstWeekOfYear_FirstFullWeek" = "Première semaine entière"; +"Prevent from being invited to appointments" = "Interdire de m'inviter à des rendez-vous"; +"White list for appointment invitations:" = "Liste blanche pour les invitations"; +"Contacts Names" = "Noms des contacts"; + /* Default Calendar */ "Default calendar :" = "Calendrier par défaut :"; "selectedCalendar" = "le calendrier sélectionné"; @@ -130,15 +134,6 @@ "Show subscribed mailboxes only" = "Afficher seulement les abonnements"; "Sort messages by threads" = "Grouper les discussions"; "When sending mail, add unknown recipients to my" = "Lors de l'envoi d'un message, ajouter les destinataires inconnus au carnet"; -"Check for new mail:" = "Vérifier l'arrivée de messages :"; -"messagecheck_manually" = "Manuellement"; -"messagecheck_every_minute" = "Chaque minute"; -"messagecheck_every_2_minutes" = "Toutes les 2 minutes"; -"messagecheck_every_5_minutes" = "Toutes les 5 minutes"; -"messagecheck_every_10_minutes" = "Toutes les 10 minutes"; -"messagecheck_every_20_minutes" = "Toutes les 20 minutes"; -"messagecheck_every_30_minutes" = "Toutes les 30 minutes"; -"messagecheck_once_per_hour" = "Une fois par heure"; "Forward messages:" = "Transférer les messages :"; "messageforward_inline" = "intégrés"; @@ -207,8 +202,8 @@ "Contacts" = "Carnet d'adresses"; "Mail" = "Courrier"; "Last" = "Dernier utilisé"; -"Default module :" = "Module par défaut :"; -"SOGo version:" = "Version :"; +"Default Module :" = "Module par défaut :"; +"SOGo Version :" = "Version :"; "Language :" = "Langue :"; "choose" = "Choisir ..."; @@ -236,6 +231,16 @@ "Ukrainian" = "Українська"; "Welsh" = "Cymraeg"; +"Refresh View :" = "Actualisation :"; +"refreshview_manually" = "Manuellement"; +"refreshview_every_minute" = "Chaque minute"; +"refreshview_every_2_minutes" = "Toutes les 2 minutes"; +"refreshview_every_5_minutes" = "Toutes les 5 minutes"; +"refreshview_every_10_minutes" = "Toutes les 10 minutes"; +"refreshview_every_20_minutes" = "Toutes les 20 minutes"; +"refreshview_every_30_minutes" = "Toutes les 30 minutes"; +"refreshview_once_per_hour" = "Une fois par heure"; + /* Return receipts */ "When I receive a request for a return receipt:" = "Lors de la réception d'une demande d'accusé de réception :"; "Never send a return receipt" = "Ne jamais envoyer d'accusé de réception"; diff --git a/UI/PreferencesUI/German.lproj/Localizable.strings b/UI/PreferencesUI/German.lproj/Localizable.strings index 41abe64a3..baca7235c 100644 --- a/UI/PreferencesUI/German.lproj/Localizable.strings +++ b/UI/PreferencesUI/German.lproj/Localizable.strings @@ -22,7 +22,7 @@ /* vacation (auto-reply) */ "Enable vacation auto reply" = "Automatische Abwesenheitsnachricht aktivieren"; -"Auto reply message :" = "Folgende Nachricht wird automatisch an jeden Absender gesendet:"; +"Auto reply message :" = "Mit folgender Nachricht auf jede eingehende E-Mail antworten:"; "Email addresses (separated by commas) :" = "E-Mail-Adressen (getrennt durch Kommata):"; "Add default email addresses" = "Standardadresse hinzufügen"; "Days between responses :" = "Tage zwischen automatischen Abwesenheitsnachrichten:"; @@ -104,6 +104,10 @@ "firstWeekOfYear_First4DayWeek" = "Erste 4 Tage Woche"; "firstWeekOfYear_FirstFullWeek" = "Erste ganze Woche"; +"Prevent from being invited to appointments" = "Verhindere, dass ich zu Terminen eingeladen werde"; +"White list for appointment invitations:" = "White-Liste für Termineinladungen:"; +"Contacts Names" = "Kontaktnamen"; + /* Default Calendar */ "Default calendar :" = "Standardkalender"; "selectedCalendar" = "Gewählter Kalender"; @@ -129,16 +133,16 @@ "Label" = "Schlagwort"; "Show subscribed mailboxes only" = "Nur abonnierte Ordner anzeigen"; "Sort messages by threads" = "Nachrichten nach Thema sortieren"; -"When sending mail, add unknown recipients to my" = "Wenn ich E-Mails sende, füge die unbekannten Empfänger zu meinem"; +"When sending mail, add unknown recipients to my" = "Unbekannte Empfänger meiner E-Mails hinzufügen zu"; "Check for new mail:" = "Auf neue Nachrichten prüfen:"; -"messagecheck_manually" = "Manuell"; -"messagecheck_every_minute" = "Minütlich"; -"messagecheck_every_2_minutes" = "Alle 2 Minuten"; -"messagecheck_every_5_minutes" = "Alle 5 Minuten"; -"messagecheck_every_10_minutes" = "Alle 10 Minuten"; -"messagecheck_every_20_minutes" = "Alle 20 Minuten"; -"messagecheck_every_30_minutes" = "Alle 30 Minuten"; -"messagecheck_once_per_hour" = "Stündlich"; +"refreshview_manually" = "Manuell"; +"refreshview_every_minute" = "Minütlich"; +"refreshview_every_2_minutes" = "Alle 2 Minuten"; +"refreshview_every_5_minutes" = "Alle 5 Minuten"; +"refreshview_every_10_minutes" = "Alle 10 Minuten"; +"refreshview_every_20_minutes" = "Alle 20 Minuten"; +"refreshview_every_30_minutes" = "Alle 30 Minuten"; +"refreshview_once_per_hour" = "Stündlich"; "Forward messages:" = "Nachrichten weiterleiten:"; "messageforward_inline" = "Eingebunden"; @@ -207,8 +211,8 @@ "Contacts" = "Adressbuch"; "Mail" = "E-Mail"; "Last" = "Zuletzt benutztes"; -"Default module :" = "Standard Modul:"; -"SOGo version:" = "SOGo Version:"; +"Default Module :" = "Standard Modul:"; +"SOGo Version :" = "SOGo Version:"; "Language :" = "Sprache:"; "choose" = "Auswählen ..."; @@ -310,7 +314,7 @@ "Password change failed - Password is too short" = "Passwortänderung fehlgeschlagen - Passwort ist zu kurz"; "Password change failed - Password is too young" = "Passwortänderung fehlgeschlagen - zu frühe Passwortänderung"; "Password change failed - Password is in history" = "Passwortänderung fehlgeschlagen - Passwort wurde zuvor verwendet"; -"Unhandled policy error: %{0}" = "Unbearbeiteter Vorgabenfehler: %{0}"; -"Unhandled error response" = "Unbearbeitete Fehlerantwort"; +"Unhandled policy error: %{0}" = "Unerwarteter Fehler bei Passwortänderung: %{0}"; +"Unhandled error response" = "Unerwarteter Fehler"; "Password change is not supported." = "Passwortänderung wird nicht unterstützt."; -"Unhandled HTTP error code: %{0}" = "Unbearbeiteter HTTP-Fehlercode: %{0}"; +"Unhandled HTTP error code: %{0}" = "Unerwarteter HTTP-Fehlercode: %{0}"; diff --git a/UI/PreferencesUI/Hungarian.lproj/Localizable.strings b/UI/PreferencesUI/Hungarian.lproj/Localizable.strings index d4be47a0c..2e3908993 100644 --- a/UI/PreferencesUI/Hungarian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Hungarian.lproj/Localizable.strings @@ -104,6 +104,10 @@ "firstWeekOfYear_First4DayWeek" = "Első 4 napos héttől"; "firstWeekOfYear_FirstFullWeek" = "Első teljes héttől"; +"Prevent from being invited to appointments" = "Megakadályozza a meghívást találkozókra"; +"White list for appointment invitations:" = "Engedélyezési lista a találkozók meghívásaihoz:"; +"Contacts Names" = "Kapcsolatok nevei"; + /* Default Calendar */ "Default calendar :" = "Alapértelmezett naptár"; "selectedCalendar" = "Kiválasztott naptár"; @@ -131,14 +135,14 @@ "Sort messages by threads" = "Üzenetek beszélgetések szerinti rendezése "; "When sending mail, add unknown recipients to my" = "Levél küldésekor az ismeretlen címeket adja hozzá a következőhöz"; "Check for new mail:" = "Új üzenetek letöltése:"; -"messagecheck_manually" = "Kézi"; -"messagecheck_every_minute" = "Percenként"; -"messagecheck_every_2_minutes" = "Kétpercenként"; -"messagecheck_every_5_minutes" = "Ötpercenként"; -"messagecheck_every_10_minutes" = "Tízpercenként"; -"messagecheck_every_20_minutes" = "Húszpercenként"; -"messagecheck_every_30_minutes" = "Harminc percenként"; -"messagecheck_once_per_hour" = "Óránként"; +"refreshview_manually" = "Kézi"; +"refreshview_every_minute" = "Percenként"; +"refreshview_every_2_minutes" = "Kétpercenként"; +"refreshview_every_5_minutes" = "Ötpercenként"; +"refreshview_every_10_minutes" = "Tízpercenként"; +"refreshview_every_20_minutes" = "Húszpercenként"; +"refreshview_every_30_minutes" = "Harminc percenként"; +"refreshview_once_per_hour" = "Óránként"; "Forward messages:" = "Üzenetek továbbítása:"; "messageforward_inline" = "Levélként"; @@ -207,8 +211,8 @@ "Contacts" = "Címjegyzék"; "Mail" = "Levél"; "Last" = "Utoljára használt"; -"Default module :" = "Alapértelmezett modul"; -"SOGo version:" = "SOGo verzió:"; +"Default Module :" = "Alapértelmezett modul"; +"SOGo Version :" = "SOGo verzió:"; "Language :" = "Nyelv :"; "choose" = "Válasszon ..."; diff --git a/UI/PreferencesUI/Icelandic.lproj/Localizable.strings b/UI/PreferencesUI/Icelandic.lproj/Localizable.strings index c4b496ecd..264172a04 100644 --- a/UI/PreferencesUI/Icelandic.lproj/Localizable.strings +++ b/UI/PreferencesUI/Icelandic.lproj/Localizable.strings @@ -107,14 +107,14 @@ "Label" = "Merki"; "Show subscribed mailboxes only" = "Sýna aðeins pósthólf sem eru í áskrift"; "Check for new mail:" = "Sækja nýjan póst:"; -"messagecheck_manually" = "Handvirkt"; -"messagecheck_every_minute" = "Hverjar mínútu"; -"messagecheck_every_2_minutes" = "Hverjar 2 mínútur"; -"messagecheck_every_5_minutes" = "Hverjar 5 mínútur"; -"messagecheck_every_10_minutes" = "Hverjar 10 mínútur"; -"messagecheck_every_20_minutes" = "Hverjar 20 mínútur"; -"messagecheck_every_30_minutes" = "Hverjar 30 mínútur"; -"messagecheck_once_per_hour" = "Á klukkustundar fresti"; +"refreshview_manually" = "Handvirkt"; +"refreshview_every_minute" = "Hverjar mínútu"; +"refreshview_every_2_minutes" = "Hverjar 2 mínútur"; +"refreshview_every_5_minutes" = "Hverjar 5 mínútur"; +"refreshview_every_10_minutes" = "Hverjar 10 mínútur"; +"refreshview_every_20_minutes" = "Hverjar 20 mínútur"; +"refreshview_every_30_minutes" = "Hverjar 30 mínútur"; +"refreshview_once_per_hour" = "Á klukkustundar fresti"; "Forward messages:" = "Áframsenda póst:"; "messageforward_inline" = "Innfellt"; @@ -169,7 +169,7 @@ "Contacts" = "Nafnaskrá"; "Mail" = "Póstur"; "Last" = "Síðast notað"; -"Default module :" = "Sjálfgefið viðmót :"; +"Default Module :" = "Sjálfgefið viðmót :"; "Language :" = "Tungumál :"; "choose" = "Velja..."; diff --git a/UI/PreferencesUI/Italian.lproj/Localizable.strings b/UI/PreferencesUI/Italian.lproj/Localizable.strings index 546109f36..dda3400af 100644 --- a/UI/PreferencesUI/Italian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Italian.lproj/Localizable.strings @@ -127,14 +127,14 @@ "Show subscribed mailboxes only" = "Mostra solo le cartelle sottoscritte"; "Sort messages by threads" = "Ordina i messaggi per conversazione"; "Check for new mail:" = "Controlla la posta in arrivo:"; -"messagecheck_manually" = "Manualmente"; -"messagecheck_every_minute" = "Ogni minuto"; -"messagecheck_every_2_minutes" = "Ogni 2 minuti"; -"messagecheck_every_5_minutes" = "Ogni 5 minuti"; -"messagecheck_every_10_minutes" = "Ogni 10 minuti"; -"messagecheck_every_20_minutes" = "Ogni 20 minuti"; -"messagecheck_every_30_minutes" = "Ogni 30 minuti"; -"messagecheck_once_per_hour" = "Ogni ora"; +"refreshview_manually" = "Manualmente"; +"refreshview_every_minute" = "Ogni minuto"; +"refreshview_every_2_minutes" = "Ogni 2 minuti"; +"refreshview_every_5_minutes" = "Ogni 5 minuti"; +"refreshview_every_10_minutes" = "Ogni 10 minuti"; +"refreshview_every_20_minutes" = "Ogni 20 minuti"; +"refreshview_every_30_minutes" = "Ogni 30 minuti"; +"refreshview_once_per_hour" = "Ogni ora"; "Forward messages:" = "Inoltra messaggi come:"; "messageforward_inline" = "Parte del messaggio"; @@ -190,7 +190,7 @@ "Contacts" = "Rubrica"; "Mail" = "Posta"; "Last" = "Ultimo usato"; -"Default module :" = "Modulo di default :"; +"Default Module :" = "Modulo di default :"; "Language :" = "Lingua :"; "choose" = "Scegli..."; diff --git a/UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings b/UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings index 221db266c..dcac7f017 100644 --- a/UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings +++ b/UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings @@ -97,44 +97,52 @@ "Show time as busy outside working hours" = "Vis tid som opptatt utenfor arbeidstid"; "First week of year :" = "Første uken i året:"; "Enable reminders for Calendar items" = "Aktivere påminnelser for kalenderelementer"; -"Play a sound when a reminder comes due" -= "Avspill lyd ved en påminnelse"; +"Play a sound when a reminder comes due" = "Avspill lyd ved en påminnelse"; "Default reminder :" = "Standardpåminnelse:"; "firstWeekOfYear_January1" = "Begynner den 1. januar"; "firstWeekOfYear_First4DayWeek" = "Første firedagersuken i året"; "firstWeekOfYear_FirstFullWeek" = "Første hele uken i året"; +"Prevent from being invited to appointments" = "Hindre fra å bli invitert til avtaler"; +"White list for appointment invitations:" = "Hviteliste for avtale invitasjoner:"; +"Contacts Names" = "Kontakters navn"; + /* Default Calendar */ "Default calendar :" = "Standard kalender:"; "selectedCalendar" = "Valgte kalender"; "personalCalendar" = "Personlig kalender"; "firstCalendar" = "Første aktiverte kalender"; +"reminder_NONE" = "Ingen påminnelse"; "reminder_5_MINUTES_BEFORE" = "5 minutter"; "reminder_10_MINUTES_BEFORE" = "10 minutter"; "reminder_15_MINUTES_BEFORE" = "15 minutter"; "reminder_30_MINUTES_BEFORE" = "30 minutter"; +"reminder_45_MINUTES_BEFORE" = "45 minutter før"; "reminder_1_HOUR_BEFORE" = "1 time"; "reminder_2_HOURS_BEFORE" = "2 timer"; -"reminder_5_HOURS_BEFORE"= "5 timer"; -"reminder_15_HOURS_BEFORE"= "15 timer"; +"reminder_5_HOURS_BEFORE" = "5 timer"; +"reminder_15_HOURS_BEFORE" = "15 timer"; "reminder_1_DAY_BEFORE" = "1 dag"; "reminder_2_DAYS_BEFORE" = "2 dager"; +"reminder_1_WEEK_BEFORE" = "1 uke før"; /* Mailer */ +"Labels" = "Merkelapper"; "Label" = "Etikett"; "Show subscribed mailboxes only" = "Vis bare abonnerte postbokser"; "Sort messages by threads" = "Sorter meldinger etter tråder"; +"When sending mail, add unknown recipients to my" = "Ved sending av mail, legg til ukjente mottakere til min"; "Check for new mail:" = "Hent ny post:"; -"messagecheck_manually" = "Manuelt"; -"messagecheck_every_minute" = "Hvert minutt"; -"messagecheck_every_2_minutes" = "Hvert 2. minutt"; -"messagecheck_every_5_minutes" = "Hvert 5. minutt"; -"messagecheck_every_10_minutes" = "Hvert 10. minutt"; -"messagecheck_every_20_minutes" = "Hvert 20. minutt"; -"messagecheck_every_30_minutes" = "Hvert 30. minutt"; -"messagecheck_once_per_hour" = "Hver time"; +"refreshview_manually" = "Manuelt"; +"refreshview_every_minute" = "Hvert minutt"; +"refreshview_every_2_minutes" = "Hvert 2. minutt"; +"refreshview_every_5_minutes" = "Hvert 5. minutt"; +"refreshview_every_10_minutes" = "Hvert 10. minutt"; +"refreshview_every_20_minutes" = "Hvert 20. minutt"; +"refreshview_every_30_minutes" = "Hvert 30. minutt"; +"refreshview_once_per_hour" = "Hver time"; "Forward messages:" = "Videresend meldinger:"; "messageforward_inline" = "Innsatt"; @@ -153,6 +161,10 @@ "displayremoteinlineimages_never" = "Aldri"; "displayremoteinlineimages_always" = "Alltid"; +/* Contact */ +"Personal Address Book" = "Personlig addressebok"; +"Collected Address Book" = "Samlet addressebok"; + /* IMAP Accounts */ "New Mail Account" = "Ny e-postkonto"; @@ -199,7 +211,8 @@ "Contacts" = "Adressebok"; "Mail" = "E-post"; "Last" = "Sist besøkt"; -"Default module :" = "Standardmodul:"; +"Default Module :" = "Standardmodul:"; +"SOGo Version :" = "SOGo versjon:"; "Language :" = "Språk:"; "choose" = "Velg ..."; @@ -244,6 +257,8 @@ "Active" = "Aktiv"; "Move Up" = "Flytt opp"; "Move Down" = "Flytt ned"; +"Connection error" = "Koblingsfeil"; +"Service temporarily unavailable" = "Tjeneste midlertidig utilgjengelig"; /* Filters - UIxFilterEditor */ "Filter name:" = "Filternavn:"; @@ -261,6 +276,7 @@ "To or Cc" = "Til eller Kopi"; "Size (Kb)" = "Størrelse (Kb)"; "Header" = "Tittel"; +"Body" = "Kropp"; "Flag the message with:" = "Flagg meldingen med:"; "Discard the message" = "Forkast meldingen"; "File the message in:" = "Fil meldingen i:"; @@ -287,12 +303,8 @@ "Flagged" = "Flagget"; "Junk" = "Søppel"; "Not Junk" = "Ikke søppel"; -"Label 1" = "Etikett 1"; -"Label 2" = "Etikett 2"; -"Label 3" = "Etikett 3"; -"Label 4" = "Etikett 4"; -"Label 5" = "Etikett 5"; +/* Password policy */ "The password was changed successfully." = "Passordet ble endret."; "Password must not be empty." = "Passord kan ikke være tomme."; "The passwords do not match. Please try again." = "Passordene er ikke like. Prøv igjen."; diff --git a/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings b/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings index 718b758bc..3c8f23b64 100644 --- a/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings +++ b/UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings @@ -114,14 +114,14 @@ "Show subscribed mailboxes only" = "Vis bare abonnerte postbokser"; "Sort messages by threads" = "Sort messages by threads"; "Check for new mail:" = "Hent ny post:"; -"messagecheck_manually" = "Manuelt"; -"messagecheck_every_minute" = "Hvert minutt"; -"messagecheck_every_2_minutes" = "Hvert 2 minutt"; -"messagecheck_every_5_minutes" = "Hvert 5 minutt"; -"messagecheck_every_10_minutes" = "Hvert 10 minutt"; -"messagecheck_every_20_minutes" = "Hvert 20 minutt"; -"messagecheck_every_30_minutes" = "Hvert 30 minutt"; -"messagecheck_once_per_hour" = "Hver time"; +"refreshview_manually" = "Manuelt"; +"refreshview_every_minute" = "Hvert minutt"; +"refreshview_every_2_minutes" = "Hvert 2 minutt"; +"refreshview_every_5_minutes" = "Hvert 5 minutt"; +"refreshview_every_10_minutes" = "Hvert 10 minutt"; +"refreshview_every_20_minutes" = "Hvert 20 minutt"; +"refreshview_every_30_minutes" = "Hvert 30 minutt"; +"refreshview_once_per_hour" = "Hver time"; "Forward messages:" = "Videresend melding:"; "messageforward_inline" = "Innsatt"; @@ -176,7 +176,7 @@ "Contacts" = "Adressebok"; "Mail" = "E-post"; "Last" = "Sist besøkt"; -"Default module :" = "Standardmodul:"; +"Default Module :" = "Standardmodul:"; "Language :" = "Språk:"; "choose" = "Velg ..."; diff --git a/UI/PreferencesUI/Polish.lproj/Localizable.strings b/UI/PreferencesUI/Polish.lproj/Localizable.strings index 1144a3740..6606ec007 100644 --- a/UI/PreferencesUI/Polish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Polish.lproj/Localizable.strings @@ -104,6 +104,10 @@ "firstWeekOfYear_First4DayWeek" = "pierwszy 4-dniowy tydzień"; "firstWeekOfYear_FirstFullWeek" = "pierwszy pełny tydzień"; +"Prevent from being invited to appointments" = "Zablokuj możliwość zapraszania cię na wydarzenia"; +"White list for appointment invitations:" = "Osoby, którym pozwalasz się zapraszać:"; +"Contacts Names" = "Nazwy kontaktów"; + /* Default Calendar */ "Default calendar :" = "Domyślny kalendarz:"; "selectedCalendar" = "Wybrany kalendarz"; @@ -131,14 +135,14 @@ "Sort messages by threads" = "Sortuj wiadomości według wątków"; "When sending mail, add unknown recipients to my" = "Gdy wysyłam e-mail, dodaj adresy nowych odbiorców do"; "Check for new mail:" = "Sprawdzaj nowe wiadomości:"; -"messagecheck_manually" = "ręcznie"; -"messagecheck_every_minute" = "co minutę"; -"messagecheck_every_2_minutes" = "co 2 minuty"; -"messagecheck_every_5_minutes" = "co 5 minut"; -"messagecheck_every_10_minutes" = "co 10 minut"; -"messagecheck_every_20_minutes" = "co 20 minut"; -"messagecheck_every_30_minutes" = "co 30 minut"; -"messagecheck_once_per_hour" = "raz na godzinę"; +"refreshview_manually" = "ręcznie"; +"refreshview_every_minute" = "co minutę"; +"refreshview_every_2_minutes" = "co 2 minuty"; +"refreshview_every_5_minutes" = "co 5 minut"; +"refreshview_every_10_minutes" = "co 10 minut"; +"refreshview_every_20_minutes" = "co 20 minut"; +"refreshview_every_30_minutes" = "co 30 minut"; +"refreshview_once_per_hour" = "raz na godzinę"; "Forward messages:" = "Przekaż wiadomości:"; "messageforward_inline" = "w treści"; @@ -207,8 +211,8 @@ "Contacts" = "Książka adresowa"; "Mail" = "Poczta"; "Last" = "Ostatnio używane"; -"Default module :" = "Tryb domyślny:"; -"SOGo version:" = "Wersja SOGo:"; +"Default Module :" = "Tryb domyślny:"; +"SOGo Version :" = "Wersja SOGo:"; "Language :" = "Język:"; "choose" = "Wybierz"; diff --git a/UI/PreferencesUI/Russian.lproj/Localizable.strings b/UI/PreferencesUI/Russian.lproj/Localizable.strings index b9fcba4d9..59876e6e7 100644 --- a/UI/PreferencesUI/Russian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Russian.lproj/Localizable.strings @@ -131,14 +131,14 @@ "Sort messages by threads" = "Сортировать сообщения по нитям"; "When sending mail, add unknown recipients to my" = "При отправке писем добавлять неизвестных получателей в мою"; "Check for new mail:" = "Проверять новую почту:"; -"messagecheck_manually" = "Вручную"; -"messagecheck_every_minute" = "Каждую минуту"; -"messagecheck_every_2_minutes" = "Каждые 2 минуты"; -"messagecheck_every_5_minutes" = "Каждые 5 минут"; -"messagecheck_every_10_minutes" = "Каждые 10 минут"; -"messagecheck_every_20_minutes" = "Каждые 20 минут"; -"messagecheck_every_30_minutes" = "Каждые 30 минут"; -"messagecheck_once_per_hour" = "Раз в час"; +"refreshview_manually" = "Вручную"; +"refreshview_every_minute" = "Каждую минуту"; +"refreshview_every_2_minutes" = "Каждые 2 минуты"; +"refreshview_every_5_minutes" = "Каждые 5 минут"; +"refreshview_every_10_minutes" = "Каждые 10 минут"; +"refreshview_every_20_minutes" = "Каждые 20 минут"; +"refreshview_every_30_minutes" = "Каждые 30 минут"; +"refreshview_once_per_hour" = "Раз в час"; "Forward messages:" = "Пересылать сообщения:"; "messageforward_inline" = "В теле письма"; @@ -207,8 +207,8 @@ "Contacts" = "Адресная книга"; "Mail" = "Почта"; "Last" = "Последнее использование"; -"Default module :" = "Модуль по умолчанию :"; -"SOGo version:" = "Версия SOGo:"; +"Default Module :" = "Модуль по умолчанию :"; +"SOGo Version :" = "Версия SOGo:"; "Language :" = "Язык :"; "choose" = "Выбрать ..."; diff --git a/UI/PreferencesUI/Slovak.lproj/Localizable.strings b/UI/PreferencesUI/Slovak.lproj/Localizable.strings index dc8208fa2..e564e8c50 100644 --- a/UI/PreferencesUI/Slovak.lproj/Localizable.strings +++ b/UI/PreferencesUI/Slovak.lproj/Localizable.strings @@ -131,14 +131,14 @@ "Sort messages by threads" = "Zoraď správy do konverzácií"; "When sending mail, add unknown recipients to my" = "Pri odosielaní emailu, pridaj neznámych príjemcov do môjho"; "Check for new mail:" = "Kontrola nových mailov:"; -"messagecheck_manually" = "Ručne"; -"messagecheck_every_minute" = "Každú minútu"; -"messagecheck_every_2_minutes" = "Každé 2 minúty"; -"messagecheck_every_5_minutes" = "Každých 5 minút"; -"messagecheck_every_10_minutes" = "Každých 10 minút"; -"messagecheck_every_20_minutes" = "Každých 20 minút"; -"messagecheck_every_30_minutes" = "Každých 30 minút"; -"messagecheck_once_per_hour" = "Raz za hodinu"; +"refreshview_manually" = "Ručne"; +"refreshview_every_minute" = "Každú minútu"; +"refreshview_every_2_minutes" = "Každé 2 minúty"; +"refreshview_every_5_minutes" = "Každých 5 minút"; +"refreshview_every_10_minutes" = "Každých 10 minút"; +"refreshview_every_20_minutes" = "Každých 20 minút"; +"refreshview_every_30_minutes" = "Každých 30 minút"; +"refreshview_once_per_hour" = "Raz za hodinu"; "Forward messages:" = "Preposielaj správy:"; "messageforward_inline" = "Vrade"; @@ -207,8 +207,8 @@ "Contacts" = "Adresár"; "Mail" = "Pošta"; "Last" = "Naposledy použité"; -"Default module :" = "Predvolený modul:"; -"SOGo version:" = "Verzia SOGo:"; +"Default Module :" = "Predvolený modul:"; +"SOGo Version :" = "Verzia SOGo:"; "Language :" = "Jazyk:"; "choose" = "Vyber..."; diff --git a/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings b/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings index c2b003477..98f09d019 100644 --- a/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings +++ b/UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings @@ -131,14 +131,14 @@ "Sort messages by threads" = "Ordenar mensajes por conversaciones"; "When sending mail, add unknown recipients to my" = "Al enviar correos, agregar destinatarios desconocidos a mi"; "Check for new mail:" = "Comprobar si hay nuevos correos: "; -"messagecheck_manually" = "Manualmente"; -"messagecheck_every_minute" = "Cada minuto"; -"messagecheck_every_2_minutes" = "Cada 2 minutos"; -"messagecheck_every_5_minutes" = "Cada 5 minutos"; -"messagecheck_every_10_minutes" = "Cada 10 minutos"; -"messagecheck_every_20_minutes" = "Cada 20 minutos"; -"messagecheck_every_30_minutes" = "Cada 30 minutos"; -"messagecheck_once_per_hour" = "Cada hora"; +"refreshview_manually" = "Manualmente"; +"refreshview_every_minute" = "Cada minuto"; +"refreshview_every_2_minutes" = "Cada 2 minutos"; +"refreshview_every_5_minutes" = "Cada 5 minutos"; +"refreshview_every_10_minutes" = "Cada 10 minutos"; +"refreshview_every_20_minutes" = "Cada 20 minutos"; +"refreshview_every_30_minutes" = "Cada 30 minutos"; +"refreshview_once_per_hour" = "Cada hora"; "Forward messages:" = "Reenviar mensajes:"; "messageforward_inline" = "Incorporado"; @@ -207,8 +207,8 @@ "Contacts" = "Libreta de direcciones"; "Mail" = "Correo"; "Last" = "Ultimo usado"; -"Default module :" = "Módulo por defecto :"; -"SOGo version:" = "Versión de SOGo:"; +"Default Module :" = "Módulo por defecto :"; +"SOGo Version :" = "Versión de SOGo:"; "Language :" = "Idioma :"; "choose" = "Elija ..."; diff --git a/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings b/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings index a0abeb7aa..3cc800f6f 100644 --- a/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings +++ b/UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings @@ -104,6 +104,10 @@ "firstWeekOfYear_First4DayWeek" = "Primera semana del año de 4 días"; "firstWeekOfYear_FirstFullWeek" = "Primera semana del año completa"; +"Prevent from being invited to appointments" = "Evitar invitaciones a eventos"; +"White list for appointment invitations:" = "Lista blanca de invitaciones a eventos:"; +"Contacts Names" = "Nombres de Contacto"; + /* Default Calendar */ "Default calendar :" = "Calendario por defecto"; "selectedCalendar" = "Calendario seleccionado"; @@ -131,14 +135,14 @@ "Sort messages by threads" = "Ordenar mensajes por temas"; "When sending mail, add unknown recipients to my" = "Cuando se envía correo, añade destinatarios desconocidos a mi"; "Check for new mail:" = "Comprobar correo nuevo: "; -"messagecheck_manually" = "Manualmente"; -"messagecheck_every_minute" = "Cada minuto"; -"messagecheck_every_2_minutes" = "Cada 2 minutos"; -"messagecheck_every_5_minutes" = "Cada 5 minutos"; -"messagecheck_every_10_minutes" = "Cada 10 minutos"; -"messagecheck_every_20_minutes" = "Cada 20 minutos"; -"messagecheck_every_30_minutes" = "Cada 30 minutos"; -"messagecheck_once_per_hour" = "Cada hora"; +"refreshview_manually" = "Manualmente"; +"refreshview_every_minute" = "Cada minuto"; +"refreshview_every_2_minutes" = "Cada 2 minutos"; +"refreshview_every_5_minutes" = "Cada 5 minutos"; +"refreshview_every_10_minutes" = "Cada 10 minutos"; +"refreshview_every_20_minutes" = "Cada 20 minutos"; +"refreshview_every_30_minutes" = "Cada 30 minutos"; +"refreshview_once_per_hour" = "Cada hora"; "Forward messages:" = "Reenviar mensajes:"; "messageforward_inline" = "Incorporado"; @@ -207,8 +211,8 @@ "Contacts" = "Libreta de direcciones"; "Mail" = "Correo"; "Last" = "Ultimo usado"; -"Default module :" = "Módulo por defecto :"; -"SOGo version:" = "Versión de SOGo:"; +"Default Module :" = "Módulo por defecto :"; +"SOGo Version :" = "Versión de SOGo:"; "Language :" = "Idioma :"; "choose" = "Elija ..."; diff --git a/UI/PreferencesUI/Swedish.lproj/Localizable.strings b/UI/PreferencesUI/Swedish.lproj/Localizable.strings index 73e4b0282..915b3d324 100644 --- a/UI/PreferencesUI/Swedish.lproj/Localizable.strings +++ b/UI/PreferencesUI/Swedish.lproj/Localizable.strings @@ -114,14 +114,14 @@ "Show subscribed mailboxes only" = "Visa endast prenumrerade postlådor"; "Sort messages by threads" = "Sort messages by threads"; "Check for new mail:" = "Hämta ny post:"; -"messagecheck_manually" = "Manuellt"; -"messagecheck_every_minute" = "Varje minut"; -"messagecheck_every_2_minutes" = "Varje 2 minuter"; -"messagecheck_every_5_minutes" = "Varje 5 minuter"; -"messagecheck_every_10_minutes" = "Varje 10 minuter"; -"messagecheck_every_20_minutes" = "Varje 20 minuter"; -"messagecheck_every_30_minutes" = "Varje 30 minuter"; -"messagecheck_once_per_hour" = "Varje timme"; +"refreshview_manually" = "Manuellt"; +"refreshview_every_minute" = "Varje minut"; +"refreshview_every_2_minutes" = "Varje 2 minuter"; +"refreshview_every_5_minutes" = "Varje 5 minuter"; +"refreshview_every_10_minutes" = "Varje 10 minuter"; +"refreshview_every_20_minutes" = "Varje 20 minuter"; +"refreshview_every_30_minutes" = "Varje 30 minuter"; +"refreshview_once_per_hour" = "Varje timme"; "Forward messages:" = "Vidarebefordra meddelanden:"; "messageforward_inline" = "Infogade"; @@ -178,7 +178,7 @@ Servernamn:"; "Contacts" = "Adressbok"; "Mail" = "E-post"; "Last" = "Senast använd"; -"Default module :" = "Standardmodul:"; +"Default Module :" = "Standardmodul:"; "Language :" = "Språk:"; "choose" = "Välj ..."; diff --git a/UI/PreferencesUI/UIxPreferences.m b/UI/PreferencesUI/UIxPreferences.m index 0900c4d3a..5017a4bec 100644 --- a/UI/PreferencesUI/UIxPreferences.m +++ b/UI/PreferencesUI/UIxPreferences.m @@ -19,6 +19,7 @@ */ #import +#import #import #import #import @@ -41,6 +42,7 @@ #import #import #import +#import #import #import #import @@ -638,6 +640,48 @@ static NSArray *reminderValues = nil; return [userDefaults busyOffHours]; } +- (NSArray *) whiteList +{ + SOGoUserSettings *us; + NSMutableDictionary *moduleSettings; + NSArray *whiteList; + + us = [user userSettings]; + moduleSettings = [us objectForKey: @"Calendar"]; + whiteList = [moduleSettings objectForKey:@"PreventInvitationsWhitelist"]; + return whiteList; +} + +- (void) setWhiteList: (NSString *) whiteListString +{ + SOGoUserSettings *us; + NSMutableDictionary *moduleSettings; + + us = [user userSettings]; + moduleSettings = [us objectForKey: @"Calendar"]; + [moduleSettings setObject: whiteListString forKey: @"PreventInvitationsWhitelist"]; + [us synchronize]; +} + +- (void) setPreventInvitations: (BOOL) preventInvitations +{ + SOGoUserSettings *us; + NSMutableDictionary *moduleSettings; + us = [user userSettings]; + moduleSettings = [us objectForKey: @"Calendar"]; + [moduleSettings setObject: [NSNumber numberWithBool: preventInvitations] forKey: @"PreventInvitations"]; + [us synchronize]; +} + +- (BOOL) preventInvitations +{ + SOGoUserSettings *us; + NSMutableDictionary *moduleSettings; + us = [user userSettings]; + moduleSettings = [us objectForKey: @"Calendar"]; + return [[moduleSettings objectForKey: @"PreventInvitations"] boolValue]; +} + - (NSArray *) firstWeekList { return [NSArray arrayWithObjects: @@ -761,15 +805,15 @@ static NSArray *reminderValues = nil; [userDefaults setSelectedAddressBook: newSelectedAddressBook]; } -- (NSArray *) messageCheckList +- (NSArray *) refreshViewList { NSArray *intervalsList; - NSMutableArray *messageCheckList; + NSMutableArray *refreshViewList; NSString *value; int count, max, interval; - intervalsList = [[user domainDefaults] mailPollingIntervals]; - messageCheckList = [NSMutableArray arrayWithObjects: @"manually", nil]; + intervalsList = [[user domainDefaults] refreshViewIntervals]; + refreshViewList = [NSMutableArray arrayWithObjects: @"manually", nil]; max = [intervalsList count]; for (count = 0; count < max; count++) { @@ -788,26 +832,25 @@ static NSArray *reminderValues = nil; value = nil; } if (value) - [messageCheckList addObject: value]; + [refreshViewList addObject: value]; } - return messageCheckList; + return refreshViewList; } -- (NSString *) itemMessageCheckText +- (NSString *) itemRefreshViewCheckText { - return [self labelForKey: - [NSString stringWithFormat: @"messagecheck_%@", item]]; + return [self labelForKey: [NSString stringWithFormat: @"refreshview_%@", item]]; } -- (NSString *) userMessageCheck +- (NSString *) userRefreshViewCheck { - return [userDefaults mailMessageCheck]; + return [userDefaults refreshViewCheck]; } -- (void) setUserMessageCheck: (NSString *) newMessageCheck +- (void) setUserRefreshViewCheck: (NSString *) newRefreshViewCheck { - [userDefaults setMailMessageCheck: newMessageCheck]; + [userDefaults setRefreshViewCheck: newRefreshViewCheck]; } - (NSArray *) messageForwardingList diff --git a/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings b/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings index ead74f31c..0f3ef69ec 100644 --- a/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings +++ b/UI/PreferencesUI/Ukrainian.lproj/Localizable.strings @@ -126,14 +126,14 @@ "Show subscribed mailboxes only" = "Показувати лише поштові скриньки, на які я підписаний"; "Sort messages by threads" = "Сортувати повідомлення за гілками"; "Check for new mail:" = "Перевіряти нову пошту:"; -"messagecheck_manually" = "Вручну"; -"messagecheck_every_minute" = "Щохвилини"; -"messagecheck_every_2_minutes" = "Кожні 2 хвилини"; -"messagecheck_every_5_minutes" = "Кожні 5 хвилин"; -"messagecheck_every_10_minutes" = "Кожні 10 хвилин"; -"messagecheck_every_20_minutes" = "Кожні 20 хвилин"; -"messagecheck_every_30_minutes" = "Кожні 30 хвилин"; -"messagecheck_once_per_hour" = "Раз на годину"; +"refreshview_manually" = "Вручну"; +"refreshview_every_minute" = "Щохвилини"; +"refreshview_every_2_minutes" = "Кожні 2 хвилини"; +"refreshview_every_5_minutes" = "Кожні 5 хвилин"; +"refreshview_every_10_minutes" = "Кожні 10 хвилин"; +"refreshview_every_20_minutes" = "Кожні 20 хвилин"; +"refreshview_every_30_minutes" = "Кожні 30 хвилин"; +"refreshview_once_per_hour" = "Раз на годину"; "Forward messages:" = "Перенаправляти повідомлення:"; "messageforward_inline" = "В тілі листа"; @@ -188,7 +188,7 @@ "Contacts" = "Адресна книга"; "Mail" = "Електронна пошта"; "Last" = "Останнє"; -"Default module :" = "Модуль за замовчанням :"; +"Default Module :" = "Модуль за замовчанням :"; "Language :" = "Мова :"; "choose" = "Choose ..."; diff --git a/UI/PreferencesUI/Welsh.lproj/Localizable.strings b/UI/PreferencesUI/Welsh.lproj/Localizable.strings index fd7dc78a1..f7a9f46ba 100644 --- a/UI/PreferencesUI/Welsh.lproj/Localizable.strings +++ b/UI/PreferencesUI/Welsh.lproj/Localizable.strings @@ -114,14 +114,14 @@ "Show subscribed mailboxes only" = "Show subscribed mailboxes only"; "Sort messages by threads" = "Sort messages by threads"; "Check for new mail:" = "Chwilio am ebost newydd:"; -"messagecheck_manually" = "Corfforol"; -"messagecheck_every_minute" = "Pob munud"; -"messagecheck_every_2_minutes" = "Pob 2 munud"; -"messagecheck_every_5_minutes" = "Pob 5 munud"; -"messagecheck_every_10_minutes" = "Pob 10 munud"; -"messagecheck_every_20_minutes" = "Pob 20 munud"; -"messagecheck_every_30_minutes" = "Pob 30 munud"; -"messagecheck_once_per_hour" = "Unwaith pob awr"; +"refreshview_manually" = "Corfforol"; +"refreshview_every_minute" = "Pob munud"; +"refreshview_every_2_minutes" = "Pob 2 munud"; +"refreshview_every_5_minutes" = "Pob 5 munud"; +"refreshview_every_10_minutes" = "Pob 10 munud"; +"refreshview_every_20_minutes" = "Pob 20 munud"; +"refreshview_every_30_minutes" = "Pob 30 munud"; +"refreshview_once_per_hour" = "Unwaith pob awr"; "Forward messages:" = "Blaenyrru negeseuon:"; "messageforward_inline" = "Mewn llinell"; @@ -176,7 +176,7 @@ "Contacts" = "Address Book"; "Mail" = "Mail"; "Last" = "Last used"; -"Default module :" = "Default module :"; +"Default Module :" = "Default module :"; "Language :" = "Iaith :"; "choose" = "Dewis ..."; diff --git a/UI/Scheduler/Catalan.lproj/Localizable.strings b/UI/Scheduler/Catalan.lproj/Localizable.strings index 030dd4644..30f5ef20b 100644 --- a/UI/Scheduler/Catalan.lproj/Localizable.strings +++ b/UI/Scheduler/Catalan.lproj/Localizable.strings @@ -5,6 +5,7 @@ "Create a new event" = "Crear un esdeveniment nou en el calendari"; "Create a new task" = "Crear una tasca nova"; "Edit this event or task" = "Modificar aquest esdeveniment o tasca"; +"Print the current calendar view" = "Imprimeix la vista de calendari actual"; "Delete this event or task" = "Esborrar aquest esdeveniment o tasca"; "Go to today" = "Anar a avui"; "Switch to day view" = "Canviar a vista diària"; @@ -117,7 +118,7 @@ "Name of the Calendar" = "Nom del calendari"; "new" = "Nou"; -"printview" = "Vista preliminar"; +"Print view" = "Imprimeix la vista"; "edit" = "Modificar"; "delete" = "Esborrar"; "proposal" = "Proposta"; @@ -145,6 +146,17 @@ "Hide already accepted and rejected appointments" = "Ocultar cites confirmades o rebutjades"; "Show already accepted and rejected appointments" = "Mostrar cites confirmades o rebutjades"; +/* Print view */ + +"LIST" = "Llista"; +"Print Settings" = "Configuració d'Impressió"; +"Title:" = "Títol:"; +"Layout:" = "Disseny:"; +"What to Print" = "Què imprimir"; +"Options" = "Opcions"; +"Tasks with no due date" = "Tasques sense data de finalització"; +"Display working hours only" = "Mostra només les hores de treball"; +"Completed tasks" = "Tasques Completades"; /* Appointments */ @@ -179,6 +191,8 @@ "Reminder:" = "Recordatori:"; "General:" = "General:"; "Reply:" = "Respondre:"; +"Created by:" = "Creat per:"; + "Target:" = "URL document:"; @@ -223,8 +237,13 @@ "view_future" = "Tots els esdeveniments futurs"; "view_selectedday" = "Dia seleccionat"; +"view_not_started" = "Tasques no iniciades"; +"view_overdue" = "Tasques vençudes"; +"view_incomplete" = "Incomplete tasks"; + "View:" = "Veure:"; -"Title or Description" = "Títol o descripció"; +"Title, category or location" = "Títol, categoria o ubicació"; +"Entire content" = "Tot el contingut"; "Search" = "Cercar"; "Search attendees" = "Cercar assistents"; @@ -373,6 +392,9 @@ "Show Time as Free" = "Mostrar temps com a lliure"; +/* email notifications */ +"Send Appointment Notifications" = "Envia notificacions de cites"; + /* validation errors */ validate_notitle = "Sense títol, continuar?"; @@ -501,7 +523,7 @@ vtodo_class2 = "(Tasca confidencial)"; "Links to this Calendar" = "Enllaços a aquest calendari"; "Authenticated User Access" = "Accés autenticat"; -"CalDAV URL" = "url CalDAV"; +"CalDAV URL" = "URL CalDAV:"; "WebDAV ICS URL" = "url WebDAV ICS"; "WebDAV XML URL" = "url WebDAV XML"; @@ -521,6 +543,8 @@ vtodo_class2 = "(Tasca confidencial)"; "tagWasRemoved" = "Si tragueu aquest calendari de la sincronització, haureu de recarregar les dades en el dispositiu mòbil.\nVoleu continuar?"; "DestinationCalendarError" = "El calendari origen i destinació són el mateix. Intenteu copiar-lo en un calendari diferent."; "EventCopyError" = "Error de còpia. Intenteu-lo copiar en un calendari diferent."; +"Please select at least one calendar" = "Per favor, seleccionar almenys un calendari"; + "Open Task..." = "Obrir tasca..."; "Mark Completed" = "Marcar com a completada"; @@ -534,3 +558,4 @@ vtodo_class2 = "(Tasca confidencial)"; "Web Calendar" = "Calendari Web"; "Reload on login" = "Actualitzar en connectar-se"; "Invalid number." = "Número incorrecte"; +"Please identify yourself to %{0}" = "Per favor, identifica't davant %{0}"; diff --git a/UI/Scheduler/Czech.lproj/Localizable.strings b/UI/Scheduler/Czech.lproj/Localizable.strings index 1b24ca753..9691ea6fc 100644 --- a/UI/Scheduler/Czech.lproj/Localizable.strings +++ b/UI/Scheduler/Czech.lproj/Localizable.strings @@ -155,6 +155,7 @@ "What to Print" = "Rozsah tisku"; "Options" = "Možnosti"; "Tasks with no due date" = "Úkoly bez termínu"; +"Display working hours only" = "Zobrazit jen pracovní hodiny"; "Completed tasks" = "Dokončené úkoly"; /* Appointments */ @@ -377,7 +378,7 @@ "Choose a Reminder Action" = "Zvolit akci"; "Show an Alert" = "Zobrazit výstrahu"; -"Send an E-mail" = "Poslat e-mai"; +"Send an E-mail" = "Poslat e-mail"; "Email Organizer" = "E-mail organizátorovi"; "Email Attendees" = "E-mail účastníkům"; @@ -392,7 +393,7 @@ "Show Time as Free" = "Čas zobrazit jako volný"; /* email notifications */ -"Send Appointment Notifications" = "Poslat upozornění na schůzku"; +"Send Appointment Notifications" = "Poslat pozvání na schůzku"; /* validation errors */ @@ -493,7 +494,7 @@ vtodo_class1 = "(Soukromý úkol)"; vtodo_class2 = "(Důvěrný úkol)"; "closeThisWindowMessage" = "Děkujeme! Nyní můžete okno zavřít nebo se podívat na vaše "; -"Multicolumn Day View" = "Vícesloupkové denní zobrazení"; +"Multicolumn Day View" = "Sloupce"; "Please select an event or a task." = "Vyberte prosím událost nebo úkol."; @@ -542,6 +543,8 @@ vtodo_class2 = "(Důvěrný úkol)"; "tagWasRemoved" = "Odstraníte-li u tohoto kalendáře synchronizaci, budete muset znovu načíst data do svého mobilního zařízení.\nPokračovat?"; "DestinationCalendarError" = "The source and destination calendars are the same. Please try to copy to a different calendar."; "EventCopyError" = "The copy failed. Please try to copy to a difference calendar."; +"Please select at least one calendar" = "Prosím zvolte alespoň jeden kalendář"; + "Open Task..." = "Otevřít úkol..."; "Mark Completed" = "Označit jako dokončené"; diff --git a/UI/Scheduler/Dutch.lproj/Localizable.strings b/UI/Scheduler/Dutch.lproj/Localizable.strings index 5bcdf0a7b..3e67f14ca 100644 --- a/UI/Scheduler/Dutch.lproj/Localizable.strings +++ b/UI/Scheduler/Dutch.lproj/Localizable.strings @@ -155,6 +155,7 @@ "What to Print" = "Wat moet afgedrukt worden"; "Options" = "Opties:"; "Tasks with no due date" = "Taken zonder verloopdatum"; +"Display working hours only" = "Alleen werktijd weergeven"; "Completed tasks" = "Afgeronde taken"; /* Appointments */ @@ -518,7 +519,7 @@ vtodo_class2 = "(Vertrouwelijke taak)"; "Notifications" = "Notificaties"; "Receive a mail when I modify my calendar" = "Ontvang een e-mail als ik mijn agenda verander"; "Receive a mail when someone else modifies my calendar" = "Ontvang een e-mail als iemand anders mijn agenda verandert"; -"When I modify my calendar, send a mail to:" = "Als ik mijn agenda verader, stuur een e-mail naar:"; +"When I modify my calendar, send a mail to:" = "Als ik mijn agenda verander, stuur een e-mail naar:"; "Links to this Calendar" = "Koppelingen naar deze agenda"; "Authenticated User Access" = "Toegang voor geauthenticeerde gebruikers"; @@ -542,6 +543,8 @@ vtodo_class2 = "(Vertrouwelijke taak)"; "tagWasRemoved" = "Als u deze agenda wil verwijderen van de synchronisatie, moet u de gegevens opnieuw laden op uw mobiele apparaat.\nDoorgaan?"; "DestinationCalendarError" = "De bron en bestemming agenda's zijn hetzelfde. Probeer te kopiëren naar een andere agenda."; "EventCopyError" = "De kopie is mislukt. Probeer te kopiëren naar een verschil-agenda."; +"Please select at least one calendar" = "Selecteer tenminste een agenda"; + "Open Task..." = "Taak openen..."; "Mark Completed" = "Markeren als voltooid"; diff --git a/UI/Scheduler/English.lproj/Localizable.strings b/UI/Scheduler/English.lproj/Localizable.strings index ecb4a9135..321198ea8 100644 --- a/UI/Scheduler/English.lproj/Localizable.strings +++ b/UI/Scheduler/English.lproj/Localizable.strings @@ -157,6 +157,9 @@ "Tasks with no due date" = "Tasks with no due date"; "Display working hours only" = "Display working hours only"; "Completed tasks" = "Completed Tasks"; +"Display events and tasks colors" = "Display events and tasks colors"; +"Borders" = "Borders"; +"Backgrounds" = "Backgrounds"; /* Appointments */ @@ -314,7 +317,6 @@ "Repeat" = "Repeat"; "Daily" = "Daily"; -"Multi-Columns" = "Multi-Columns"; "Weekly" = "Weekly"; "Monthly" = "Monthly"; "Yearly" = "Yearly"; diff --git a/UI/Scheduler/Finnish.lproj/Localizable.strings b/UI/Scheduler/Finnish.lproj/Localizable.strings index 83a4c7f03..8c1209232 100644 --- a/UI/Scheduler/Finnish.lproj/Localizable.strings +++ b/UI/Scheduler/Finnish.lproj/Localizable.strings @@ -155,6 +155,7 @@ "What to Print" = "Tulostusvalinnat"; "Options" = "Asetukset"; "Tasks with no due date" = "Tehtävät ilman määräpäivää"; +"Display working hours only" = "Näytä vain työajat"; "Completed tasks" = "Valmistuneet tehtävät"; /* Appointments */ @@ -542,6 +543,8 @@ vtodo_class2 = "(Luottamuksellinen tehtävä)"; "tagWasRemoved" = "Mobiililaitteesi kalenteritiedot on päivitettävä mikäli haluat poistaa tämän kalenterin synkronoinnista.⏎ Jatketaanko?"; "DestinationCalendarError" = "Lähde- ja kohdekalenterit ovat samoja. Ole hyvä ja yritä kopiointia toiseen kalenteriin."; "EventCopyError" = "Kopiointi epäonnistui. Ole hyvä ja yritä kopiointia toiseen kalenteriin."; +"Please select at least one calendar" = "Ole hyvä ja valitse ainakin yksi kalenteri"; + "Open Task..." = "Avaa tehtävä..."; "Mark Completed" = "Merkitse valmistuneeksi"; diff --git a/UI/Scheduler/French.lproj/Localizable.strings b/UI/Scheduler/French.lproj/Localizable.strings index 42cdb99f7..5516e8152 100644 --- a/UI/Scheduler/French.lproj/Localizable.strings +++ b/UI/Scheduler/French.lproj/Localizable.strings @@ -151,11 +151,15 @@ "LIST" = "Liste"; "Print Settings" = "Paramètres d'impression"; "Title:" = "Titre :"; -"Layout:" = "Disposition :"; +"Layout:" = "Vue :"; "What to Print" = "Contenu"; "Options" = "Options"; "Tasks with no due date" = "Tâches sans date d'échéance"; +"Display working hours only" = "Afficher les heures de bureau seulement"; "Completed tasks" = "Tâches complétées"; +"Display events and tasks colors" = "Affichage en couleurs"; +"Borders" = "Bordures"; +"Backgrounds" = "Arrière-plans"; /* Appointments */ @@ -542,6 +546,8 @@ vtodo_class2 = "(Tâche confidentielle)"; "tagWasRemoved" = "Afin de ne plus synchroniser ce calendrier, vous devrez recharger les données sur votre appareil mobile.\nVoulez-vous continuer?"; "DestinationCalendarError" = "Le calendrier de destination est le même que celui de l'événement. Choisissez un calendrier de destination différent."; "EventCopyError" = "La copie a échouée. Choisissez un calendrier de destination différent."; +"Please select at least one calendar" = "Veuillez sélectionner au moins un calendrier."; + "Open Task..." = "Ouvrir la tâche..."; "Mark Completed" = "Marquer comme accomplie"; diff --git a/UI/Scheduler/German.lproj/Localizable.strings b/UI/Scheduler/German.lproj/Localizable.strings index 63a9fdce0..bec5f0bed 100644 --- a/UI/Scheduler/German.lproj/Localizable.strings +++ b/UI/Scheduler/German.lproj/Localizable.strings @@ -155,6 +155,7 @@ "What to Print" = "Was soll gedruckt werden"; "Options" = "Optionen"; "Tasks with no due date" = "Aufgaben ohne Fälligkeitsdatum"; +"Display working hours only" = "Nur Arbeitszeit anzeigen"; "Completed tasks" = "Abgeschlossene Aufgaben"; /* Appointments */ @@ -542,6 +543,8 @@ vtodo_class2 = "(Vertrauliche Aufgabe)"; "tagWasRemoved" = "Wenn der Kalender aus der Synchronisation entfernt wird, müssen die Daten erneut auf das Mobilgerät geladen werden.\nFortfahren?"; "DestinationCalendarError" = "Quell- und Zielkalender sind identisch. Bitte kopieren Sie in einen anderen Kalender."; "EventCopyError" = "Kopieren fehlgeschlagen. Bitte kopieren Sie in einen anderen Kalender."; +"Please select at least one calendar" = "Bitte mindestens einen Kalender auswählen"; + "Open Task..." = "Aufgabe öffnen..."; "Mark Completed" = "Als abgeschlossen markieren"; diff --git a/UI/Scheduler/Hungarian.lproj/Localizable.strings b/UI/Scheduler/Hungarian.lproj/Localizable.strings index d5923b66b..8cd3c6762 100644 --- a/UI/Scheduler/Hungarian.lproj/Localizable.strings +++ b/UI/Scheduler/Hungarian.lproj/Localizable.strings @@ -155,6 +155,7 @@ "What to Print" = "Mi kerüljön nyomtatásra"; "Options" = "Beállítások"; "Tasks with no due date" = "Befejező dátum nélküli feladatok"; +"Display working hours only" = "Csak a munkaidő órái jelenjen meg"; "Completed tasks" = "Befejezett feladatok"; /* Appointments */ @@ -542,6 +543,8 @@ vtodo_class2 = "(Bizalmas feladat)"; "tagWasRemoved" = "Amennyiben nem szinkronizálja a továbbiakban a naptárat, frissíteni kell az adatokat a mobil készülékén."; "DestinationCalendarError" = "A forrás- és cél naptár ugyanaz. Kérem válasszon egy másik naptárat."; "EventCopyError" = "Hiba történt a másoláskor. Kérem válasszon egy másik naptárat."; +"Please select at least one calendar" = "Kérem válasszon legalább egy naptárat"; + "Open Task..." = "Feladat megnyitása..."; "Mark Completed" = "A jelölés befejeződött."; diff --git a/UI/Scheduler/NorwegianBokmal.lproj/Localizable.strings b/UI/Scheduler/NorwegianBokmal.lproj/Localizable.strings index 244420adc..2f75da580 100644 --- a/UI/Scheduler/NorwegianBokmal.lproj/Localizable.strings +++ b/UI/Scheduler/NorwegianBokmal.lproj/Localizable.strings @@ -5,6 +5,7 @@ "Create a new event" = "Opprett en ny hendelse"; "Create a new task" = "Opprett en ny oppgave"; "Edit this event or task" = "Endre hendelse eller oppgave"; +"Print the current calendar view" = "Skriv ut den gjeldende kalendervisningen"; "Delete this event or task" = "Slett denne hendelsen eller oppgaven"; "Go to today" = "Gå til i dag"; "Switch to day view" = "Endre til dagsvisning"; @@ -117,7 +118,7 @@ "Name of the Calendar" = "Navn på kalenderen"; "new" = "Ny"; -"printview" = "Skriv ut"; +"Print view" = "Forhåndsvisning"; "edit" = "Endre"; "delete" = "Slett"; "proposal" = "Foreslå"; @@ -145,6 +146,17 @@ "Hide already accepted and rejected appointments" = "Skjul allerede aksepterte og avviste avtaler"; "Show already accepted and rejected appointments" = "Vis aksepterte og avviste avtaler"; +/* Print view */ + +"LIST" = "Liste"; +"Print Settings" = "Utskrift instillinger"; +"Title:" = "Tittel:"; +"Layout:" = "Visning:"; +"What to Print" = "Hva som skal printes"; +"Options" = "Alternativer"; +"Tasks with no due date" = "Oppgaver uten forfallsdato"; +"Display working hours only" = "Vis kun arbeidstimer"; +"Completed tasks" = "Fullførte oppgaver"; /* Appointments */ @@ -179,6 +191,7 @@ "Reminder:" = "Påminnelse:"; "General:" = "Generell:"; "Reply:" = "Svar:"; +"Created by:" = "Opprettet av:"; "Target:" = "Mål:"; @@ -224,8 +237,13 @@ "view_future" = "Alle fremtidige hendelser"; "view_selectedday" = "Valgt dag"; +"view_not_started" = "Oppgaver som ikke er startet"; +"view_overdue" = "Forfalte oppgaver"; +"view_incomplete" = "Ufullstendige oppgaver"; + "View:" = "Vis:"; -"Title or Description" = "Tittel eller beskrivelse"; +"Title, category or location" = "Tittel, kategori eller sted"; +"Entire content" = "Fullstendig innhold"; "Search" = "Søk"; "Search attendees" = "Søk deltakere"; @@ -375,6 +393,7 @@ "Show Time as Free" = "Vis tid som ledig"; /* email notifications */ +"Send Appointment Notifications" = "Send avtale varslinger"; /* validation errors */ @@ -504,7 +523,7 @@ vtodo_class2 = "(Konfidensiell oppgave)"; "Links to this Calendar" = "Linker til denne kalenderen"; "Authenticated User Access" = "Autentisert brukertilgang"; -"CalDAV URL" = "CalDAV url"; +"CalDAV URL" = "CalDAV URL:"; "WebDAV ICS URL" = "WebDAV ICS URL"; "WebDAV XML URL" = "WebDAV XML URL"; @@ -524,6 +543,8 @@ vtodo_class2 = "(Konfidensiell oppgave)"; "tagWasRemoved" = "Om du fjerner kalenderen fra synkronisering trenger du å laste dataene i din mobiltelefon på nytt.\nFortsette?"; "DestinationCalendarError" = "Kilde- og målkalendere er de samme. Prøv å kopiere til en annen kalender."; "EventCopyError" = "Kopiering feilet. Vennligst prøv å kopiere til en annen kalender."; +"Please select at least one calendar" = "Velg minst én kalender"; + "Open Task..." = "Åpne oppgave..."; "Mark Completed" = "Merk utført"; diff --git a/UI/Scheduler/Polish.lproj/Localizable.strings b/UI/Scheduler/Polish.lproj/Localizable.strings index f4975b374..3c1e6a346 100644 --- a/UI/Scheduler/Polish.lproj/Localizable.strings +++ b/UI/Scheduler/Polish.lproj/Localizable.strings @@ -155,6 +155,7 @@ "What to Print" = "Elementy do wydruku"; "Options" = "Opcje"; "Tasks with no due date" = "Zadania bez terminu"; +"Display working hours only" = "Pokazuj tylko godziny robocze"; "Completed tasks" = "Zadania ukończone"; /* Appointments */ @@ -493,7 +494,7 @@ vtodo_class1 = "(Zadanie prywatne)"; vtodo_class2 = "(Zadanie poufne)"; "closeThisWindowMessage" = "Dziękuję! Możesz teraz zamknąć to okno lub wyświetlić twoje "; -"Multicolumn Day View" = "Wielokolumnowy widok dnia"; +"Multicolumn Day View" = "Widok dnia wielokolumnowy"; "Please select an event or a task." = "Zaznacz wydarzenie lub zadanie."; @@ -542,6 +543,8 @@ vtodo_class2 = "(Zadanie poufne)"; "tagWasRemoved" = "Jeśli usuniesz ten kalendarz z synchronizacji, będziesz musiał(a) przeładować dane na swoim urządzeniu mobilnym.\nKontynuować?"; "DestinationCalendarError" = "Chcesz kopiować do tego samego kalendarza. Musisz wybrać inny kalendarz."; "EventCopyError" = "Kopiowanie nie udało się. Wybierz inny kalendarz."; +"Please select at least one calendar" = "Wybierz co najmniej jeden kalendarz"; + "Open Task..." = "Otwórz zadanie"; "Mark Completed" = "Oznacz jako ukończone"; diff --git a/UI/Scheduler/SpanishSpain.lproj/Localizable.strings b/UI/Scheduler/SpanishSpain.lproj/Localizable.strings index 7756be8d8..6e03471c8 100644 --- a/UI/Scheduler/SpanishSpain.lproj/Localizable.strings +++ b/UI/Scheduler/SpanishSpain.lproj/Localizable.strings @@ -155,6 +155,7 @@ "What to Print" = "Que imprimir"; "Options" = "Parametros"; "Tasks with no due date" = "Tareas sin fecha de vencimiento"; +"Display working hours only" = "Enseñar solo horas de trabajo"; "Completed tasks" = "Tareas Finalizadas"; /* Appointments */ @@ -542,6 +543,8 @@ vtodo_class2 = "(Tarea confidencial)"; "tagWasRemoved" = "Si quita este calendario de la sincronización, necesitará recargar los datos en su teléfono móvil.\n¿Continuar?"; "DestinationCalendarError" = "El calendario origen y el destino son iguales. Por favor, intente copiar a otro calendario."; "EventCopyError" = "La copia falló. Por favor, intente copiar a un calendario distinto."; +"Please select at least one calendar" = "Por favor, selecciona al menos un calendario"; + "Open Task..." = "Abrir tarea..."; "Mark Completed" = "Marcar como completo"; diff --git a/UI/Scheduler/Toolbars/SOGoAppointmentFolders.toolbar b/UI/Scheduler/Toolbars/SOGoAppointmentFolders.toolbar index 14e5fcd5e..58bae28ab 100644 --- a/UI/Scheduler/Toolbars/SOGoAppointmentFolders.toolbar +++ b/UI/Scheduler/Toolbars/SOGoAppointmentFolders.toolbar @@ -22,7 +22,7 @@ image = "day-view.png"; tooltip = "Switch to day view"; }, { link = "#"; - label="Multi-Columns View"; + label="Multicolumn Day View"; onclick = "return onMulticolumnDayOverview();"; image = "day-view-multicolumn.png"; tooltip = "Switch to multi-columns day view"; }, diff --git a/UI/Scheduler/UIxAppointmentActions.m b/UI/Scheduler/UIxAppointmentActions.m index db6d55c91..7c7dc045f 100644 --- a/UI/Scheduler/UIxAppointmentActions.m +++ b/UI/Scheduler/UIxAppointmentActions.m @@ -23,6 +23,9 @@ #import #import #import +#import + +#import #import #import @@ -40,6 +43,7 @@ #import #import #import +#import #import @@ -52,19 +56,24 @@ WOResponse *response; WORequest *rq; SOGoAppointmentObject *co; + SoSecurityManager *sm; iCalEvent *event; NSCalendarDate *start, *newStart, *end, *newEnd; NSTimeInterval newDuration; SOGoUserDefaults *ud; - NSString *daysDelta, *startDelta, *durationDelta; + NSString *daysDelta, *startDelta, *durationDelta, *destionationCalendar; NSTimeZone *tz; NSException *ex; + SOGoAppointmentFolder *targetCalendar, *sourceCalendar; + SOGoAppointmentFolders *folders; rq = [context request]; daysDelta = [rq formValueForKey: @"days"]; startDelta = [rq formValueForKey: @"start"]; durationDelta = [rq formValueForKey: @"duration"]; + destionationCalendar = [rq formValueForKey: @"destination"]; + if ([daysDelta length] > 0 || [startDelta length] > 0 || [durationDelta length] > 0) { @@ -105,10 +114,30 @@ } if ([event hasRecurrenceRules]) - [event updateRecurrenceRulesUntilDate: end]; + [event updateRecurrenceRulesUntilDate: end]; [event setLastModified: [NSCalendarDate calendarDate]]; ex = [co saveComponent: event]; + // This condition will be executed only if the event is moved from a calendar to another. If destionationCalendar == 0; there is no calendar change + if (![destionationCalendar isEqualToString:@"0"]) + { + folders = [[self->context activeUser] calendarsFolderInContext: self->context]; + sourceCalendar = [co container]; + targetCalendar = [folders lookupName:[destionationCalendar stringValue] inContext: self->context acquire: 0]; + // The event was moved to a different calendar. + sm = [SoSecurityManager sharedSecurityManager]; + if (![sm validatePermission: SoPerm_DeleteObjects + onObject: sourceCalendar + inContext: context]) + { + if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles + onObject: targetCalendar + inContext: context]) + ex = [co moveToFolder: targetCalendar]; + } + + + } if (ex) { NSDictionary *jsonResponse; diff --git a/UI/Scheduler/UIxCalDayTable.m b/UI/Scheduler/UIxCalDayTable.m index 799d55f57..7cfde1b1f 100644 --- a/UI/Scheduler/UIxCalDayTable.m +++ b/UI/Scheduler/UIxCalDayTable.m @@ -191,7 +191,7 @@ NSMutableDictionary *calendar; unsigned int count, foldersCount; NSString *folderName, *fDisplayName; - NSNumber *isActive; + BOOL *isActive; co = [self clientObject]; folders = [co subFolders]; diff --git a/UI/Scheduler/UIxCalListingActions.m b/UI/Scheduler/UIxCalListingActions.m index 607dc9062..aa8749fb6 100644 --- a/UI/Scheduler/UIxCalListingActions.m +++ b/UI/Scheduler/UIxCalListingActions.m @@ -75,8 +75,6 @@ static NSArray *tasksFields = nil; #define maxBlocks (offsetBlocks * 2) // maximum number of blocks to search // for a free slot (10 days) -@class SOGoAppointment; - @implementation UIxCalListingActions + (void) initialize @@ -101,7 +99,7 @@ static NSArray *tasksFields = nil; @"c_status", @"c_title", @"c_enddate", @"c_classification", @"c_location", @"c_category", @"editable", @"erasable", - @"c_priority", nil]; + @"c_priority", @"c_owner", nil]; [tasksFields retain]; } } diff --git a/UI/Scheduler/UIxCalMainView.m b/UI/Scheduler/UIxCalMainView.m index 58362137f..f9fd125eb 100644 --- a/UI/Scheduler/UIxCalMainView.m +++ b/UI/Scheduler/UIxCalMainView.m @@ -22,6 +22,7 @@ #import #import #import +#import #import #import #import @@ -140,7 +141,7 @@ - (NSString *) tasksFilterLabel { - return [self labelForKey: [self valueForKey:@"taskFilter"]]; + return [self labelForKey: [self valueForKey: @"taskFilter"]]; } - (NSString *) selectedTasksFilter diff --git a/UI/Scheduler/UIxCalViewPrint.h b/UI/Scheduler/UIxCalViewPrint.h index a0977d594..dd3326bec 100644 --- a/UI/Scheduler/UIxCalViewPrint.h +++ b/UI/Scheduler/UIxCalViewPrint.h @@ -19,9 +19,9 @@ * Boston, MA 02111-1307, USA. */ -#include +#include -@interface UIxCalViewPrint : UIxComponent +@interface UIxCalViewPrint : UIxCalendarSelector { id item; SOGoUserSettings *us; diff --git a/UI/Scheduler/UIxCalViewPrint.m b/UI/Scheduler/UIxCalViewPrint.m index d186ef4fa..813e2e2ad 100644 --- a/UI/Scheduler/UIxCalViewPrint.m +++ b/UI/Scheduler/UIxCalViewPrint.m @@ -2,7 +2,6 @@ * * Copyright (C) 2006-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) @@ -38,7 +37,6 @@ static NSArray *layoutItems = nil; layoutItems = [NSArray arrayWithObjects: @"LIST", @"Daily", @"Multi-Columns", @"Weekly", nil]; [layoutItems retain]; } - } - (id) init @@ -53,12 +51,9 @@ static NSArray *layoutItems = nil; [item release]; } -/****************************************************************/ -/* Interfacing; populating the popup list for the print layouts */ - - (void) setItem: (NSString *) newItem { - ASSIGN (item, newItem); + ASSIGN(item, newItem); } - (NSString *) item @@ -76,13 +71,16 @@ static NSArray *layoutItems = nil; return [self labelForKey: [NSString stringWithFormat: item]]; } +// +// The objective here is to return the parent view layout and select the print +// layout corresponding. Default print view: list view +// - (NSString *) parentPrintLayout { - // The objective here is to return the parent view layout and select the print layout corresponding. Default print view: list view SOGoUser *activeUser; NSString *parentView; - activeUser = [context activeUser]; + activeUser = [[self context] activeUser]; us = [activeUser userSettings]; parentView = [[us objectForKey:@"Calendar"] objectForKey:@"View" ]; @@ -99,7 +97,4 @@ static NSArray *layoutItems = nil; return @"LIST"; } -/******************************************************************/ -/* */ - @end diff --git a/UI/Scheduler/UIxCalendarSelector.m b/UI/Scheduler/UIxCalendarSelector.m index 2c8a6910d..c81587116 100644 --- a/UI/Scheduler/UIxCalendarSelector.m +++ b/UI/Scheduler/UIxCalendarSelector.m @@ -121,7 +121,8 @@ _intValueFromHex (NSString *hexString) [calendar setObject: [folder ownerInContext: context] forKey: @"owner"]; fActiveTasks = [folder activeTasks]; - [calendar setObject:fActiveTasks forKey:@"activeTasks" ]; + if (fActiveTasks > 0) + [calendar setObject: fActiveTasks forKey:@"activeTasks" ]; [calendars addObject: calendar]; } } diff --git a/UI/Scheduler/UIxComponentEditor.m b/UI/Scheduler/UIxComponentEditor.m index ce56a9644..4699e929b 100644 --- a/UI/Scheduler/UIxComponentEditor.m +++ b/UI/Scheduler/UIxComponentEditor.m @@ -1712,7 +1712,7 @@ RANGE(2); - (BOOL) isWriteableClientObject { return [[self clientObject] - respondsToSelector: @selector(saveContentString:)]; + respondsToSelector: @selector(saveCompontent:)]; } /* access */ diff --git a/UI/Scheduler/UIxDatePicker.h b/UI/Scheduler/UIxDatePicker.h index dcfd2cf46..cc30eae69 100644 --- a/UI/Scheduler/UIxDatePicker.h +++ b/UI/Scheduler/UIxDatePicker.h @@ -1,8 +1,6 @@ /* UIxDatePicker.h - this file is part of SOGo * - * Copyright (C) 2009 Inverse inc. - * - * Author: Francis Lachapelle + * 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/UI/Scheduler/UIxDatePicker.m b/UI/Scheduler/UIxDatePicker.m index f0618a744..921736814 100644 --- a/UI/Scheduler/UIxDatePicker.m +++ b/UI/Scheduler/UIxDatePicker.m @@ -1,23 +1,22 @@ -/* - Copyright (C) 2004-2005 SKYRIX Software AG - - This file is part of OpenGroupware.org. - - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - OGo 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 Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ +/* UIxDatePicker.h - this file is part of SOGo + * + * 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 + * 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 #import @@ -38,13 +37,18 @@ { if ((self = [super init])) { + dateID = nil; + day = nil; + year = nil; + label = nil; isDisabled = NO; } return self; } -- (void)dealloc { +- (void) dealloc +{ [self->dateID release]; [self->day release]; [self->month release]; @@ -55,60 +59,79 @@ /* Accessors */ -- (void)setDateID:(NSString *)_dateID { +- (void) setDateID: (NSString *) _dateID +{ ASSIGNCOPY(self->dateID, _dateID); } -- (NSString *)dateID { +- (NSString *) dateID +{ return self->dateID; } -- (void)setDay:(id)_day { +- (void) setDay: (id) _day +{ ASSIGN(self->day, _day); } -- (id)day { - return self->day; +- (id) day +{ + return self->day; } -- (void)setMonth:(id)_month { + +- (void) setMonth: (id) _month +{ ASSIGN(self->month, _month); } -- (id)month { - return self->month; +- (id) month +{ + return self->month; } -- (void)setYear:(id)_year { + +- (void) setYear: (id) _year +{ ASSIGN(self->year, _year); } -- (id)year { - return self->year; +- (id) year +{ + return self->year; } - -- (void)setLabel:(NSString *)_label { +- (void) setLabel: (NSString *) _label +{ ASSIGNCOPY(self->label, _label); } -- (NSString *)label { +- (NSString *) label +{ return self->label; } /* formats */ -- (BOOL)useISOFormats { +- (BOOL) useISOFormats +{ + NSNumber *useISOFormats; WOContext *ctx; - NSNumber *useISOFormats; - ctx = [self context]; + ctx = [self context]; useISOFormats = [ctx valueForKey:@"useISOFormats"]; - if (!useISOFormats) { - NSArray *languages = [ctx resourceLookupLanguages]; - if (languages && [languages count] > 0) { - if ([[languages objectAtIndex:0] isEqualToString:@"French"]) { - useISOFormats = [NSNumber numberWithBool:NO]; + if (!useISOFormats) + { + NSArray *languages; + + languages = [ctx resourceLookupLanguages]; + if (languages && [languages count] > 0) + { + if ([[languages objectAtIndex:0] isEqualToString:@"French"]) + { + useISOFormats = [NSNumber numberWithBool:NO]; + } } - } if (!useISOFormats) - useISOFormats = [NSNumber numberWithBool:YES]; - [ctx takeValue:useISOFormats forKey:@"useISOFormats"]; - } + useISOFormats = [NSNumber numberWithBool: YES]; + + [ctx takeValue: useISOFormats forKey:@"useISOFormats"]; + } + return [useISOFormats boolValue]; } @@ -116,17 +139,19 @@ { char buf[22]; - if ([self useISOFormats]) { - sprintf(buf, "%04d-%02d-%02d", - [[self year] intValue], - [[self month] intValue], - [[self day] intValue]); - } - else { - sprintf(buf, "%02d/%02d/%04d", - [[self day] intValue], - [[self month] intValue], - [[self year] intValue]); + if ([self useISOFormats]) + { + sprintf(buf, "%04d-%02d-%02d", + [[self year] intValue], + [[self month] intValue], + [[self day] intValue]); + } + else + { + sprintf(buf, "%02d/%02d/%04d", + [[self day] intValue], + [[self month] intValue], + [[self year] intValue]); } return [NSString stringWithCString:buf]; } @@ -141,29 +166,30 @@ return [self useISOFormats] ? @"yyyy-mm-dd" : @"dd/mm/yyyy"; } -/* action */ - - (void) takeValuesFromRequest: (WORequest *) _rq - inContext: (WOContext *)_ctx + inContext: (WOContext *) _ctx { - NSString *dateString; - NSCalendarDate *d; NSInteger dateTZOffset, userTZOffset; NSTimeZone *systemTZ, *userTZ; SOGoUserDefaults *ud; + NSString *dateString; + NSCalendarDate *d; dateString = [_rq formValueForKey:[self dateID]]; - if (dateString == nil) { - [self debugWithFormat:@"got no date string!"]; - return; - } + + if (dateString == nil) + { + [self debugWithFormat:@"got no date string!"]; + return; + } - d = [NSCalendarDate dateWithString:dateString - calendarFormat:[self dateFormat]]; - if (d == nil) { - [self warnWithFormat:@"Could not parse dateString: '%@'", + d = [NSCalendarDate dateWithString: dateString + calendarFormat: [self dateFormat]]; + if (!d) + { + [self warnWithFormat: @"Could not parse dateString: '%@'", dateString]; - } + } /* we must adjust the date timezone because "dateWithString:..." uses the system timezone, which can be different from the user's. */ @@ -178,11 +204,11 @@ seconds: (dateTZOffset - userTZOffset)]; [d setTimeZone: userTZ]; - [self setDay: [NSNumber numberWithInt:[d dayOfMonth]]]; - [self setMonth:[NSNumber numberWithInt:[d monthOfYear]]]; + [self setDay: [NSNumber numberWithInt:[d dayOfMonth]]]; + [self setMonth: [NSNumber numberWithInt:[d monthOfYear]]]; [self setYear: [NSNumber numberWithInt:[d yearOfCommonEra]]]; - [super takeValuesFromRequest:_rq inContext:_ctx]; + [super takeValuesFromRequest: _rq inContext: _ctx]; } - (void) setDisabled: (BOOL) disabled diff --git a/UI/Scheduler/UIxTaskEditor.m b/UI/Scheduler/UIxTaskEditor.m index 36cfa036a..597e7b83e 100644 --- a/UI/Scheduler/UIxTaskEditor.m +++ b/UI/Scheduler/UIxTaskEditor.m @@ -1,8 +1,6 @@ /* UIxTaskEditor.m - this file is part of SOGo * - * Copyright (C) 2007-2009 Inverse inc. - * - * Author: Wolfgang Sourdeau + * Copyright (C) 2007-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 @@ -421,18 +419,6 @@ return [self jsCloseWithRefreshMethod: @"refreshTasks()"]; } -// - (id ) saveAction -// { -// SOGoTaskObject *clientObject; -// NSString *iCalString; - -// clientObject = [self clientObject]; -// iCalString = [[clientObject calendar: NO secure: NO] versitString]; -// [clientObject saveContentString: iCalString]; - -// return [self jsCloseWithRefreshMethod: @"refreshTasks()"]; -// } - - (id ) viewAction { WOResponse *result; diff --git a/UI/Templates/ContactsUI/UIxContactEditor.wox b/UI/Templates/ContactsUI/UIxContactEditor.wox index 75bbae736..893b94ddd 100644 --- a/UI/Templates/ContactsUI/UIxContactEditor.wox +++ b/UI/Templates/ContactsUI/UIxContactEditor.wox @@ -8,7 +8,7 @@ xmlns:label="OGo:label" className="UIxPageFrame" title="name" - const:userDefaultsKeys="SOGoContactsCategories" + const:userDefaultsKeys="SOGoRefreshViewCheck, SOGoContactsCategories" const:popup="YES" const:cssFiles="datepicker.css" const:jsFiles="datepicker.js"> diff --git a/UI/Templates/ContactsUI/UIxContactFoldersView.wox b/UI/Templates/ContactsUI/UIxContactFoldersView.wox index 9ee97a9ee..2f8a4c415 100644 --- a/UI/Templates/ContactsUI/UIxContactFoldersView.wox +++ b/UI/Templates/ContactsUI/UIxContactFoldersView.wox @@ -110,7 +110,7 @@ var:class="currentContactFolderClass" var:acl-editing="currentContactFolderAclEditing" var:list-editing="currentContactFolderListEditing" - > diff --git a/UI/Templates/MailerUI/UIxMailMainFrame.wox b/UI/Templates/MailerUI/UIxMailMainFrame.wox index 6e83548a6..aa924a35d 100644 --- a/UI/Templates/MailerUI/UIxMailMainFrame.wox +++ b/UI/Templates/MailerUI/UIxMailMainFrame.wox @@ -7,11 +7,13 @@ xmlns:label="OGo:label" className="UIxPageFrame" title="title" - const:userDefaultsKeys="SOGoMailMessageCheck,SOGoMailSortByThreads,SOGoMailListViewColumnsOrder,SOGoMailDisplayRemoteInlineImages" + const:userDefaultsKeys="SOGoMailMessageCheck,SOGoRefreshViewCheck,SOGoMailSortByThreads,SOGoMailListViewColumnsOrder,SOGoMailDisplayRemoteInlineImages" const:userSettingsKeys="Mail" - const:jsFiles="dtree.js,MailerUIdTree.js,SOGoAutoCompletion.js,SOGoResizableTable.js,SOGoMailDataSource.js,SOGoDataTable.js,jquery-ui.js"> + const:jsFiles="dtree.js,MailerUIdTree.js,SOGoAutoCompletion.js,SOGoResizableTable.js,SOGoMailDataSource.js,SOGoDataTable.js,jquery-ui.js, UIxMailSearch.js" + const:cssFiles="UIxMailSearch.css"> diff --git a/UI/Templates/MailerUI/UIxMailSearch.wox b/UI/Templates/MailerUI/UIxMailSearch.wox new file mode 100644 index 000000000..f26233de5 --- /dev/null +++ b/UI/Templates/MailerUI/UIxMailSearch.wox @@ -0,0 +1,89 @@ + +
+ + + + + + + + + + + + + + + + + +
+ + + + + + + +
+
+ +
+
+
+ + + + +
+
+
+
+
+
+ + + + + + + + + + + + + + + + +
+
+
+ + + + +
+
+
+
diff --git a/UI/Templates/PreferencesUI/UIxPreferences.wox b/UI/Templates/PreferencesUI/UIxPreferences.wox index 9cc6675a2..7184e9417 100644 --- a/UI/Templates/PreferencesUI/UIxPreferences.wox +++ b/UI/Templates/PreferencesUI/UIxPreferences.wox @@ -11,12 +11,15 @@ title="title" const:popup="YES" const:cssFiles="datepicker.css" - const:jsFiles="RowEditionController.js,PasswordPolicy.js,ckeditor/ckeditor.js,datepicker.js" + const:jsFiles="RowEditionController.js,PasswordPolicy.js,ckeditor/ckeditor.js,datepicker.js, SOGoAutoCompletion.js" > - +
+
    +
    + - +
    • -
    • - + +
    • -
    • + label:value="Calendar Options"/> +
    • + label:value="Mail Options"/>
    • + label:value="IMAP Accounts"/>
    • -
      + label:value="Vacation"/>
    • +
    • -
    • -
      + +
    -
    +
    + const:id="language" + const:name="language" + string="languageText" + selection="language" + label:noSelectionString="choose" />
    + const:id="timezone" + const:name="timezone" + string="item" selection="userTimeZone" />
    + const:id="shortDateFormat" + const:name="shortDateFormat" + string="itemShortDateFormatText" selection="userShortDateFormat"/>
    + const:id="longDateFormat" + const:name="longDateFormat" + string="itemLongDateFormatText" selection="userLongDateFormat" + />
    -
    + const:id="timeFormat" + const:name="timeFormat" + string="itemTimeFormatText" selection="userTimeFormat" + /> +
    + const:id="defaultModule" + const:name="defaultModule" + string="itemModuleText" selection="userDefaultModule"/> +
    +
    @@ -167,88 +175,132 @@
    + const:id="weekStartDay" + const:name="weekStartDay" + string="itemWeekStartDay" selection="userWeekStartDay"/>
    + const:id="dayStartTime" + const:name="dayStartTime" + string="item" selection="userDayStartTime"/>
    + const:id="dayEndTime" + const:name="dayEndTime" + string="item" selection="userDayEndTime"/>
    -
    +
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    - - -
    - - - - - - - -
    -
    - - -
    +
    +
      +
    • +
    • +
    • +
    • +
    +
    +
    +
    + + + + + + + + + + + + + + + +
    + +
    + +
    +
    +
    + + + + +
    + +
    + +
    +
    +
    +
    + +
    + + + + + + + + + + + + + +
    +
    +
    +
    +
    + + + + +
    + +
    +
    +
    +
    @@ -258,14 +310,14 @@ + > + > @@ -278,135 +330,130 @@
    - +
    -
    -
    -
    -
    -
    -
    -
    + const:name="subscribedFoldersOnly" + const:id="subscribedFoldersOnly" + var:checked="showSubscribedFoldersOnly" /> + +
    +
    +
    +
    +
    +
    -
    -
    + const:id="messageForwarding" + const:name="messageForwarding" + string="itemMessageForwardingText" + selection="userMessageForwarding"/>
    + const:id="replyPlacementList" + const:name="replyPlacementList" + string="itemReplyPlacementText" + selection="userReplyPlacement"/>
    + const:id="signaturePlacementList" + const:name="signaturePlacementList" + string="itemSignaturePlacementText" + selection="userSignaturePlacement"/>
    + const:id="composeMessagesType" + const:name="composeMessagesType" + string="itemComposeMessagesText" + selection="userComposeMessagesType"/>
    + const:id="displayRemoteInlineImages" + const:name="displayRemoteInlineImages" + string="itemDisplayRemoteInlineImagesText" + selection="userDisplayRemoteInlineImages"/>
    - +
    • + >
    • + label:value="Labels"/>
    - -
    - - - - + + +
    + +
    + + + + - - - - -
    - -
    - -
    - +
    + +
    + +
    +
    + > + > - + - - + +
    -
    -
    +
    +
    @@ -414,33 +461,33 @@ + const:name="mailLabelsValue" var:value="mailLabelsValue"/>
    - +
    + var:value="mailAccounts"/>
    + >
    @@ -448,15 +495,15 @@
    - -
    + +
    - - - - -
    + + + + +
    @@ -464,11 +511,11 @@
    - + - +
    @@ -481,40 +528,40 @@
    - +

    + label:value="When I receive a request for a return receipt:" + />

    + label:value="Never send a return receipt"/>

    + label:value="Allow return receipts for some messages"/>
    + label:value="If I'm not in the To or Cc of the message:"/>
    - + + label:value="If the sender is outside my domain:"/>
    - + + label:value="In all other cases:"/> +
    ",CKEDITOR.document); -a.insertAfter(c);c.hide();c.$.form&&b._attachToForm()}else b.setData(a.getHtml(),null,true);b.on("loaded",function(){b.fire("uiReady");b.editable(a);b.container=a;b.setData(b.getData(1));b.resetDirty();b.fire("contentDom");b.mode="wysiwyg";b.fire("mode");b.status="ready";b.fireOnce("instanceReady");CKEDITOR.fire("instanceReady",null,b)},null,null,1E4);b.on("destroy",function(){if(c){b.container.clearCustomData();b.container.remove();c.show()}b.element.clearCustomData();delete b.element});return b}; -CKEDITOR.inlineAll=function(){var a,e,b;for(b in CKEDITOR.dtd.$editable)for(var c=CKEDITOR.document.getElementsByTag(b),d=0,f=c.count();d{voiceLabel}<{outerEl} class="cke_inner cke_reset" role="presentation">{topHtml}<{outerEl} id="{contentId}" class="cke_contents cke_reset" role="presentation">{bottomHtml}')); -b=CKEDITOR.dom.element.createFromHtml(c.output({id:a.id,name:b,langDir:a.lang.dir,langCode:a.langCode,voiceLabel:[a.lang.editor,a.name].join(", "),topHtml:i?''+i+"":"",contentId:a.ui.spaceId("contents"),bottomHtml:k?''+k+"":"",outerEl:CKEDITOR.env.ie?"span":"div"}));if(n==CKEDITOR.ELEMENT_MODE_REPLACE){e.hide(); -b.insertAfter(e)}else e.append(b);a.container=b;i&&a.ui.space("top").unselectable();k&&a.ui.space("bottom").unselectable();e=a.config.width;n=a.config.height;e&&b.setStyle("width",CKEDITOR.tools.cssLength(e));n&&a.ui.space("contents").setStyle("height",CKEDITOR.tools.cssLength(n));b.disableContextMenu();CKEDITOR.env.webkit&&b.on("focus",function(){a.focus()});a.fireOnce("uiReady")}CKEDITOR.replace=function(b,c){return a(b,c,null,CKEDITOR.ELEMENT_MODE_REPLACE)};CKEDITOR.appendTo=function(b,c,e){return a(b, -c,e,CKEDITOR.ELEMENT_MODE_APPENDTO)};CKEDITOR.replaceAll=function(){for(var a=document.getElementsByTagName("textarea"),b=0;b",m="",a=f+a.replace(e,function(){return m+f})+m}a=a.replace(/\n/g,"
    ");b||(a=a.replace(RegExp("
    (?=)"),function(a){return d.repeat(a,2)}));a=a.replace(/^ | $/g," ");a=a.replace(/(>|\s) /g,function(a,b){return b+" "}).replace(/ (?=<)/g," ");r(this,"text",a)},insertElement:function(a,b){b?this.insertElementIntoRange(a,b):this.insertElementIntoSelection(a)},insertElementIntoRange:function(a, -b){var c=this.editor,d=c.config.enterMode,e=a.getName(),f=CKEDITOR.dtd.$block[e];if(b.checkReadOnly())return false;b.deleteContents(1);b.startContainer.type==CKEDITOR.NODE_ELEMENT&&b.startContainer.is({tr:1,table:1,tbody:1,thead:1,tfoot:1})&&v(b);var m,h;if(f)for(;(m=b.getCommonAncestor(0,1))&&(h=CKEDITOR.dtd[m.getName()])&&(!h||!h[e]);)if(m.getName()in CKEDITOR.dtd.span)b.splitElement(m);else if(b.checkStartOfBlock()&&b.checkEndOfBlock()){b.setStartBefore(m);b.collapse(true);m.remove()}else b.splitBlock(d== -CKEDITOR.ENTER_DIV?"div":"p",c.editable());b.insertNode(a);return true},insertElementIntoSelection:function(a){h(this);var b=this.editor,d=b.activeEnterMode,b=b.getSelection(),e=b.getRanges()[0],f=a.getName(),f=CKEDITOR.dtd.$block[f];if(this.insertElementIntoRange(a,e)){e.moveToPosition(a,CKEDITOR.POSITION_AFTER_END);if(f)if((f=a.getNext(function(a){return c(a)&&!i(a)}))&&f.type==CKEDITOR.NODE_ELEMENT&&f.is(CKEDITOR.dtd.$block))f.getDtd()["#"]?e.moveToElementEditStart(f):e.moveToElementEditEnd(a); -else if(!f&&d!=CKEDITOR.ENTER_BR){f=e.fixBlock(true,d==CKEDITOR.ENTER_DIV?"div":"p");e.moveToElementEditStart(f)}}b.selectRanges([e]);n(this)},setData:function(a,b){b||(a=this.editor.dataProcessor.toHtml(a));this.setHtml(a);if(this.status=="unloaded")this.status="ready";this.editor.fire("dataReady")},getData:function(a){var b=this.getHtml();a||(b=this.editor.dataProcessor.toDataFormat(b));return b},setReadOnly:function(a){this.setAttribute("contenteditable",!a)},detach:function(){this.removeClass("cke_editable"); -this.status="detached";var a=this.editor;this._.detach();delete a.document;delete a.window},isInline:function(){return this.getDocument().equals(CKEDITOR.document)},setup:function(){var a=this.editor;this.attachListener(a,"beforeGetData",function(){var b=this.getData();this.is("textarea")||a.config.ignoreEmptyParagraph!==false&&(b=b.replace(k,function(a,b){return b}));a.setData(b,null,1)},this);this.attachListener(a,"getSnapshot",function(a){a.data=this.getData(1)},this);this.attachListener(a,"afterSetData", -function(){this.setData(a.getData(1))},this);this.attachListener(a,"loadSnapshot",function(a){this.setData(a.data,1)},this);this.attachListener(a,"beforeFocus",function(){var b=a.getSelection();(b=b&&b.getNative())&&b.type=="Control"||this.focus()},this);this.attachListener(a,"insertHtml",function(a){this.insertHtml(a.data.dataValue,a.data.mode)},this);this.attachListener(a,"insertElement",function(a){this.insertElement(a.data)},this);this.attachListener(a,"insertText",function(a){this.insertText(a.data)}, -this);this.setReadOnly(a.readOnly);this.attachClass("cke_editable");this.attachClass(a.elementMode==CKEDITOR.ELEMENT_MODE_INLINE?"cke_editable_inline":a.elementMode==CKEDITOR.ELEMENT_MODE_REPLACE||a.elementMode==CKEDITOR.ELEMENT_MODE_APPENDTO?"cke_editable_themed":"");this.attachClass("cke_contents_"+a.config.contentsLangDirection);a.keystrokeHandler.blockedKeystrokes[8]=+a.readOnly;a.keystrokeHandler.attach(this);this.on("blur",function(){this.hasFocus=false},null,null,-1);this.on("focus",function(){this.hasFocus= -true},null,null,-1);a.focusManager.add(this);if(this.equals(CKEDITOR.document.getActive())){this.hasFocus=true;a.once("contentDom",function(){a.focusManager.focus()})}this.isInline()&&this.changeAttr("tabindex",a.tabIndex);if(!this.is("textarea")){a.document=this.getDocument();a.window=this.getWindow();var d=a.document;this.changeAttr("spellcheck",!a.config.disableNativeSpellChecker);var e=a.config.contentsLangDirection;this.getDirection(1)!=e&&this.changeAttr("dir",e);var h=CKEDITOR.getCss();if(h){e= -d.getHead();if(!e.getCustomData("stylesheet")){h=d.appendStyleText(h);h=new CKEDITOR.dom.element(h.ownerNode||h.owningElement);e.setCustomData("stylesheet",h);h.data("cke-temp",1)}}e=d.getCustomData("stylesheet_ref")||0;d.setCustomData("stylesheet_ref",e+1);this.setCustomData("cke_includeReadonly",!a.config.disableReadonlyStyling);this.attachListener(this,"click",function(a){var a=a.data,b=(new CKEDITOR.dom.elementPath(a.getTarget(),this)).contains("a");b&&(a.$.button!=2&&b.isReadOnly())&&a.preventDefault()}); -var l={8:1,46:1};this.attachListener(a,"key",function(b){if(a.readOnly)return true;var c=b.data.domEvent.getKey(),d;if(c in l){var b=a.getSelection(),e,q=b.getRanges()[0],h=q.startPath(),i,k,p,c=c==8;if(CKEDITOR.env.ie&&CKEDITOR.env.version<11&&(e=b.getSelectedElement())||(e=f(b))){a.fire("saveSnapshot");q.moveToPosition(e,CKEDITOR.POSITION_BEFORE_START);e.remove();q.select();a.fire("saveSnapshot");d=1}else if(q.collapsed)if((i=h.block)&&(p=i[c?"getPrevious":"getNext"](o))&&p.type==CKEDITOR.NODE_ELEMENT&& -p.is("table")&&q[c?"checkStartOfBlock":"checkEndOfBlock"]()){a.fire("saveSnapshot");q[c?"checkEndOfBlock":"checkStartOfBlock"]()&&i.remove();q["moveToElementEdit"+(c?"End":"Start")](p);q.select();a.fire("saveSnapshot");d=1}else if(h.blockLimit&&h.blockLimit.is("td")&&(k=h.blockLimit.getAscendant("table"))&&q.checkBoundaryOfElement(k,c?CKEDITOR.START:CKEDITOR.END)&&(p=k[c?"getPrevious":"getNext"](o))){a.fire("saveSnapshot");q["moveToElementEdit"+(c?"End":"Start")](p);q.checkStartOfBlock()&&q.checkEndOfBlock()? -p.remove():q.select();a.fire("saveSnapshot");d=1}else if((k=h.contains(["td","th","caption"]))&&q.checkBoundaryOfElement(k,c?CKEDITOR.START:CKEDITOR.END))d=1}return!d});a.blockless&&(CKEDITOR.env.ie&&CKEDITOR.env.needsBrFiller)&&this.attachListener(this,"keyup",function(b){if(b.data.getKeystroke()in l&&!this.getFirst(c)){this.appendBogus();b=a.createRange();b.moveToPosition(this,CKEDITOR.POSITION_AFTER_START);b.select()}});this.attachListener(this,"dblclick",function(b){if(a.readOnly)return false; -b={element:b.data.getTarget()};a.fire("doubleclick",b)});CKEDITOR.env.ie&&this.attachListener(this,"click",b);CKEDITOR.env.ie||this.attachListener(this,"mousedown",function(b){var c=b.data.getTarget();if(c.is("img","hr","input","textarea","select")&&!c.isReadOnly()){a.getSelection().selectElement(c);c.is("input","textarea","select")&&b.data.preventDefault()}});CKEDITOR.env.gecko&&this.attachListener(this,"mouseup",function(b){if(b.data.$.button==2){b=b.data.getTarget();if(!b.getOuterHtml().replace(k, -"")){var c=a.createRange();c.moveToElementEditStart(b);c.select(true)}}});if(CKEDITOR.env.webkit){this.attachListener(this,"click",function(a){a.data.getTarget().is("input","select")&&a.data.preventDefault()});this.attachListener(this,"mouseup",function(a){a.data.getTarget().is("input","textarea")&&a.data.preventDefault()})}CKEDITOR.env.webkit&&this.attachListener(a,"key",function(b){b=b.data.domEvent.getKey();if(b in l){var b=b==8,c=a.getSelection(),d=c.getRanges()[0],e=d.startPath(),f=e.block;if(d.collapsed&& -f&&d[b?"checkStartOfBlock":"checkEndOfBlock"]()&&d.moveToClosestEditablePosition(f,!b)&&d.collapsed){if(d.startContainer.type==CKEDITOR.NODE_ELEMENT){var h=d.startContainer.getChild(d.startOffset-(b?1:0));if(h&&h.type==CKEDITOR.NODE_ELEMENT&&h.is("hr")){a.fire("saveSnapshot");h.remove();a.fire("saveSnapshot");return false}}if((d=d.startPath().block)&&(!d||!d.contains(f))){a.fire("saveSnapshot");for(var i=f.getCommonAncestor(d),k=b?f:d,h=k;(k=k.getParent())&&!i.equals(k)&&k.getChildCount()==1;)h=k; -var p;(p=(b?d:f).getBogus())&&p.remove();p=c.createBookmarks();(b?f:d).moveChildren(b?d:f,false);e.lastElement.mergeSiblings();h.remove();c.selectBookmarks(p);a.fire("saveSnapshot");return false}}}},this,null,100)}}},_:{detach:function(){this.editor.setData(this.editor.getData(),0,1);this.clearListeners();this.restoreAttrs();var a;if(a=this.removeCustomData("classes"))for(;a.length;)this.removeClass(a.pop());if(!this.is("textarea")){a=this.getDocument();var b=a.getHead();if(b.getCustomData("stylesheet")){var c= -a.getCustomData("stylesheet_ref");if(--c)a.setCustomData("stylesheet_ref",c);else{a.removeCustomData("stylesheet_ref");b.removeCustomData("stylesheet").remove()}}}this.editor.fire("contentDomUnload");delete this.editor}}});CKEDITOR.editor.prototype.editable=function(a){var b=this._.editable;if(b&&a)return 0;if(arguments.length)b=this._.editable=a?a instanceof CKEDITOR.editable?a:new CKEDITOR.editable(this,a):(b&&b.detach(),null);return b};var i=CKEDITOR.dom.walker.bogus(),k=/(^|]*>)\s*<(p|div|address|h\d|center|pre)[^>]*>\s*(?:]*>| |\u00A0| )?\s*(:?<\/\2>)?\s*(?=$|<\/body>)/gi, -o=CKEDITOR.dom.walker.whitespaces(true),s=CKEDITOR.dom.walker.bookmark(false,true);CKEDITOR.on("instanceLoaded",function(b){var c=b.editor;c.on("insertElement",function(a){a=a.data;if(a.type==CKEDITOR.NODE_ELEMENT&&(a.is("input")||a.is("textarea"))){a.getAttribute("contentEditable")!="false"&&a.data("cke-editable",a.hasAttribute("contenteditable")?"true":"1");a.setAttribute("contentEditable",false)}});c.on("selectionChange",function(b){if(!c.readOnly){var d=c.getSelection();if(d&&!d.isLocked){d=c.checkDirty(); -c.fire("lockSnapshot");a(b);c.fire("unlockSnapshot");!d&&c.resetDirty()}}})});CKEDITOR.on("instanceCreated",function(a){var b=a.editor;b.on("mode",function(){var a=b.editable();if(a&&a.isInline()){var c=b.title;a.changeAttr("role","textbox");a.changeAttr("aria-label",c);c&&a.changeAttr("title",c);if(c=this.ui.space(this.elementMode==CKEDITOR.ELEMENT_MODE_INLINE?"top":"contents")){var d=CKEDITOR.tools.getNextId(),e=CKEDITOR.dom.element.createFromHtml(''+this.lang.common.editorHelp+ -"");c.append(e);a.changeAttr("aria-describedby",d)}}})});CKEDITOR.addCss(".cke_editable{cursor:text}.cke_editable img,.cke_editable input,.cke_editable textarea{cursor:default}");var r=function(){function a(b){return b.type==CKEDITOR.NODE_ELEMENT}function b(c,d){var e,f,j,l,t=[],q=d.range.startContainer;e=d.range.startPath();for(var q=m[q.getName()],h=0,k=c.getChildren(),i=k.count(),u=-1,o=-1,n=0,s=e.contains(m.$list);h-1)t[u].firstNotAllowed=1;if(o>-1)t[o].lastNotAllowed=1;return t}function d(b,c){var e=[],f=b.getChildren(),j=f.count(),l,t=0,q=m[c],h=!b.is(m.$inline)|| -b.is("br");for(h&&e.push(" ");t ",o.document);o.insertNode(x); -o.setStartAfter(x)}y=new CKEDITOR.dom.elementPath(o.startContainer);i.endPath=B=new CKEDITOR.dom.elementPath(o.endContainer);if(!o.collapsed){var t=B.block||B.blockLimit,N=o.getCommonAncestor();t&&(!t.equals(N)&&!t.contains(N)&&o.checkEndOfBlock())&&i.zombies.push(t);o.deleteContents()}for(;(w=a(o.startContainer)&&o.startContainer.getChild(o.startOffset-1))&&a(w)&&w.isBlockBoundary()&&y.contains(w);)o.moveToPosition(w,CKEDITOR.POSITION_BEFORE_END);f(o,i.blockLimit,y,B);if(x){o.setEndBefore(x);o.collapse(); -x.remove()}x=o.startPath();if(t=x.contains(e,false,1)){o.splitElement(t);i.inlineStylesRoot=t;i.inlineStylesPeak=x.lastElement}x=o.createBookmark();(t=x.startNode.getPrevious(c))&&a(t)&&e(t)&&r.push(t);(t=x.startNode.getNext(c))&&a(t)&&e(t)&&r.push(t);for(t=x.startNode;(t=t.getParent())&&e(t);)r.push(t);o.moveToBookmark(x);if(x=u){x=i.range;if(i.type=="text"&&i.inlineStylesRoot){w=i.inlineStylesPeak;o=w.getDocument().createText("{cke-peak}");for(r=i.inlineStylesRoot.getParent();!w.equals(r);){o=o.appendTo(w.clone()); -w=w.getParent()}u=o.getOuterHtml().split("{cke-peak}").join(u)}w=i.blockLimit.getName();if(/^\s+|\s+$/.test(u)&&"span"in CKEDITOR.dtd[w])var M=' ',u=M+u+M;u=i.editor.dataProcessor.toHtml(u,{context:null,fixForBody:false,dontFilter:i.dontFilter,filter:i.editor.activeFilter,enterMode:i.editor.activeEnterMode});w=x.document.createElement("body");w.setHtml(u);if(M){w.getFirst().remove();w.getLast().remove()}if((M=x.startPath().block)&&!(M.getChildCount()==1&&M.getBogus()))a:{var D; -if(w.getChildCount()==1&&a(D=w.getFirst())&&D.is(k)){M=D.getElementsByTag("*");x=0;for(r=M.count();x0;else{v=D.startPath();if(!B.isBlock&&i.editor.config.autoParagraph!==false&&(i.editor.activeEnterMode!=CKEDITOR.ENTER_BR&&i.editor.editable().equals(v.blockLimit)&& -!v.block)&&(Q=i.editor.activeEnterMode!=CKEDITOR.ENTER_BR&&i.editor.config.autoParagraph!==false?i.editor.activeEnterMode==CKEDITOR.ENTER_DIV?"div":"p":false)){Q=M.createElement(Q);Q.appendBogus();D.insertNode(Q);CKEDITOR.env.needsBrFiller&&(I=Q.getBogus())&&I.remove();D.moveToPosition(Q,CKEDITOR.POSITION_BEFORE_END)}if((v=D.startPath().block)&&!v.equals(E)){if(I=v.getBogus()){I.remove();w.push(v)}E=v}B.firstNotAllowed&&(o=1);if(o&&B.isElement){v=D.startContainer;for(K=null;v&&!m[v.getName()][B.name];){if(v.equals(u)){v= -null;break}K=v;v=v.getParent()}if(v){if(K){S=D.splitElement(K);i.zombies.push(S);i.zombies.push(K)}}else{K=u.getName();T=!x;v=x==y.length-1;K=d(B.node,K);for(var O=[],W=K.length,X=0,Z=void 0,$=0,aa=-1;X0;){d=a.getItem(b);if(!CKEDITOR.tools.trim(d.getHtml())){d.appendBogus();CKEDITOR.env.ie&&(CKEDITOR.env.version<9&&d.getChildCount())&&d.getFirst().remove()}}}return function(d){var e=d.startContainer,f=e.getAscendant("table",1),m=false;c(f.getElementsByTag("td"));c(f.getElementsByTag("th"));f=d.clone();f.setStart(e,0);f=a(f).lastBackward();if(!f){f=d.clone();f.setEndAt(e,CKEDITOR.POSITION_BEFORE_END);f=a(f).lastForward();m=true}f|| -(f=e);if(f.is("table")){d.setStartAt(f,CKEDITOR.POSITION_BEFORE_START);d.collapse(true);f.remove()}else{f.is({tbody:1,thead:1,tfoot:1})&&(f=b(f,"tr",m));f.is("tr")&&(f=b(f,f.getParent().is("thead")?"th":"td",m));(e=f.getBogus())&&e.remove();d.moveToPosition(f,m?CKEDITOR.POSITION_AFTER_START:CKEDITOR.POSITION_BEFORE_END)}}}()})(); -(function(){function a(){var a=this._.fakeSelection,b;if(a){b=this.getSelection(1);if(!b||!b.isHidden()){a.reset();a=0}}if(!a){a=b||this.getSelection(1);if(!a||a.getType()==CKEDITOR.SELECTION_NONE)return}this.fire("selectionCheck",a);b=this.elementPath();if(!b.compare(this._.selectionPreviousPath)){if(CKEDITOR.env.webkit)this._.previousActive=this.document.getActive();this._.selectionPreviousPath=b;this.fire("selectionChange",{selection:a,path:b})}}function e(){r=true;if(!s){b.call(this);s=CKEDITOR.tools.setTimeout(b, -200,this)}}function b(){s=null;if(r){CKEDITOR.tools.setTimeout(a,0,this);r=false}}function c(a){function b(c,d){return!c||c.type==CKEDITOR.NODE_TEXT?false:a.clone()["moveToElementEdit"+(d?"End":"Start")](c)}if(!(a.root instanceof CKEDITOR.editable))return false;var c=a.startContainer,d=a.getPreviousNode(v,null,c),e=a.getNextNode(v,null,c);return b(d)||b(e,1)||!d&&!e&&!(c.type==CKEDITOR.NODE_ELEMENT&&c.isBlockBoundary()&&c.getBogus())?true:false}function d(a){return a.getCustomData("cke-fillingChar")} -function f(a,b){var c=a&&a.removeCustomData("cke-fillingChar");if(c){if(b!==false){var d,e=a.getDocument().getSelection().getNative(),f=e&&e.type!="None"&&e.getRangeAt(0);if(c.getLength()>1&&f&&f.intersectsNode(c.$)){d=[e.anchorOffset,e.focusOffset];f=e.focusNode==c.$&&e.focusOffset>0;e.anchorNode==c.$&&e.anchorOffset>0&&d[0]--;f&&d[1]--;var g;f=e;if(!f.isCollapsed){g=f.getRangeAt(0);g.setStart(f.anchorNode,f.anchorOffset);g.setEnd(f.focusNode,f.focusOffset);g=g.collapsed}g&&d.unshift(d.pop())}}c.setText(h(c.getText())); -if(d){c=e.getRangeAt(0);c.setStart(c.startContainer,d[0]);c.setEnd(c.startContainer,d[1]);e.removeAllRanges();e.addRange(c)}}}function h(a){return a.replace(/\u200B( )?/g,function(a){return a[1]?" ":""})}function n(a,b,c){var d=a.on("focus",function(a){a.cancel()},null,null,-100);if(CKEDITOR.env.ie)var e=a.getDocument().on("selectionchange",function(a){a.cancel()},null,null,-100);else{var f=new CKEDITOR.dom.range(a);f.moveToElementEditStart(a);var g=a.getDocument().$.createRange();g.setStart(f.startContainer.$, -f.startOffset);g.collapse(1);b.removeAllRanges();b.addRange(g)}c&&a.focus();d.removeListener();e&&e.removeListener()}function i(a){var b=CKEDITOR.dom.element.createFromHtml('
     
    ',a.document);a.fire("lockSnapshot");a.editable().append(b);var c=a.getSelection(1),d=a.createRange(),e=c.root.on("selectionchange",function(a){a.cancel()},null,null,0);d.setStartAt(b,CKEDITOR.POSITION_AFTER_START); -d.setEndAt(b,CKEDITOR.POSITION_BEFORE_END);c.selectRanges([d]);e.removeListener();a.fire("unlockSnapshot");a._.hiddenSelectionContainer=b}function k(a){var b={37:1,39:1,8:1,46:1};return function(c){var d=c.data.getKeystroke();if(b[d]){var e=a.getSelection().getRanges(),f=e[0];if(e.length==1&&f.collapsed)if((d=f[d<38?"getPreviousEditableNode":"getNextEditableNode"]())&&d.type==CKEDITOR.NODE_ELEMENT&&d.getAttribute("contenteditable")=="false"){a.getSelection().fake(d);c.data.preventDefault();c.cancel()}}}} -function o(a){for(var b=0;b=d.getLength()?h.setStartAfter(d):h.setStartBefore(d)); -e&&e.type==CKEDITOR.NODE_TEXT&&(g?h.setEndAfter(e):h.setEndBefore(e));d=new CKEDITOR.dom.walker(h);d.evaluator=function(d){if(d.type==CKEDITOR.NODE_ELEMENT&&d.isReadOnly()){var e=c.clone();c.setEndBefore(d);c.collapsed&&a.splice(b--,1);if(!(d.getPosition(h.endContainer)&CKEDITOR.POSITION_CONTAINS)){e.setStartAfter(d);e.collapsed||a.splice(b+1,0,e)}return true}return false};d.next()}}return a}var s,r,v=CKEDITOR.dom.walker.invisible(1),g=function(){function a(b){return function(a){var c=a.editor.createRange(); +(function(){CKEDITOR.inline=function(a,c){if(!CKEDITOR.env.isCompatible)return null;a=CKEDITOR.dom.element.get(a);if(a.getEditor())throw'The editor instance "'+a.getEditor().name+'" is already attached to the provided element.';var b=new CKEDITOR.editor(c,a,CKEDITOR.ELEMENT_MODE_INLINE),f=a.is("textarea")?a:null;if(f){b.setData(f.getValue(),null,true);a=CKEDITOR.dom.element.createFromHtml('
    '+f.getValue()+"
    ",CKEDITOR.document); +a.insertAfter(f);f.hide();f.$.form&&b._attachToForm()}else b.setData(a.getHtml(),null,true);b.on("loaded",function(){b.fire("uiReady");b.editable(a);b.container=a;b.setData(b.getData(1));b.resetDirty();b.fire("contentDom");b.mode="wysiwyg";b.fire("mode");b.status="ready";b.fireOnce("instanceReady");CKEDITOR.fire("instanceReady",null,b)},null,null,1E4);b.on("destroy",function(){if(f){b.container.clearCustomData();b.container.remove();f.show()}b.element.clearCustomData();delete b.element});return b}; +CKEDITOR.inlineAll=function(){var a,c,b;for(b in CKEDITOR.dtd.$editable)for(var f=CKEDITOR.document.getElementsByTag(b),d=0,e=f.count();d{voiceLabel}<{outerEl} class="cke_inner cke_reset" role="presentation">{topHtml}<{outerEl} id="{contentId}" class="cke_contents cke_reset" role="presentation">{bottomHtml}')); +b=CKEDITOR.dom.element.createFromHtml(f.output({id:a.id,name:b,langDir:a.lang.dir,langCode:a.langCode,voiceLabel:[a.lang.editor,a.name].join(", "),topHtml:h?''+h+"":"",contentId:a.ui.spaceId("contents"),bottomHtml:i?''+i+"":"",outerEl:CKEDITOR.env.ie?"span":"div"}));if(n==CKEDITOR.ELEMENT_MODE_REPLACE){c.hide(); +b.insertAfter(c)}else c.append(b);a.container=b;h&&a.ui.space("top").unselectable();i&&a.ui.space("bottom").unselectable();c=a.config.width;n=a.config.height;c&&b.setStyle("width",CKEDITOR.tools.cssLength(c));n&&a.ui.space("contents").setStyle("height",CKEDITOR.tools.cssLength(n));b.disableContextMenu();CKEDITOR.env.webkit&&b.on("focus",function(){a.focus()});a.fireOnce("uiReady")}CKEDITOR.replace=function(b,c){return a(b,c,null,CKEDITOR.ELEMENT_MODE_REPLACE)};CKEDITOR.appendTo=function(b,c,f){return a(b, +c,f,CKEDITOR.ELEMENT_MODE_APPENDTO)};CKEDITOR.replaceAll=function(){for(var a=document.getElementsByTagName("textarea"),b=0;b",v="",a=f+a.replace(e,function(){return v+f})+v}a=a.replace(/\n/g,"
    ");b||(a=a.replace(RegExp("
    (?=)"),function(a){return d.repeat(a,2)}));a=a.replace(/^ | $/g," ");a=a.replace(/(>|\s) /g,function(a,b){return b+" "}).replace(/ (?=<)/g," ");x(this,"text",a)},insertElement:function(a, +b){b?this.insertElementIntoRange(a,b):this.insertElementIntoSelection(a)},insertElementIntoRange:function(a,b){var c=this.editor,d=c.config.enterMode,e=a.getName(),f=CKEDITOR.dtd.$block[e];if(b.checkReadOnly())return false;b.deleteContents(1);b.startContainer.type==CKEDITOR.NODE_ELEMENT&&b.startContainer.is({tr:1,table:1,tbody:1,thead:1,tfoot:1})&&q(b);var g,h;if(f)for(;(g=b.getCommonAncestor(0,1))&&(h=CKEDITOR.dtd[g.getName()])&&(!h||!h[e]);)if(g.getName()in CKEDITOR.dtd.span)b.splitElement(g);else if(b.checkStartOfBlock()&& +b.checkEndOfBlock()){b.setStartBefore(g);b.collapse(true);g.remove()}else b.splitBlock(d==CKEDITOR.ENTER_DIV?"div":"p",c.editable());b.insertNode(a);return true},insertElementIntoSelection:function(a){g(this);var b=this.editor,c=b.activeEnterMode,b=b.getSelection(),d=b.getRanges()[0],e=a.getName(),e=CKEDITOR.dtd.$block[e];if(this.insertElementIntoRange(a,d)){d.moveToPosition(a,CKEDITOR.POSITION_AFTER_END);if(e)if((e=a.getNext(function(a){return f(a)&&!i(a)}))&&e.type==CKEDITOR.NODE_ELEMENT&&e.is(CKEDITOR.dtd.$block))e.getDtd()["#"]? +d.moveToElementEditStart(e):d.moveToElementEditEnd(a);else if(!e&&c!=CKEDITOR.ENTER_BR){e=d.fixBlock(true,c==CKEDITOR.ENTER_DIV?"div":"p");d.moveToElementEditStart(e)}}b.selectRanges([d]);n(this)},setData:function(a,b){b||(a=this.editor.dataProcessor.toHtml(a));this.setHtml(a);if(this.status=="unloaded")this.status="ready";this.editor.fire("dataReady")},getData:function(a){var b=this.getHtml();a||(b=this.editor.dataProcessor.toDataFormat(b));return b},setReadOnly:function(a){this.setAttribute("contenteditable", +!a)},detach:function(){this.removeClass("cke_editable");this.status="detached";var a=this.editor;this._.detach();delete a.document;delete a.window},isInline:function(){return this.getDocument().equals(CKEDITOR.document)},setup:function(){var a=this.editor;this.attachListener(a,"beforeGetData",function(){var b=this.getData();this.is("textarea")||a.config.ignoreEmptyParagraph!==false&&(b=b.replace(m,function(a,b){return b}));a.setData(b,null,1)},this);this.attachListener(a,"getSnapshot",function(a){a.data= +this.getData(1)},this);this.attachListener(a,"afterSetData",function(){this.setData(a.getData(1))},this);this.attachListener(a,"loadSnapshot",function(a){this.setData(a.data,1)},this);this.attachListener(a,"beforeFocus",function(){var b=a.getSelection();(b=b&&b.getNative())&&b.type=="Control"||this.focus()},this);this.attachListener(a,"insertHtml",function(a){this.insertHtml(a.data.dataValue,a.data.mode)},this);this.attachListener(a,"insertElement",function(a){this.insertElement(a.data)},this);this.attachListener(a, +"insertText",function(a){this.insertText(a.data)},this);this.setReadOnly(a.readOnly);this.attachClass("cke_editable");this.attachClass(a.elementMode==CKEDITOR.ELEMENT_MODE_INLINE?"cke_editable_inline":a.elementMode==CKEDITOR.ELEMENT_MODE_REPLACE||a.elementMode==CKEDITOR.ELEMENT_MODE_APPENDTO?"cke_editable_themed":"");this.attachClass("cke_contents_"+a.config.contentsLangDirection);a.keystrokeHandler.blockedKeystrokes[8]=+a.readOnly;a.keystrokeHandler.attach(this);this.on("blur",function(){this.hasFocus= +false},null,null,-1);this.on("focus",function(){this.hasFocus=true},null,null,-1);a.focusManager.add(this);if(this.equals(CKEDITOR.document.getActive())){this.hasFocus=true;a.once("contentDom",function(){a.focusManager.focus()})}this.isInline()&&this.changeAttr("tabindex",a.tabIndex);if(!this.is("textarea")){a.document=this.getDocument();a.window=this.getWindow();var c=a.document;this.changeAttr("spellcheck",!a.config.disableNativeSpellChecker);var d=a.config.contentsLangDirection;this.getDirection(1)!= +d&&this.changeAttr("dir",d);var k=CKEDITOR.getCss();if(k){d=c.getHead();if(!d.getCustomData("stylesheet")){k=c.appendStyleText(k);k=new CKEDITOR.dom.element(k.ownerNode||k.owningElement);d.setCustomData("stylesheet",k);k.data("cke-temp",1)}}d=c.getCustomData("stylesheet_ref")||0;c.setCustomData("stylesheet_ref",d+1);this.setCustomData("cke_includeReadonly",!a.config.disableReadonlyStyling);this.attachListener(this,"click",function(a){var a=a.data,b=(new CKEDITOR.dom.elementPath(a.getTarget(),this)).contains("a"); +b&&(a.$.button!=2&&b.isReadOnly())&&a.preventDefault()});var l={8:1,46:1};this.attachListener(a,"key",function(b){if(a.readOnly)return true;var c=b.data.domEvent.getKey(),d;if(c in l){var b=a.getSelection(),f,k=b.getRanges()[0],g=k.startPath(),h,i,m,c=c==8;if(CKEDITOR.env.ie&&CKEDITOR.env.version<11&&(f=b.getSelectedElement())||(f=e(b))){a.fire("saveSnapshot");k.moveToPosition(f,CKEDITOR.POSITION_BEFORE_START);f.remove();k.select();a.fire("saveSnapshot");d=1}else if(k.collapsed)if((h=g.block)&&(m= +h[c?"getPrevious":"getNext"](p))&&m.type==CKEDITOR.NODE_ELEMENT&&m.is("table")&&k[c?"checkStartOfBlock":"checkEndOfBlock"]()){a.fire("saveSnapshot");k[c?"checkEndOfBlock":"checkStartOfBlock"]()&&h.remove();k["moveToElementEdit"+(c?"End":"Start")](m);k.select();a.fire("saveSnapshot");d=1}else if(g.blockLimit&&g.blockLimit.is("td")&&(i=g.blockLimit.getAscendant("table"))&&k.checkBoundaryOfElement(i,c?CKEDITOR.START:CKEDITOR.END)&&(m=i[c?"getPrevious":"getNext"](p))){a.fire("saveSnapshot");k["moveToElementEdit"+ +(c?"End":"Start")](m);k.checkStartOfBlock()&&k.checkEndOfBlock()?m.remove():k.select();a.fire("saveSnapshot");d=1}else if((i=g.contains(["td","th","caption"]))&&k.checkBoundaryOfElement(i,c?CKEDITOR.START:CKEDITOR.END))d=1}return!d});a.blockless&&(CKEDITOR.env.ie&&CKEDITOR.env.needsBrFiller)&&this.attachListener(this,"keyup",function(b){if(b.data.getKeystroke()in l&&!this.getFirst(f)){this.appendBogus();b=a.createRange();b.moveToPosition(this,CKEDITOR.POSITION_AFTER_START);b.select()}});this.attachListener(this, +"dblclick",function(b){if(a.readOnly)return false;b={element:b.data.getTarget()};a.fire("doubleclick",b)});CKEDITOR.env.ie&&this.attachListener(this,"click",b);CKEDITOR.env.ie||this.attachListener(this,"mousedown",function(b){var c=b.data.getTarget();if(c.is("img","hr","input","textarea","select")&&!c.isReadOnly()){a.getSelection().selectElement(c);c.is("input","textarea","select")&&b.data.preventDefault()}});CKEDITOR.env.gecko&&this.attachListener(this,"mouseup",function(b){if(b.data.$.button==2){b= +b.data.getTarget();if(!b.getOuterHtml().replace(m,"")){var c=a.createRange();c.moveToElementEditStart(b);c.select(true)}}});if(CKEDITOR.env.webkit){this.attachListener(this,"click",function(a){a.data.getTarget().is("input","select")&&a.data.preventDefault()});this.attachListener(this,"mouseup",function(a){a.data.getTarget().is("input","textarea")&&a.data.preventDefault()})}CKEDITOR.env.webkit&&this.attachListener(a,"key",function(b){b=b.data.domEvent.getKey();if(b in l){var c=b==8,d=a.getSelection().getRanges()[0], +b=d.startPath();if(d.collapsed){var e;a:{var f=b.block;if(f)if(d[c?"checkStartOfBlock":"checkEndOfBlock"]())if(!d.moveToClosestEditablePosition(f,!c)||!d.collapsed)e=false;else{if(d.startContainer.type==CKEDITOR.NODE_ELEMENT){var k=d.startContainer.getChild(d.startOffset-(c?1:0));if(k&&k.type==CKEDITOR.NODE_ELEMENT&&k.is("hr")){a.fire("saveSnapshot");k.remove();e=true;break a}}if((d=d.startPath().block)&&(!d||!d.contains(f))){a.fire("saveSnapshot");var g;(g=(c?d:f).getBogus())&&g.remove();e=a.getSelection(); +g=e.createBookmarks();(c?f:d).moveChildren(c?d:f,false);b.lastElement.mergeSiblings();h(f,d,!c);e.selectBookmarks(g);e=true}}else e=false;else e=false}if(!e)return}else{c=d;e=b.block;g=c.endPath().block;if(!e||!g||e.equals(g))b=false;else{a.fire("saveSnapshot");(f=e.getBogus())&&f.remove();c.deleteContents();if(g.getParent()){g.moveChildren(e,false);b.lastElement.mergeSiblings();h(e,g,true)}c=a.getSelection().getRanges()[0];c.collapse(1);c.select();b=true}if(!b)return}a.getSelection().scrollIntoView(); +a.fire("saveSnapshot");return false}},this,null,100)}}},_:{detach:function(){this.editor.setData(this.editor.getData(),0,1);this.clearListeners();this.restoreAttrs();var a;if(a=this.removeCustomData("classes"))for(;a.length;)this.removeClass(a.pop());if(!this.is("textarea")){a=this.getDocument();var b=a.getHead();if(b.getCustomData("stylesheet")){var c=a.getCustomData("stylesheet_ref");if(--c)a.setCustomData("stylesheet_ref",c);else{a.removeCustomData("stylesheet_ref");b.removeCustomData("stylesheet").remove()}}}this.editor.fire("contentDomUnload"); +delete this.editor}}});CKEDITOR.editor.prototype.editable=function(a){var b=this._.editable;if(b&&a)return 0;if(arguments.length)b=this._.editable=a?a instanceof CKEDITOR.editable?a:new CKEDITOR.editable(this,a):(b&&b.detach(),null);return b};var i=CKEDITOR.dom.walker.bogus(),m=/(^|]*>)\s*<(p|div|address|h\d|center|pre)[^>]*>\s*(?:]*>| |\u00A0| )?\s*(:?<\/\2>)?\s*(?=$|<\/body>)/gi,p=CKEDITOR.dom.walker.whitespaces(true),s=CKEDITOR.dom.walker.bookmark(false,true);CKEDITOR.on("instanceLoaded", +function(b){var c=b.editor;c.on("insertElement",function(a){a=a.data;if(a.type==CKEDITOR.NODE_ELEMENT&&(a.is("input")||a.is("textarea"))){a.getAttribute("contentEditable")!="false"&&a.data("cke-editable",a.hasAttribute("contenteditable")?"true":"1");a.setAttribute("contentEditable",false)}});c.on("selectionChange",function(b){if(!c.readOnly){var d=c.getSelection();if(d&&!d.isLocked){d=c.checkDirty();c.fire("lockSnapshot");a(b);c.fire("unlockSnapshot");!d&&c.resetDirty()}}})});CKEDITOR.on("instanceCreated", +function(a){var b=a.editor;b.on("mode",function(){var a=b.editable();if(a&&a.isInline()){var c=b.title;a.changeAttr("role","textbox");a.changeAttr("aria-label",c);c&&a.changeAttr("title",c);var d=b.fire("ariaEditorHelpLabel",{}).label;if(d)if(c=this.ui.space(this.elementMode==CKEDITOR.ELEMENT_MODE_INLINE?"top":"contents")){var e=CKEDITOR.tools.getNextId(),d=CKEDITOR.dom.element.createFromHtml(''+d+"");c.append(d);a.changeAttr("aria-describedby",e)}}})}); +CKEDITOR.addCss(".cke_editable{cursor:text}.cke_editable img,.cke_editable input,.cke_editable textarea{cursor:default}");var x=function(){function a(b){return b.type==CKEDITOR.NODE_ELEMENT}function b(c,d){var e,f,j,k,l=[],r=d.range.startContainer;e=d.range.startPath();for(var r=g[r.getName()],h=0,i=c.getChildren(),m=i.count(),n=-1,q=-1,p=0,s=e.contains(g.$list);h-1)l[n].firstNotAllowed=1;if(q>-1)l[q].lastNotAllowed=1;return l}function c(b,d){var e=[],f=b.getChildren(),j=f.count(),k,l=0,r=g[d],h=!b.is(g.$inline)||b.is("br");for(h&&e.push(" ");l ",p.document);p.insertNode(t);p.setStartAfter(t)}E=new CKEDITOR.dom.elementPath(p.startContainer); +m.endPath=y=new CKEDITOR.dom.elementPath(p.endContainer);if(!p.collapsed){var z=y.block||y.blockLimit,I=p.getCommonAncestor();z&&(!z.equals(I)&&!z.contains(I)&&p.checkEndOfBlock())&&m.zombies.push(z);p.deleteContents()}for(;(C=a(p.startContainer)&&p.startContainer.getChild(p.startOffset-1))&&a(C)&&C.isBlockBoundary()&&E.contains(C);)p.moveToPosition(C,CKEDITOR.POSITION_BEFORE_END);e(p,m.blockLimit,E,y);if(t){p.setEndBefore(t);p.collapse();t.remove()}t=p.startPath();if(z=t.contains(d,false,1)){p.splitElement(z); +m.inlineStylesRoot=z;m.inlineStylesPeak=t.lastElement}t=p.createBookmark();(z=t.startNode.getPrevious(f))&&a(z)&&d(z)&&w.push(z);(z=t.startNode.getNext(f))&&a(z)&&d(z)&&w.push(z);for(z=t.startNode;(z=z.getParent())&&d(z);)w.push(z);p.moveToBookmark(t);if(t=q){t=m.range;if(m.type=="text"&&m.inlineStylesRoot){C=m.inlineStylesPeak;p=C.getDocument().createText("{cke-peak}");for(w=m.inlineStylesRoot.getParent();!C.equals(w);){p=p.appendTo(C.clone());C=C.getParent()}q=p.getOuterHtml().split("{cke-peak}").join(q)}C= +m.blockLimit.getName();if(/^\s+|\s+$/.test(q)&&"span"in CKEDITOR.dtd[C])var x=' ',q=x+q+x;q=m.editor.dataProcessor.toHtml(q,{context:null,fixForBody:false,dontFilter:m.dontFilter,filter:m.editor.activeFilter,enterMode:m.editor.activeEnterMode});C=t.document.createElement("body");C.setHtml(q);if(x){C.getFirst().remove();C.getLast().remove()}if((x=t.startPath().block)&&!(x.getChildCount()==1&&x.getBogus()))a:{var G;if(C.getChildCount()==1&&a(G=C.getFirst())&&G.is(r)){x= +G.getElementsByTag("*");t=0;for(w=x.count();t0;else{B=G.startPath();if(!y.isBlock&&m.editor.config.autoParagraph!==false&&(m.editor.activeEnterMode!=CKEDITOR.ENTER_BR&&m.editor.editable().equals(B.blockLimit)&&!B.block)&&(Q=m.editor.activeEnterMode!= +CKEDITOR.ENTER_BR&&m.editor.config.autoParagraph!==false?m.editor.activeEnterMode==CKEDITOR.ENTER_DIV?"div":"p":false)){Q=x.createElement(Q);Q.appendBogus();G.insertNode(Q);CKEDITOR.env.needsBrFiller&&(K=Q.getBogus())&&K.remove();G.moveToPosition(Q,CKEDITOR.POSITION_BEFORE_END)}if((B=G.startPath().block)&&!B.equals(H)){if(K=B.getBogus()){K.remove();C.push(B)}H=B}y.firstNotAllowed&&(p=1);if(p&&y.isElement){B=G.startContainer;for(M=null;B&&!g[B.getName()][y.name];){if(B.equals(q)){B=null;break}M=B; +B=B.getParent()}if(B){if(M){T=G.splitElement(M);m.zombies.push(T);m.zombies.push(M)}}else{M=q.getName();U=!t;B=t==E.length-1;M=c(y.node,M);for(var N=[],W=M.length,X=0,Z=void 0,$=0,aa=-1;X0;){d=a.getItem(b);if(!CKEDITOR.tools.trim(d.getHtml())){d.appendBogus();CKEDITOR.env.ie&&(CKEDITOR.env.version<9&&d.getChildCount())&&d.getFirst().remove()}}}return function(d){var e=d.startContainer,f=e.getAscendant("table",1),g=false;c(f.getElementsByTag("td"));c(f.getElementsByTag("th"));f=d.clone();f.setStart(e,0);f=a(f).lastBackward();if(!f){f=d.clone();f.setEndAt(e,CKEDITOR.POSITION_BEFORE_END);f=a(f).lastForward();g=true}f|| +(f=e);if(f.is("table")){d.setStartAt(f,CKEDITOR.POSITION_BEFORE_START);d.collapse(true);f.remove()}else{f.is({tbody:1,thead:1,tfoot:1})&&(f=b(f,"tr",g));f.is("tr")&&(f=b(f,f.getParent().is("thead")?"th":"td",g));(e=f.getBogus())&&e.remove();d.moveToPosition(f,g?CKEDITOR.POSITION_AFTER_START:CKEDITOR.POSITION_BEFORE_END)}}}()})(); +(function(){function a(){var a=this._.fakeSelection,b;if(a){b=this.getSelection(1);if(!b||!b.isHidden()){a.reset();a=0}}if(!a){a=b||this.getSelection(1);if(!a||a.getType()==CKEDITOR.SELECTION_NONE)return}this.fire("selectionCheck",a);b=this.elementPath();if(!b.compare(this._.selectionPreviousPath)){if(CKEDITOR.env.webkit)this._.previousActive=this.document.getActive();this._.selectionPreviousPath=b;this.fire("selectionChange",{selection:a,path:b})}}function c(){s=true;if(!p){b.call(this);p=CKEDITOR.tools.setTimeout(b, +200,this)}}function b(){p=null;if(s){CKEDITOR.tools.setTimeout(a,0,this);s=false}}function f(a){function b(c,d){return!c||c.type==CKEDITOR.NODE_TEXT?false:a.clone()["moveToElementEdit"+(d?"End":"Start")](c)}if(!(a.root instanceof CKEDITOR.editable))return false;var c=a.startContainer,d=a.getPreviousNode(x,null,c),e=a.getNextNode(x,null,c);return b(d)||b(e,1)||!d&&!e&&!(c.type==CKEDITOR.NODE_ELEMENT&&c.isBlockBoundary()&&c.getBogus())?true:false}function d(a){return a.getCustomData("cke-fillingChar")} +function e(a,b){var c=a&&a.removeCustomData("cke-fillingChar");if(c){if(b!==false){var d,e=a.getDocument().getSelection().getNative(),f=e&&e.type!="None"&&e.getRangeAt(0);if(c.getLength()>1&&f&&f.intersectsNode(c.$)){d=[e.anchorOffset,e.focusOffset];f=e.focusNode==c.$&&e.focusOffset>0;e.anchorNode==c.$&&e.anchorOffset>0&&d[0]--;f&&d[1]--;var h;f=e;if(!f.isCollapsed){h=f.getRangeAt(0);h.setStart(f.anchorNode,f.anchorOffset);h.setEnd(f.focusNode,f.focusOffset);h=h.collapsed}h&&d.unshift(d.pop())}}c.setText(g(c.getText())); +if(d){c=e.getRangeAt(0);c.setStart(c.startContainer,d[0]);c.setEnd(c.startContainer,d[1]);e.removeAllRanges();e.addRange(c)}}}function g(a){return a.replace(/\u200B( )?/g,function(a){return a[1]?" ":""})}function n(a,b,c){var d=a.on("focus",function(a){a.cancel()},null,null,-100);if(CKEDITOR.env.ie)var e=a.getDocument().on("selectionchange",function(a){a.cancel()},null,null,-100);else{var f=new CKEDITOR.dom.range(a);f.moveToElementEditStart(a);var g=a.getDocument().$.createRange();g.setStart(f.startContainer.$, +f.startOffset);g.collapse(1);b.removeAllRanges();b.addRange(g)}c&&a.focus();d.removeListener();e&&e.removeListener()}function h(a){var b=CKEDITOR.dom.element.createFromHtml('
     
    ',a.document);a.fire("lockSnapshot");a.editable().append(b);var c=a.getSelection(1),d=a.createRange(),e=c.root.on("selectionchange",function(a){a.cancel()},null,null,0);d.setStartAt(b,CKEDITOR.POSITION_AFTER_START); +d.setEndAt(b,CKEDITOR.POSITION_BEFORE_END);c.selectRanges([d]);e.removeListener();a.fire("unlockSnapshot");a._.hiddenSelectionContainer=b}function i(a){var b={37:1,39:1,8:1,46:1};return function(c){var d=c.data.getKeystroke();if(b[d]){var e=a.getSelection().getRanges(),f=e[0];if(e.length==1&&f.collapsed)if((d=f[d<38?"getPreviousEditableNode":"getNextEditableNode"]())&&d.type==CKEDITOR.NODE_ELEMENT&&d.getAttribute("contenteditable")=="false"){a.getSelection().fake(d);c.data.preventDefault();c.cancel()}}}} +function m(a){for(var b=0;b=d.getLength()?h.setStartAfter(d):h.setStartBefore(d)); +e&&e.type==CKEDITOR.NODE_TEXT&&(g?h.setEndAfter(e):h.setEndBefore(e));d=new CKEDITOR.dom.walker(h);d.evaluator=function(d){if(d.type==CKEDITOR.NODE_ELEMENT&&d.isReadOnly()){var e=c.clone();c.setEndBefore(d);c.collapsed&&a.splice(b--,1);if(!(d.getPosition(h.endContainer)&CKEDITOR.POSITION_CONTAINS)){e.setStartAfter(d);e.collapsed||a.splice(b+1,0,e)}return true}return false};d.next()}}return a}var p,s,x=CKEDITOR.dom.walker.invisible(1),q=function(){function a(b){return function(a){var c=a.editor.createRange(); c.moveToClosestEditablePosition(a.selected,b)&&a.editor.getSelection().selectRanges([c]);return false}}function b(a){return function(b){var c=b.editor,d=c.createRange(),e;if(!(e=d.moveToClosestEditablePosition(b.selected,a)))e=d.moveToClosestEditablePosition(b.selected,!a);e&&c.getSelection().selectRanges([d]);c.fire("saveSnapshot");b.selected.remove();if(!e){d.moveToElementEditablePosition(c.editable());c.getSelection().selectRanges([d])}c.fire("saveSnapshot");return false}}var c=a(),d=a(1);return{37:c, -38:c,39:d,40:d,8:b(),46:b(1)}}();CKEDITOR.on("instanceCreated",function(b){function c(){var a=d.getSelection();a&&a.removeAllRanges()}var d=b.editor;d.on("contentDom",function(){var b=d.document,c=CKEDITOR.document,g=d.editable(),j=b.getBody(),l=b.getDocumentElement(),h=g.isInline(),i,o;CKEDITOR.env.gecko&&g.attachListener(g,"focus",function(a){a.removeListener();if(i!==0)if((a=d.getSelection().getNative())&&a.isCollapsed&&a.anchorNode==g.$){a=d.createRange();a.moveToElementEditStart(g);a.select()}}, -null,null,-2);g.attachListener(g,CKEDITOR.env.webkit?"DOMFocusIn":"focus",function(){i&&CKEDITOR.env.webkit&&(i=d._.previousActive&&d._.previousActive.equals(b.getActive()));d.unlockSelection(i);i=0},null,null,-1);g.attachListener(g,"mousedown",function(){i=0});if(CKEDITOR.env.ie||h){var n=function(){o=new CKEDITOR.dom.selection(d.getSelection());o.lock()};p?g.attachListener(g,"beforedeactivate",n,null,null,-1):g.attachListener(d,"selectionCheck",n,null,null,-1);g.attachListener(g,CKEDITOR.env.webkit? -"DOMFocusOut":"blur",function(){d.lockSelection(o);i=1},null,null,-1);g.attachListener(g,"mousedown",function(){i=0})}if(CKEDITOR.env.ie&&!h){var t;g.attachListener(g,"mousedown",function(a){if(a.data.$.button==2){a=d.document.getSelection();if(!a||a.getType()==CKEDITOR.SELECTION_NONE)t=d.window.getScrollPosition()}});g.attachListener(g,"mouseup",function(a){if(a.data.$.button==2&&t){d.document.$.documentElement.scrollLeft=t.x;d.document.$.documentElement.scrollTop=t.y}t=null});if(b.$.compatMode!= -"BackCompat"){if(CKEDITOR.env.ie7Compat||CKEDITOR.env.ie6Compat)l.on("mousedown",function(a){function b(a){a=a.data.$;if(e){var c=j.$.createTextRange();try{c.moveToPoint(a.x,a.y)}catch(d){}e.setEndPoint(g.compareEndPoints("StartToStart",c)<0?"EndToEnd":"StartToStart",c);e.select()}}function d(){l.removeListener("mousemove",b);c.removeListener("mouseup",d);l.removeListener("mouseup",d);e.select()}a=a.data;if(a.getTarget().is("html")&&a.$.y7&&CKEDITOR.env.version<11){l.on("mousedown",function(a){if(a.data.getTarget().is("html")){c.on("mouseup",x);l.on("mouseup",x)}});var x=function(){c.removeListener("mouseup",x);l.removeListener("mouseup",x);var a=CKEDITOR.document.$.selection,d=a.createRange();a.type!="None"&&d.parentElement().ownerDocument==b.$&&d.select()}}}}g.attachListener(g,"selectionchange", -a,d);g.attachListener(g,"keyup",e,d);g.attachListener(g,CKEDITOR.env.webkit?"DOMFocusIn":"focus",function(){d.forceNextSelectionCheck();d.selectionChange(1)});if(h&&(CKEDITOR.env.webkit||CKEDITOR.env.gecko)){var y;g.attachListener(g,"mousedown",function(){y=1});g.attachListener(b.getDocumentElement(),"mouseup",function(){y&&e.call(d);y=0})}else g.attachListener(CKEDITOR.env.ie?g:b.getDocumentElement(),"mouseup",e,d);CKEDITOR.env.webkit&&g.attachListener(b,"keydown",function(a){switch(a.data.getKey()){case 13:case 33:case 34:case 35:case 36:case 37:case 39:case 8:case 45:case 46:f(g)}}, -null,null,-1);g.attachListener(g,"keydown",k(d),null,null,-1)});d.on("setData",function(){d.unlockSelection();CKEDITOR.env.webkit&&c()});d.on("contentDomUnload",function(){d.unlockSelection()});if(CKEDITOR.env.ie9Compat)d.on("beforeDestroy",c,null,null,9);d.on("dataReady",function(){delete d._.fakeSelection;delete d._.hiddenSelectionContainer;d.selectionChange(1)});d.on("loadSnapshot",function(){var a=d.editable().getLast(function(a){return a.type==CKEDITOR.NODE_ELEMENT});a&&a.hasAttribute("data-cke-hidden-sel")&& -a.remove()},null,null,100);d.on("key",function(a){if(d.mode=="wysiwyg"){var b=d.getSelection();if(b.isFake){var c=g[a.data.keyCode];if(c)return c({editor:d,selected:b.getSelectedElement(),selection:b,keyEvent:a})}}})});CKEDITOR.on("instanceReady",function(a){var b=a.editor;if(CKEDITOR.env.webkit){b.on("selectionChange",function(){var a=b.editable(),c=d(a);c&&(c.getCustomData("ready")?f(a):c.setCustomData("ready",1))},null,null,-1);b.on("beforeSetMode",function(){f(b.editable())},null,null,-1);var c, -e,a=function(){var a=b.editable();if(a)if(a=d(a)){var f=b.document.$.defaultView.getSelection();f.type=="Caret"&&f.anchorNode==a.$&&(e=1);c=a.getText();a.setText(h(c))}},g=function(){var a=b.editable();if(a)if(a=d(a)){a.setText(c);if(e){b.document.$.defaultView.getSelection().setPosition(a.$,a.getLength());e=0}}};b.on("beforeUndoImage",a);b.on("afterUndoImage",g);b.on("beforeGetData",a,null,null,0);b.on("getData",g)}});CKEDITOR.editor.prototype.selectionChange=function(b){(b?a:e).call(this)};CKEDITOR.editor.prototype.getSelection= +38:c,39:d,40:d,8:b(),46:b(1)}}();CKEDITOR.on("instanceCreated",function(b){function d(){var a=f.getSelection();a&&a.removeAllRanges()}var f=b.editor;f.on("contentDom",function(){var b=f.document,d=CKEDITOR.document,g=f.editable(),k=b.getBody(),l=b.getDocumentElement(),h=g.isInline(),m,n;CKEDITOR.env.gecko&&g.attachListener(g,"focus",function(a){a.removeListener();if(m!==0)if((a=f.getSelection().getNative())&&a.isCollapsed&&a.anchorNode==g.$){a=f.createRange();a.moveToElementEditStart(g);a.select()}}, +null,null,-2);g.attachListener(g,CKEDITOR.env.webkit?"DOMFocusIn":"focus",function(){m&&CKEDITOR.env.webkit&&(m=f._.previousActive&&f._.previousActive.equals(b.getActive()));f.unlockSelection(m);m=0},null,null,-1);g.attachListener(g,"mousedown",function(){m=0});if(CKEDITOR.env.ie||h){var q=function(){n=new CKEDITOR.dom.selection(f.getSelection());n.lock()};o?g.attachListener(g,"beforedeactivate",q,null,null,-1):g.attachListener(f,"selectionCheck",q,null,null,-1);g.attachListener(g,CKEDITOR.env.webkit? +"DOMFocusOut":"blur",function(){f.lockSelection(n);m=1},null,null,-1);g.attachListener(g,"mousedown",function(){m=0})}if(CKEDITOR.env.ie&&!h){var w;g.attachListener(g,"mousedown",function(a){if(a.data.$.button==2){a=f.document.getSelection();if(!a||a.getType()==CKEDITOR.SELECTION_NONE)w=f.window.getScrollPosition()}});g.attachListener(g,"mouseup",function(a){if(a.data.$.button==2&&w){f.document.$.documentElement.scrollLeft=w.x;f.document.$.documentElement.scrollTop=w.y}w=null});if(b.$.compatMode!= +"BackCompat"){if(CKEDITOR.env.ie7Compat||CKEDITOR.env.ie6Compat)l.on("mousedown",function(a){function b(a){a=a.data.$;if(e){var c=k.$.createTextRange();try{c.moveToPoint(a.x,a.y)}catch(d){}e.setEndPoint(g.compareEndPoints("StartToStart",c)<0?"EndToEnd":"StartToStart",c);e.select()}}function c(){l.removeListener("mousemove",b);d.removeListener("mouseup",c);l.removeListener("mouseup",c);e.select()}a=a.data;if(a.getTarget().is("html")&&a.$.y7&&CKEDITOR.env.version<11){l.on("mousedown",function(a){if(a.data.getTarget().is("html")){d.on("mouseup",z);l.on("mouseup",z)}});var z=function(){d.removeListener("mouseup",z);l.removeListener("mouseup",z);var a=CKEDITOR.document.$.selection,c=a.createRange();a.type!="None"&&c.parentElement().ownerDocument==b.$&&c.select()}}}}g.attachListener(g,"selectionchange", +a,f);g.attachListener(g,"keyup",c,f);g.attachListener(g,CKEDITOR.env.webkit?"DOMFocusIn":"focus",function(){f.forceNextSelectionCheck();f.selectionChange(1)});if(h&&(CKEDITOR.env.webkit||CKEDITOR.env.gecko)){var t;g.attachListener(g,"mousedown",function(){t=1});g.attachListener(b.getDocumentElement(),"mouseup",function(){t&&c.call(f);t=0})}else g.attachListener(CKEDITOR.env.ie?g:b.getDocumentElement(),"mouseup",c,f);CKEDITOR.env.webkit&&g.attachListener(b,"keydown",function(a){switch(a.data.getKey()){case 13:case 33:case 34:case 35:case 36:case 37:case 39:case 8:case 45:case 46:e(g)}}, +null,null,-1);g.attachListener(g,"keydown",i(f),null,null,-1)});f.on("setData",function(){f.unlockSelection();CKEDITOR.env.webkit&&d()});f.on("contentDomUnload",function(){f.unlockSelection()});if(CKEDITOR.env.ie9Compat)f.on("beforeDestroy",d,null,null,9);f.on("dataReady",function(){delete f._.fakeSelection;delete f._.hiddenSelectionContainer;f.selectionChange(1)});f.on("loadSnapshot",function(){var a=f.editable().getLast(function(a){return a.type==CKEDITOR.NODE_ELEMENT});a&&a.hasAttribute("data-cke-hidden-sel")&& +a.remove()},null,null,100);f.on("key",function(a){if(f.mode=="wysiwyg"){var b=f.getSelection();if(b.isFake){var c=q[a.data.keyCode];if(c)return c({editor:f,selected:b.getSelectedElement(),selection:b,keyEvent:a})}}})});CKEDITOR.on("instanceReady",function(a){var b=a.editor;if(CKEDITOR.env.webkit){b.on("selectionChange",function(){var a=b.editable(),c=d(a);c&&(c.getCustomData("ready")?e(a):c.setCustomData("ready",1))},null,null,-1);b.on("beforeSetMode",function(){e(b.editable())},null,null,-1);var c, +f,a=function(){var a=b.editable();if(a)if(a=d(a)){var e=b.document.$.defaultView.getSelection();e.type=="Caret"&&e.anchorNode==a.$&&(f=1);c=a.getText();a.setText(g(c))}},h=function(){var a=b.editable();if(a)if(a=d(a)){a.setText(c);if(f){b.document.$.defaultView.getSelection().setPosition(a.$,a.getLength());f=0}}};b.on("beforeUndoImage",a);b.on("afterUndoImage",h);b.on("beforeGetData",a,null,null,0);b.on("getData",h)}});CKEDITOR.editor.prototype.selectionChange=function(b){(b?a:c).call(this)};CKEDITOR.editor.prototype.getSelection= function(a){if((this._.savedSelection||this._.fakeSelection)&&!a)return this._.savedSelection||this._.fakeSelection;return(a=this.editable())&&this.mode=="wysiwyg"?new CKEDITOR.dom.selection(a):null};CKEDITOR.editor.prototype.lockSelection=function(a){a=a||this.getSelection(1);if(a.getType()!=CKEDITOR.SELECTION_NONE){!a.isLocked&&a.lock();this._.savedSelection=a;return true}return false};CKEDITOR.editor.prototype.unlockSelection=function(a){var b=this._.savedSelection;if(b){b.unlock(a);delete this._.savedSelection; -return true}return false};CKEDITOR.editor.prototype.forceNextSelectionCheck=function(){delete this._.selectionPreviousPath};CKEDITOR.dom.document.prototype.getSelection=function(){return new CKEDITOR.dom.selection(this)};CKEDITOR.dom.range.prototype.select=function(){var a=this.root instanceof CKEDITOR.editable?this.root.editor.getSelection():new CKEDITOR.dom.selection(this.root);a.selectRanges([this]);return a};CKEDITOR.SELECTION_NONE=1;CKEDITOR.SELECTION_TEXT=2;CKEDITOR.SELECTION_ELEMENT=3;var p= -typeof window.getSelection!="function",z=1;CKEDITOR.dom.selection=function(a){if(a instanceof CKEDITOR.dom.selection)var b=a,a=a.root;var c=a instanceof CKEDITOR.dom.element;this.rev=b?b.rev:z++;this.document=a instanceof CKEDITOR.dom.document?a:a.getDocument();this.root=a=c?a:this.document.getBody();this.isLocked=0;this._={cache:{}};if(b){CKEDITOR.tools.extend(this._.cache,b._.cache);this.isFake=b.isFake;this.isLocked=b.isLocked;return this}b=p?this.document.$.selection:this.document.getWindow().$.getSelection(); -if(CKEDITOR.env.webkit)(b.type=="None"&&this.document.getActive().equals(a)||b.type=="Caret"&&b.anchorNode.nodeType==CKEDITOR.NODE_DOCUMENT)&&n(a,b);else if(CKEDITOR.env.gecko)b&&(this.document.getActive().equals(a)&&b.anchorNode&&b.anchorNode.nodeType==CKEDITOR.NODE_DOCUMENT)&&n(a,b,true);else if(CKEDITOR.env.ie){var d;try{d=this.document.getActive()}catch(e){}if(p)b.type=="None"&&(d&&d.equals(this.document.getDocumentElement()))&&n(a,null,true);else{(b=b&&b.anchorNode)&&(b=new CKEDITOR.dom.node(b)); +return true}return false};CKEDITOR.editor.prototype.forceNextSelectionCheck=function(){delete this._.selectionPreviousPath};CKEDITOR.dom.document.prototype.getSelection=function(){return new CKEDITOR.dom.selection(this)};CKEDITOR.dom.range.prototype.select=function(){var a=this.root instanceof CKEDITOR.editable?this.root.editor.getSelection():new CKEDITOR.dom.selection(this.root);a.selectRanges([this]);return a};CKEDITOR.SELECTION_NONE=1;CKEDITOR.SELECTION_TEXT=2;CKEDITOR.SELECTION_ELEMENT=3;var o= +typeof window.getSelection!="function",u=1;CKEDITOR.dom.selection=function(a){if(a instanceof CKEDITOR.dom.selection)var b=a,a=a.root;var c=a instanceof CKEDITOR.dom.element;this.rev=b?b.rev:u++;this.document=a instanceof CKEDITOR.dom.document?a:a.getDocument();this.root=a=c?a:this.document.getBody();this.isLocked=0;this._={cache:{}};if(b){CKEDITOR.tools.extend(this._.cache,b._.cache);this.isFake=b.isFake;this.isLocked=b.isLocked;return this}b=o?this.document.$.selection:this.document.getWindow().$.getSelection(); +if(CKEDITOR.env.webkit)(b.type=="None"&&this.document.getActive().equals(a)||b.type=="Caret"&&b.anchorNode.nodeType==CKEDITOR.NODE_DOCUMENT)&&n(a,b);else if(CKEDITOR.env.gecko)b&&(this.document.getActive().equals(a)&&b.anchorNode&&b.anchorNode.nodeType==CKEDITOR.NODE_DOCUMENT)&&n(a,b,true);else if(CKEDITOR.env.ie){var d;try{d=this.document.getActive()}catch(e){}if(o)b.type=="None"&&(d&&d.equals(this.document.getDocumentElement()))&&n(a,null,true);else{(b=b&&b.anchorNode)&&(b=new CKEDITOR.dom.node(b)); d&&(d.equals(this.document.getDocumentElement())&&b&&(a.equals(b)||a.contains(b)))&&n(a,null,true)}}d=this.getNative();var f,g;if(d)if(d.getRangeAt)f=(g=d.rangeCount&&d.getRangeAt(0))&&new CKEDITOR.dom.node(g.commonAncestorContainer);else{try{g=d.createRange()}catch(h){}f=g&&CKEDITOR.dom.element.get(g.item&&g.item(0)||g.parentElement())}if(!f||!(f.type==CKEDITOR.NODE_ELEMENT||f.type==CKEDITOR.NODE_TEXT)||!this.root.equals(f)&&!this.root.contains(f)){this._.cache.type=CKEDITOR.SELECTION_NONE;this._.cache.startElement= -null;this._.cache.selectedElement=null;this._.cache.selectedText="";this._.cache.ranges=new CKEDITOR.dom.rangeList}return this};var A={img:1,hr:1,li:1,table:1,tr:1,td:1,th:1,embed:1,object:1,ol:1,ul:1,a:1,input:1,form:1,select:1,textarea:1,button:1,fieldset:1,thead:1,tfoot:1};CKEDITOR.dom.selection.prototype={getNative:function(){return this._.cache.nativeSel!==void 0?this._.cache.nativeSel:this._.cache.nativeSel=p?this.document.$.selection:this.document.getWindow().$.getSelection()},getType:p?function(){var a= +null;this._.cache.selectedElement=null;this._.cache.selectedText="";this._.cache.ranges=new CKEDITOR.dom.rangeList}return this};var A={img:1,hr:1,li:1,table:1,tr:1,td:1,th:1,embed:1,object:1,ol:1,ul:1,a:1,input:1,form:1,select:1,textarea:1,button:1,fieldset:1,thead:1,tfoot:1};CKEDITOR.dom.selection.prototype={getNative:function(){return this._.cache.nativeSel!==void 0?this._.cache.nativeSel:this._.cache.nativeSel=o?this.document.$.selection:this.document.getWindow().$.getSelection()},getType:o?function(){var a= this._.cache;if(a.type)return a.type;var b=CKEDITOR.SELECTION_NONE;try{var c=this.getNative(),d=c.type;if(d=="Text")b=CKEDITOR.SELECTION_TEXT;if(d=="Control")b=CKEDITOR.SELECTION_ELEMENT;if(c.createRange().parentElement())b=CKEDITOR.SELECTION_TEXT}catch(e){}return a.type=b}:function(){var a=this._.cache;if(a.type)return a.type;var b=CKEDITOR.SELECTION_TEXT,c=this.getNative();if(!c||!c.rangeCount)b=CKEDITOR.SELECTION_NONE;else if(c.rangeCount==1){var c=c.getRangeAt(0),d=c.startContainer;if(d==c.endContainer&& -d.nodeType==1&&c.endOffset-c.startOffset==1&&A[d.childNodes[c.startOffset].nodeName.toLowerCase()])b=CKEDITOR.SELECTION_ELEMENT}return a.type=b},getRanges:function(){var a=p?function(){function a(b){return(new CKEDITOR.dom.node(b)).getIndex()}var b=function(b,c){b=b.duplicate();b.collapse(c);var d=b.parentElement();if(!d.hasChildNodes())return{container:d,offset:0};for(var e=d.children,f,g,h=b.duplicate(),i=0,l=e.length-1,t=-1,m,k;i<=l;){t=Math.floor((i+l)/2);f=e[t];h.moveToElementText(f);m=h.compareEndPoints("StartToStart", -b);if(m>0)l=t-1;else if(m<0)i=t+1;else return{container:d,offset:a(f)}}if(t==-1||t==e.length-1&&m<0){h.moveToElementText(d);h.setEndPoint("StartToStart",b);h=h.text.replace(/(\r\n|\r)/g,"\n").length;e=d.childNodes;if(!h){f=e[e.length-1];return f.nodeType!=CKEDITOR.NODE_TEXT?{container:d,offset:e.length}:{container:f,offset:f.nodeValue.length}}for(d=e.length;h>0&&d>0;){g=e[--d];if(g.nodeType==CKEDITOR.NODE_TEXT){k=g;h=h-g.nodeValue.length}}return{container:k,offset:-h}}h.collapse(m>0?true:false);h.setEndPoint(m> -0?"StartToStart":"EndToStart",b);h=h.text.replace(/(\r\n|\r)/g,"\n").length;if(!h)return{container:d,offset:a(f)+(m>0?0:1)};for(;h>0;)try{g=f[m>0?"previousSibling":"nextSibling"];if(g.nodeType==CKEDITOR.NODE_TEXT){h=h-g.nodeValue.length;k=g}f=g}catch(o){return{container:d,offset:a(f)}}return{container:k,offset:m>0?-h:k.nodeValue.length+h}};return function(){var a=this.getNative(),c=a&&a.createRange(),d=this.getType();if(!a)return[];if(d==CKEDITOR.SELECTION_TEXT){a=new CKEDITOR.dom.range(this.root); -d=b(c,true);a.setStart(new CKEDITOR.dom.node(d.container),d.offset);d=b(c);a.setEnd(new CKEDITOR.dom.node(d.container),d.offset);a.endContainer.getPosition(a.startContainer)&CKEDITOR.POSITION_PRECEDING&&a.endOffset<=a.startContainer.getIndex()&&a.collapse();return[a]}if(d==CKEDITOR.SELECTION_ELEMENT){for(var d=[],e=0;e0)h=w-1;else if(i<0)j=w+1;else return{container:d,offset:a(f)}}if(w==-1||w==e.length-1&&i<0){k.moveToElementText(d);k.setEndPoint("StartToStart",b);k=k.text.replace(/(\r\n|\r)/g,"\n").length;e=d.childNodes;if(!k){f=e[e.length-1];return f.nodeType!=CKEDITOR.NODE_TEXT?{container:d,offset:e.length}:{container:f,offset:f.nodeValue.length}}for(d=e.length;k>0&&d>0;){g=e[--d];if(g.nodeType==CKEDITOR.NODE_TEXT){t=g;k=k-g.nodeValue.length}}return{container:t,offset:-k}}k.collapse(i>0?true:false);k.setEndPoint(i> +0?"StartToStart":"EndToStart",b);k=k.text.replace(/(\r\n|\r)/g,"\n").length;if(!k)return{container:d,offset:a(f)+(i>0?0:1)};for(;k>0;)try{g=f[i>0?"previousSibling":"nextSibling"];if(g.nodeType==CKEDITOR.NODE_TEXT){k=k-g.nodeValue.length;t=g}f=g}catch(m){return{container:d,offset:a(f)}}return{container:t,offset:i>0?-k:t.nodeValue.length+k}};return function(){var a=this.getNative(),c=a&&a.createRange(),d=this.getType();if(!a)return[];if(d==CKEDITOR.SELECTION_TEXT){a=new CKEDITOR.dom.range(this.root); +d=b(c,true);a.setStart(new CKEDITOR.dom.node(d.container),d.offset);d=b(c);a.setEnd(new CKEDITOR.dom.node(d.container),d.offset);a.endContainer.getPosition(a.startContainer)&CKEDITOR.POSITION_PRECEDING&&a.endOffset<=a.startContainer.getIndex()&&a.collapse();return[a]}if(d==CKEDITOR.SELECTION_ELEMENT){for(var d=[],e=0;e1){h=a[a.length- -1];a[0].setEnd(h.endContainer,h.endOffset)}h=a[0];var a=h.collapsed,o,n,r;if((d=h.getEnclosedNode())&&d.type==CKEDITOR.NODE_ELEMENT&&d.getName()in A&&(!d.is("a")||!d.getText()))try{r=d.$.createControlRange();r.addElement(d.$);r.select();return}catch(s){}(h.startContainer.type==CKEDITOR.NODE_ELEMENT&&h.startContainer.getName()in b||h.endContainer.type==CKEDITOR.NODE_ELEMENT&&h.endContainer.getName()in b)&&h.shrink(CKEDITOR.NODE_ELEMENT,true);r=h.createBookmark();b=r.startNode;if(!a)g=r.endNode;r=h.document.$.body.createTextRange(); -r.moveToElementText(b.$);r.moveStart("character",1);if(g){i=h.document.$.body.createTextRange();i.moveToElementText(g.$);r.setEndPoint("EndToEnd",i);r.moveEnd("character",-1)}else{o=b.getNext(k);n=b.hasAscendant("pre");o=!(o&&o.getText&&o.getText().match(i))&&(n||!b.hasPrevious()||b.getPrevious().is&&b.getPrevious().is("br"));n=h.document.createElement("span");n.setHtml("");n.insertBefore(b);o&&h.document.createText("").insertBefore(b)}h.setStartBefore(b);b.remove();if(a){if(o){r.moveStart("character", --1);r.select();h.document.$.selection.clear()}else r.select();h.moveToPosition(n,CKEDITOR.POSITION_BEFORE_START);n.remove()}else{h.setEndBefore(g);g.remove();r.select()}}else{g=this.getNative();if(!g)return;this.removeAllRanges();for(r=0;r=0){h.collapse(1);n.setEnd(h.endContainer.$,h.endOffset)}else throw t;}g.addRange(n)}}this.reset();this.root.fire("selectionchange")}}},fake:function(a){var b=this.root.editor;this.reset();i(b);var c=this._.cache,d=new CKEDITOR.dom.range(this.root);d.setStartBefore(a);d.setEndAfter(a);c.ranges=new CKEDITOR.dom.rangeList(d);c.selectedElement=c.startElement=a;c.type=CKEDITOR.SELECTION_ELEMENT; -c.selectedText=c.nativeSel=null;this.isFake=1;this.rev=z++;b._.fakeSelection=this;this.root.fire("selectionchange")},isHidden:function(){var a=this.getCommonAncestor();a&&a.type==CKEDITOR.NODE_TEXT&&(a=a.getParent());return!(!a||!a.data("cke-hidden-sel"))},createBookmarks:function(a){a=this.getRanges().createBookmarks(a);this.isFake&&(a.isFake=1);return a},createBookmarks2:function(a){a=this.getRanges().createBookmarks2(a);this.isFake&&(a.isFake=1);return a},selectBookmarks:function(a){for(var b= +!(c.type==CKEDITOR.NODE_ELEMENT&&A[c.getName()]&&(d=c)));e--)a.shrink(CKEDITOR.SHRINK_ELEMENT);return d&&d.$});return a.selectedElement=c?new CKEDITOR.dom.element(c):null},getSelectedText:function(){var a=this._.cache;if(a.selectedText!==void 0)return a.selectedText;var b=this.getNative(),b=o?b.type=="Control"?"":b.createRange().text:b.toString();return a.selectedText=b},lock:function(){this.getRanges();this.getStartElement();this.getSelectedElement();this.getSelectedText();this._.cache.nativeSel= +null;this.isLocked=1},unlock:function(a){if(this.isLocked){if(a)var b=this.getSelectedElement(),c=!b&&this.getRanges(),d=this.isFake;this.isLocked=0;this.reset();if(a)(a=b||c[0]&&c[0].getCommonAncestor())&&a.getAscendant("body",1)&&(d?this.fake(b):b?this.selectElement(b):this.selectRanges(c))}},reset:function(){this._.cache={};this.isFake=0;var a=this.root.editor;if(a&&a._.fakeSelection&&this.rev==a._.fakeSelection.rev){delete a._.fakeSelection;var b=a._.hiddenSelectionContainer;if(b){var c=a.checkDirty(); +a.fire("lockSnapshot");b.remove();a.fire("unlockSnapshot");!c&&a.resetDirty()}delete a._.hiddenSelectionContainer}this.rev=u++},selectElement:function(a){var b=new CKEDITOR.dom.range(this.root);b.setStartBefore(a);b.setEndAfter(a);this.selectRanges([b])},selectRanges:function(a){var b=this.root.editor,b=b&&b._.hiddenSelectionContainer;this.reset();if(b)for(var b=this.root,c,d=0;d1){h=a[a.length-1];a[0].setEnd(h.endContainer,h.endOffset)}h=a[0];var a=h.collapsed,n,q,p;if((c=h.getEnclosedNode())&&c.type==CKEDITOR.NODE_ELEMENT&&c.getName()in A&&(!c.is("a")||!c.getText()))try{p=c.$.createControlRange();p.addElement(c.$);p.select();return}catch(s){}if(h.startContainer.type==CKEDITOR.NODE_ELEMENT&&h.startContainer.getName()in b||h.endContainer.type==CKEDITOR.NODE_ELEMENT&&h.endContainer.getName()in b){h.shrink(CKEDITOR.NODE_ELEMENT,true);a=h.collapsed}p=h.createBookmark(); +b=p.startNode;if(!a)g=p.endNode;p=h.document.$.body.createTextRange();p.moveToElementText(b.$);p.moveStart("character",1);if(g){i=h.document.$.body.createTextRange();i.moveToElementText(g.$);p.setEndPoint("EndToEnd",i);p.moveEnd("character",-1)}else{n=b.getNext(m);q=b.hasAscendant("pre");n=!(n&&n.getText&&n.getText().match(i))&&(q||!b.hasPrevious()||b.getPrevious().is&&b.getPrevious().is("br"));q=h.document.createElement("span");q.setHtml("");q.insertBefore(b);n&&h.document.createText("").insertBefore(b)}h.setStartBefore(b); +b.remove();if(a){if(n){p.moveStart("character",-1);p.select();h.document.$.selection.clear()}else p.select();h.moveToPosition(q,CKEDITOR.POSITION_BEFORE_START);q.remove()}else{h.setEndBefore(g);g.remove();p.select()}}else{g=this.getNative();if(!g)return;this.removeAllRanges();for(p=0;p=0){h.collapse(1);q.setEnd(h.endContainer.$,h.endOffset)}else throw w;}g.addRange(q)}}this.reset();this.root.fire("selectionchange")}}},fake:function(a){var b=this.root.editor;this.reset();h(b);var c=this._.cache,d=new CKEDITOR.dom.range(this.root);d.setStartBefore(a);d.setEndAfter(a);c.ranges=new CKEDITOR.dom.rangeList(d);c.selectedElement=c.startElement=a;c.type=CKEDITOR.SELECTION_ELEMENT; +c.selectedText=c.nativeSel=null;this.isFake=1;this.rev=u++;b._.fakeSelection=this;this.root.fire("selectionchange")},isHidden:function(){var a=this.getCommonAncestor();a&&a.type==CKEDITOR.NODE_TEXT&&(a=a.getParent());return!(!a||!a.data("cke-hidden-sel"))},createBookmarks:function(a){a=this.getRanges().createBookmarks(a);this.isFake&&(a.isFake=1);return a},createBookmarks2:function(a){a=this.getRanges().createBookmarks2(a);this.isFake&&(a.isFake=1);return a},selectBookmarks:function(a){for(var b= [],c=0;c]*>)[ \t\r\n]*/gi,"$1");f=f.replace(/([ \t\n\r]+| )/g, -" ");f=f.replace(/]*>/gi,"\n");if(CKEDITOR.env.ie){var g=a.getDocument().createElement("div");g.append(e);e.$.outerHTML="
    "+f+"
    ";e.copyAttributes(g.getFirst());e=g.getFirst().remove()}else e.setHtml(f);b=e}else f?b=s(c?[a.getHtml()]:k(a),b):a.moveChildren(b);b.replace(a);if(d){var c=b,h;if((h=c.getPrevious(C))&&h.type==CKEDITOR.NODE_ELEMENT&&h.is("pre")){d=o(h.getHtml(),/\n$/,"")+"\n\n"+o(c.getHtml(),/^\n/,"");CKEDITOR.env.ie?c.$.outerHTML="
    "+d+"
    ":c.setHtml(d);h.remove()}}else c&& -p(b)}function k(a){a.getName();var b=[];o(a.getOuterHtml(),/(\S\s*)\n(?:\s|(]+data-cke-bookmark.*?\/span>))*\n(?!$)/gi,function(a,b,c){return b+""+c+"
    "}).replace(/([\s\S]*?)<\/pre>/gi,function(a,c){b.push(c)});return b}function o(a,b,c){var d="",e="",a=a.replace(/(^]+data-cke-bookmark.*?\/span>)|(]+data-cke-bookmark.*?\/span>$)/gi,function(a,b,c){b&&(d=b);c&&(e=c);return""});return d+a.replace(b,c)+e}function s(a,b){var c;a.length>1&&(c=new CKEDITOR.dom.documentFragment(b.getDocument()));
    -for(var d=0;d"),e=e.replace(/[ \t]{2,}/g,function(a){return CKEDITOR.tools.repeat(" ",a.length-1)+" "});if(c){var f=b.clone();f.setHtml(e);c.append(f)}else b.setHtml(e)}return c||b}function r(a,b){var c=this._.definition,
    -d=c.attributes,c=c.styles,e=j(this)[a.getName()],f=CKEDITOR.tools.isEmpty(d)&&CKEDITOR.tools.isEmpty(c),h;for(h in d)if(!((h=="class"||this._.definition.fullMatch)&&a.getAttribute(h)!=m(h,d[h]))&&!(b&&h.slice(0,5)=="data-")){f=a.hasAttribute(h);a.removeAttribute(h)}for(var i in c)if(!(this._.definition.fullMatch&&a.getStyle(i)!=m(i,c[i],true))){f=f||!!a.getStyle(i);a.removeStyle(i)}g(a,e,P[a.getName()]);f&&(this._.definition.alwaysRemoveElement?p(a,1):!CKEDITOR.dtd.$block[a.getName()]||this._.enterMode==
    -CKEDITOR.ENTER_BR&&!a.hasAttributes()?p(a):a.renameNode(this._.enterMode==CKEDITOR.ENTER_P?"p":"div"))}function v(a){for(var b=j(this),c=a.getElementsByTag(this.element),d,e=c.count();--e>=0;){d=c.getItem(e);d.isReadOnly()||r.call(this,d,true)}for(var f in b)if(f!=this.element){c=a.getElementsByTag(f);for(e=c.count()-1;e>=0;e--){d=c.getItem(e);d.isReadOnly()||g(d,b[f])}}}function g(a,b,c){if(b=b&&b.attributes)for(var d=0;d]*>)[ \t\r\n]*/gi,"$1");f=f.replace(/([ \t\n\r]+| )/g,
    +" ");f=f.replace(/]*>/gi,"\n");if(CKEDITOR.env.ie){var g=a.getDocument().createElement("div");g.append(e);e.$.outerHTML="
    "+f+"
    ";e.copyAttributes(g.getFirst());e=g.getFirst().remove()}else e.setHtml(f);b=e}else f?b=p(c?[a.getHtml()]:i(a),b):a.moveChildren(b);b.replace(a);if(d){var c=b,h;if((h=c.getPrevious(D))&&h.type==CKEDITOR.NODE_ELEMENT&&h.is("pre")){d=m(h.getHtml(),/\n$/,"")+"\n\n"+m(c.getHtml(),/^\n/,"");CKEDITOR.env.ie?c.$.outerHTML="
    "+d+"
    ":c.setHtml(d);h.remove()}}else c&& +o(b)}function i(a){a.getName();var b=[];m(a.getOuterHtml(),/(\S\s*)\n(?:\s|(]+data-cke-bookmark.*?\/span>))*\n(?!$)/gi,function(a,b,c){return b+"
    "+c+"
    "}).replace(/([\s\S]*?)<\/pre>/gi,function(a,c){b.push(c)});return b}function m(a,b,c){var d="",e="",a=a.replace(/(^]+data-cke-bookmark.*?\/span>)|(]+data-cke-bookmark.*?\/span>$)/gi,function(a,b,c){b&&(d=b);c&&(e=c);return""});return d+a.replace(b,c)+e}function p(a,b){var c;a.length>1&&(c=new CKEDITOR.dom.documentFragment(b.getDocument()));
    +for(var d=0;d"),e=e.replace(/[ \t]{2,}/g,function(a){return CKEDITOR.tools.repeat(" ",a.length-1)+" "});if(c){var f=b.clone();f.setHtml(e);c.append(f)}else b.setHtml(e)}return c||b}function s(a,b){var c=this._.definition,
    +d=c.attributes,c=c.styles,e=l(this)[a.getName()],f=CKEDITOR.tools.isEmpty(d)&&CKEDITOR.tools.isEmpty(c),g;for(g in d)if(!((g=="class"||this._.definition.fullMatch)&&a.getAttribute(g)!=j(g,d[g]))&&!(b&&g.slice(0,5)=="data-")){f=a.hasAttribute(g);a.removeAttribute(g)}for(var h in c)if(!(this._.definition.fullMatch&&a.getStyle(h)!=j(h,c[h],true))){f=f||!!a.getStyle(h);a.removeStyle(h)}q(a,e,I[a.getName()]);f&&(this._.definition.alwaysRemoveElement?o(a,1):!CKEDITOR.dtd.$block[a.getName()]||this._.enterMode==
    +CKEDITOR.ENTER_BR&&!a.hasAttributes()?o(a):a.renameNode(this._.enterMode==CKEDITOR.ENTER_P?"p":"div"))}function x(a){for(var b=l(this),c=a.getElementsByTag(this.element),d,e=c.count();--e>=0;){d=c.getItem(e);d.isReadOnly()||s.call(this,d,true)}for(var f in b)if(f!=this.element){c=a.getElementsByTag(f);for(e=c.count()-1;e>=0;e--){d=c.getItem(e);d.isReadOnly()||q(d,b[f])}}}function q(a,b,c){if(b=b&&b.attributes)for(var d=0;d",a||b.name,"");return c.join("")},getDefinition:function(){return this._.definition}};CKEDITOR.style.getStyleText=function(a){var b=a._ST;if(b)return b;var b=a.styles,c=a.attributes&&a.attributes.style||"",
    -d="";c.length&&(c=c.replace(L,";"));for(var e in b){var f=b[e],g=(e+":"+f).replace(L,";");f=="inherit"?d=d+g:c=c+g}c.length&&(c=CKEDITOR.tools.normalizeCssText(c,true));return a._ST=c+d};CKEDITOR.style.customHandlers={};CKEDITOR.style.addCustomHandler=function(a){var b=function(a){this._={definition:a};this.setup&&this.setup(a)};b.prototype=CKEDITOR.tools.extend(CKEDITOR.tools.prototypedCopy(CKEDITOR.style.prototype),{assignedTo:CKEDITOR.STYLE_OBJECT},a,true);return this.customHandlers[a.type]=b};
    -var J=CKEDITOR.POSITION_PRECEDING|CKEDITOR.POSITION_IDENTICAL|CKEDITOR.POSITION_IS_CONTAINED,F=CKEDITOR.POSITION_FOLLOWING|CKEDITOR.POSITION_IDENTICAL|CKEDITOR.POSITION_IS_CONTAINED})();CKEDITOR.styleCommand=function(a,e){this.requiredContent=this.allowedContent=this.style=a;CKEDITOR.tools.extend(this,e,true)};CKEDITOR.styleCommand.prototype.exec=function(a){a.focus();this.state==CKEDITOR.TRISTATE_OFF?a.applyStyle(this.style):this.state==CKEDITOR.TRISTATE_ON&&a.removeStyle(this.style)};
    -CKEDITOR.stylesSet=new CKEDITOR.resourceManager("","stylesSet");CKEDITOR.addStylesSet=CKEDITOR.tools.bind(CKEDITOR.stylesSet.add,CKEDITOR.stylesSet);CKEDITOR.loadStylesSet=function(a,e,b){CKEDITOR.stylesSet.addExternal(a,e,"");CKEDITOR.stylesSet.load(a,b)};
    -CKEDITOR.tools.extend(CKEDITOR.editor.prototype,{attachStyleStateChange:function(a,e){var b=this._.styleStateChangeCallbacks;if(!b){b=this._.styleStateChangeCallbacks=[];this.on("selectionChange",function(a){for(var d=0;d"}});"use strict";
    -(function(){var a={},e={},b;for(b in CKEDITOR.dtd.$blockLimit)b in CKEDITOR.dtd.$list||(a[b]=1);for(b in CKEDITOR.dtd.$block)b in CKEDITOR.dtd.$blockLimit||b in CKEDITOR.dtd.$empty||(e[b]=1);CKEDITOR.dom.elementPath=function(b,d){var f=null,h=null,n=[],i=b,k,d=d||b.getDocument().getBody();do if(i.type==CKEDITOR.NODE_ELEMENT){n.push(i);if(!this.lastElement){this.lastElement=i;if(i.is(CKEDITOR.dtd.$object)||i.getAttribute("contenteditable")=="false")continue}if(i.equals(d))break;if(!h){k=i.getName();
    -i.getAttribute("contenteditable")=="true"?h=i:!f&&e[k]&&(f=i);if(a[k]){var o;if(o=!f){if(k=k=="div"){a:{k=i.getChildren();o=0;for(var s=k.count();o-1}:typeof a=="function"?c=a:typeof a=="object"&&(c=
    -function(b){return b.getName()in a});var d=this.elements,f=d.length;e&&f--;if(b){d=Array.prototype.slice.call(d,0);d.reverse()}for(e=0;e=c){f=d.createText("");f.insertAfter(this)}else{a=d.createText("");a.insertAfter(f);a.remove()}return f},substring:function(a,
    -e){return typeof e!="number"?this.$.nodeValue.substr(a):this.$.nodeValue.substring(a,e)}});
    -(function(){function a(a,c,d){var e=a.serializable,h=c[d?"endContainer":"startContainer"],n=d?"endOffset":"startOffset",i=e?c.document.getById(a.startNode):a.startNode,a=e?c.document.getById(a.endNode):a.endNode;if(h.equals(i.getPrevious())){c.startOffset=c.startOffset-h.getLength()-a.getPrevious().getLength();h=a.getNext()}else if(h.equals(a.getPrevious())){c.startOffset=c.startOffset-h.getLength();h=a.getNext()}h.equals(i.getParent())&&c[n]++;h.equals(a.getParent())&&c[n]++;c[d?"endContainer":"startContainer"]=
    -h;return c}CKEDITOR.dom.rangeList=function(a){if(a instanceof CKEDITOR.dom.rangeList)return a;a?a instanceof CKEDITOR.dom.range&&(a=[a]):a=[];return CKEDITOR.tools.extend(a,e)};var e={createIterator:function(){var a=this,c=CKEDITOR.dom.walker.bookmark(),d=[],e;return{getNextRange:function(h){e=e==void 0?0:e+1;var n=a[e];if(n&&a.length>1){if(!e)for(var i=a.length-1;i>=0;i--)d.unshift(a[i].createBookmark(true));if(h)for(var k=0;a[e+k+1];){for(var o=n.document,h=0,i=o.getById(d[k].endNode),o=o.getById(d[k+
    -1].startNode);;){i=i.getNextSourceNode(false);if(o.equals(i))h=1;else if(c(i)||i.type==CKEDITOR.NODE_ELEMENT&&i.isBlockBoundary())continue;break}if(!h)break;k++}for(n.moveToBookmark(d.shift());k--;){i=a[++e];i.moveToBookmark(d.shift());n.setEnd(i.endContainer,i.endOffset)}}return n}}},createBookmarks:function(b){for(var c=[],d,e=0;e"}});"use strict";
    +(function(){var a={},c={},b;for(b in CKEDITOR.dtd.$blockLimit)b in CKEDITOR.dtd.$list||(a[b]=1);for(b in CKEDITOR.dtd.$block)b in CKEDITOR.dtd.$blockLimit||b in CKEDITOR.dtd.$empty||(c[b]=1);CKEDITOR.dom.elementPath=function(b,d){var e=null,g=null,n=[],h=b,i,d=d||b.getDocument().getBody();do if(h.type==CKEDITOR.NODE_ELEMENT){n.push(h);if(!this.lastElement){this.lastElement=h;if(h.is(CKEDITOR.dtd.$object)||h.getAttribute("contenteditable")=="false")continue}if(h.equals(d))break;if(!g){i=h.getName();
    +h.getAttribute("contenteditable")=="true"?g=h:!e&&c[i]&&(e=h);if(a[i]){var m;if(m=!e){if(i=i=="div"){a:{i=h.getChildren();m=0;for(var p=i.count();m-1}:typeof a=="function"?f=a:typeof a=="object"&&(f=
    +function(b){return b.getName()in a});var d=this.elements,e=d.length;c&&e--;if(b){d=Array.prototype.slice.call(d,0);d.reverse()}for(c=0;c=f){e=d.createText("");e.insertAfter(this)}else{a=d.createText("");a.insertAfter(e);a.remove()}return e},substring:function(a,
    +c){return typeof c!="number"?this.$.nodeValue.substr(a):this.$.nodeValue.substring(a,c)}});
    +(function(){function a(a,c,d){var e=a.serializable,g=c[d?"endContainer":"startContainer"],n=d?"endOffset":"startOffset",h=e?c.document.getById(a.startNode):a.startNode,a=e?c.document.getById(a.endNode):a.endNode;if(g.equals(h.getPrevious())){c.startOffset=c.startOffset-g.getLength()-a.getPrevious().getLength();g=a.getNext()}else if(g.equals(a.getPrevious())){c.startOffset=c.startOffset-g.getLength();g=a.getNext()}g.equals(h.getParent())&&c[n]++;g.equals(a.getParent())&&c[n]++;c[d?"endContainer":"startContainer"]=
    +g;return c}CKEDITOR.dom.rangeList=function(a){if(a instanceof CKEDITOR.dom.rangeList)return a;a?a instanceof CKEDITOR.dom.range&&(a=[a]):a=[];return CKEDITOR.tools.extend(a,c)};var c={createIterator:function(){var a=this,c=CKEDITOR.dom.walker.bookmark(),d=[],e;return{getNextRange:function(g){e=e==void 0?0:e+1;var n=a[e];if(n&&a.length>1){if(!e)for(var h=a.length-1;h>=0;h--)d.unshift(a[h].createBookmark(true));if(g)for(var i=0;a[e+i+1];){for(var m=n.document,g=0,h=m.getById(d[i].endNode),m=m.getById(d[i+
    +1].startNode);;){h=h.getNextSourceNode(false);if(m.equals(h))g=1;else if(c(h)||h.type==CKEDITOR.NODE_ELEMENT&&h.isBlockBoundary())continue;break}if(!g)break;i++}for(n.moveToBookmark(d.shift());i--;){h=a[++e];h.moveToBookmark(d.shift());n.setEnd(h.endContainer,h.endOffset)}}return n}}},createBookmarks:function(b){for(var c=[],d,e=0;eb?-1:1}),e=0,f;e
    ',CKEDITOR.document);a.appendTo(CKEDITOR.document.getHead());try{var e=a.getComputedStyle("border-top-color"),b=a.getComputedStyle("border-right-color");CKEDITOR.env.hc=!!(e&&e==b)}catch(c){CKEDITOR.env.hc=false}a.remove()}if(CKEDITOR.env.hc)CKEDITOR.env.cssClass=CKEDITOR.env.cssClass+" cke_hc"; -CKEDITOR.document.appendStyleText(".cke{visibility:hidden;}");CKEDITOR.status="loaded";CKEDITOR.fireOnce("loaded");if(a=CKEDITOR._.pending){delete CKEDITOR._.pending;for(e=0;eb?-1:1}),e=0,f;e
    ',CKEDITOR.document);a.appendTo(CKEDITOR.document.getHead());try{var c=a.getComputedStyle("border-top-color"),b=a.getComputedStyle("border-right-color");CKEDITOR.env.hc=!!(c&&c==b)}catch(f){CKEDITOR.env.hc=false}a.remove()}if(CKEDITOR.env.hc)CKEDITOR.env.cssClass=CKEDITOR.env.cssClass+" cke_hc"; +CKEDITOR.document.appendStyleText(".cke{visibility:hidden;}");CKEDITOR.status="loaded";CKEDITOR.fireOnce("loaded");if(a=CKEDITOR._.pending){delete CKEDITOR._.pending;for(c=0;carguments.length)){var c=i.call(this,a);c.labelId=CKEDITOR.tools.getNextId()+"_label";this._.children=[];CKEDITOR.ui.dialog.uiElement.call(this,b,a,d,"div",null,{role:"presentation"},function(){var f=[],d=a.required?" cke_required":"";"horizontal"!= -a.labelLayout?f.push('",'
    ',e.call(this,b,a),"
    "):(d={type:"hbox",widths:a.widths,padding:0,children:[{type:"html",html:'