Compare commits

...
Author SHA1 Message Date
Francis Lachapelle dc3741f436 (html,js) Adjust padding of dialogs
Following this commit:
https://github.com/angular/material/commit/d2140b1565b7e91d4f0b11b6c3983b976abd00f8
2015-10-14 10:31:15 -04:00
Francis Lachapelle feac8bb8f4 (js) Compute contrast color for background color 2015-10-14 10:00:14 -04:00
Francis Lachapelle 9f11e68de8 Group functions in utils.js 2015-10-14 10:00:13 -04:00
Ludovic Marcotte 15de70b938 (fix) make sure English is always used when generating Date headers using EAS (#3356) 2015-10-14 09:58:42 -04:00
Ludovic Marcotte ea24e9586b (fix) we no longer always rewrite emails for Outlook when using EAS 2015-10-14 09:30:35 -04:00
Ludovic Marcotte 4e9911419a (fix) handle references correctly over EAS (#3365) 2015-10-14 09:28:12 -04:00
Ludovic Marcotte bfa3cf379c (fix) numerous EAS fixes when connections are dropped before the EAS client receives the response (#3058, #2849)
Conflicts:

	NEWS
2015-10-14 09:23:03 -04:00
Francis Lachapelle 4002da9323 (css) Adjustment in day/week views 2015-10-14 09:06:11 -04:00
Francis Lachapelle d5c09f6114 Change order of JS files in Mail module 2015-10-14 09:00:01 -04:00
Francis Lachapelle 737a19e653 (html) Add labels in appointment & task editors 2015-10-14 08:59:11 -04:00
Francis Lachapelle 43248fb5ea (html) Minor improvements to message viewer 2015-10-14 08:55:57 -04:00
Francis Lachapelle 1118dbceff Bump version to 3.0.0b3 2015-10-13 16:09:36 -04:00
Francis Lachapelle 369060a52f (js/css) Update generated files
With Angular Material 0.11.3
2015-10-13 16:05:39 -04:00
Francis Lachapelle 4da53b88b6 Fix display of Admin module on small devices 2015-10-13 14:30:09 -04:00
Francis Lachapelle 8924a2b670 (css) Adjustments in Contact viewer and editor 2015-10-09 16:50:54 -04:00
Francis Lachapelle 62537e0c15 (doc) Improve API documentation 2015-10-09 16:50:09 -04:00
Francis Lachapelle 7ef976bfe4 (css) Adjustments in Contact viewer and editor 2015-10-09 16:49:33 -04:00
Francis Lachapelle 05515673b7 Improve ACL editor 2015-10-09 16:49:08 -04:00
Francis Lachapelle 84f4698225 (js) Add missing dependency in Scheduler routing 2015-10-09 16:20:19 -04:00
Francis Lachapelle e5fdc28241 (js) Improve error handling in Contacts routing 2015-10-09 16:19:28 -04:00
Francis Lachapelle 7ddf7be705 Improve Administration module 2015-10-09 16:10:39 -04:00
Francis Lachapelle b63ee6998d (js) Fix avatar for groups 2015-10-09 16:03:05 -04:00
Francis Lachapelle 0b3095f7ed (js) Add missing dependency to Contact module 2015-10-09 16:01:56 -04:00
Francis Lachapelle e88af22708 (css) Minor improvement to preferences template 2015-10-09 13:37:01 -04:00
Francis Lachapelle d68008b5fd Update npm packages and add time-grunt 2015-10-09 12:11:37 -04:00
Francis Lachapelle e2d6e8ffe9 (js) Fix "file into" option of Sieve filter 2015-10-08 16:42:24 -04:00
Francis Lachapelle 2ad02b8386 (js) Improve User.$filter 2015-10-08 10:04:25 -04:00
Ludovic Marcotte 58afdedcc1 (fix) added missing template 2015-10-07 16:21:06 -04:00
Ludovic Marcotte e6be7e00ba (fix) improved the start/end delta handling and fixed the attendees viewer 2015-10-07 14:42:37 -04:00
Ludovic Marcotte 74eb0846e8 (fix) correctly round up the start/end/due time to 15 mins 2015-10-07 09:22:26 -04:00
Ludovic Marcotte 4f4b00cc54 (feat) first pass of working code for ACL admin module 2015-10-06 16:38:16 -04:00
Francis Lachapelle ad40bff91f Add missing content type to mixed part viewer 2015-10-06 15:38:37 -04:00
Francis Lachapelle 13f4e5a4de (css) Update SASS for latest md 2015-10-06 13:38:15 -04:00
Francis Lachapelle 459b183a42 Add jsdoc.json
Configuration file for jsdoc:

  jsdoc -c jsdoc.json
2015-10-06 09:55:32 -04:00
Francis Lachapelle 9fa9b7acab Improve mail (Sieve) filter editor
- Used 'controller as' syntax;
- Don't create new filter if user cancels the edition;
- Set default values for new filter.
2015-10-05 10:45:30 -04:00
Francis Lachapelle a393128d7a (js,html) View raw source of components 2015-10-02 17:01:06 -04:00
Francis Lachapelle 73ac55acfe (js) Fix advanced search construction 2015-10-02 15:43:02 -04:00
Francis Lachapelle e1393c3af2 Improve display of current day in month view 2015-10-02 15:13:30 -04:00
Francis Lachapelle d27c792324 (js,html) Allow the removable of attendees 2015-10-02 11:22:30 -04:00
Francis Lachapelle 42d306a24b (js) Remove some debugging output 2015-10-02 11:16:47 -04:00
Francis Lachapelle 6a7e06b0bb (html) Fix display of attendees
This bug is due to changes in the CSS of Angular Material.
2015-10-02 11:15:26 -04:00
Francis Lachapelle e36e9fd826 (html) Fix secondary click in calendars list 2015-10-02 09:41:57 -04:00
Francis Lachapelle aface8f8cf (html) Restore shadows in toolbars 2015-10-01 09:34:37 -04:00
Francis Lachapelle 790fc8e940 (js) Fix (sub)mailboxes state restore 2015-09-30 16:41:29 -04:00
Francis Lachapelle 35d1ca8775 Restore expandable mailboxes 2015-09-30 16:17:05 -04:00
Francis Lachapelle e8ca329070 Save mailboxes state as an array, not a string 2015-09-30 16:12:23 -04:00
Francis Lachapelle d07e254026 (js) Simplify Mailbox service 2015-09-30 11:29:05 -04:00
Francis Lachapelle 0f08571a83 (js) Fix ensureTarget filter to preserve CSS 2015-09-30 11:28:22 -04:00
Francis Lachapelle 45bc5a5368 Multicolumn day view
Restored multicolumn day view from v2.

Month view as also been improved.
2015-09-29 14:17:04 -04:00
Francis Lachapelle d51bef162a (js) Add border class for folder colors 2015-09-29 14:01:16 -04:00
Francis Lachapelle 1535660a31 Localization 2015-09-29 09:34:01 -04:00
Francis Lachapelle 90063ae563 (js) Fix error in advanced search mode 2015-09-29 08:58:23 -04:00
Francis Lachapelle b27cfc2bd8 (js) Delete an event or a task 2015-09-28 11:27:20 -04:00
Francis Lachapelle 5496dd97d9 Improve lists by compacting items 2015-09-25 16:07:56 -04:00
Francis Lachapelle 31f2e4b549 (js) Fix selected message class in advanced search
Also improved display of accounts subheaders.
2015-09-24 21:42:33 -04:00
Francis Lachapelle ec8c2d08e2 Fix handling of mail priority 2015-09-24 14:36:37 -04:00
Ludovic Marcotte 1ce9d71635 (fix) add robustness to part states handling 2015-09-24 13:21:23 -04:00
Francis Lachapelle a6c440d245 (js) Improve renaming an address book 2015-09-24 11:12:57 -04:00
Ludovic Marcotte e3d3eb171d (fix) small optimization 2015-09-24 11:03:19 -04:00
Francis Lachapelle 3a4365b15a (js) Fix ngSwitch in Contacts module 2015-09-24 11:01:27 -04:00
Francis Lachapelle 7f3d57c8dc (html) Sort by lastname and firstname in Contacts 2015-09-24 10:55:08 -04:00
Ludovic Marcotte e93853d910 (fix) make sure to open card's url in new window 2015-09-23 20:35:55 -04:00
Francis Lachapelle fcba6f2ddb Improve Calendar views
Moved toolbars inside "list" and "detail" views, like we do in the other
modules. With this change, it was possible to move the navitation button
(chevrons) inside the top toolbar of the detail view. Also move the view
headers inside a toolbar. The all-day events of the day and week views
have also been moved inside this toolbar.
2015-09-23 15:24:24 -04:00
Ludovic Marcotte eb50f94b2b (fix) remove ng-click to allow FF to select/unselect personal calendars 2015-09-23 09:22:01 -04:00
Francis Lachapelle 9f7b69b037 (html) Restore active module icon as disabled 2015-09-22 12:00:07 -04:00
Francis Lachapelle c6cd3b0e1d Update angular-material 2015-09-22 11:58:18 -04:00
Francis Lachapelle 31026a5bed (js/css) Update generated files 2015-09-22 11:56:33 -04:00
Francis Lachapelle 08fbc00d7a (js) Add "today" button to Calendar module 2015-09-22 10:17:17 -04:00
Francis Lachapelle 957336b24c (css) Fix display of attendees editor 2015-09-22 09:41:47 -04:00
Francis Lachapelle 2a925de52c (js, css) Improve timepicker 2015-09-21 21:13:28 -04:00
Francis Lachapelle cf8dd02411 (css) Wrap Scheduler directives in unique selector 2015-09-21 20:56:55 -04:00
Francis Lachapelle da37b3f868 Rename Slovenian template
Closes #104
2015-09-21 20:40:39 -04:00
Francis Lachapelle 24450a9c20 Update NEWS file
Conflicts:

	NEWS
2015-09-21 16:13:16 -04:00
Francis Lachapelle 964f79db35 Update NEWS file 2015-09-21 16:10:43 -04:00
Ludovic Marcotte c0f51cf731 (fix) don't offer the delete option on personal resources 2015-09-21 15:00:32 -04:00
Francis Lachapelle ae3ce7186d (css) Set flex width of search field menu 2015-09-21 12:29:07 -04:00
Francis Lachapelle 5034a72ed1 (css) Lower events overlapping (5% => 2%) 2015-09-21 12:29:06 -04:00
Ludovic Marcotte fdad1e061a (fix) set default's module path name to SOGo 2015-09-21 12:02:33 -04:00
Francis Lachapelle d5d54822b0 (html) Improve mail editor 2015-09-21 11:58:48 -04:00
Francis Lachapelle 81cdf5be7b (css) Update SASS for latest md 2015-09-21 11:50:47 -04:00
Francis Lachapelle 959ea0f62f (html) Show active tasks count in Calendar module 2015-09-21 09:49:14 -04:00
Ludovic Marcotte 1c666e8ead (fix) simplified logic and improved code readability 2015-09-21 08:59:36 -04:00
Ludovic Marcotte 7692832672 (fix) define -modulePath to avoid broken ApplicationURL path 2015-09-21 08:41:31 -04:00
Francis Lachapelle 2bfd9f983d (html) Hide timepicker when editing all-day event 2015-09-19 22:22:39 -04:00
Francis Lachapelle 2a5e19a762 (css) Improve Calendar views 2015-09-19 22:22:39 -04:00
Ludovic Marcotte 258f5e0b25 (fix) correctly handle module name for "folder less" SOGo modules 2015-09-18 20:30:35 -04:00
Ludovic Marcotte 3609e41205 (fix) make sure the autoreply addresses are always defined to avoid an exception in v2 if not 2015-09-18 19:55:37 -04:00
Francis Lachapelle 2ea2edbccd Display all-day events in day & week views
All-day events are now displayed and the day/week views are now built using flexbox.
2015-09-18 16:11:31 -04:00
Francis Lachapelle fab9e938fc Fix URL escaping unti test 2015-09-18 13:53:32 -04:00
Ludovic Marcotte 9a2f3914e2 (fix) additional fix for issue #3152 2015-09-17 16:29:21 -04:00
Ludovic Marcotte f13b3dd201 (fix) typo in previous commit 2015-09-17 16:02:45 -04:00
Ludovic Marcotte 413550a025 (fix) memory leak when sync'ing GCS collections 2015-09-17 15:59:20 -04:00
Francis Lachapelle 06ac255f1a (css) Badge for mail folders unread count 2015-09-17 15:08:21 -04:00
Francis Lachapelle 0682b24438 (fix) Selection of Mail advanced search criteria 2015-09-17 15:06:48 -04:00
Francis Lachapelle a01926b878 (html) Improve folder subscription dialog box 2015-09-17 10:37:35 -04:00
Francis Lachapelle 58ff8139e9 Allow rename of calendars 2015-09-17 10:33:12 -04:00
Francis Lachapelle 70bb512bb8 Improve folders rename 2015-09-16 16:15:04 -04:00
Francis Lachapelle 7b580f8af6 (js) Fix variable verification in sgSearch 2015-09-16 14:26:24 -04:00
Francis Lachapelle 11d9765a69 Add verbose output to "sogo-tool remove" command 2015-09-16 11:40:14 -04:00
Ludovic Marcotte 24a3ecd2d6 Updated .gitingore for EAS bundle 2015-09-16 10:41:20 -04:00
Ludovic Marcotte 139ed39663 (fix) update the doc regarding the temporarily dropping rhel/old debian/ubuntu support 2015-09-16 09:58:36 -04:00
Francis Lachapelle 875d23aea0 (js/css) Update generated files 2015-09-15 12:05:37 -04:00
Francis Lachapelle c4ad8a64fa (html) Improve login page 2015-09-15 11:42:47 -04:00
Francis Lachapelle c464791d92 Improve display of mail advanced search 2015-09-14 21:35:34 -04:00
Francis Lachapelle 92bf494973 (css) Improve display of folder name 2015-09-14 14:58:00 -04:00
Francis Lachapelle 47ab4b837c (js) Fix issues with the mail advanced search
- fixed loading of headers when scrolling the virtual repeater;
- fixed viewing a single message;
- fixed search from a mailbox that contains subfolders.
2015-09-14 14:58:00 -04:00
Ludovic Marcotte e30fb0d1fb (feat) basic (but empty) admin module for SOGo v3 2015-09-14 14:09:29 -04:00
Ludovic Marcotte 3efbf65f80 (fix) added missing test 2015-09-14 13:40:29 -04:00
Juan Vallés 0982d50643 Improve first character check in CSS identifiers
A CSS identifier can't start with a digit, so when a folder name does,
a '_' character is appended at the beginning of its CSS identifier.

The check for this first character used the `isdigit()` function, which
takes a `char` argument, while `[self objectAtIndex: 0]` returns a
`unichar`, i.e. a 16-bit unsigned integer. This caused some non-digit
characters to pass this check (e.g. Chinese characters), ending up with
an underscore at the beginning of the folder name.
2015-09-14 12:55:32 -04:00
Enrique J. Hernández Blasco 532105f1fa oc-task: Save tasks from Outlook
It was not working because we try to save as component the
full calendar and its parent was nil. We have to save the calendar
itself to save the task in the personal calendar.
2015-09-14 12:55:26 -04:00
Ludovic Marcotte 7cdfaf4d9c (fix) add missing flex attribute 2015-09-14 11:23:27 -04:00
Ludovic Marcotte 8cb19546df (fix) improve spec file regarding disabling OC build 2015-09-14 11:06:18 -04:00
Ludovic Marcotte 6dc02abe80 (fix) Start/end shifting by 1 hour due to timezone change on last Sunday of October 2015 (#3344) 2015-09-14 10:56:14 -04:00
Francis Lachapelle f70876aa05 (html) Alternate conditions in topnav toolbar
Fixes an issue with Firefox
2015-09-14 10:14:00 -04:00
Francis Lachapelle 3efdbf0166 (js) Fix mdColors module dependencies 2015-09-11 12:24:30 -04:00
Francis Lachapelle 348c81bf6c (html) Fix display of Calendar module on "sm" 2015-09-11 12:08:18 -04:00
Francis Lachapelle 44158af057 (html) Fix display of avatars in invitation mails 2015-09-11 11:56:26 -04:00
Francis Lachapelle 4bb1fa0995 (html) Improve Task editor 2015-09-11 11:32:20 -04:00
Francis Lachapelle 05882362f0 (html) Fix UserRightsEditor templates 2015-09-11 11:30:54 -04:00
Francis Lachapelle 90a3fcacaf (js) Disable debug data if UIxDebug not enabled
Disable some Angular debugging information if SOGoUIxDebugEnabled is not
set.
2015-09-11 10:58:17 -04:00
Francis Lachapelle 45ac6845a5 (html) Improve appointment editor 2015-09-11 09:33:39 -04:00
Francis Lachapelle b4af6f5033 (css) Improve sgTimepicker 2015-09-11 09:32:17 -04:00
Ludovic Marcotte 6bbd182559 (fix) safe-guard against not-yet-created calendar of local users 2015-09-10 20:08:48 -04:00
Ludovic Marcotte 7290fa2ea2 (fix) disable oc builds on el6 for now 2015-09-10 19:45:59 -04:00
Ludovic Marcotte 788f36b96e (fix) slight modification for fix #3293 2015-09-10 19:11:37 -04:00
Ludovic Marcotte cb0d327f90 (feat) initial implementation of advanced email search
There's one bug where viewing of the messages from the results
is still impossible due to the ui-router. This will be fixed
very soon.
2015-09-10 16:38:30 -04:00
Francis Lachapelle b646e35f5d (js) Fix mail labels handling in the Mail module 2015-09-10 11:44:09 -04:00
Francis Lachapelle f3a5df37ae (js) Don't load card in multi-selection mode 2015-09-10 09:19:28 -04:00
Francis Lachapelle 7a7547c7c3 (js) mdColors module to expose the themes colors 2015-09-09 16:39:56 -04:00
Francis Lachapelle 11e7af3450 Adopt theming system of Angular Material
The SASS has been simplified so that different themes can be applied by
only using the $mdThemingProvider serivce.

The resulting design is more "Materialized" and less prone to the
future changes of Angular Material.
2015-09-09 16:35:09 -04:00
Francis Lachapelle 16d312eb98 (js) Don't load item in multi-selection mode
Also review the toolbars of the Calendar module.
2015-09-09 16:06:17 -04:00
Ludovic Marcotte 295b53df66 (fix) bumped to beta 2 2015-09-09 15:40:13 -04:00
Francis Lachapelle cab6a8c1e2 (html) Use md-subheader in contacts/messages lists 2015-09-09 14:43:09 -04:00
Ludovic Marcotte 59ad8a2e5a (fix) improved EAS speed during initial syncing of large mailboxes (#3293)
Conflicts:

	SoObjects/Mailer/SOGoMailFolder.h
	SoObjects/SOGo/SOGoGCSFolder.m
2015-09-09 10:22:29 -04:00
Ludovic Marcotte ab8feff67d (fix) avoid too-many LIST commands over EAS (#3294) 2015-09-09 10:13:27 -04:00
Ludovic Marcotte b9294da996 Updated NEWS file regarding PR#102 2015-09-09 08:25:23 -04:00
Ludovic Marcotte 18a300ca5b (fix) appplied fix from Zentyal - PR#102
Original commit: 33a1d2c397
2015-09-09 08:22:33 -04:00
Francis Lachapelle b8b45eda75 (js) Fix autocompletion of mail tags 2015-09-08 15:29:49 -04:00
Francis Lachapelle 370be4f835 (fix) Reload of message when saving draft 2015-09-08 12:22:37 -04:00
Francis Lachapelle 2720aebf04 Initial popup view for messages 2015-09-04 17:31:16 -04:00
Francis Lachapelle bf02b3046f (fix) Handle user's default for remote images 2015-09-04 16:43:01 -04:00
Francis Lachapelle 3d4a965412 (html) Use sgAvatarImage directive in Scheduler 2015-09-04 12:11:20 -04:00
Francis Lachapelle 372206e69e (html) Improve folder subscription dialog 2015-09-04 10:32:26 -04:00
Francis Lachapelle 9dc00acd53 (html) Fix toolbar padding 2015-09-04 10:31:40 -04:00
Francis Lachapelle 9d5f42eefa (js,html) Improve sgAvatarImage directive 2015-09-04 09:09:43 -04:00
Francis Lachapelle f4dcd37570 (html) Improve task editor 2015-09-04 08:53:47 -04:00
Francis Lachapelle c4858ffb2b (css) Decrease minimum height of list items 2015-09-03 22:36:07 -04:00
Francis Lachapelle d72188e2da Improve toolbars on small screens
- normalized toolbars in message viewer, contact viewer and contact
  editor;
- lower width of sidenav on medium screens;
- reworked display of message headers
2015-09-03 16:17:21 -04:00
Francis Lachapelle 3647f066da (js) Fix reference to org attribute in card 2015-09-03 15:51:55 -04:00
Francis Lachapelle e207cc79a7 (html) Improve appointment editor 2015-09-03 15:47:05 -04:00
Francis Lachapelle eb84e618e6 Localization 2015-09-03 15:42:54 -04:00
Francis Lachapelle 669bd99a2d (js) Fix bug when editing a saved draft 2015-09-03 14:11:39 -04:00
Francis Lachapelle 3b0ead7bbd (js) Cleanup unused dependencies 2015-09-03 14:11:39 -04:00
Francis Lachapelle 4483d47e89 (js) Bottom sheet is no longer used 2015-09-03 14:11:38 -04:00
Francis Lachapelle a5213b3321 (css) Improve subheaders with no buttons 2015-09-03 14:11:38 -04:00
Francis Lachapelle e6e34ae053 (js) Don't show progress indicator in dry mode 2015-09-03 14:11:37 -04:00
Francis Lachapelle 815b3b4b03 (html) Improve contact editor 2015-09-03 14:11:37 -04:00
Francis Lachapelle b08d98e2e1 (css) Fix width of sidenav on large screens 2015-09-03 14:11:36 -04:00
Francis Lachapelle 1d2c0ed350 (css) Cleanup Sass 2015-09-03 14:11:35 -04:00
Ludovic Marcotte 0bdbe5bbac (feat) added new sg-placeholder directive for dynamic placeholders 2015-09-03 13:25:44 -04:00
Francis Lachapelle acfa96263b (css) Improve md-dialog on small screens 2015-09-02 15:52:18 -04:00
Francis Lachapelle 99b1353faf (css) Add classes for star and star-border icons 2015-09-02 15:51:07 -04:00
Ludovic Marcotte 27e57e397e (fix) wrong handling of "last module" when logging in 2015-09-01 16:43:56 -04:00
Ludovic Marcotte 778612bf41 (feat) ensure links in mail are opened in a blank target 2015-09-01 15:00:03 -04:00
Ludovic Marcotte 9d92318524 (feat) autorefresh support for calendars 2015-09-01 10:50:50 -04:00
Francis Lachapelle cf9b7fab36 Update .gitignore 2015-09-01 09:55:55 -04:00
Ludovic Marcotte f1037e7d48 Updates for the 3.0b1 release. 2015-08-31 11:27:40 -04:00
Ludovic Marcotte 233b9c0613 (js/css) updates for the new packages 2015-08-31 11:08:41 -04:00
Francis Lachapelle 89206f3705 (css) Fix for latest md changes 2015-08-31 09:51:08 -04:00
Ludovic Marcotte a87daf282c (fix) workaround for Firefox with menus which include md-checkbox
See https://github.com/angular/material/issues/4212 for details
on the Angular Material bug.
2015-08-29 16:30:07 -04:00
Ludovic Marcotte fb4889a15e (fix) small code refactoring 2015-08-28 20:11:21 -04:00
Ludovic Marcotte 3fdabf8671 (fix) properly update flags when message was answered/forwarded 2015-08-28 15:50:46 -04:00
Ludovic Marcotte 66c52f22dd (fix) added timepicker directive 2015-08-28 15:26:21 -04:00
Francis Lachapelle 8f7334f11d (js) Simplify Account and Message services 2015-08-28 15:17:04 -04:00
Ludovic Marcotte 624833eca9 (fix) updates for new packages 2015-08-28 14:09:20 -04:00
Ludovic Marcotte 431c53b84d (feat) new time picker component + enabled it in the calendar module 2015-08-28 13:59:41 -04:00
Francis Lachapelle c9d9f7b47a (js) Add caching of cards 2015-08-27 15:55:56 -04:00
Francis Lachapelle 1667f8879d (js) New progress indicator in AddressBook module 2015-08-27 14:09:43 -04:00
Francis Lachapelle 3611c4021d Improve view detail (messages, cards) 2015-08-27 13:37:47 -04:00
Luc Charland bba0d6f44d make the tests more robust when data is invalid in DB/Webdav 2015-08-27 11:51:58 -04:00
Luc Charland bf390f17dd fixed when strings not initialized in prefs 2015-08-27 11:51:58 -04:00
Ludovic Marcotte 834aba85a6 (fix) cleanups 2015-08-27 11:47:51 -04:00
Ludovic Marcotte d878c69c15 (feat) can now create mails from address book module 2015-08-27 11:01:23 -04:00
Francis Lachapelle d9d59c6b52 (fix) Force a single mail composer dialog 2015-08-26 21:22:39 -04:00
Francis Lachapelle 69000a0929 Review animations of view detail (messages, cards) 2015-08-26 17:28:03 -04:00
Ludovic Marcotte 876d9cfa21 (fix) make messages loading very fast
Avoid calling angular.extend() loading when unwrapping the mailbox
and never call again Message.id directly, but rather use Message.$absolutePath()
which does initialization and caching
2015-08-26 11:56:47 -04:00
Luc Charland 117e6b6bc1 added the multiple phone test 2015-08-26 11:28:54 -04:00
Francis Lachapelle 01e2256483 (css) Improve autocompletion menu 2015-08-26 09:48:47 -04:00
Francis Lachapelle 49e7cf0994 (css) Adapt fullscreen dialogs to new md layout 2015-08-26 09:41:49 -04:00
Ludovic Marcotte b3d6973fb1 (fix) small optimization 2015-08-26 09:31:08 -04:00
Ludovic Marcotte aae522259f (fix) expose the json accounts call 2015-08-26 08:28:19 -04:00
Ludovic Marcotte 68ef8784a0 (fix) added json call to get all mail accounts 2015-08-26 08:27:45 -04:00
Francis Lachapelle da821ea6c7 (js) Improve recipient auto-completion 2015-08-25 22:17:57 -04:00
Ludovic Marcotte 4dee99e0ba (fix) improved padding usage to better align icons 2015-08-25 20:47:12 -04:00
Francis Lachapelle eea08d083d (html) Improve contacts list 2015-08-25 14:56:30 -04:00
Francis Lachapelle 8055acfd4a (fix) Restore previous sort in Contacts module 2015-08-25 14:20:20 -04:00
Francis Lachapelle 1fc947d8c8 (fix) Define CSS styles for ALL calendars 2015-08-25 13:46:33 -04:00
Francis Lachapelle 7b3a238fb9 (js) New progress indicator in Mail module 2015-08-25 12:20:46 -04:00
Ludovic Marcotte 99d121fd3e (fix) updates for new packages 2015-08-25 11:46:14 -04:00
Ludovic Marcotte eab3ee111a (feat) now use md-on-demand for (hopefully) faster mailbox loading 2015-08-25 11:45:17 -04:00
Francis Lachapelle eeb6be1f08 (css) Match new ng-material files names 2015-08-25 11:01:28 -04:00
Francis Lachapelle 29108bbb62 (html) Localization 2015-08-25 06:34:34 -04:00
Ludovic Marcotte 9c6884bd7d (feat) add single export/save as actions 2015-08-24 20:04:56 -04:00
Francis Lachapelle 0aac4897fa (fix) Set virtual item size in contacts list 2015-08-24 17:28:01 -04:00
Ludovic Marcotte 2a99d1fd80 (fix) reset search filter when switching address books 2015-08-24 16:17:44 -04:00
Francis Lachapelle ba148eec5b (js) Fix error in Component.prototype.$reset 2015-08-24 14:54:25 -04:00
Francis Lachapelle 49c4884cc9 (html) Improve grids layout in recurrence editor 2015-08-24 14:40:20 -04:00
Francis Lachapelle e5d6ee93c3 Update Angular to version 1.4.x 2015-08-24 11:32:40 -04:00
Ludovic Marcotte 40eff05d4e (feat) added toolbar to account editing dialog 2015-08-24 11:26:00 -04:00
Francis Lachapelle be031fe2c9 (html) Restore animation when opening a card 2015-08-24 11:24:51 -04:00
Francis Lachapelle 0fe1419289 (html) Enlarge folder links dialog width 2015-08-24 11:23:34 -04:00
Francis Lachapelle 9db633cada (html) Replace md-layout-fill by layout-fill 2015-08-24 11:21:17 -04:00
Ludovic Marcotte 8a2d7d56e0 (feat) added toolbar to filter editor dialog 2015-08-24 11:20:23 -04:00
Francis Lachapelle 0c17e745f1 (html) Localization 2015-08-24 11:11:23 -04:00
Ludovic Marcotte 18bc75e50b (fix) adjust import path due to md changes 2015-08-24 08:49:31 -04:00
Ludovic Marcotte 68ad3c1045 (fix) localize 'me' 2015-08-22 07:51:13 -04:00
Ludovic Marcotte 85c8aebbd4 (feat) initial support for contact import in the address book module 2015-08-21 20:56:21 -04:00
Ludovic Marcotte 0a8834193f (fix) updates for new packages 2015-08-21 13:40:07 -04:00
Ludovic Marcotte 91e4c2cefc (feat) improved the mailviewer with regards to sender + recipients 2015-08-21 13:37:18 -04:00
Ludovic Marcotte b80d8dbb06 (fix) Session fix when SOGoEnableDomainBasedUID is enabled but logins are domain-less 2015-08-21 10:51:45 -04:00
Ludovic Marcotte 525d1ad80f (fix) also return all calendars in findAll 2015-08-20 22:38:01 -04:00
Ludovic Marcotte 332dd0d96d (fix) small optimization 2015-08-20 22:37:27 -04:00
Ludovic Marcotte a45000d15d (fix) show delete option only where objectEraser is on 2015-08-20 19:42:24 -04:00
Ludovic Marcotte 3301bdb055 (fix) return the complete list of addressbooks
This fixes many bugs / side effects, especially with ACLs
2015-08-20 19:37:30 -04:00
Francis Lachapelle 2f82b5cfd3 (js) Localization of toast message 2015-08-20 16:39:34 -04:00
Francis Lachapelle e3b64e035d (css) New md-fab-overlap-bottom class 2015-08-20 16:28:11 -04:00
Francis Lachapelle 1470258b5c (js) Simplify Account.prototype.$newMailbox 2015-08-20 16:28:11 -04:00
Francis Lachapelle 83fee27ca8 (js) Simplify Mailbox.prototype.$emptyTrash 2015-08-20 16:28:10 -04:00
Ludovic Marcotte e5b0596bc9 (fix) don't convert back the birthday date object on non-deep omits 2015-08-20 15:47:41 -04:00
Ludovic Marcotte 26e8276b08 (feat) improved ACLs handling for address book 2015-08-20 15:47:11 -04:00
Ludovic Marcotte 078ba95f8e (feat) show small toast when preferences are saved to give user's feedback 2015-08-20 14:53:43 -04:00
Ludovic Marcotte 65525dd33b (feat) added date picker in contact editor + now handle correctly birthday 2015-08-20 10:29:47 -04:00
Ludovic Marcotte da81f7bc58 (fix) set background to transparent by default 2015-08-19 20:45:07 -04:00
Ludovic Marcotte 8fcd594be6 (fix) removed unused code 2015-08-19 19:38:42 -04:00
Ludovic Marcotte 548f056b9c (fix) small formatting nit 2015-08-19 11:23:03 -04:00
Ludovic Marcotte dffdbf318c (feat) new 'viewable' property on events/tasks for full viewers 2015-08-19 11:22:17 -04:00
Ludovic Marcotte 161bcfac87 (fix) proper loading of transalations 2015-08-19 10:39:10 -04:00
Ludovic Marcotte c6f17cd45c (feat) make use of the sieve capabilities 2015-08-19 08:47:33 -04:00
Ludovic Marcotte 38713a6c67 (feat) handle multiple home/work phones, emails and urls 2015-08-18 16:21:47 -04:00
Ludovic Marcotte 8cef98eba1 (fix) add flex/flex-sm attributes 2015-08-18 15:32:44 -04:00
Ludovic Marcotte 2144d3122a (feat) added support back for SOGoForwardConstraints 2015-08-18 15:25:51 -04:00
Ludovic Marcotte 9d272ae063 (feat) added the 'add default email addresses' feature in vacation module 2015-08-18 14:57:29 -04:00
Ludovic Marcotte fcd0140ffd Revert "(fix) by default, open links in new tab/window from html mails"
This reverts commit 869b3c4cd1.
2015-08-18 14:52:47 -04:00
Ludovic Marcotte d783782af7 (fix) adding basic flags 2015-08-18 11:31:42 -04:00
Ludovic Marcotte 869b3c4cd1 (fix) by default, open links in new tab/window from html mails 2015-08-18 11:13:27 -04:00
Ludovic Marcotte 2da2937391 (fix) formatting improvements 2015-08-18 11:01:59 -04:00
Ludovic Marcotte 5034d6655a (fix) correctly refresh the mailboxes list when empty'ing the trash that contains subfolders 2015-08-18 10:10:37 -04:00
Ludovic Marcotte d1446f7dc1 (fix) broken top level mailbox creation 2015-08-18 09:57:07 -04:00
Ludovic Marcotte c5a219add8 (fix) wrong check for web calendars 2015-08-18 09:27:33 -04:00
Ludovic Marcotte bc2c839fbc Update for the new packaging 2015-08-18 08:39:20 -04:00
Ludovic Marcotte 2e736490d5 Merge branch 'master' of https://github.com/inverse-inc/sogo 2015-08-17 16:22:41 -04:00
Ludovic Marcotte 20bf66d931 (feat) handle links to addressbooks like we do for calendars
also included missing template commit for calendar
2015-08-17 16:21:10 -04:00
Francis Lachapelle 864a7f8028 (html) Improve Preferences module 2015-08-17 16:14:47 -04:00
Ludovic Marcotte 7b5541b5d1 (fix) reuse the calendar urls from the model 2015-08-17 13:44:21 -04:00
Ludovic Marcotte 744cba4422 (feat) integrated md-datepicker and enabled it in the Preferences module (Vacation)
The date picker suffers from this bug: https://github.com/angular/material/issues/4192

Which makes it not that usable right now.
2015-08-17 10:10:57 -04:00
Ludovic Marcotte ded467e0a2 (fix) add empty settings hashes to prever JS exceptions 2015-08-17 09:55:14 -04:00
Ludovic Marcotte a718b87d91 (fix) minor gui improvement to links templates 2015-08-16 21:42:53 -04:00
Ludovic Marcotte 6e2e71b202 (fix) correctly set flex attribut on sm devices 2015-08-15 17:25:04 -04:00
Ludovic Marcotte 04e63e5edf (fix) add minimal constraints when creating events or tasks 2015-08-15 17:08:15 -04:00
Ludovic Marcotte f1a50bcb74 (feat) added refresh support to calendar 2015-08-14 16:31:29 -04:00
Ludovic Marcotte 0672b96b8b (fix) show the alarm name as a floating label for now
This might change as the text is so tiny!
2015-08-14 15:51:17 -04:00
Ludovic Marcotte 93fa74d279 (feat) save/restore sorting state for address books
The initial loading doesn't work yet. Will work on this later.
2015-08-14 15:22:59 -04:00
Ludovic Marcotte ffa4079fa8 (fix) b0rk3n sort saving regarding the asc/desc state 2015-08-14 15:11:07 -04:00
Luc Charland 8f75bdce2d Use Json for testing webcalendar 2015-08-14 12:46:37 -04:00
Luc Charland 4f53ea70e1 Commented out testing two phones same type until fixed 2015-08-14 12:46:03 -04:00
Ludovic Marcotte b72a9383a4 (feat) major rework of the sidenav and top toolbars 2015-08-14 12:14:15 -04:00
extrafu d3d9a720dc Merge pull request #99 from inverse-inc/feature/saveSortContacts
Added sort saving to Contacts
2015-08-13 15:29:59 -04:00
Ludovic Marcotte 02daebe36b (fix) also handle tasks deletion correctly 2015-08-12 18:31:06 -04:00
Ludovic Marcotte 336e4c93e9 (fix) added temporary fix for file inputs on firefox 2015-08-12 18:25:15 -04:00
Ludovic Marcotte 47252affba (feat) initial selection + ops in calendar module 2015-08-12 16:11:38 -04:00
Luc Charland 8fb5c1d09f Added sort saving to Contacts 2015-08-12 14:39:26 -04:00
Ludovic Marcotte 54334b294e (fix) small fix in the template 2015-08-12 10:31:06 -04:00
Ludovic Marcotte c51bed2580 (fix) updated js/css/map files for packaging 2015-08-12 09:43:55 -04:00
Ludovic Marcotte 935df2bd00 (fix) wrong label name 2015-08-11 21:16:16 -04:00
Ludovic Marcotte 85a843bc1e (fix) now show the event/task list instead of the day/week/month views on sm devices 2015-08-11 20:58:39 -04:00
Ludovic Marcotte 723372dddb (fix) when batch deleting, update the unseen count accordingly 2015-08-11 15:05:31 -04:00
Ludovic Marcotte 9362513fee (fix) moved the mailbox cleanup after expunge in the service 2015-08-11 14:48:45 -04:00
Ludovic Marcotte 2e3057c2ce (fix) calendar views now honor "breakpoints"
We also now use again the default top nav toolbar and we
hide the fab button on sm devices. This of course needs a bit
more work but we are getting there!
2015-08-11 13:40:38 -04:00
Ludovic Marcotte 456649a38a (fix) allow fullsreen editor on sm devices 2015-08-11 13:38:28 -04:00
Ludovic Marcotte 86818d4f64 (fix) fixed scrolling during contact edition 2015-08-11 13:10:11 -04:00
Ludovic Marcotte b59287d6df (fix) removed unused code 2015-08-11 11:18:41 -04:00
Ludovic Marcotte bdc8dfb2b4 (fix) removed unused code 2015-08-11 09:38:53 -04:00
Ludovic Marcotte 2825285b72 (fix) author cleanup 2015-08-11 08:45:05 -04:00
Ludovic Marcotte 314dcfd30e (fix) properly cleanup the Trash folder after empty'ing 2015-08-10 23:48:59 -04:00
Ludovic Marcotte d28fccf71e (fix) open attachment in new window 2015-08-10 23:14:40 -04:00
Ludovic Marcotte ff632ec3e8 (fix) leave some room for the message date 2015-08-10 22:02:43 -04:00
Ludovic Marcotte b7af83f7e3 (feat) pimped the login window 2015-08-10 20:47:58 -04:00
Ludovic Marcotte d2aeee39ef (feat) now able to rename folders w/o double-tap 2015-08-10 14:13:25 -04:00
Ludovic Marcotte ecc0c453d7 (fix) pimped the priority editor 2015-08-10 13:27:35 -04:00
Ludovic Marcotte 420dece1e7 (feat) flag selected messages and mark selected messages as unread 2015-08-10 13:10:45 -04:00
Ludovic Marcotte c738a2dc70 (fix) wrong use of this which was breaking ACL module 2015-08-10 11:02:23 -04:00
Ludovic Marcotte e41e17c5d9 (fix) use proper var:string value 2015-08-10 09:54:14 -04:00
Ludovic Marcotte 5741d7f95c (feat) can now copy selected cards 2015-08-10 09:48:47 -04:00
Ludovic Marcotte e13f7a267b (feat) show email priority 2015-08-09 20:21:09 -04:00
Ludovic Marcotte dd7b929f17 (feat) compose mail with clicked email address 2015-08-09 10:36:54 -04:00
Ludovic Marcotte ad9b969d6d (fix) avoid displaying the chips line 2015-08-07 21:14:17 -04:00
Ludovic Marcotte 704307e6d6 (feat) pimped the mail part viewers 2015-08-07 20:38:26 -04:00
Francis Lachapelle ce8a4b6d8a (js) Make events clickable in month view 2015-08-07 15:32:00 -04:00
Francis Lachapelle 8ab591a1b6 (html) Improve task viewer 2015-08-07 14:25:57 -04:00
Francis Lachapelle 4434547a18 (css) Don't highlight disabled list items 2015-08-07 14:25:14 -04:00
Francis Lachapelle 29d86ad478 (js) Respect user's settings in Calendar module 2015-08-07 14:15:26 -04:00
Ludovic Marcotte 15a5ab0b12 (fix) added tooltip for consistency 2015-08-07 13:46:01 -04:00
Francis Lachapelle 3b5013555d (html) Improve/fix appointment/task viewer/editor 2015-08-07 11:22:24 -04:00
Francis Lachapelle 655a499cd4 (css) Fix subheader margin 2015-08-07 11:18:40 -04:00
Francis Lachapelle 52559087b3 (css) Merge autoScrollList.scss with list.scss 2015-08-07 11:17:09 -04:00
Francis Lachapelle fb7d5b628f (js) Make use of 'controllerAs' in MainUI module 2015-08-07 11:04:04 -04:00
Francis Lachapelle 6e82a7ba7b (js) Indentation 2015-08-07 11:02:34 -04:00
Ludovic Marcotte fbcaf25428 Bumped to v3.0.0 2015-08-07 10:46:45 -04:00
Ludovic Marcotte a17e59af58 (feat) added v3 js/css/map files for packaging 2015-08-07 10:37:36 -04:00
Francis Lachapelle b966fd3e30 (js) Move *.app.js files to their directories 2015-08-07 09:56:47 -04:00
Ludovic Marcotte caa50d25cb (fix) removed broken parts from the template 2015-08-07 09:49:51 -04:00
Francis Lachapelle 636e5c375e (js) Merge utils.js to Common.js 2015-08-07 09:38:21 -04:00
Ludovic Marcotte f6ea2229f8 (fix) reworked the targets to not always generate static files 2015-08-07 09:15:38 -04:00
Francis Lachapelle 7f2ef967b4 Merge pull request #97 from inverse-inc/feature/saveFilterEventsTasks
Reworked saving Events filter and added Tasks filter
2015-08-06 17:49:03 -04:00
Francis Lachapelle 063e973ef3 (css) Cleanup variables already in material 2015-08-06 17:39:01 -04:00
Francis Lachapelle eb543ce14f (js) Allow to change the labels of Dialog.confirm 2015-08-06 17:39:00 -04:00
Francis Lachapelle 9465fbad4b (obj-c) Indentation 2015-08-06 17:39:00 -04:00
Ludovic Marcotte 560feb45d9 (fix) massively pimped the email composer 2015-08-06 16:18:53 -04:00
Ludovic Marcotte b5ca697b43 (fix) imprived the gravatar directive to extract email address between < > 2015-08-06 16:18:10 -04:00
Ludovic Marcotte 33d0f30d97 (fix) avoid closing the email composition window by error 2015-08-06 16:17:31 -04:00
Francis Lachapelle 0d537ab27b Return recurrence rules of occurrence 2015-08-06 16:15:12 -04:00
Francis Lachapelle 2239286697 (js) Use CK Editor in mail account editor (Prefs) 2015-08-06 16:13:10 -04:00
Francis Lachapelle d0d79728d2 (js) Set locale to CKEditor in Mail editor 2015-08-06 16:09:49 -04:00
Francis Lachapelle e6c9289784 Add LocaleCode to JSON defaults 2015-08-06 16:07:46 -04:00
Francis Lachapelle ae017b50bc (js) Improve ckEditor directive
The directive will now consider the following attributes:

- ck-options: a JSON object of options
- ck-locale: the locale code to be used as the language and the speller
  language
2015-08-06 16:04:39 -04:00
Luc Charland 42eb9e51b4 Reworked saving Events filter and added Tasks filter 2015-08-06 14:28:54 -04:00
Francis Lachapelle 8690cee221 Merge pull request #96 from inverse-inc/feature/saveSortEventsTasks
Feature/save sort events tasks
2015-08-06 14:26:33 -04:00
Luc Charland 7bb551a4ad Added saving of sort order for Events and Tasks 2015-08-06 13:48:39 -04:00
303 changed files with 111409 additions and 5806 deletions

No files matched your search

+3 -5
View File
@@ -9,6 +9,7 @@
*/*/obj/
*/obj/
.scss-lint-config.yml_
ActiveSync/ActiveSync.SOGo
Documentation/*.docbook
Documentation/*.pdf
SoObjects/SOGo/SOGo.framework/
@@ -20,11 +21,8 @@ UI/WebServerResources/bower_components/
UI/WebServerResources/css/
UI/WebServerResources/css/styles.css
UI/WebServerResources/css/styles.css.map
UI/WebServerResources/js/Common.js*
UI/WebServerResources/js/Contacts.js*
UI/WebServerResources/js/Mailer.js*
UI/WebServerResources/js/Preferences.js*
UI/WebServerResources/js/Scheduler.js*
UI/WebServerResources/js/*.js
UI/WebServerResources/js/*.js.map
UI/WebServerResources/js/vendor/
UI/WebServerResources/node_modules/
UI/WebServerResources/scss/.sass-cache/
+366 -77
View File
@@ -112,6 +112,30 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@implementation SOGoActiveSyncDispatcher (Sync)
- (void) _setOrUnsetSyncInProgress: (BOOL) set
invalidate: (BOOL) invalidate
{
SOGoCacheGCSObject *o;
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil useCache: NO];
[o setObjectType: ActiveSyncGlobalCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
if (set)
if (invalidate)
[[o properties] setObject: [NSCalendarDate date] forKey: @"InvalidateSyncInProgress"];
else
[[o properties] setObject: [NSCalendarDate date] forKey: @"SyncInProgress"];
else
{
[[o properties] removeObjectForKey: @"SyncInProgress"];
[[o properties] removeObjectForKey: @"InvalidateSyncInProgress"];
}
[o save];
}
- (void) _setFolderMetadata: (NSDictionary *) theFolderMetadata
forKey: (NSString *) theFolderKey
{
@@ -133,6 +157,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
[[o properties] addEntriesFromDictionary: values];
[o save];
@@ -159,7 +184,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSString *nameInCache;
if (theFolderType == ActiveSyncMailFolder)
nameInCache= [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]];
nameInCache = [imapFolderGUIDS objectForKey: [theCollection nameInContainer]];
else
{
NSString *component_name;
@@ -170,7 +195,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
component_name = @"vtodo";
nameInCache= [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]];
nameInCache = [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]];
}
return nameInCache;
@@ -404,7 +429,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[sogoObject saveComponent: o];
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
}
break;
case ActiveSyncEventFolder:
@@ -415,14 +439,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[sogoObject saveComponent: o];
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
}
break;
case ActiveSyncMailFolder:
default:
{
NSDictionary *result;
NSString *modseq;
NSNumber *modseq;
[sogoObject takeActiveSyncValues: allChanges inContext: context];
@@ -430,11 +453,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
modseq = [[[result objectForKey: @"RawResponse"] objectForKey: @"fetch"] objectForKey: @"modseq"];
if (modseq)
[syncCache setObject: modseq forKey: serverId];
[syncCache setObject: [modseq stringValue] forKey: serverId];
}
}
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
[theBuffer appendString: @"<Change>"];
@@ -577,36 +600,43 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
withFilterType: (NSCalendarDate *) theFilterType
inBuffer: (NSMutableString *) theBuffer
lastServerKey: (NSString **) theLastServerKey
{
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
NSString *davCollectionTagToStore;
NSAutoreleasePool *pool;
NSMutableString *s;
BOOL more_available;
BOOL cleanup_needed, more_available;
int i, max;
s = [NSMutableString string];
more_available = NO;
cleanup_needed = more_available = NO;
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
// If this is a new sync operation, DateCache and SyncCache needs to be deleted
// If this is a new sync operation, DateCache and SyncCache need to be deleted
if ([theSyncKey isEqualToString: @"-1"])
{
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
}
else if ([folderMetadata objectForKey: @"SyncKey"] && !([theSyncKey isEqualToString: [folderMetadata objectForKey: @"SyncKey"]]))
{
// The syncKey received from the client doesn't match the syncKey we have in cache - client might have missed a response.
// We need to cleanup this mess.
[self logWithFormat: @"Cache cleanup needed for device %@ - user: %@", [context objectForKey: @"DeviceId"], [[context activeUser] login]];
cleanup_needed = YES;
}
syncCache = [folderMetadata objectForKey: @"SyncCache"];
dateCache = [folderMetadata objectForKey: @"DateCache"];
if ((theFolderType == ActiveSyncMailFolder || theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder) &&
!([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize
!([theSyncKey isEqualToString: @"-1"]) && // new sync operation
theFilterType)
(cleanup_needed ||
( !([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize
!([folderMetadata objectForKey: @"InitialLoadSequence"]))) &&
theFilterType
)
{
NSArray *allKeys;
NSString *key;
@@ -622,14 +652,36 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ([[dateCache objectForKey:key] compare: theFilterType] == NSOrderedAscending)
{
[s appendString: @"<SoftDelete xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", key];
[s appendString: @"</SoftDelete>"];
if ([syncCache objectForKey:key])
{
if (debugOn)
[self logWithFormat: @"EAS - SoftDelete %@", key];
[syncCache removeObjectForKey: key];
[dateCache removeObjectForKey: key];
[s appendString: @"<SoftDelete xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", key];
[s appendString: @"</SoftDelete>"];
[syncCache removeObjectForKey: key];
//[dateCache removeObjectForKey: key];
softdelete_count++;
softdelete_count++;
}
else if (cleanup_needed)
{
if (debugOn)
[self logWithFormat: @"EAS - SoftDelete cleanup %@", key];
// With this we make sure that a SoftDelete is set again on next sync.
[syncCache setObject: @"0" forKey: key];
}
else
{
if (debugOn)
[self logWithFormat: @"EAS - SoftDelete final delete %@", key];
// Now we are save to remove the dateCache entry.
[dateCache removeObjectForKey: key];
}
}
if (softdelete_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
@@ -656,8 +708,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
if ([theSyncKey isEqualToString: [theCollection davCollectionTag]] && !([s length]))
return;
more_available = NO;
davCollectionTagToStore = [theCollection davCollectionTag];
@@ -673,7 +723,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSDictionary *component;
NSArray *allComponents;
BOOL updated;
BOOL updated, initialLoadInProgress;
int deleted, return_count;
if (theFolderType == ActiveSyncContactFolder)
@@ -683,8 +733,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
component_name = @"vtodo";
allComponents = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
allComponents = [allComponents sortedArrayUsingDescriptors: [NSArray arrayWithObjects: [[NSSortDescriptor alloc] initWithKey: @"c_lastmodified" ascending:YES], nil]];
initialLoadInProgress = NO;
if ([theSyncKey isEqualToString: @"-1"])
[folderMetadata setObject: davCollectionTagToStore forKey: @"InitialLoadSequence"];
if ([folderMetadata objectForKey: @"InitialLoadSequence"])
{
if ([theSyncKey intValue] < [[folderMetadata objectForKey: @"InitialLoadSequence"] intValue])
initialLoadInProgress = YES;
else
[folderMetadata removeObjectForKey: @"InitialLoadSequence"];
}
allComponents = [theCollection syncTokenFieldsWithProperties: nil
matchingSyncToken: theSyncKey
fromDate: theFilterType
initialLoad: initialLoadInProgress];
allComponents = [allComponents sortedArrayUsingDescriptors: [NSArray arrayWithObject: [[[NSSortDescriptor alloc] initWithKey: @"c_lastmodified" ascending: YES] autorelease]]];
// Check for the WindowSize
@@ -847,12 +913,39 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSMutableArray *allCacheObjects, *sortedBySequence;
SOGoMailObject *mailObject;
NSArray *allMessages;
NSArray *allMessages, *a;
NSString *lastSequence, *firstUidAdded;
int j, k, return_count;
BOOL found_in_cache;
int j, k, return_count, highestmodseq;
BOOL found_in_cache, initialLoadInProgress;
allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
initialLoadInProgress = NO;
found_in_cache = NO;
lastSequence = nil;
firstUidAdded = nil;
if ([theSyncKey isEqualToString: @"-1"])
{
highestmodseq = 0;
a = [[theCollection davCollectionTag] componentsSeparatedByString: @"-"];
[folderMetadata setObject: [a objectAtIndex: 1] forKey: @"InitialLoadSequence"];
}
else
{
a = [theSyncKey componentsSeparatedByString: @"-"];
highestmodseq = [[a objectAtIndex: 1] intValue];
}
if ([folderMetadata objectForKey: @"InitialLoadSequence"])
{
if (highestmodseq < [[folderMetadata objectForKey: @"InitialLoadSequence"] intValue])
initialLoadInProgress = YES;
else
[folderMetadata removeObjectForKey: @"InitialLoadSequence"];
}
allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType initialLoad: initialLoadInProgress];
max = [allMessages count];
allCacheObjects = [NSMutableArray array];
@@ -860,7 +953,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
for (i = 0; i < max; i++)
{
[allCacheObjects addObject: [SOGoSyncCacheObject syncCacheObjectWithUID: [[[allMessages objectAtIndex: i] allKeys] lastObject]
sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
}
sortedBySequence = [[NSMutableArray alloc] initWithDictionary: syncCache];
@@ -869,15 +962,82 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[allCacheObjects sortUsingSelector: @selector(compareSequence:)];
//NSLog(@"sortedBySequence (%d) - lastObject: %@", [sortedBySequence count], [sortedBySequence lastObject]);
//NSLog(@"allCacheObjects (%d) - lastObject: %@", [allCacheObjects count], [allCacheObjects lastObject]);
if (debugOn)
{
[self logWithFormat: @"EAS - sortedBySequence (%d) - lastObject: %@", [sortedBySequence count], [sortedBySequence lastObject]];
[self logWithFormat: @"EAS - allCacheObjects (%d) - lastObject: %@", [allCacheObjects count], [allCacheObjects lastObject]];
}
lastCacheObject = [sortedBySequence lastObject];
//
// Cleanup the mess
//
if (cleanup_needed)
{
NSMutableArray *sortedByUID;
int uidnextFromCache;
sortedByUID = [[NSMutableArray alloc] initWithDictionary: syncCache];
[sortedByUID sortUsingSelector: @selector(compareUID:)];
// Get the uid from SyncKey in cache. The uid is the first uid added to cache by the last sync request.
a = [[folderMetadata objectForKey: @"SyncKey"] componentsSeparatedByString: @"-"];
uidnextFromCache = [[a objectAtIndex: 0] intValue];
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: from uid: %d to uid: %d", uidnextFromCache, [[[sortedByUID lastObject] uid] intValue]];
// Remove all entries from cache beginning with the first uid added by the last sync request.
for (j = uidnextFromCache; j <= [[[sortedByUID lastObject] uid] intValue]; j++)
{
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: ADD %d", j];
[syncCache removeObjectForKey: [NSString stringWithFormat:@"%d", j]];
[dateCache removeObjectForKey: [NSString stringWithFormat:@"%d", j]];
}
RELEASE(sortedByUID);
for (j = 0; j < [allCacheObjects count]; j++)
{
// Update the modseq in cache, sence othersie, it would be identical to the modseq from server
//and we would skip the cache when generating the response.
if ([syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]] && ![[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]])
{
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: CHANGE %@", [[allCacheObjects objectAtIndex: j] uid]];
[syncCache setObject: @"0" forKey:[[allCacheObjects objectAtIndex: j] uid]];
}
else if ([[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]])
{
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: DELETE %@", [[allCacheObjects objectAtIndex: j] uid]];
// For deletes we have to recreate a cache entry to have the <Delete> included in the response.
[syncCache setObject: @"0" forKey:[[allCacheObjects objectAtIndex: j] uid]];
}
}
}
if ([folderMetadata objectForKey: @"MoreAvailable"] && lastCacheObject)
if (!cleanup_needed &&
[folderMetadata objectForKey: @"MoreAvailable"] &&
lastCacheObject &&
!([[lastCacheObject sequence] isEqual: @"0"])) // Sequence 0 is set during cache cleanup.
{
for (j = 0; j < [allCacheObjects count]; j++)
{
if (([[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]] && [syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]]) ||
([[allCacheObjects objectAtIndex: j] sequence] && ![syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]]))
{
// We need to continue with adds or deletes from here.
found_in_cache = YES;
j--;
break;
}
if ([[lastCacheObject uid] isEqual: [[allCacheObjects objectAtIndex: j] uid]])
{
// Found out where we're at, let's continue from there...
@@ -892,12 +1052,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (found_in_cache)
k = j+1;
else
{
k = 0;
j = 0;
}
//NSLog(@"found in cache: %d k = %d", found_in_cache, k);
j = k = 0;
if (debugOn)
[self logWithFormat: @"EAS - found in cache: %d k = %d", found_in_cache, k];
return_count = 0;
@@ -908,23 +1066,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Check for the WindowSize and slice accordingly
if (return_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
{
NSString *lastSequence;
more_available = YES;
lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? @"1" : [aCacheObject sequence]);
*theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", [aCacheObject uid], lastSequence];
//NSLog(@"Reached windowSize - lastUID will be: %@", *theLastServerKey);
if (!firstUidAdded)
{
a = [davCollectionTagToStore componentsSeparatedByString: @"-"];
firstUidAdded = [a objectAtIndex: 0];
RETAIN(firstUidAdded);
}
lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? [NSString stringWithFormat:@"%d", highestmodseq] : [aCacheObject sequence]);
//RETAIN(lastSequence);
*theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", firstUidAdded, lastSequence];
if (debugOn)
[self logWithFormat: @"EAS - Reached windowSize - lastUID will be: %@", *theLastServerKey];
DESTROY(pool);
break;
}
aCacheObject = [allCacheObjects objectAtIndex: k];
// If found in cache, it's either a Change or a Delete
if (debugOn)
[self logWithFormat: @"EAS - Dealing with cacheObject: %@", aCacheObject];
// If found in cache, it's either a Change or a Delete operation.
if ([syncCache objectForKey: [aCacheObject uid]])
{
if ([[aCacheObject sequence] isEqual: [NSNull null]])
{
if (debugOn)
[self logWithFormat: @"EAS - DELETE!"];
// Deleted
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
@@ -942,9 +1115,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
mailObject = [theCollection lookupName: [aCacheObject uid]
inContext: context
acquire: 0];
if (![[aCacheObject sequence] isEqual: [syncCache objectForKey: [aCacheObject uid]]])
{
if (debugOn)
[self logWithFormat: @"EAS - CHANGE!"];
[s appendString: @"<Change xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
@@ -960,6 +1136,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
else
{
if (debugOn)
[self logWithFormat: @"EAS - ADD!"];
// Added
if (![[aCacheObject sequence] isEqual: [NSNull null]])
{
@@ -992,11 +1171,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[syncCache setObject: [aCacheObject sequence] forKey: [aCacheObject uid]];
[dateCache setObject: [NSCalendarDate date] forKey: [aCacheObject uid]];
// Save the frist UID we add. We will use it for the synckey late.
if (!firstUidAdded)
{
firstUidAdded = [aCacheObject uid];
RETAIN(firstUidAdded);
if (debugOn)
[self logWithFormat: @"EAS - first uid added %@", firstUidAdded];
}
return_count++;
}
else
{
//NSLog(@"skipping old deleted UID: %@", [aCacheObject uid]);
if (debugOn)
[self logWithFormat: @"EAS - skipping old deleted UID: %@", [aCacheObject uid]];
}
}
@@ -1005,16 +1195,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (more_available)
{
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
//[folderMetadata setObject: lastSequence forKey: @"MoreAvailable"];
[folderMetadata setObject: [NSNumber numberWithInt: YES] forKey: @"MoreAvailable"];
[folderMetadata setObject: *theLastServerKey forKey: @"SyncKey"];
//RELEASE(lastSequence);
}
else
{
[folderMetadata removeObjectForKey: @"MoreAvailable"];
[folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
if (firstUidAdded)
{
a = [davCollectionTagToStore componentsSeparatedByString: @"-"];
[folderMetadata setObject: [[NSString alloc] initWithFormat: @"%@-%@", firstUidAdded, [a objectAtIndex: 1]] forKey: @"SyncKey"];
}
else
[folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
}
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
RELEASE(firstUidAdded);
RELEASE(*theLastServerKey);
} // default:
@@ -1113,15 +1314,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
changeDetected: (BOOL *) changeDetected
maxSyncResponseSize: (int) theMaxSyncResponseSize
{
NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *mimeSupport, *lastServerKey, *syncKeyInCache;
SOGoMicrosoftActiveSyncFolderType folderType;
id collection, value;
NSMutableString *changeBuffer, *commandsBuffer;
BOOL getChanges, first_sync;
unsigned int windowSize, v, status;
NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *mimeSupport, *lastServerKey, *syncKeyInCache, *folderKey;
NSMutableDictionary *folderMetadata, *folderOptions;
NSMutableString *changeBuffer, *commandsBuffer;
id collection, value;
SOGoMicrosoftActiveSyncFolderType folderType;
unsigned int windowSize, v, status;
BOOL getChanges, first_sync;
changeBuffer = [NSMutableString string];
commandsBuffer = [NSMutableString string];
@@ -1144,7 +1345,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//[theBuffer appendString: @"</Collection>"];
return;
}
//
// First check if we have any concurrent Sync requests going on for this device.
// If we do and we are still within our maximumSyncInterval, we let our EAS
// device know to retry.
//
folderKey = [self _getNameInCache: collection withType: folderType];
folderMetadata = [self _folderMetadataForKey: folderKey];
// We check for a window size, default to 100 if not specfied or out of bounds
windowSize = [[[(id)[theDocumentElement getElementsByTagName: @"WindowSize"] lastObject] textValue] intValue];
@@ -1169,8 +1378,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
first_sync = NO;
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
if ([syncKey isEqualToString: @"0"])
{
davCollectionTag = @"-1";
@@ -1225,7 +1432,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
folderOptions = [[NSDictionary alloc] initWithObjectsAndKeys: mimeSupport, @"MIMESupport", bodyPreferenceType, @"BodyPreferenceType", nil];
[folderMetadata setObject: folderOptions forKey: @"FolderOptions"];
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: collection withType: folderType]];
[self _setFolderMetadata: folderMetadata forKey: folderKey];
}
}
@@ -1271,7 +1478,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
lastServerKey: &lastServerKey];
}
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
folderMetadata = [self _folderMetadataForKey: folderKey];
// If we got any changes or if we have applied any commands
// let's regenerate our SyncKey based on the collection tag.
@@ -1433,16 +1640,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSArray *allCollections;
NSData *d;
int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize;
int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize, total_sleep;
BOOL changeDetected;
changeDetected = NO;
maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize];
// We initialize our output buffer
output = [[NSMutableString alloc] init];
defaults = [SOGoSystemDefaults sharedSystemDefaults];
defaultInterval = [defaults maximumSyncInterval];
[output appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[output appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
[output appendString: @"<Sync xmlns=\"AirSync:\">"];
@@ -1457,12 +1663,53 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[output appendString: @"</Sync>"];
d = [[output dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
[theResponse setContent: d];
RELEASE(output);
return;
}
if ([[self globalMetadataForDevice] objectForKey: @"SyncInProgress"])
{
NSCalendarDate *syncStartDate;
// An other sync is going on right now. Lets break it and wait
// until the other process clears it up.
[self _setOrUnsetSyncInProgress: YES invalidate: YES];
total_sleep = 0;
syncStartDate = [[self globalMetadataForDevice] objectForKey: @"SyncInProgress"];
while ([[self globalMetadataForDevice] objectForKey: @"SyncInProgress"])
{
// Don't go into a heartbeat loop.
heartbeatInterval = 0;
// We waited too long. Return a fatal error to the client.
if (abs([syncStartDate timeIntervalSinceNow]) > defaultInterval)
{
if (debugOn)
[self logWithFormat: @"EAS - We waited too long. syncStartDate: %@ %@", syncStartDate, [self globalMetadataForDevice]];
[theResponse setStatus: 503];
[self _setOrUnsetSyncInProgress: NO invalidate: NO];
RELEASE(output);
return;
}
if (debugOn)
[self logWithFormat: @"EAS - globalMetadataForDevice %@", [self globalMetadataForDevice]];
[self logWithFormat: @"Sync in progress for device %@ (login: %@). Lock will expire in %d seconds", [context objectForKey: @"DeviceId"], [[context activeUser] login], defaultInterval-total_sleep];
sleep(5);
total_sleep += 5;
}
}
// No lock or broke the existing one, lets grab it and proceed
[self _setOrUnsetSyncInProgress: YES invalidate: NO];
defaults = [SOGoSystemDefaults sharedSystemDefaults];
changeDetected = NO;
maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize];
heartbeatInterval = [[[(id)[theDocumentElement getElementsByTagName: @"HeartbeatInterval"] lastObject] textValue] intValue];
defaultInterval = [defaults maximumSyncInterval];
internalInterval = [defaults internalSyncInterval];
// If the request doesn't contain "HeartbeatInterval" there is no reason to delay the response.
@@ -1472,11 +1719,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// We check to see if our heartbeat interval falls into the supported ranges.
if (heartbeatInterval > defaultInterval || heartbeatInterval < 1)
{
int limit;
// Interval is too long, inform the client.
heartbeatInterval = defaultInterval;
// Outlook doesn't like this...
//[output appendFormat: @"<Limit>%d</Limit>", defaultInterval];
// When Status = 14, the Wait interval is specified in minutes while
// defaultInterval is specifed in seconds. Adjust accordinlgy.
limit = defaultInterval/60;
if (limit < 1) limit = 1;
if (limit > 59) limit = 59;
//[output appendFormat: @"<Limit>%d</Limit>", limit];
//[output appendFormat: @"<Status>%d</Status>", 14];
}
@@ -1492,7 +1744,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
for (j = 0; j < [allCollections count]; j++)
{
aCollection = [allCollections objectAtIndex: j];
[self processSyncCollection: aCollection
inBuffer: s
changeDetected: &changeDetected
@@ -1504,13 +1756,46 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (changeDetected)
{
[self logWithFormat: @"Change detected, we push the content."];
// Don't return a response if an other Sync is waiting and this is a heartbeat request.
if ([[self globalMetadataForDevice] objectForKey: @"InvalidateSyncInProgress"] && heartbeatInterval > 1)
{
if (debugOn)
[self logWithFormat: @"EAS - Heartbeat stopped - discard response %@", [self globalMetadataForDevice]];
[theResponse setStatus: 503];
[self _setOrUnsetSyncInProgress: NO invalidate: NO];
RELEASE(output);
return;
}
[self logWithFormat: @"Change detected during Sync, we push the content."];
break;
}
else if (heartbeatInterval > 1)
{
[self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval];
sleep(internalInterval);
total_sleep = 0;
while (total_sleep < internalInterval)
{
// We check if we must break the current synchronization since an other Sync
// has just arrived.
if ([[self globalMetadataForDevice] objectForKey: @"InvalidateSyncInProgress"])
{
if (debugOn)
[self logWithFormat: @"EAS - Heartbeat stopped %@", [self globalMetadataForDevice]];
// Make sure we end the heardbeat-loop.
heartbeatInterval = internalInterval = 1;
break;
}
else
{
[self logWithFormat: @"Sleeping %d seconds while detecting changes in Sync...", internalInterval-total_sleep];
sleep(5);
total_sleep += 5;
}
}
}
else
{
@@ -1518,8 +1803,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
}
// Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0 oterwise send an empty response.
//
// Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0,
// otherwise send an empty response.
//
if (changeDetected || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"] || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"12.0"])
{
// We always return the last generated response.
@@ -1537,6 +1824,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Avoid overloading the autorelease pool here, as Sync command can
// generate fairly large responses.
RELEASE(output);
[self _setOrUnsetSyncInProgress: NO invalidate: NO];
}
@end
+6 -1
View File
@@ -1,6 +1,6 @@
/*
Copyright (c) 2014, Inverse inc.
Copyright (c) 2014-2015, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -32,14 +32,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "SOGoActiveSyncConstants.h"
@class NSException;
@class NSMutableDictionary;
@class NSURL;
@interface SOGoActiveSyncDispatcher : NSObject
{
NSURL *folderTableURL;
NSDictionary *imapFolderGUIDS;
id context;
BOOL debugOn;
}
- (NSMutableDictionary *) globalMetadataForDevice;
- (id) collectionFromId: (NSString *) theCollectionId
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
+37 -22
View File
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <Foundation/NSArray.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSLocale.h>
#import <Foundation/NSProcessInfo.h>
#import <Foundation/NSTimeZone.h>
#import <Foundation/NSURL.h>
@@ -143,20 +144,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@implementation SOGoActiveSyncDispatcher
static BOOL debugOn = NO;
- (id) init
{
[super init];
debugOn = [[SOGoSystemDefaults sharedSystemDefaults] easDebugEnabled];
folderTableURL = nil;
imapFolderGUIDS = nil;
return self;
}
- (void) dealloc
{
RELEASE(folderTableURL);
RELEASE(imapFolderGUIDS);
[super dealloc];
}
@@ -169,16 +170,16 @@ static BOOL debugOn = NO;
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
[[o properties] removeAllObjects];
[[o properties] addEntriesFromDictionary: [NSDictionary dictionaryWithObject: theSyncKey forKey: @"FolderSyncKey"]];
[[o properties] setObject: theSyncKey
forKey: @"FolderSyncKey"];
[o save];
}
- (NSMutableDictionary *) _globalMetadataForDevice
- (NSMutableDictionary *) globalMetadataForDevice
{
SOGoCacheGCSObject *o;
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil];
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil useCache: NO];
[o setObjectType: ActiveSyncGlobalCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
@@ -202,7 +203,7 @@ static BOOL debugOn = NO;
if (theFilter)
{
o = [SOGoCacheGCSObject objectWithName: [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theCollectionId] inContainer: nil];
[o setObjectType: ActiveSyncGlobalCacheObject];
[o setObjectType: ActiveSyncFolderCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
@@ -229,23 +230,27 @@ static BOOL debugOn = NO;
SOGoMailAccounts *accountsFolder;
SOGoMailAccount *accountFolder;
SOGoUserFolder *userFolder;
NSDictionary *imapGUIDs;
userFolder = [[context activeUser] homeFolderInContext: context];
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
// Get the GUID of the IMAP folder
imapGUIDs = [accountFolder imapFolderGUIDs];
//return [[imapGUIDs allKeysForObject: theIdToTranslate] objectAtIndex: 0];
return [[[imapGUIDs allKeysForObject: [NSString stringWithFormat: @"folder%@", theIdToTranslate]] objectAtIndex: 0] substringFromIndex: 6] ;
if (!imapFolderGUIDS)
{
userFolder = [[context activeUser] homeFolderInContext: context];
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
// Get the GUID of the IMAP folder
imapFolderGUIDS = [accountFolder imapFolderGUIDs];
[imapFolderGUIDS retain];
}
return [[[imapFolderGUIDS allKeysForObject: [NSString stringWithFormat: @"folder%@", theIdToTranslate]] objectAtIndex: 0] substringFromIndex: 6] ;
}
return theIdToTranslate;
}
//
//
//
@@ -710,7 +715,7 @@ static BOOL debugOn = NO;
BOOL first_sync;
sm = [SoSecurityManager sharedSecurityManager];
metadata = [self _globalMetadataForDevice];
metadata = [self globalMetadataForDevice];
syncKey = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
s = [NSMutableString string];
@@ -938,6 +943,7 @@ static BOOL debugOn = NO;
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
[o save];
command_count++;
@@ -1023,6 +1029,7 @@ static BOOL debugOn = NO;
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
}
[o save];
@@ -1046,6 +1053,7 @@ static BOOL debugOn = NO;
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
}
[o save];
@@ -1204,7 +1212,7 @@ static BOOL debugOn = NO;
filter = [NSCalendarDate dateFromFilterType: [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"FilterType"] lastObject] textValue]];
syncKey = [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"SyncKey"] lastObject] textValue];
allMessages = [currentCollection syncTokenFieldsWithProperties: nil matchingSyncToken: syncKey fromDate: filter];
allMessages = [currentCollection syncTokenFieldsWithProperties: nil matchingSyncToken: syncKey fromDate: filter initialLoad: NO];
count = [allMessages count];
@@ -2024,13 +2032,13 @@ static BOOL debugOn = NO;
if ([foldersWithChanges count])
{
[self logWithFormat: @"Change detected, we push the content."];
[self logWithFormat: @"Change detected using Ping, we let the EAS client know to send a Sync."];
status = 2;
break;
}
else
{
[self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval];
[self logWithFormat: @"Sleeping %d seconds while detecting changes in Ping...", internalInterval];
sleep(internalInterval);
}
}
@@ -2619,6 +2627,11 @@ static BOOL debugOn = NO;
[map setObject: [mailObject messageId] forKey: @"in-reply-to"];
references = [[[[[mailObject mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] mutableCopy] autorelease];
// If there is no References: header, initialize it with In-Reply-To.
if ([mailObject inReplyTo] && ![references count])
references = [NSMutableArray arrayWithObject: [mailObject inReplyTo]];
if ([references count] > 0)
{
// If there are more than ten identifiers listed, we eliminate the second one.
@@ -2935,7 +2948,9 @@ static BOOL debugOn = NO;
{
NSString *value;
value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z" timeZone: [NSTimeZone timeZoneWithName: @"GMT"] locale: nil];
value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z"
timeZone: [NSTimeZone timeZoneWithName: @"GMT"]
locale: [[[NSLocale alloc] initWithLocaleIdentifier: @"en_US"] autorelease]];
s = [NSString stringWithFormat: @"Date: %@\n%@", value, [theRequest contentAsString]];
}
else
+8 -2
View File
@@ -541,7 +541,7 @@ struct GlobalObjectId {
// We sanitize the content *ONLY* for Outlook clients and if the content-transfer-encoding is 8bit. Outlook has strange issues
// with quoted-printable/base64 encoded text parts. It just doesn't decode them.
encoding = [[self lookupInfoForBodyPart: @""] objectForKey: @"encoding"];
if ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"] || ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame))
if (encoding && ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame))
d = [self _sanitizedMIMEMessage];
else
d = [self content];
@@ -1071,9 +1071,15 @@ struct GlobalObjectId {
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.0"] ||
[[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.1"])
{
id value;
NSString *reference;
reference = [[[[self mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] objectAtIndex: 0];
value = [[self mailHeaders] objectForKey: @"references"];
if ([value isKindOfClass: [NSArray class]])
reference = [[[value objectAtIndex: 0] componentsSeparatedByString: @" "] objectAtIndex: 0];
else
reference = [[value componentsSeparatedByString: @" "] objectAtIndex: 0];
if ([reference length] > 0)
[s appendFormat: @"<ConversationId xmlns=\"Email2:\">%@</ConversationId>", [[reference dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
+1
View File
@@ -37,6 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@interface SOGoSyncCacheObject : NSObject
{
@public
id _uid;
id _sequence;
}
+22 -13
View File
@@ -32,9 +32,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <Foundation/NSNull.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
static Class NSNullK;
@implementation SOGoSyncCacheObject
+ (void) initialize
{
NSNullK = [NSNull class];
}
- (id) init
{
if ((self = [super init]))
@@ -46,14 +54,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
return self;
}
+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence;
+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence
{
id o;
o = [[self alloc] init];
[o setUID: theUID];
[o setSequence: theSequence];
[o setUID: [NSNumber numberWithInt: [theUID intValue]]];
[o setSequence: ([theSequence isKindOfClass: NSNullK] ? theSequence : [NSNumber numberWithInt: [theSequence intValue]])];
return [o autorelease];
}
@@ -67,7 +75,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (id) uid
{
return _uid;
return [_uid description];
}
- (void) setUID: (id) theUID
@@ -77,7 +85,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (id) sequence
{
return _sequence;
return ([_sequence isKindOfClass: NSNullK] ? _sequence : [_sequence description]);
}
- (void) setSequence: (id) theSequence
@@ -88,7 +96,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (NSComparisonResult) compareUID: (SOGoSyncCacheObject *) theSyncCacheObject
{
return [[self uid] compare: [theSyncCacheObject uid]];
return [self->_uid compare: theSyncCacheObject->_uid];
}
//
@@ -97,21 +105,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
- (NSComparisonResult) compareSequence: (SOGoSyncCacheObject *) theSyncCacheObject
{
if ([[self sequence] isEqual: [NSNull null]] &&
[[theSyncCacheObject sequence] isEqual: [NSNull null]])
if ([self->_sequence isEqual: [NSNull null]] &&
[theSyncCacheObject->_sequence isEqual: [NSNull null]])
return [self compareUID: theSyncCacheObject];
if (![[self sequence] isEqual: [NSNull null]] && [[theSyncCacheObject sequence] isEqual: [NSNull null]])
if (![self->_sequence isEqual: [NSNull null]] && [theSyncCacheObject->_sequence isEqual: [NSNull null]])
return NSOrderedDescending;
if ([[self sequence] isEqual: [NSNull null]] && ![[theSyncCacheObject sequence] isEqual: [NSNull null]])
if ([self->_sequence isEqual: [NSNull null]] && ![theSyncCacheObject->_sequence isEqual: [NSNull null]])
return NSOrderedAscending;
// Must check this here, to avoid comparing NSNull objects
if ([[self sequence] compare: [theSyncCacheObject sequence]] == NSOrderedSame)
if ([self->_sequence compare: theSyncCacheObject->_sequence] == NSOrderedSame)
return [self compareUID: theSyncCacheObject];
return [[self sequence] compare: [theSyncCacheObject sequence]];
return [self->_sequence compare: theSyncCacheObject->_sequence];
}
- (NSString *) description
@@ -120,3 +128,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
@end
+13104 -402
View File
File diff suppressed because it is too large. Load diff
@@ -233,6 +233,7 @@ Installation
This section will guide you through the installation of the native
Microsoft Outlook compatibility layer SOGo offers.
////
Red Hat Enterprise Linux v6 x86_64
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -316,7 +317,7 @@ from this guide.
On Ubuntu 12.04, the Samba init scripts need to be modified to
disable the upstart check. For more details, refer to:
https://wiki.samba.org/index.php/Samba4/InitScript
////
Debian 8 (Jessie) and Ubuntu 14.04 (Trusty Tahr)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -395,7 +396,7 @@ samba-tool user setexpiry administrator --noexpiry
----
You might consider changing the realm and domain used, to suit your
enviroment.
environment.
You might also have to
remove `/etc/samba/smb.conf` prior running this command.
@@ -441,7 +442,7 @@ Your Samba 4 configuration file should look like this:
OpenChange Configuration
~~~~~~~~~~~~~~~~~~~~~~~~
OpenChange 2.2 stores its metadata in MySQL so you need to have it installed.
Since v2.2, OpenChange stores its metadata in MySQL so you need to have it installed.
First, create the OpenChange MySQL user:
@@ -512,9 +513,11 @@ mapistore:indexing_backend = mysql://openchange-user:openchange$123@localhost/op
mapiproxy:openchangedb = mysql://openchange-user:openchange$123@localhost/openchange
----
////
On RHEL, make sure SELinux is disabled:
setenforce 0
////
Next, you can start Samba using the usual command:
@@ -545,9 +548,11 @@ This service runs as a WSGI application under apache (mod_wsgi).
While HTTPS is not required to access this service, it is strongly
recommended.
////
On RHEL-based distributions, the apache configuration required by these
services can be found in `/etc/httpd/conf.d/ocsmanager.conf` and
`/etc/httpd/conf.d/rpcproxy.conf`.
////
For Debian-based distributions, these files can be found
in `/etc/apache2/conf.d/` or `/etc/apache2/conf-available`.
@@ -563,8 +568,10 @@ The OCS Manager and RPC Proxy configuration module can be enabled using:
a2enconf ocsmanager
a2enconf rpcproxy
////
On RHEL-based distributions, make sure the `LoadModule` directive is
uncommented in `/etc/httpd/conf.d/wsgi.conf`.
////
The _reqtimeout_ apache module is known to cause problems when using the
default configuration shipped with Debian-based systems. On such
@@ -580,10 +587,12 @@ To avoid this problem, use a much higher timeout or disable the module:
a2dismod reqtimeout
You should now restart the Apache service and make sure it will start on
boot. On RHEL-based distributions, do:
boot.
////
On RHEL-based distributions, do:
chkconfig httpd on && /etc/init.d/httpd restart
////
On Debian-based distributions, do:
update-rc.d apache2 defaults && /etc/init.d/apache2 restart
@@ -813,7 +822,7 @@ the `openchange_user_cleanup` script distributed with SOGo. The script
can be found in `/usr/share/doc/sogo/` (`/usr/share/sogo-VERSION/` on
RHEL).
To reset a user, run the script as root:
`python openchange_user_cleanup username`. See the usage output for additional options.
`openchange_user_cleanup username`. See the usage output for additional options.
* The "Out of Office Assistant" will not currently work. This feature
has not been implemented.
* Creating folders below INBOX (when not normally permitted by the IMAP
+33 -5
View File
@@ -1,12 +1,39 @@
2.3.x (2015-MM-DD)
2.3.3 (2015-mm-dd)
------------------
New features
Enhancements
- we no longer always entirely rewrite messages for Outlook 2013 when using EAS
Bug fixes
- numerous EAS fixes when connections are dropped before the EAS client receives the response (#3058, #2849)
- correctly handle the References header over EAS (#3365)
- make sure English is always used when generating Date headers using EAS (#3356)
2.3.2 (2015-09-16)
------------------
Enhancements
- improved EAS speed and memory usage, avoiding many IMAP LIST commands (#3294)
- improved EAS speed during initial syncing of large mailboxes (#3293)
- updated CKEditor to version 4.5.3
Bug fixes
- fixed display of whitelisted attendees in Preferences window on Firefox (#3285)
- Non-latin subfolder names are displayed correctly on Outlook (Zentyal)
- Fixed several sync issues on environments with multiple users (Zentyal)
- Folders from other users will no longer appear on your Outlook (Zentyal)
- Use right auth in multidomain environments in contacts and calendar from Outlook (Zentyal)
- non-latin subfolder names are displayed correctly on Outlook (Zentyal)
- fixed several sync issues on environments with multiple users (Zentyal)
- folders from other users will no longer appear on your Outlook (Zentyal)
- use right auth in multidomain environments in contacts and calendar from Outlook (Zentyal)
- session fix when SOGoEnableDomainBasedUID is enabled but logins are domain-less
- less sync issues when setting read flag (Zentyal)
- attachments with non-latin filenames sent by Outlook are now received (Zentyal)
- support attachments from more mail clients (Zentyal)
- avoid conflicting message on saving a draft mail (Zentyal)
- less conflicting messages in Outlook while moving messages between folders (Zentyal)
- start/end shifting by 1 hour due to timezone change on last Sunday of October 2015 (#3344)
- fixed localization of calendar categories with empty profile (#3295)
- fixed options availability in contextual menu of Contacts module (#3342)
2.3.1 (2015-07-23)
------------------
@@ -19,6 +46,7 @@ Enhancements
- added create-folder subcommand to sogo-tool to create contact and calendar folders
- group mail addresses can be used as recipient in Outlook
- added 'ActiveSync' module constraints
- updated CKEditor to version 4.5.1
- added Slovenian translation - thanks to Jens Riecken
- added Chinese (Taiwan) translation
+1 -1
View File
@@ -671,7 +671,7 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
return newAttachment;
}
- (int) setReadFlag: (uint8_t) flag
- (enum mapistore_error) setReadFlag: (uint8_t) flag
{
return MAPISTORE_SUCCESS;
}
+136
View File
@@ -22,6 +22,7 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSData.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
@@ -34,6 +35,7 @@
#import "MAPIStoreDBFolder.h"
#import "MAPIStoreDBMessage.h"
#import "MAPIStoreTypes.h"
#import "NSData+MAPIStore.h"
#import "NSObject+MAPIStore.h"
#import "NSString+MAPIStore.h"
@@ -104,6 +106,105 @@
return objectVersion;
}
- (void) _updatePredecessorChangeList
{
BOOL updated;
enum mapistore_error rc;
NSData *currentChangeList, *changeKey;
NSMutableArray *changeKeys;
NSMutableData *newChangeList;
NSUInteger count, len;
struct SizedXid *changes;
struct SPropValue property;
struct SRow aRow;
struct XID *currentChangeKey;
TALLOC_CTX *localMemCtx;
uint32_t nChanges;
localMemCtx = talloc_new (NULL);
if (!localMemCtx)
{
[self errorWithFormat: @"No more memory"];
return;
}
changeKey = [self getReplicaKeyFromGlobCnt: [self objectVersion]];
currentChangeList = [properties objectForKey: MAPIPropertyKey (PidTagPredecessorChangeList)];
if (!currentChangeList)
{
/* Create a new PredecessorChangeList */
len = [changeKey length];
newChangeList = [NSMutableData dataWithCapacity: len + 1];
[newChangeList appendUInt8: len];
[newChangeList appendData: changeKey];
}
else
{
/* Update current predecessor change list with new change key */
changes = [currentChangeList asSizedXidArrayInMemCtx: localMemCtx
with: &nChanges];
updated = NO;
currentChangeKey = [changeKey asXIDInMemCtx: localMemCtx];
for (count = 0; count < nChanges && !updated; count++)
{
if (GUID_equal(&changes[count].XID.NameSpaceGuid, &currentChangeKey->NameSpaceGuid))
{
NSData *globCnt, *oldGlobCnt;
oldGlobCnt = [NSData dataWithBytes: changes[count].XID.LocalId.data length: changes[count].XID.LocalId.length];
globCnt = [NSData dataWithBytes: currentChangeKey->LocalId.data length: currentChangeKey->LocalId.length];
if ([globCnt compare: oldGlobCnt] == NSOrderedDescending)
{
if ([globCnt length] != [oldGlobCnt length])
{
[self errorWithFormat: @"Cannot compare globcnt with different length: %@ and %@", globCnt, oldGlobCnt];
abort();
}
memcpy (changes[count].XID.LocalId.data, currentChangeKey->LocalId.data, currentChangeKey->LocalId.length);
updated = YES;
}
}
}
/* Serialise it */
changeKeys = [NSMutableArray array];
if (!updated)
[changeKeys addObject: changeKey];
for (count = 0; count < nChanges; count++)
{
changeKey = [NSData dataWithXID: &changes[count].XID];
[changeKeys addObject: changeKey];
}
[changeKeys sortUsingFunction: MAPIChangeKeyGUIDCompare context: localMemCtx];
newChangeList = [NSMutableData data];
len = [changeKeys count];
for (count = 0; count < len; count++)
{
changeKey = [changeKeys objectAtIndex: count];
[newChangeList appendUInt8: [changeKey length]];
[newChangeList appendData: changeKey];
}
}
if ([newChangeList length] > 0)
{
property.ulPropTag = PidTagPredecessorChangeList;
property.value.bin = *[newChangeList asBinaryInMemCtx: localMemCtx];
aRow.cValues = 1;
aRow.lpProps = &property;
rc = [self addPropertiesFromRow: &aRow];
if (rc != MAPISTORE_SUCCESS)
[self errorWithFormat: @"Impossible to add a new predecessor change list: %d", rc];
}
talloc_free (localMemCtx);
}
//
// FIXME: how this can happen?
//
@@ -166,6 +267,9 @@
[properties setObject: [NSNumber numberWithUnsignedLongLong: newVersion]
forKey: @"version"];
/* Update PredecessorChangeList accordingly */
[self _updatePredecessorChangeList];
[self logWithFormat: @"%d props in dict", [properties count]];
[sogoObject save];
@@ -209,4 +313,36 @@
return [sogoObject lastModified];
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
{
/* Modify PidTagMessageFlags from SetMessageReadFlag and
SyncImportReadStateChanges ROPs */
NSNumber *flags;
uint32_t newFlag;
flags = [properties objectForKey: MAPIPropertyKey (PR_MESSAGE_FLAGS)];
if (flags)
{
newFlag = [flags unsignedLongValue];
if (flag & SUPPRESS_RECEIPT)
newFlag |= MSGFLAG_READ;
if (flag & CLEAR_RN_PENDING)
newFlag &= ~MSGFLAG_RN_PENDING;
if (flag & CLEAR_READ_FLAG)
newFlag &= ~MSGFLAG_READ;
if (flag & CLEAR_NRN_PENDING)
newFlag &= ~MSGFLAG_NRN_PENDING;
}
else
{
newFlag = MSGFLAG_READ;
if (flag & CLEAR_READ_FLAG)
newFlag = 0x0;
}
[properties setObject: [NSNumber numberWithUnsignedLong: newFlag]
forKey: MAPIPropertyKey (PR_MESSAGE_FLAGS)];
return MAPISTORE_SUCCESS;
}
@end
+1
View File
@@ -121,6 +121,7 @@
fromFolder: (MAPIStoreFolder *) sourceFolder
withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
wantCopy: (uint8_t) want_copy
inMemCtx: (TALLOC_CTX *) memCtx;
+20 -8
View File
@@ -642,6 +642,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
fromFolder: (MAPIStoreFolder *) sourceFolder
withMID: (uint64_t) targetMid
andChangeKey: (struct Binary_r *) targetChangeKey
andPredecessorChangeList: (struct Binary_r *) targetPredecessorChangeList
wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
{
@@ -669,15 +670,18 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
[sourceMsg copyToMessage: destMsg inMemCtx: memCtx];
if (targetChangeKey)
if (targetPredecessorChangeList)
{
property.ulPropTag = PidTagChangeKey;
property.value.bin = *targetChangeKey;
property.ulPropTag = PidTagPredecessorChangeList;
property.value.bin = *targetPredecessorChangeList;
aRow.cValues = 1;
aRow.lpProps = &property;
rc = [destMsg addPropertiesFromRow: &aRow];
if (rc != MAPISTORE_SUCCESS)
goto end;
{
[self errorWithFormat: @"Cannot add PredecessorChangeList on move"];
goto end;
}
}
[destMsg save: memCtx];
if (!wantCopy)
@@ -696,6 +700,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
fromFolder: (MAPIStoreFolder *) sourceFolder
withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
{
@@ -705,7 +710,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
NSString *oldMessageURL;
MAPIStoreMapping *mapping;
SOGoUser *ownerUser;
struct Binary_r *targetChangeKey;
struct Binary_r *targetChangeKey, *targetPredecessorChangeList;
//TALLOC_CTX *memCtx;
//memCtx = talloc_zero (NULL, TALLOC_CTX);
@@ -726,14 +731,21 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
if (oldMessageURL)
{
[oldMessageURLs addObject: oldMessageURL];
if (targetChangeKeys)
targetChangeKey = targetChangeKeys[count];
if (targetChangeKeys && targetPredecessorChangeList)
{
targetChangeKey = targetChangeKeys[count];
targetPredecessorChangeList = targetPredecessorChangeLists[count];
}
else
targetChangeKey = NULL;
{
targetChangeKey = NULL;
targetPredecessorChangeList = NULL;
}
rc = [self _moveCopyMessageWithMID: srcMids[count]
fromFolder: sourceFolder
withMID: targetMids[count]
andChangeKey: targetChangeKey
andPredecessorChangeList: targetPredecessorChangeList
wantCopy: wantCopy
inMemCtx: memCtx];
}
+2 -1
View File
@@ -42,7 +42,8 @@
/* synchronisation */
- (BOOL) synchroniseCache;
- (void) updateVersionsForMessageWithKey: (NSString *) messageKey
withChangeKey: (NSData *) newChangeKey;
withChangeKey: (NSData *) oldChangeKey
andPredecessorChangeList: (NSData *) pcl;
- (NSNumber *) lastModifiedFromMessageChangeNumber: (NSString *) changeNumber;
- (NSString *) changeNumberForMessageWithKey: (NSString *) messageKey;
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey;
+85 -18
View File
@@ -75,6 +75,7 @@ static Class NSNumberK;
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
inContainer: dbFolder]);
[versionsMessage setObjectType: MAPIInternalCacheObject];
[versionsMessage reloadIfNeeded];
}
- (void) dealloc
@@ -261,7 +262,6 @@ static Class NSNumberK;
*/
- (void) _setChangeKey: (NSData *) changeKey
forMessageEntry: (NSMutableDictionary *) messageEntry
inChangeListOnly: (BOOL) inChangeListOnly
{
struct XID *xid;
NSString *guid;
@@ -270,19 +270,15 @@ static Class NSNumberK;
NSMutableDictionary *changeList;
xid = [changeKey asXIDInMemCtx: NULL];
guid = [NSString stringWithGUID: &xid->GUID];
globCnt = [NSData dataWithBytes: xid->Data length: xid->Size];
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
talloc_free (xid);
if (!inChangeListOnly)
{
/* 1. set change key association */
changeKeyDict = [NSDictionary dictionaryWithObjectsAndKeys:
guid, @"GUID",
globCnt, @"LocalId",
nil];
[messageEntry setObject: changeKeyDict forKey: @"ChangeKey"];
}
/* 1. set change key association */
changeKeyDict = [NSDictionary dictionaryWithObjectsAndKeys: guid, @"GUID",
globCnt, @"LocalId",
nil];
[messageEntry setObject: changeKeyDict forKey: @"ChangeKey"];
/* 2. append/update predecessor change list */
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
@@ -296,6 +292,77 @@ static Class NSNumberK;
[changeList setObject: globCnt forKey: guid];
}
- (void) _updatePredecessorChangeList: (NSData *) predecessorChangeList
forMessageEntry: (NSMutableDictionary *) messageEntry
withOldChangeKey: (NSData *) oldChangeKey
{
NSData *globCnt, *oldGlobCnt;
NSDictionary *changeKeyDict;
NSString *guid;
NSMutableDictionary *changeList;
struct SizedXid *sizedXIDList;
struct XID xid, *givenChangeKey;
TALLOC_CTX *localMemCtx;
uint32_t i, length;
localMemCtx = talloc_new (NULL);
if (!localMemCtx)
{
[self errorWithFormat: @"No more memory"];
return;
}
if (predecessorChangeList)
{
sizedXIDList = [predecessorChangeList asSizedXidArrayInMemCtx: localMemCtx with: &length];
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
if (!changeList)
{
changeList = [NSMutableDictionary new];
[messageEntry setObject: changeList
forKey: @"PredecessorChangeList"];
[changeList release];
}
if (sizedXIDList) {
for (i = 0; i < length; i++)
{
xid = sizedXIDList[i].XID;
guid = [NSString stringWithGUID: &xid.NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid.LocalId.data length: xid.LocalId.length];
oldGlobCnt = [changeList objectForKey: guid];
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
[changeList setObject: globCnt forKey: guid];
}
}
}
if (oldChangeKey)
{
givenChangeKey = [oldChangeKey asXIDInMemCtx: localMemCtx];
if (givenChangeKey) {
guid = [NSString stringWithGUID: &givenChangeKey->NameSpaceGuid];
globCnt = [NSData dataWithBytes: givenChangeKey->LocalId.data length: givenChangeKey->LocalId.length];
changeKeyDict = [messageEntry objectForKey: @"ChangeKey"];
if (!changeKeyDict ||
([guid isEqualToString: [changeKeyDict objectForKey: @"GUID"]]
&& ([globCnt compare: [changeKeyDict objectForKey: @"LocalId"]] == NSOrderedDescending)))
{
/* The given change key is greater than current one stored in
metadata or it does not exist */
[messageEntry setObject: [NSDictionary dictionaryWithObjectsAndKeys: guid, @"GUID",
globCnt, @"LocalId",
nil]
forKey: @"ChangeKey"];
}
}
}
talloc_free (localMemCtx);
}
- (EOQualifier *) componentQualifier
{
if (!componentQualifier)
@@ -465,8 +532,7 @@ static Class NSNumberK;
// A GLOBCNT structure is a 6-byte global namespace counter,
// we strip the first 2 bytes. The first two bytes is the ReplicaId
changeKey = [self getReplicaKeyFromGlobCnt: newChangeNum >> 16];
[self _setChangeKey: changeKey forMessageEntry: messageEntry
inChangeListOnly: NO];
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
}
now = [NSCalendarDate date];
@@ -483,12 +549,13 @@ static Class NSNumberK;
}
- (void) updateVersionsForMessageWithKey: (NSString *) messageKey
withChangeKey: (NSData *) newChangeKey
withChangeKey: (NSData *) oldChangeKey
andPredecessorChangeList: (NSData *) pcl
{
NSMutableDictionary *messages, *messageEntry;
[self synchroniseCache];
if (newChangeKey)
if (oldChangeKey || pcl)
{
messages = [[versionsMessage properties] objectForKey: @"Messages"];
messageEntry = [messages objectForKey: messageKey];
@@ -496,8 +563,8 @@ static Class NSNumberK;
[NSException raise: @"MAPIStoreIOException"
format: @"no version record found for message '%@'",
messageKey];
[self _setChangeKey: newChangeKey forMessageEntry: messageEntry
inChangeListOnly: YES];
[self _updatePredecessorChangeList: pcl forMessageEntry: messageEntry
withOldChangeKey: oldChangeKey];
[versionsMessage save];
}
}
+6 -3
View File
@@ -209,13 +209,16 @@
- (void) updateVersions
{
NSData *newChangeKey;
/* Update ChangeKey and PredecessorChangeList on message's save */
NSData *newChangeKey, *predecessorChangeList;
newChangeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
predecessorChangeList = [properties objectForKey: MAPIPropertyKey (PR_PREDECESSOR_CHANGE_LIST)];
[(MAPIStoreGCSFolder *) container
updateVersionsForMessageWithKey: [self nameInContainer]
withChangeKey: newChangeKey];
updateVersionsForMessageWithKey: [self nameInContainer]
withChangeKey: newChangeKey
andPredecessorChangeList: predecessorChangeList];
}
@end
+4 -15
View File
@@ -30,6 +30,7 @@
#import <SOGo/NSArray+Utilities.h>
#import <Mailer/SOGoMailBodyPart.h>
#import <Mailer/SOGoMailObject.h>
#import <Mailer/NSDictionary+Mail.h>
#import "MAPIStoreTypes.h"
#import "MAPIStoreMailMessage.h"
@@ -108,7 +109,7 @@
static char recordBytes[] = {0xd9, 0xd8, 0x11, 0xa3, 0xe2, 0x90, 0x18, 0x41,
0x9e, 0x04, 0x58, 0x46, 0x9d, 0x6d, 0x1b,
0x68};
*data = [[NSData dataWithBytes: recordBytes length: 16]
asBinaryInMemCtx: memCtx];
@@ -117,19 +118,7 @@
- (NSString *) _fileName
{
NSString *fileName;
NSDictionary *parameters;
fileName = [[bodyInfo objectForKey: @"parameterList"]
objectForKey: @"name"];
if (!fileName)
{
parameters = [[bodyInfo objectForKey: @"disposition"]
objectForKey: @"parameterList"];
fileName = [parameters objectForKey: @"filename"];
}
return fileName;
return [bodyInfo filename];
}
- (int) getPidTagAttachLongFilename: (void **) data
@@ -178,7 +167,7 @@
- (int) getPidTagAttachContentId: (void **) data
inMemCtx: (TALLOC_CTX *) memCtx
{
{
*data = [[bodyInfo objectForKey: @"bodyId"]
asUnicodeInMemCtx: memCtx];
+2
View File
@@ -52,6 +52,8 @@
- (NSString *) changeNumberForMessageUID: (NSString *) messageUid;
- (void) setChangeKey: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey;
- (BOOL) updatePredecessorChangeListWith: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey;
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey;
- (NSData *) predecessorChangeListForMessageWithKey: (NSString *) messageKey;
+98 -10
View File
@@ -68,6 +68,8 @@
static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
#include <gen_ndr/exchange.h>
#undef DEBUG
#include <util/attr.h>
#include <libmapi/libmapi.h>
@@ -109,6 +111,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
inContainer: dbFolder]);
[versionsMessage setObjectType: MAPIInternalCacheObject];
[versionsMessage reloadIfNeeded];
}
- (BOOL) ensureFolderExists
@@ -516,6 +519,44 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
return [modseq1 compare: modseq2];
}
- (void) _updatePredecessorChangeListWith: (NSData *) predecessorChangeList
forMessageEntry: (NSMutableDictionary *) messageEntry
{
NSData *globCnt, *oldGlobCnt;
NSMutableDictionary *changeList;
NSString *guid;
struct SizedXid *sizedXIDList;
struct XID xid;
uint32_t i, length;
sizedXIDList = [predecessorChangeList asSizedXidArrayInMemCtx: NULL with: &length];
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
if (!changeList)
{
changeList = [NSMutableDictionary new];
[messageEntry setObject: changeList
forKey: @"PredecessorChangeList"];
[changeList release];
}
if (sizedXIDList) {
for (i = 0; i < length; i++)
{
xid = sizedXIDList[i].XID;
guid = [NSString stringWithGUID: &xid.NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid.LocalId.data length: xid.LocalId.length];
oldGlobCnt = [changeList objectForKey: guid];
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
[changeList setObject: globCnt forKey: guid];
}
talloc_free (sizedXIDList);
}
[versionsMessage save];
}
- (void) _setChangeKey: (NSData *) changeKey
forMessageEntry: (NSMutableDictionary *) messageEntry
{
@@ -526,8 +567,8 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
NSMutableDictionary *changeList;
xid = [changeKey asXIDInMemCtx: NULL];
guid = [NSString stringWithGUID: &xid->GUID];
globCnt = [NSData dataWithBytes: xid->Data length: xid->Size];
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
talloc_free (xid);
/* 1. set change key association */
@@ -924,8 +965,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
return changeNumber;
}
- (void) setChangeKey: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey
- (NSMutableDictionary *) _messageEntryFromMessageKey: (NSString *) messageKey
{
NSMutableDictionary *messages, *messageEntry;
NSString *messageUid;
@@ -936,7 +976,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
messageEntry = [messages objectForKey: messageUid];
if (!messageEntry)
{
[self warnWithFormat: @"attempting to synchronise to set the change key for "
[self warnWithFormat: @"attempting to synchronise to get the message entry for "
@"this message %@", messageKey];
synced = [self synchroniseCacheForUID: messageUid];
if (synced)
@@ -947,11 +987,57 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
abort ();
}
}
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
return messageEntry;
}
- (void) setChangeKey: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey
{
[self _setChangeKey: changeKey
forMessageEntry: [self _messageEntryFromMessageKey: messageKey]];
[versionsMessage save];
}
- (BOOL) updatePredecessorChangeListWith: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey
{
/* Update predecessor change list property given the change key. It
returns if the change key has been added to the list or not */
BOOL added = NO;
NSData *globCnt, *oldGlobCnt;
NSDictionary *messageEntry;
NSMutableDictionary *changeList;
NSString *guid;
struct XID *xid;
xid = [changeKey asXIDInMemCtx: NULL];
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
talloc_free (xid);
messageEntry = [self _messageEntryFromMessageKey: messageKey];
if (messageEntry)
{
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
if (changeList)
{
oldGlobCnt = [changeList objectForKey: guid];
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
{
[changeList setObject: globCnt forKey: guid];
[versionsMessage save];
added = YES;
}
}
else
[self errorWithFormat: @"Missing predecessor change list to update"];
}
return added;
}
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey
{
NSDictionary *messages, *changeKeyDict;
@@ -1217,6 +1303,7 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
fromFolder: (MAPIStoreFolder *) sourceFolder
withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
@@ -1231,12 +1318,13 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
NSDictionary *result;
NSUInteger count;
NSArray *a;
NSData *changeKey;
NSData *changeList;
if (![sourceFolder isKindOfClass: [MAPIStoreMailFolder class]])
return [super moveCopyMessagesWithMIDs: srcMids andCount: midCount
fromFolder: sourceFolder withMIDs: targetMids
andChangeKeys: targetChangeKeys
andPredecessorChangeLists: targetPredecessorChangeLists
wantCopy: wantCopy
inMemCtx: memCtx];
@@ -1325,11 +1413,11 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
[self synchroniseCache];
for (count = 0; count < midCount; count++)
{
changeKey = [NSData dataWithBinary: targetChangeKeys[count]];
changeList = [NSData dataWithBinary: targetPredecessorChangeLists[count]];
messageKey = [NSString stringWithFormat: @"%@.eml",
[destUIDs objectAtIndex: count]];
[self setChangeKey: changeKey
forMessageWithKey: messageKey];
[self _updatePredecessorChangeListWith: changeList
forMessageEntry: [self _messageEntryFromMessageKey: messageKey]];
}
}
+38 -26
View File
@@ -40,6 +40,7 @@
#import <Mailer/NSData+Mail.h>
#import <Mailer/SOGoMailBodyPart.h>
#import <Mailer/SOGoMailObject.h>
#import <Mailer/NSDictionary+Mail.h>
#import "Codepages.h"
#import "NSData+MAPIStore.h"
@@ -196,7 +197,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
count1 = [keys indexOfObject: data1];
data2 = [entry2 objectForKey: @"mimeType"];
count2 = [keys indexOfObject: data2];
if (count1 == count2)
{
data1 = [entry1 objectForKey: @"key"];
@@ -529,7 +530,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
else
stringValue = @"";
*data = [stringValue asUnicodeInMemCtx: memCtx];
return MAPISTORE_SUCCESS;
}
@@ -624,7 +625,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
NSDictionary *coreInfos;
NSArray *flags;
unsigned int v = 0;
coreInfos = [sogoObject fetchCoreInfos];
flags = [coreInfos objectForKey: @"flags"];
@@ -636,7 +637,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
if ([[self attachmentKeys]
count] > 0)
v |= MSGFLAG_HASATTACH;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -656,7 +657,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
v = 2;
else
v = 0;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -668,15 +669,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
NSDictionary *coreInfos;
NSArray *flags;
unsigned int v;
coreInfos = [sogoObject fetchCoreInfos];
flags = [coreInfos objectForKey: @"flags"];
if ([flags containsObject: @"flagged"])
v = 6;
else
v = 0;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -755,7 +756,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
if ([ngAddress isKindOfClass: [NGMailAddress class]])
{
cn = [ngAddress displayName];
// If we don't have a displayName, we use the email address instead. This
// avoid bug #2119 - where Outlook won't display anything in the "From" field,
// nor in the recipient field if we reply to the email.
@@ -809,7 +810,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
entryId = MAPIStoreExternalEntryId (cn, email);
*data = [entryId asBinaryInMemCtx: memCtx];
rc = MAPISTORE_SUCCESS;
}
else
@@ -966,15 +967,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
{
uint32_t v;
NSString *s;
s = [[sogoObject mailHeaders] objectForKey: @"x-priority"];
v = 0x1;
if ([s hasPrefix: @"1"]) v = 0x2;
else if ([s hasPrefix: @"2"]) v = 0x2;
else if ([s hasPrefix: @"4"]) v = 0x0;
else if ([s hasPrefix: @"5"]) v = 0x0;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -1163,14 +1164,14 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
if (!headerSetup)
[self _fetchHeaderData];
if ([headerMimeType isEqualToString: @"text/plain"])
format = EDITOR_FORMAT_PLAINTEXT;
else if ([headerMimeType isEqualToString: @"text/html"])
format = EDITOR_FORMAT_HTML;
else
format = 0; /* EDITOR_FORMAT_DONTKNOW */
*data = MAPILongValue (memCtx, format);
return MAPISTORE_SUCCESS;
@@ -1517,7 +1518,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
p = 0;
recipient->data = talloc_array (msgData, void *, msgData->columns->cValues);
memset (recipient->data, 0, msgData->columns->cValues * sizeof (void *));
// PR_OBJECT_TYPE = MAPI_MAILUSER (see MAPI_OBJTYPE)
recipient->data[p] = MAPILongValue (msgData, MAPI_MAILUSER);
p++;
@@ -1525,7 +1526,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
// PR_DISPLAY_TYPE = DT_MAILUSER (see MS-NSPI)
recipient->data[p] = MAPILongValue (msgData, 0);
p++;
// PR_7BIT_DISPLAY_NAME_UNICODE
recipient->data[p] = [cn asUnicodeInMemCtx: msgData];
p++;
@@ -1533,7 +1534,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
// PR_SMTP_ADDRESS_UNICODE
recipient->data[p] = [email asUnicodeInMemCtx: msgData];
p++;
// PR_SEND_INTERNET_ENCODING = 0x00060000 (plain text, see OXCMAIL)
recipient->data[p] = MAPILongValue (msgData, 0x00060000);
p++;
@@ -1566,12 +1567,9 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
withPrefix: (NSString *) keyPrefix
{
NSArray *parts;
NSDictionary *parameters;
NSUInteger count, max;
parameters = [[bodyInfo objectForKey: @"disposition"]
objectForKey: @"parameterList"];
if ([[parameters objectForKey: @"filename"] length] > 0)
if ([[bodyInfo filename] length] > 0)
{
if ([keyPrefix length] == 0)
keyPrefix = @"0";
@@ -1645,15 +1643,29 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
return attachment;
}
- (int) setReadFlag: (uint8_t) flag
- (enum mapistore_error) setReadFlag: (uint8_t) flag
{
BOOL modified = NO;
BOOL alreadyRead = NO;
NSString *imapFlag = @"\\Seen";
alreadyRead = [[[sogoObject fetchCoreInfos] objectForKey: @"flags"]
containsObject: @"seen"];
/* TODO: notifications should probably be emitted from here */
if (flag & CLEAR_READ_FLAG)
[sogoObject removeFlags: imapFlag];
{
[sogoObject removeFlags: imapFlag];
modified = alreadyRead;
}
else
[sogoObject addFlags: imapFlag];
{
[sogoObject addFlags: imapFlag];
modified = !alreadyRead;
}
if (modified)
[(MAPIStoreMailFolder *)[self container] synchroniseCache];
return MAPISTORE_SUCCESS;
}
@@ -1694,7 +1706,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
return nil;
}
- (void) save: (TALLOC_CTX *) memCtx
- (void) save: (TALLOC_CTX *) memCtx
{
NSNumber *value;
+22 -7
View File
@@ -161,15 +161,30 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK;
//[self logWithFormat: @"change number from oxcfxics: %.16lx", [value unsignedLongLongValue]];
//[self logWithFormat: @" modseq: %.16lx", [modseq unsignedLongLongValue]];
if (modseq)
modseq = [NSNumber numberWithUnsignedLongLong:
[modseq unsignedLongLongValue] + 1];
{
if (res->relop == RELOP_GT)
modseq = [NSNumber numberWithUnsignedLongLong:
[modseq unsignedLongLongValue] + 1];
}
else
modseq = [NSNumber numberWithUnsignedLongLong: 0];
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"MODSEQ"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
value: modseq];
[*qualifier autorelease];
rc = MAPIRestrictionStateNeedsEval;
if (res->relop == RELOP_GT || res->relop == RELOP_GE)
{
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"MODSEQ"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
value: modseq];
[*qualifier autorelease];
rc = MAPIRestrictionStateNeedsEval;
}
else
{
/* Ignore other operations as IMAP only support MODSEQ >= X */
[self warnWithFormat: @"Ignoring %@ as only supported operators are > and >=",
[self operatorFromRestrictionOperator: res->relop]];
rc = MAPIRestrictionStateAlwaysTrue;
}
}
break;
+25 -11
View File
@@ -34,6 +34,7 @@
#import <NGExtensions/NGBase64Coding.h>
#import <NGExtensions/NGHashMap.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSObject+Values.h>
#import <NGExtensions/NSString+Encoding.h>
#import <NGMime/NGMimeBodyPart.h>
#import <NGMime/NGMimeMultipartBody.h>
@@ -285,7 +286,7 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" };
version = [properties objectForKey: @"version"];
return (version
? exchange_globcnt ([version unsignedLongLongValue])
? [version unsignedLongLongValue]
: ULLONG_MAX);
}
@@ -1110,7 +1111,8 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
- (void) save: (TALLOC_CTX *) memCtx
{
NSString *folderName, *flag, *newIdString, *messageKey;
BOOL updatedMetadata;
NSString *folderName, *flag, *newIdString, *messageKey, *changeNumber;
NSData *changeKey, *messageData;
NGImap4Connection *connection;
NGImap4Client *client;
@@ -1146,21 +1148,33 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
[sogoObject setNameInContainer: messageKey];
[mapping registerURL: [self url] withID: mid];
/* synchronise the cache and update the change key with the one provided
by the client. Before doing this, lets issue a unselect/select combo
because of timing issues with Dovecot in obtaining the latest modseq.
Sometimes, Dovecot doesn't return the newly appended UID if we do
a "UID SORT (DATE) UTF-8 (MODSEQ XYZ) (NOT DELETED)" command (where
XYZ is the HIGHESTMODSEQ+1) immediately after IMAP APPEND */
/* synchronise the cache and update the predecessor change list
with the change key provided by the client. Before doing
this, lets issue a unselect/select combo because of timing
issues with Dovecot in obtaining the latest modseq.
Sometimes, Dovecot doesn't return the newly appended UID if
we do a "UID SORT (DATE) UTF-8 (MODSEQ XYZ) (NOT DELETED)"
command (where XYZ is the HIGHESTMODSEQ+1) immediately after
IMAP APPEND */
[client unselect];
[client select: folderName];
[(MAPIStoreMailFolder *) container synchroniseCache];
changeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
if (changeKey)
[(MAPIStoreMailFolder *) container
setChangeKey: changeKey
forMessageWithKey: messageKey];
{
updatedMetadata = [(MAPIStoreMailFolder *) container updatePredecessorChangeListWith: changeKey
forMessageWithKey: messageKey];
if (!updatedMetadata)
[self warnWithFormat: @"Predecessor change list not updated with client data"];
}
/* Update version property (PR_CHANGE_KEY indeed) as it is
requested once it is saved */
changeNumber = [(MAPIStoreMailFolder *) container changeNumberForMessageUID: newIdString];
if (changeNumber)
[properties setObject: [NSNumber numberWithUnsignedLongLong: [changeNumber unsignedLongLongValue] >> 16]
forKey: @"version"];
}
}
+1 -1
View File
@@ -63,7 +63,7 @@
withAID: (uint32_t) aid;
- (int) getAttachmentTable: (MAPIStoreAttachmentTable **) tablePtr
andRowCount: (uint32_t *) countPtr;
- (int) setReadFlag: (uint8_t) flag;
- (enum mapistore_error) setReadFlag: (uint8_t) flag;
- (enum mapistore_error) saveMessage: (TALLOC_CTX *) memCtx;
- (NSArray *) activeContainerMessageTables;
+3 -2
View File
@@ -549,11 +549,12 @@ rtf2html (NSData *compressedRTF)
}
[self save: memCtx];
/* We make sure that any change-related properties are removes from the
/* We make sure that any change-related properties are removed from the
properties dictionary, to make sure that related methods will be
invoked the next time they are requested. */
[properties removeObjectForKey: MAPIPropertyKey (PidTagChangeKey)];
[properties removeObjectForKey: MAPIPropertyKey (PidTagChangeNumber)];
[properties removeObjectForKey: MAPIPropertyKey (PidTagPredecessorChangeList)];
if ([container isKindOfClass: MAPIStoreFolderK])
{
@@ -918,7 +919,7 @@ rtf2html (NSData *compressedRTF)
return [self getNo: data inMemCtx: memCtx];;
}
- (int) setReadFlag: (uint8_t) flag
- (enum mapistore_error) setReadFlag: (uint8_t) flag
{
// [self subclassResponsibility: _cmd];
+3 -1
View File
@@ -674,6 +674,7 @@ sogo_folder_move_copy_messages(void *folder_object,
uint32_t mid_count,
uint64_t *src_mids, uint64_t *t_mids,
struct Binary_r **target_change_keys,
struct Binary_r **target_predecessor_change_lists,
uint8_t want_copy)
{
MAPIStoreFolder *sourceFolder, *targetFolder;
@@ -698,6 +699,7 @@ sogo_folder_move_copy_messages(void *folder_object,
fromFolder: sourceFolder
withMIDs: t_mids
andChangeKeys: target_change_keys
andPredecessorChangeLists: target_predecessor_change_lists
wantCopy: want_copy
inMemCtx: mem_ctx];
TRYCATCH_END(pool)
@@ -1118,7 +1120,7 @@ sogo_message_set_read_flag (void *message_object, uint8_t flag)
struct MAPIStoreTallocWrapper *wrapper;
NSAutoreleasePool *pool;
MAPIStoreMessage *message;
int rc;
enum mapistore_error rc;
if (message_object)
{
+1 -1
View File
@@ -531,7 +531,7 @@
}
[vToDo setTimeStampAsDate: now];
[sogoObject saveComponent: vCalendar];
[sogoObject saveCalendar: vCalendar];
[self updateVersions];
}
+2
View File
@@ -41,6 +41,8 @@
+ (id) dataWithXID: (const struct XID *) xid;
- (struct XID *) asXIDInMemCtx: (void *) memCtx;
- (struct SizedXid *) asSizedXidArrayInMemCtx: (void *) memCtx
with: (uint32_t *) length;
+ (id) dataWithChangeKeyGUID: (NSString *) guidString
andCnt: (NSData *) globCnt;
+40 -6
View File
@@ -22,6 +22,7 @@
#import <NGExtensions/NSObject+Logs.h>
#import "MAPIStoreTypes.h"
#import "NSObject+MAPIStore.h"
#import "NSString+MAPIStore.h"
@@ -29,6 +30,7 @@
#undef DEBUG
#include <stdbool.h>
#include <libmapi/libmapi.h>
#include <talloc.h>
#include <util/time.h>
#include <gen_ndr/exchange.h>
@@ -136,11 +138,11 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
NSMutableData *xidData;
struct FlatUID_r flatUID;
_fillFlatUIDWithGUID (&flatUID, &xid->GUID);
_fillFlatUIDWithGUID (&flatUID, &xid->NameSpaceGuid);
xidData = [NSMutableData dataWithCapacity: 16 + xid->Size];
xidData = [NSMutableData dataWithCapacity: 16 + xid->LocalId.length];
[xidData appendBytes: flatUID.ab length: 16];
[xidData appendBytes: xid->Data length: xid->Size];
[xidData appendBytes: xid->LocalId.data length: xid->LocalId.length];
return xidData;
}
@@ -156,12 +158,12 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
{
xid = talloc_zero (memCtx, struct XID);
[self _extractGUID: &xid->GUID];
[self _extractGUID: &xid->NameSpaceGuid];
xid->Size = max - 16;
xid->LocalId.length = max - 16;
bytes = (uint8_t *) [self bytes];
xid->Data = talloc_memdup (xid, (bytes+16), xid->Size);
xid->LocalId.data = talloc_memdup (xid, (bytes+16), xid->LocalId.length);
}
else
{
@@ -172,6 +174,38 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
return xid;
}
- (struct SizedXid *) asSizedXidArrayInMemCtx: (void *) memCtx
with: (uint32_t *) length
{
struct Binary_r bin;
struct SizedXid *sizedXIDArray;
bin.cb = [self length];
bin.lpb = (uint8_t *)[self bytes];
sizedXIDArray = get_SizedXidArray(memCtx, &bin, length);
if (!sizedXIDArray)
{
NSLog (@"Impossible to parse SizedXID array");
return NULL;
}
return sizedXIDArray;
}
- (NSComparisonResult) compare: (NSData *) otherGlobCnt
{
uint64_t globCnt = 0, oGlobCnt = 0;
if ([self length] > 0)
globCnt = *(uint64_t *) [self bytes];
if ([otherGlobCnt length] > 0)
oGlobCnt = *(uint64_t *) [otherGlobCnt bytes];
return MAPICNCompare (globCnt, oGlobCnt, NULL);
}
+ (id) dataWithChangeKeyGUID: (NSString *) guidString
andCnt: (NSData *) globCnt;
{
+36 -4
View File
@@ -161,8 +161,11 @@
minute: [tzStart minuteOfHour] second: 0
timeZone: [NSTimeZone timeZoneWithName: @"GMT"]];
tmpDate = [tmpDate addYear: 0 month: ((pos > 0) ? 0 : 1)
day: 0 hour: 0 minute: 0
tmpDate = [tmpDate addYear: 0
month: ((pos > 0) ? 0 : 1)
day: 0
hour: 0
minute: 0
second: 0];
/* If the day of the time change is "-XSU", we need to determine whether the
@@ -197,12 +200,41 @@
END:STANDARD
END:VTIMEZONE
The time changes occure on a Sunday, but in March, the 1st is a Sunday itself and in November
The time changes occur on a Sunday, but in March, the 1st is a Sunday itself and in November
the 1st is also a Sunday. If we don't decrement "pos" by one, tmpDate (which is set to March or November 1st
because of "day: 1" will have 14 more days added for March and 7 more days added for November - which will
effectively shift the time change by a whole week.
In Europe/Berlin, we have a different use-case for November. In 2015, November 1st is a Sunday.
The time change in November must occur on October 25th but since tmpDate will be November 1st,
so a Sunday, dateDayOfWeek will be 0 and dayOfWeek will also be 0 we would decrement tmpDate by 14 days,
which is incorrect because it would shift the timezone change one week earlier. We take care about this
one with check if pos is greater or equal than 0 and if so, we don't decrement it.
BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
*/
if (dayOfWeek == dateDayOfWeek)
if (dayOfWeek == dateDayOfWeek && pos >= 0)
pos--;
offset = (dayOfWeek - dateDayOfWeek) + (pos * 7);
@@ -2988,7 +2988,10 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
theUser = [SOGoUser userWithLogin: theUID];
aParent = [theUser calendarsFolderInContext: context];
if ([aParent isKindOfClass: [NSException class]])
return nil;
aFolders = [aParent subFolders];
e = [aFolders objectEnumerator];
while ((aFolder = [e nextObject]))
@@ -1493,8 +1493,9 @@ 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"])
if (![context request] ||
[[context request] handledByDefaultHandler] ||
[[[context request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"])
{
// If an alarm was specified, let's use it. This would happen if an attendee accepts/declines/etc. an
// event invitation and also sets an alarm along the way. This would happen ONLY from the web interface.
@@ -84,17 +84,30 @@
*/
- (NSDictionary *) attributesInContext: (WOContext *) context
{
NSArray *rules;
NSArray *allEvents, *rules;
NSCalendarDate *untilDate;
NSMutableDictionary *data, *repeat;
NSString *frequency;
NSTimeZone *timeZone;
SOGoUserDefaults *ud;
iCalEvent *masterComponent;
iCalRecurrenceRule *rule;
data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]];
rules = [self recurrenceRules];
if ([self recurrenceId])
{
// If the component is an occurrence of a recurrent component,
// consider the recurrence rules of the master component.
allEvents = [[self parent] events];
masterComponent = [allEvents objectAtIndex: 0];
rules = [masterComponent recurrenceRules];
}
else
{
rules = [self recurrenceRules];
}
if ([rules count] > 0)
{
rule = [rules objectAtIndex: 0];
+5 -1
View File
@@ -1,6 +1,6 @@
/* NGVCard+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2009-2014 Inverse inc.
* Copyright (C) 2009-2015 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
@@ -31,6 +31,10 @@
- (CardElement *) elementWithTag: (NSString *) elementTag
ofType: (NSString *) type;
- (void) addElementWithTag: (NSString *) elementTag
ofType: (NSString *) type
withValue: (id) value;
- (void) updateFromLDIFRecord: (NSDictionary *) ldifRecord;
- (NSMutableDictionary *) asLDIFRecord;
+2 -2
View File
@@ -188,8 +188,8 @@ convention:
}
- (void) addElementWithTag: (NSString *) elementTag
ofType: (NSString *) type
withValue: (id) value
ofType: (NSString *) type
withValue: (id) value
{
NSArray *allValues;
NSEnumerator *list;
+6 -3
View File
@@ -1,6 +1,5 @@
/*
Copyright (C) 2006-2014 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
Copyright (C) 2006-2015 Inverse inc.
This file is part of SOGo.
@@ -32,9 +31,13 @@
@class NSString;
@interface SOGoContactGCSFolder : SOGoGCSFolder <SOGoContactFolder>
{
NSString *baseCardDAVURL, *basePublicCardDAVURL;
}
- (void) fixupContactRecord: (NSMutableDictionary *) contactRecord;
- (NSDictionary *) lookupContactWithName: (NSString *) aName;
- (NSString *) cardDavURL;
- (NSString *) publicCardDavURL;
@end
+63 -2
View File
@@ -1,6 +1,5 @@
/*
Copyright (C) 2006-2013 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
Copyright (C) 2006-2013-2015 Inverse inc.
This file is part of SOGo.
@@ -67,6 +66,24 @@ static NSArray *folderListingFields = nil;
@"c_component", nil];
}
- (id) init
{
if ((self = [super init]))
{
baseCardDAVURL = nil;
basePublicCardDAVURL = nil;
}
return self;
}
- (void) dealloc
{
[baseCardDAVURL release];
[basePublicCardDAVURL release];
[super dealloc];
}
- (Class) objectClassForContent: (NSString *) content
{
CardGroup *cardEntry;
@@ -436,4 +453,48 @@ static NSArray *folderListingFields = nil;
return [NSString stringWithFormat: @"%@:%@", nsRep, nodeName];
}
- (NSString *) _baseCardDAVURL
{
NSString *davURL;
if (!baseCardDAVURL)
{
davURL = [[self realDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
baseCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
baseCardDAVURL = davURL;
[baseCardDAVURL retain];
}
return baseCardDAVURL;
}
- (NSString *) cardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _baseCardDAVURL]];
}
- (NSString *) _basePublicCardDAVURL
{
NSString *davURL;
if (!basePublicCardDAVURL)
{
davURL = [[self publicDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
basePublicCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
basePublicCardDAVURL = davURL;
[basePublicCardDAVURL retain];
}
return basePublicCardDAVURL;
}
- (NSString *) publicCardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _basePublicCardDAVURL]];
}
@end /* SOGoContactGCSFolder */
+4 -4
View File
@@ -97,10 +97,10 @@
- (NSString *) davCollectionTag;
- (NSArray *) syncTokenFieldsWithProperties: (NSArray *) theProperties
matchingSyncToken: (NSString *) theSyncToken
fromDate: (NSCalendarDate *) theStartDate;
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
matchingSyncToken: (NSString *) syncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress;
/* flags */
- (NSException *) addFlagsToAllMessages: (id) _f;
+6 -2
View File
@@ -2106,7 +2106,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
// * 5 FETCH (UID 559 MODSEQ (17))
// * 6 FETCH (UID 560 MODSEQ (18))
// * 7 FETCH (UID 561 MODSEQ (19))
//
//
// fetchUIDsOfVanishedItems ..
//
@@ -2119,6 +2119,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
- (NSArray *) syncTokenFieldsWithProperties: (NSArray *) theProperties
matchingSyncToken: (NSString *) theSyncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress
{
EOQualifier *searchQualifier;
NSMutableArray *allTokens;
@@ -2195,6 +2196,9 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
for (i = 0; i < [fetchResults count]; i++)
{
if ([[[fetchResults objectAtIndex: i] objectForKey: @"flags"] containsObject: @"deleted"] && initialLoadInProgress)
continue;
d = [NSDictionary dictionaryWithObject: ([[[fetchResults objectAtIndex: i] objectForKey: @"flags"] containsObject: @"deleted"]) ? [NSNull null] : [[fetchResults objectAtIndex: i] objectForKey: @"modseq"]
forKey: [[[fetchResults objectAtIndex: i] objectForKey: @"uid"] stringValue]];
[allTokens addObject: d];
@@ -2205,7 +2209,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
if (highestmodseq == 0)
highestmodseq = 1;
if (highestmodseq > 0)
if (highestmodseq > 0 && !initialLoadInProgress)
{
id uid;
+1 -3
View File
@@ -1,8 +1,6 @@
/* NSArray+Utilities.h - this file is part of SOGo
*
* Copyright (C) 2006 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2015 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
+1 -3
View File
@@ -1,8 +1,6 @@
/* NSArray+Utilities.m - this file is part of SOGo
*
* Copyright (C) 2006-2011 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2015 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
+1 -3
View File
@@ -1,8 +1,6 @@
/* NSObject+Utilities.h - this file is part of SOGo
*
* Copyright (C) 2007 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2007-2015 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
+1 -3
View File
@@ -1,8 +1,6 @@
/* NSObject+Utilities.m - this file is part of SOGo
*
* Copyright (C) 2007 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2007-2015 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
+8 -2
View File
@@ -373,6 +373,7 @@ static int cssEscapingCount;
- (NSString *) asCSSIdentifier
{
NSCharacterSet *numericSet;
NSMutableString *cssIdentifier;
unichar currentChar;
int count, max, idx;
@@ -381,10 +382,12 @@ static int cssEscapingCount;
[self _setupCSSEscaping];
cssIdentifier = [NSMutableString string];
numericSet = [NSCharacterSet decimalDigitCharacterSet];
max = [self length];
if (max > 0)
{
if (isdigit([self characterAtIndex: 0]))
if ([numericSet characterIsMember: [self characterAtIndex: 0]])
// A CSS identifier can't start with a digit; we add an underscore
[cssIdentifier appendString: @"_"];
for (count = 0; count < max; count++)
@@ -415,6 +418,7 @@ static int cssEscapingCount;
- (NSString *) fromCSSIdentifier
{
NSCharacterSet *numericSet;
NSMutableString *newString;
NSString *currentString;
int count, length, max, idx;
@@ -423,12 +427,14 @@ static int cssEscapingCount;
if (!cssEscapingStrings)
[self _setupCSSEscaping];
numericSet = [NSCharacterSet decimalDigitCharacterSet];
newString = [NSMutableString string];
max = [self length];
count = 0;
if (max > 0
&& [self characterAtIndex: 0] == '_'
&& isdigit([self characterAtIndex: 1]))
&& [numericSet characterIsMember: [self characterAtIndex: 1]])
{
/* If the identifier starts with an underscore followed by a digit,
we remove the underscore */
+2
View File
@@ -56,6 +56,8 @@ typedef enum {
- (void) reloadIfNeeded;
- (void) save;
+ (id) objectWithName: (NSString *) key inContainer: (id) theContainer useCache: (BOOL) useCache;
/* accessors */
- (NSMutableString *) path; /* full filename */
+11
View File
@@ -103,11 +103,22 @@ static EOAttribute *textColumn = nil;
}
+ (id) objectWithName: (NSString *) key inContainer: (id) theContainer
{
return [self objectWithName: key
inContainer: theContainer
useCache: YES];
}
+ (id) objectWithName: (NSString *) key inContainer: (id) theContainer useCache: (BOOL) useCache
{
SOGoCache *cache;
id o;
cache = [SOGoCache sharedCache];
if (!useCache)
[cache unregisterObjectWithName: key inContainer: theContainer];
o = [cache objectNamed: key inContainer: theContainer];
if (!o)
+2 -2
View File
@@ -128,8 +128,8 @@
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
matchingSyncToken: (NSString *) syncToken
fromDate: (NSCalendarDate *) theStartDate;
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress;
/* multiget helper */
- (WOResponse *) performMultigetInContext: (WOContext *) queryContext
+15 -8
View File
@@ -1151,6 +1151,7 @@ static NSArray *childRecordFields = nil;
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
matchingSyncToken: (NSString *) syncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress
{
/* TODO:
- validation:
@@ -1200,13 +1201,17 @@ static NSArray *childRecordFields = nil;
mRecords = [NSMutableArray arrayWithArray: [self _fetchFields: fields
withQualifier: qualifier
ignoreDeleted: YES]];
qualifier = [EOQualifier qualifierWithQualifierFormat:
@"c_lastmodified > %d and c_deleted == 1",
syncTokenInt];
fields = [NSMutableArray arrayWithObjects: @"c_name", @"c_deleted", nil];
[mRecords addObjectsFromArray: [self _fetchFields: fields
withQualifier: qualifier
ignoreDeleted: NO]];
if (!initialLoadInProgress)
{
qualifier = [EOQualifier qualifierWithQualifierFormat:
@"c_lastmodified > %d and c_deleted == 1",
syncTokenInt];
fields = [NSMutableArray arrayWithObjects: @"c_name", @"c_lastmodified", @"c_deleted", nil];
[mRecords addObjectsFromArray: [self _fetchFields: fields
withQualifier: qualifier
ignoreDeleted: NO]];
}
records = mRecords;
}
else
@@ -1499,7 +1504,9 @@ static NSArray *childRecordFields = nil;
properties = [self parseDAVRequestedProperties: propElement];
records = [self syncTokenFieldsWithProperties: properties
matchingSyncToken: syncToken
fromDate: nil];
fromDate: nil
initialLoad: NO];
[self _appendComponentProperties: [properties allKeys]
fromRecords: records
matchingSyncToken: [syncToken intValue]
+4 -1
View File
@@ -778,7 +778,10 @@ static Class NSNullK;
NSRange r;
r = [theUID rangeOfString: [NSString stringWithFormat: @"@%@", theDomain]];
theUID = [theUID substringToIndex: r.location];
// We check if the range is ok here since we could be using DomainLessLogin
if (r.location != NSNotFound)
theUID = [theUID substringToIndex: r.location];
}
sogoSources = [[self authenticationSourceIDsInDomain: theDomain] objectEnumerator];
+5 -1
View File
@@ -70,7 +70,11 @@ class Carddav:
if not self.cards:
url = "/SOGo/so/%s/Contacts/personal/view" % (self.login)
content = self._get(url)
self.cards = content['cards']
#print "\nCONTENT:", content
if 'cards' in content:
self.cards = content['cards']
else:
self.cards = []
return self.cards
def get_cards(self, pattern):
+4 -4
View File
@@ -89,9 +89,9 @@ class preferences:
get = HTTPPreferencesGET(url)
get.cookie = self.cookie
self.client.execute(get)
if DEBUG: print "LUC (url):", url
if DEBUG: print "LUC (status):", get.response["status"]
if DEBUG: print "LUC (body):", get.response['body']
if DEBUG: print "DEBUG (url):", url
if DEBUG: print "DEBUG (status):", get.response["status"]
if DEBUG: print "DEBUG (body):", get.response['body']
content = simplejson.loads(get.response['body'])
result = None
try:
@@ -117,7 +117,7 @@ class preferences:
# Get the right sub-dict and change the key/value
subdict = self.find_key(self.preferences, preference)
if not subdict:
print "LUC(nosubdict):", preference, self.preferences
raise AttributeError("ERROR(nosubdict): looking for %s in: %s" %(preference, str(self.preferences)))
subdict[preference] = value
def set(self, preference, value=None):
+24
View File
@@ -85,6 +85,13 @@ class JsonDavPhoneTests(unittest.TestCase):
self.allphones = list(self.newphone)
self.allphones.extend(self.newphones_difftype)
self.allphones.extend(self.newphones_sametype)
#- In case there are no cards for this user
try:
self._get_card()
except IndexError:
path = 'Contacts/personal'
(card, path, gid) = self._create_new_card(path)
self._save_card(card)
def tearDown(self):
self._connect_as_user()
@@ -102,6 +109,23 @@ class JsonDavPhoneTests(unittest.TestCase):
def _connect_as_user(self, newuser=username, newpassword=password):
self.dv = carddav.Carddav(newuser, newpassword)
def _create_new_card(self, path):
gid = self.dv.newguid(path)
card = {'c_categories': None,
'c_cn': 'John Doe',
'c_component': 'vcard',
'c_givenname': 'John Doe',
'c_mail': 'johndoe@nothere.com',
'c_name': gid,
'c_o': '',
'c_screenname': '',
'c_sn': '',
'c_telephonenumber': '123.456.7890',
'emails': [{'type': 'pref', 'value': 'johndoe@nothere.com'}],
'phones': [{'type': 'home', 'value': '111.222.3333'}],
'id': gid}
return (card, path, gid)
def _get_card(self, name="John Doe"):
tmp_card = self.dv.get_cards(name)[0]
self.card = self.dv.get_card(tmp_card['c_name'])
@@ -28,12 +28,14 @@ class preventInvitationsTest(unittest.TestCase):
self.caldav = CalDAVSchedulingTest(self)
cal = self.prefs.get("Calendar")
if "PreventInvitationsWhitelist" not in cal:
#print "LUC (cal):", cal
cal["PreventInvitationsWhitelist"] = None
self.prefs.set("PreventInvitationsWhitelist", None)
if "PreventInvitations" not in cal:
cal["PreventInvitations"] = 0
self.prefs.set("PreventInvitations", 0)
def tearDown(self):
self.prefs.set("autoReplyText", "")
#self.prefs.set("autoReplyText", "")
self.prefs.set('PreventInvitations', 0)
self.prefs.set("PreventInvitationsWhitelist", None)
#- Manual Cleanup, not called because classs is not derived from unittest
+4 -6
View File
@@ -21,8 +21,8 @@ class UIPostsTests(unittest.TestCase):
self.cookie = sogoLogin.getAuthCookie(hostname, port, username, password)
def _urlPostData(self, client, url, data, exp_status=200):
post = webdavlib.HTTPPOST(url, data)
post.content_type = "application/x-www-form-urlencoded"
post = webdavlib.HTTPPOST(url, simplejson.dumps(data))
post.content_type = "application/json"
post.cookie = self.cookie
client.execute(post)
@@ -43,16 +43,14 @@ class UIPostsTests(unittest.TestCase):
""" Add Web Calendar """
ret=True
data = "url=%s" % webCalendarURL
data = {"url":"%s" % webCalendarURL}
calendarBaseURL="/SOGo/so/%s/Calendar" % username
addWebCalendarURL = "%s/addWebCalendar" % calendarBaseURL
response = self._urlPostData(self.client, addWebCalendarURL, data)
respJSON = simplejson.loads(response['body'])
folderID = respJSON['folderID']
calID = respJSON['id']
#sogo1:Calendar/C07-5006F300-1-370E2480
(_, calID) = folderID.split('/', 1)
self.assertNotEqual(calID, None)
# reload the cal
+2
View File
@@ -22,6 +22,8 @@ $(TEST_TOOL)_OBJC_FILES += \
TestSBJsonParser.m \
\
TestNGMimeAddressHeaderFieldGenerator.m \
TestNGMimeMessageGenerator.m \
\
TestNSData+Crypto.m \
TestNSString+Crypto.m \
TestNSString+URLEscaping.m \
+4
View File
@@ -25,6 +25,8 @@
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSData.h>
#import <Foundation/NSValue.h>
@class NSArray;
@@ -52,6 +54,8 @@
- (BOOL) run;
- (NSString*) stringFromDiffBetween: (NSString*) str1
and: (NSString*) str2;
@end
#define test(c) { \
+75
View File
@@ -185,4 +185,79 @@ static NSString *SOGoTestAssertException = @"SOGoTestAssertException";
return YES;
}
/* Helper function for diffForString:andString */
NSString *_stringForCharacterAtIndex(NSUInteger index, NSString *str, NSUInteger length)
{
NSString *chrStr;
unichar chr;
if (index < length)
{
chr = [str characterAtIndex: index];
if (isprint(chr))
{
chrStr = [NSString stringWithFormat: @"%c", chr];
}
else
{
if (chr == 10)
chrStr = @"[NL]";
else if (chr == 0)
chrStr = @"[\0]";
else
chrStr = [NSString stringWithFormat: @"[NP: %u]", chr];
}
}
else
{
chrStr = @"[none]";
}
return chrStr;
}
/*
Returns a string with a very verbose diff of the two strings.
In case the strings are equal it returns an empty string.
Example output for the strings 'flower' and 'flotera':
<begin of example>
0 |f|
1 |l|
2 |o|
3 |w|t|<--
4 |e|
5 |r|
6 |[none]|a|<--
<end of example>
*/
- (NSString*) stringFromDiffBetween: (NSString*) str1
and: (NSString*) str2
{
BOOL differencesFound = NO;
NSString *finalSTR = @"";
NSUInteger i, length1, length2;
NSString *sc1, *sc2;
length1 = [str1 length];
length2 = [str2 length];
for (i = 0; i < length1 || i < length2; i++)
{
sc1 = _stringForCharacterAtIndex(i, str1, length1);
sc2 = _stringForCharacterAtIndex(i, str2, length2);
if ([sc1 isEqualToString: sc2])
finalSTR = [finalSTR stringByAppendingFormat: @"%u |%@|\n", i, sc1];
else
{
finalSTR = [finalSTR stringByAppendingFormat: @"%u |%@|%@|<--\n", i, sc1, sc2];
differencesFound = YES;
}
}
if (!differencesFound)
return @"";
return finalSTR;
}
@end
+134
View File
@@ -0,0 +1,134 @@
/* TestNGMimeMessageGenerator.m - this file is part of SOGo
*
* 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 "SOGoTest.h"
#import <NGMail/NGMimeMessageGenerator.h>
@interface TestNGMimeMessageGenerator : SOGoTest
@end
@implementation TestNGMimeMessageGenerator
- (void) test_generateDataForHeaderField_value
{
NGMimeMessageGenerator *generator;
NSArray *cases = [NSArray arrayWithObjects:
[NSArray arrayWithObjects: @"Message-ID", @"<CADCKkzo+9X1SniJFY3yc7YGafNrmAts419RmcqNkMzd-PBqNbA@mail.gmail.com>", @"<CADCKkzo+9X1SniJFY3yc7YGafNrmAts419RmcqNkMzd-PBqNbA@mail.gmail.com>", nil],
[NSArray arrayWithObjects: @"Content-Type",
@"text/plain; charset=utf-8; format=flowed",
@"text/plain; charset=utf-8; format=flowed",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderOneHebrewOneLatin",
@"עs",
@"=?utf-8?q?=D7=A2s?=",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderOneLatineOneHebrew",
@"sע",
@"=?utf-8?q?s=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderOneCharacterHebrew",
@"ע",
@"=?utf-8?q?=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderOneCharacterRussian",
@"Б",
@"=?utf-8?q?=D0=91?=",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderParameter",
@"parameter=ע",
@"parameter==?utf-8?q?=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-MixedHeaderParameters",
@"plain; parameter=ע; parameter-plain; parameter2=ea",
@"plain;\n parameter==?utf-8?q?=D7=A2?=;\n parameter-plain; parameter2=ea",
nil],
[NSArray arrayWithObjects: @"X-MixedHeaderAndNoParameter",
@"plain; parameter=ע; parameter-plain; ע",
@"plain;\n parameter==?utf-8?q?=D7=A2?=; parameter-plain;\n =?utf-8?q?=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-MixedHeaderAndTwoParameter",
@"plain; parameter=ע; parameter-plain; z=ע",
@"plain;\n parameter==?utf-8?q?=D7=A2?=; parameter-plain;\n z==?utf-8?q?=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-MixedHeaderExtrablanks",
@"plain; parameter=ע; parameter 2spaces; parameter2=ea",
@"plain;\n \\ parameter==?utf-8?q?=D7=A2?=;\n parameter 2spaces; parameter2=ea",
nil],
[NSArray arrayWithObjects: @"X-Encoded-Unbalanced-Paramter-Quote",
@"text/plain; name=\"ע",
@"text/plain;\n name==?utf-8?q?=22=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"content-type",
@"text/plain; name=\"АБВГДЕЁЖЗИЙ, КЛМНОПРСТУФ y ЦЧШЩЪЫЬЭЮЯ.txt\"",
@"text/plain;\n name=\"=?utf-8?q?=D0=90=D0=91=D0=92=D0=93=D0=94=D0=95=D0=81=D0=96=D0=97=D0=98=D0=99=2C_=D0=9A=D0=9B=D0=9C=D0=9D=D0=9E=D0=9F=D0=A0=D0=A1=D0=A2=D0=A3=D0=A4_y_=D0=A6=D0=A7=D0=A8=D0=A9=D0=AA=D0=AB=D0=AC=D0=AD=D0=AE=D0=AF=2Etxt?=\"",
nil],
[NSArray arrayWithObjects: @"content-disposition",
@"attachment; filename=\"АБВГДЕЁЖЗИЙ, КЛМНОПРСТУФ y ЦЧШЩЪЫЬЭЮЯ.txt\"",
@"attachment;\n filename=\"=?utf-8?q?=D0=90=D0=91=D0=92=D0=93=D0=94=D0=95=D0=81=D0=96=D0=97=D0=98=D0=99=2C_=D0=9A=D0=9B=D0=9C=D0=9D=D0=9E=D0=9F=D0=A0=D0=A1=D0=A2=D0=A3=D0=A4_y_=D0=A6=D0=A7=D0=A8=D0=A9=D0=AA=D0=AB=D0=AC=D0=AD=D0=AE=D0=AF=2Etxt?=\"",
nil],
[NSArray arrayWithObjects: @"content-length", @"2912", @"2912", nil],
[NSArray arrayWithObjects: @"content-transfer-encoding", @"quoted-printable", @"quoted-printable", nil],
nil
];
NSEnumerator *enumerator;
NSArray *testCase;
[NGMimeMessageGenerator initialize];
generator = [[NGMimeMessageGenerator alloc] init];
[generator autorelease];
enumerator = [cases objectEnumerator];
while ((testCase = [enumerator nextObject]) != nil)
{
NSData *result;
NSMutableData *resultWithNulByte;
NSString *header = [testCase objectAtIndex: 0];
NSData *headerData = [testCase objectAtIndex: 1];
NSString *expected = [testCase objectAtIndex: 2];
result = [generator generateDataForHeaderField: header
value: headerData];
if (result == nil)
result = [@"[nil]" dataUsingEncoding: NSUTF8StringEncoding];
resultWithNulByte = [result mutableCopy];
[resultWithNulByte appendBytes: "\0" length: 1];
NSString *resultString = [NSString stringWithCString:[resultWithNulByte bytes]];
BOOL testResult = [resultString isEqualToString: expected];
NSString *diff = [self stringFromDiffBetween: [NSString stringWithString: resultString]
and: [NSString stringWithString: expected]];
NSString *testErrorMsg = [NSString
stringWithFormat: @">> For %@ header received:\n%@[END]\n>> instead of:\n%@[END]\n>> for:\n%@\n>> diff:\n%@\n>> lengthReceived: %u lengthExpected: %u",
header,
resultString,
expected,
headerData,
diff,
[resultString length],
[expected length]
];
testWithMessage(testResult, testErrorMsg);
}
}
@end
+1 -1
View File
@@ -35,7 +35,7 @@
{
const char *inStrings[] = { "éléphant", "2š", "sogo+test", NULL };
const char **inString;
NSString *outStrings[] = { @"%C3%A9l%C3%A9phant", @"2%C5%A1", @"sogo+test" };
NSString *outStrings[] = { @"%C3%A9l%C3%A9phant", @"2%C5%A1", @"sogo%2Btest" };
NSString **outString;
NSString *result, *error;
+9 -1
View File
@@ -116,13 +116,19 @@ static NSURL *tableURL = nil;
- (void) _removeUserFolders: (NSString *) userId
{
NSArray *folderPaths;
NSString *path;
int count, max;
folderPaths = [self _userFolderPaths: userId];
max = [folderPaths count];
if (max > 0)
for (count = 0; count < max; count++)
[fm deleteFolderAtPath: [folderPaths objectAtIndex: count]];
{
path = [folderPaths objectAtIndex: count];
[fm deleteFolderAtPath: path];
if (verbose)
NSLog (@"Deleting %@", path);
}
else
NSLog (@"No folder returned for user '%@'", userId);
}
@@ -144,6 +150,8 @@ static NSURL *tableURL = nil;
userId];
if ([fc evaluateExpressionX: sql])
NSLog (@"Unable to delete the preference record for '%@'", userId);
else if (verbose)
NSLog (@"Removed preference record for '%@'", userId);
[cm releaseChannel: fc];
}
}
+1
View File
@@ -12,6 +12,7 @@ AdministrationUI_OBJC_FILES = \
AdministrationUIProduct.m \
\
UIxAdministration.m \
UIxAdministrationAclEditor.m \
UIxAdministrationFilterPanel.m
AdministrationUI_RESOURCE_FILES += \
+5
View File
@@ -46,6 +46,11 @@
[super dealloc];
}
- (NSString *) modulePath
{
return @"Administration";
}
- (BOOL) shouldTakeValuesFromRequest: (WORequest *) request
inContext: (WOContext*) context
{
@@ -0,0 +1,48 @@
/* UIxAdministrationAclEditor.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.
*/
#ifndef UIXACLEDITOR_H
#define UIXACLEDITOR_H
#import <SOGoUI/UIxComponent.h>
@interface UIxAdministrationAclEditor : UIxComponent
{
BOOL prepared;
BOOL publishInFreeBusy;
NSArray *aclUsers;
NSArray *savedUIDs;
NSMutableDictionary *users;
NSString *currentUser;
NSString *defaultUserID;
}
- (NSArray *) aclsForObject;
- (void) setCurrentUser: (NSString *) newCurrentUser;
- (NSString *) currentUser;
- (NSString *) currentUserClass;
- (NSDictionary *) currentUserInfos;
- (NSString *) currentUserDisplayName;
- (BOOL) currentUserIsSubscribed;
- (BOOL) isPublicAccessEnabled;
@end
#endif /* UIXACLEDITOR_H */
@@ -0,0 +1,279 @@
/* UIxAdministrationAclEditor.m - this file is part of SOGo
*
* Copyright (C) 2006-2015 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 <Foundation/NSArray.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSKeyValueCoding.h>
#import <NGObjWeb/SoSecurityManager.h>
#import <NGObjWeb/SoUser.h>
#import <NGObjWeb/WORequest.h>
#import <NGObjWeb/WOResponse.h>
#import <NGCards/iCalPerson.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/SOGoContentObject.h>
#import <SOGo/SOGoGCSFolder.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserManager.h>
#import "UIxAdministrationAclEditor.h"
@implementation UIxAdministrationAclEditor
- (id) init
{
if ((self = [super init]))
{
aclUsers = nil;
prepared = NO;
publishInFreeBusy = NO;
users = [NSMutableDictionary new];
currentUser = nil;
defaultUserID = nil;
savedUIDs = nil;
}
return self;
}
- (void) dealloc
{
[savedUIDs release];
[users release];
[currentUser release];
[defaultUserID release];
[super dealloc];
}
- (NSArray *) aclsForObject
{
if (!aclUsers)
aclUsers = [[self clientObject] aclUsers];
return aclUsers;
}
- (NSString *) defaultUserID
{
if (!defaultUserID)
ASSIGN (defaultUserID, [[self clientObject] defaultUserID]);
return defaultUserID;
}
- (BOOL) canSubscribeUsers
{
return [[self clientObject]
respondsToSelector: @selector (subscribeUserOrGroup:reallyDo:response:)];
}
/**
* @api {get} /so/:username/:folderPath/acls List users with rights
* @apiVersion 1.0.0
* @apiName GetAcls
* @apiGroup Common
* @apiExample {curl} Example usage:
* curl -i http://localhost/SOGo/so/sogo1/Calendar/personal/acls
*
* @apiSuccess (Success 200) {Object[]} users List of users with ACL for the folder
* @apiSuccess (Success 200) {String} users.uid User ID
* @apiSuccess (Success 200) {String} users.userClass Either 'normal-user', 'normal-group' or 'public-access'
* @apiSuccess (Success 200) {Number} users.isSubscribed 1 if the user is subscribed to the folder
* @apiSuccess (Success 200) {String} [users.cn] User fullname
* @apiSuccess (Success 200) {String} [users.c_email] User main email address
*/
- (id <WOActionResults>) aclsAction
{
NSString *currentUID, *ownerLogin, *info;
NSDictionary *currentUserInfos;
NSMutableDictionary *userData;
id <WOActionResults> result;
NSEnumerator *aclsEnum;
if (!prepared)
{
ownerLogin = [[self clientObject] ownerInContext: context];
if (!defaultUserID)
ASSIGN (defaultUserID, [[self clientObject] defaultUserID]);
aclsEnum = [[self aclsForObject] objectEnumerator];
while ((currentUID = [aclsEnum nextObject]))
{
if (!([currentUID isEqualToString: ownerLogin]
|| [currentUID isEqualToString: defaultUserID]
|| [currentUID isEqualToString: @"anonymous"]))
{
// Set the current user in order to get information associated with it
[self setCurrentUser: currentUID];
// Build the object associated to the current UID
currentUserInfos = [self currentUserInfos];
userData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
currentUser, @"uid",
[self currentUserClass], @"userClass",
[NSNumber numberWithBool: [self currentUserIsSubscribed]], @"isSubscribed",
nil];
if ((info = [currentUserInfos objectForKey: @"cn"]) && [info length])
[userData setObject: info forKey: @"cn"];
if ((info = [currentUserInfos objectForKey: @"c_email"]) && [info length])
[userData setObject: info forKey: @"c_email"];
[users setObject: userData forKey: currentUID];
}
}
// Add the 'Any authenticated' user
if ([self canSubscribeUsers])
{
userData = [NSDictionary dictionaryWithObjectsAndKeys:
@"<default>", @"uid",
[self labelForKey: @"Any Authenticated User"], @"cn",
@"public-user", @"userClass",
nil];
[users setObject: userData forKey: @"<default>"];
}
if ([self canSubscribeUsers] && [self isPublicAccessEnabled])
{
// Add the 'public access' user
userData = [NSDictionary dictionaryWithObjectsAndKeys:
@"anonymous", @"uid",
[self labelForKey: @"Public Access"], @"cn",
@"public-user", @"userClass",
nil];
[users setObject: userData forKey: @"anonymous"];
}
prepared = YES;
}
result = [self responseWithStatus: 200
andJSONRepresentation: [NSDictionary dictionaryWithObject: users forKey: @"users"]];
return result;
}
- (void) setCurrentUser: (NSString *) newCurrentUser
{
ASSIGN (currentUser, newCurrentUser);
}
- (NSString *) currentUser
{
return ([currentUser hasPrefix: @"@"]
? [currentUser substringFromIndex: 1]
: currentUser);
}
- (NSString *) currentUserClass
{
return ([currentUser hasPrefix: @"@"]
? @"normal-group"
: @"normal-user");
}
- (NSString *) currentUserDisplayName
{
NSDictionary *infos;
NSString *uid;
SOGoUserManager *um;
um = [SOGoUserManager sharedUserManager];
uid = [self currentUser];
infos = [um contactInfosForUserWithUIDorEmail: uid inDomain: [[context activeUser] domain]];
if (infos)
{
return [NSString stringWithFormat: @"%@ <%@>",
[infos objectForKey: @"cn"],
[infos objectForKey: @"c_email"]];
}
else
return uid;
}
- (NSDictionary *) currentUserInfos
{
SOGoUserManager *um;
um = [SOGoUserManager sharedUserManager];
return [um contactInfosForUserWithUIDorEmail: [self currentUser]];
}
- (BOOL) currentUserIsSubscribed
{
SOGoGCSFolder *folder;
folder = [self clientObject];
return ([folder respondsToSelector: @selector (userIsSubscriber:)]
&& [folder userIsSubscriber: currentUser]);
}
- (void) setUserUIDS: (NSString *) retainedUsers
{
if ([retainedUsers length] > 0)
{
savedUIDs = [retainedUsers componentsSeparatedByString: @","];
[savedUIDs retain];
}
else
savedUIDs = [NSArray new];
}
- (NSString *) folderID
{
return [[self clientObject] nameInContainer];
}
- (BOOL) shouldTakeValuesFromRequest: (WORequest *) request
inContext: (WOContext *) context
{
return ([[request method] isEqualToString: @"POST"]);
}
- (id <WOActionResults>) saveAclsAction
{
NSEnumerator *aclsEnum;
SOGoObject *clientObject;
NSString *currentUID, *ownerLogin;
clientObject = [self clientObject];
ownerLogin = [clientObject ownerInContext: context];
aclsEnum = [[self aclsForObject] objectEnumerator];
while ((currentUID = [[aclsEnum nextObject] objectForKey: @"c_uid"]))
if ([currentUID isEqualToString: ownerLogin]
|| [savedUIDs containsObject: currentUID])
[users removeObjectForKey: currentUID];
[clientObject removeAclsForUsers: [users allKeys]];
return [self jsCloseWithRefreshMethod: nil];
}
- (BOOL) isPublicAccessEnabled
{
return [[SOGoSystemDefaults sharedSystemDefaults] enablePublicAccess];
}
@end
+4
View File
@@ -15,6 +15,10 @@
protectedBy = "View";
pageName = "UIxAdministration";
};
UIxAdministrationAclEditor = {
protectedBy = "View";
pageName = "UIxAdministrationAclEditor";
};
};
};
};
+10 -8
View File
@@ -408,16 +408,17 @@
- (id <WOActionResults>) copyAction
{
WORequest *request;
id <WOActionResults> response;
NSString *destinationFolderId;
NSArray *contactsId;
NSDictionary *data;
NSException *ex;
request = [context request];
data = [[[context request] contentAsString] objectFromJSONString];
contactsId = [data objectForKey: @"uids"];
destinationFolderId = [data objectForKey: @"folder"];
if ((destinationFolderId = [request formValueForKey: @"folder"]) &&
(contactsId = [request formValuesForKey: @"uid"]))
if (destinationFolderId && [contactsId count])
ex = [self _moveContacts: contactsId
toFolder: destinationFolderId
andKeepCopy: YES];
@@ -436,16 +437,17 @@
- (id <WOActionResults>) moveAction
{
WORequest *request;
id <WOActionResults> response;
NSString *destinationFolderId;
NSDictionary *data;
NSArray *contactsId;
NSException *ex;
request = [context request];
data = [[[context request] contentAsString] objectFromJSONString];
contactsId = [data objectForKey: @"uids"];
destinationFolderId = [data objectForKey: @"folder"];
if ((destinationFolderId = [request formValueForKey: @"folder"])
&& (contactsId = [request formValuesForKey: @"uid"]))
if (destinationFolderId && [contactsId count])
ex = [self _moveContacts: contactsId
toFolder: destinationFolderId
andKeepCopy: NO];
+7 -42
View File
@@ -249,14 +249,7 @@
- (NSString *) commonLocalizableStrings
{
NSString *rc;
if (isPopup)
rc = @"";
else
rc = [NSString stringWithFormat: @"var clabels = %@;", [self _stringsForFramework: nil]];
return rc;
return [NSString stringWithFormat: @"var clabels = %@;", [self _stringsForFramework: nil]];
}
- (NSString *) productLocalizableStrings
@@ -440,40 +433,6 @@
return ([[self productCSSURL] length] > 0);
}
- (BOOL) _moduleIs: (NSString *) moduleName
{
NSString *frameworkName;
frameworkName = [[context page] frameworkName];
return [frameworkName isEqualToString: moduleName];
}
- (BOOL) isCalendar
{
return [self _moduleIs: @"SchedulerUI"];
}
- (BOOL) isContacts
{
return [self _moduleIs: @"ContactsUI"];
}
- (BOOL) isMail
{
return [self _moduleIs: @"MailerUI"];
}
- (BOOL) isPreferences
{
return [self _moduleIs: @"PreferencesUI"];
}
- (BOOL) isAdministration
{
return [self _moduleIs: @"AdministrationUI"];
}
- (void) setToolbar: (NSString *) newToolbar
{
ASSIGN (toolbar, newToolbar);
@@ -509,6 +468,12 @@
return [[context activeUser] cn];
}
- (NSString *) userEmail
{
return [[context activeUser] systemEmail];
}
- (BOOL) canLogoff
{
BOOL canLogoff;
@@ -43,6 +43,27 @@
"Copy To" = "Copy To";
"Add to:" = "Add to:";
/* Subheader of empty addressbook */
"No contact" = "No contact";
/* Subheader of system addressbook */
"Start a search to browse this address book" = "Start a search to browse this address book";
/* Number of contacts in addressbook; string is prefixed by number */
"contacts" = "contacts";
/* No contact matching search criteria */
"No matching contact" = "No matching contact";
/* Number of contacts matching search criteria; string is prefixed by number */
"matching contacts" = "matching contacts";
/* Number of selected contacts in list */
"selected" = "selected";
/* Empty right pane */
"No contact selected" = "No contact selected";
/* Tooltips */
"Create a new address book card" = "Create a new address book card";
+11 -6
View File
@@ -367,6 +367,8 @@ static Class SOGoContactGCSEntryK = Nil;
day = [date dayOfMonth];
[card setBday: [NSString stringWithFormat: @"%.4d%.2d%.2d", year, month, day]];
}
else
[card setBday: nil];
if ([[attributes objectForKey: @"addresses"] isKindOfClass: [NSArray class]])
{
@@ -430,8 +432,9 @@ static Class SOGoContactGCSEntryK = Nil;
{
if ([attrs isKindOfClass: [NSDictionary class]])
{
element = [card elementWithTag: @"tel" ofType: [attrs objectForKey: @"type"]];
[element setSingleValue: [attrs objectForKey: @"value"] forKey: @""];
[card addElementWithTag: @"tel"
ofType: [attrs objectForKey: @"type"]
withValue: [attrs objectForKey: @"value"]];
}
}
}
@@ -448,8 +451,9 @@ static Class SOGoContactGCSEntryK = Nil;
{
if ([o isKindOfClass: [NSDictionary class]])
{
element = [card elementWithTag: @"email" ofType: [o objectForKey: @"type"]];
[element setSingleValue: [o objectForKey: @"value"] forKey: @""];
[card addElementWithTag: @"email"
ofType: [o objectForKey: @"type"]
withValue: [o objectForKey: @"value"]];
}
}
}
@@ -466,8 +470,9 @@ static Class SOGoContactGCSEntryK = Nil;
{
if ([attrs isKindOfClass: [NSDictionary class]])
{
element = [card elementWithTag: @"url" ofType: [attrs objectForKey: @"type"]];
[element setSingleValue: [attrs objectForKey: @"value"] forKey: @""];
[card addElementWithTag: @"url"
ofType: [attrs objectForKey: @"type"]
withValue: [attrs objectForKey: @"value"]];
}
}
}
+7 -13
View File
@@ -112,15 +112,11 @@
request = [context request];
rc = [NSMutableDictionary dictionary];
data = [request formValueForKey: @"contactsFile"];
if ([data respondsToSelector: @selector(isEqualToString:)])
fileContent = (NSString *) data;
else
{
fileContent = [[NSString alloc] initWithData: (NSData *) data
encoding: NSUTF8StringEncoding];
[fileContent autorelease];
}
data = [[[[[request httpRequest] body] parts] lastObject] body];
fileContent = [[NSString alloc] initWithData: (NSData *) data
encoding: NSUTF8StringEncoding];
[fileContent autorelease];
if (fileContent && [fileContent length])
{
@@ -132,12 +128,10 @@
imported = 0;
}
[rc setObject: [NSNumber numberWithInt: imported]
forKey: @"imported"];
[rc setObject: [NSNumber numberWithInt: imported] forKey: @"imported"];
response = [self responseWithStatus: 200];
[response setHeader: @"text/html"
forKey: @"content-type"];
[response setHeader: @"text/html" forKey: @"content-type"];
[(WOResponse*)response appendContentString: [rc jsonRepresentation]];
return response;
@@ -20,14 +20,6 @@
#import <SOGoUI/UIxComponent.h>
@class NSString;
@class SOGoContactGCSFolder;
@interface UIxContactFolderLinksTemplate : UIxComponent
{
SOGoContactGCSFolder *addressBook;
NSString *baseCardDAVURL, *basePublicCardDAVURL;
}
@end
@@ -24,69 +24,6 @@
@implementation UIxContactFolderLinksTemplate
- (id) init
{
if ((self = [super init]))
{
addressBook = [self clientObject];
baseCardDAVURL = nil;
basePublicCardDAVURL = nil;
}
return self;
}
- (void) dealloc
{
[baseCardDAVURL release];
[basePublicCardDAVURL release];
[super dealloc];
}
- (NSString *) _baseCardDAVURL
{
NSString *davURL;
if (!baseCardDAVURL)
{
davURL = [[addressBook realDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
baseCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
baseCardDAVURL = davURL;
[baseCardDAVURL retain];
}
return baseCardDAVURL;
}
- (NSString *) cardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _baseCardDAVURL]];
}
- (NSString *) _basePublicCardDAVURL
{
NSString *davURL;
if (!basePublicCardDAVURL)
{
davURL = [[addressBook publicDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
basePublicCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
basePublicCardDAVURL = davURL;
[basePublicCardDAVURL retain];
}
return basePublicCardDAVURL;
}
- (NSString *) publicCardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _basePublicCardDAVURL]];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
+48 -11
View File
@@ -71,6 +71,11 @@ Class SOGoContactSourceFolderK, SOGoGCSFolderK;
return self;
}
- (NSString *) modulePath
{
return @"Contacts";
}
- (void) _setupContext
{
SOGoUser *activeUser;
@@ -275,32 +280,70 @@ Class SOGoContactSourceFolderK, SOGoGCSFolderK;
return [[self queryParameterForKey: @"popup"] boolValue];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
return [[SOGoSystemDefaults sharedSystemDefaults] enablePublicAccess];
}
- (NSString *) contactFolders
{
SOGoContactFolders *folderContainer;
NSArray *folders;
NSMutableDictionary *urls, *acls;
NSMutableArray *foldersAttrs;
NSString *userLogin, *owner;
NSArray *folders, *allACLs;
NSDictionary *folderAttrs;
id currentFolder;
BOOL objectCreator, objectEditor, objectEraser;
int max, i;
userLogin = [[context activeUser] login];
folderContainer = [self clientObject];
// return [folderContainer subFolders];
folders = [folderContainer subFolders];
max = [folders count];
foldersAttrs = [NSMutableArray arrayWithCapacity: max];
urls = nil;
for (i = 0; i < max; i++)
{
currentFolder = [folders objectAtIndex: i];
owner = [currentFolder ownerInContext: context];
// We extract URLs for this address book
if ([currentFolder respondsToSelector: @selector(cardDavURL)])
{
urls = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[currentFolder cardDavURL], @"cardDavURL",
nil];
if ([self isPublicAccessEnabled])
{
[urls setObject: [currentFolder publicCardDavURL] forKey: @"publicCardDavURL"];
}
}
// We extract ACLs for this address book
allACLs = ([owner isEqualToString: userLogin] ? nil : [currentFolder aclsForUser: userLogin]);
objectCreator = ([owner isEqualToString: userLogin] || [allACLs containsObject: SOGoRole_ObjectCreator]);
objectEditor = ([owner isEqualToString: userLogin] || [allACLs containsObject: SOGoRole_ObjectEditor]);
objectEraser = ([owner isEqualToString: userLogin] || [allACLs containsObject: SOGoRole_ObjectEraser]);
acls = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool: objectCreator], @"objectCreator",
[NSNumber numberWithBool: objectEditor], @"objectEditor",
[NSNumber numberWithBool: objectEraser], @"objectEraser", nil];
// NOTE: keep urls as the last key/value here, to avoid chopping the dictionary
// if it is not a GCS folder
folderAttrs = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat: @"%@", [currentFolder nameInContainer]], @"id",
[currentFolder displayName], @"name",
[currentFolder ownerInContext: context], @"owner",
owner, @"owner",
[NSNumber numberWithBool: [currentFolder isKindOfClass: SOGoGCSFolderK]], @"isEditable",
[NSNumber numberWithBool: [currentFolder isKindOfClass: SOGoContactSourceFolderK]
&& ![currentFolder isPersonalSource]], @"isRemote",
acls, @"acls",
urls, @"urls",
nil];
[foldersAttrs addObject: folderAttrs];
}
@@ -419,12 +462,6 @@ Class SOGoContactSourceFolderK, SOGoGCSFolderK;
return [super defaultAction];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
return [[SOGoSystemDefaults sharedSystemDefaults] enablePublicAccess];
}
@end
@interface UIxContactViewTemplate : UIxComponent
+3 -4
View File
@@ -838,11 +838,10 @@
o = [card birthday];
if (o)
{
NSNumber *time = [NSNumber numberWithInt: [o timeIntervalSince1970]];
[data setObject: time forKey: @"birthday"];
[data setObject: [NSNumber numberWithInt: [o timeIntervalSince1970]]
forKey: @"birthday"];
}
// o = [card source];
// if (o) [data setObject: o forKey: @"source"];
o = [card tz];
if (o) [data setObject: o forKey: @"tz"];
+31 -1
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2006-2014 Inverse inc.
Copyright (C) 2006-2015 Inverse inc.
This file is part of SOGo.
@@ -39,6 +39,9 @@
#import <Contacts/SOGoContactFolder.h>
#import <Contacts/SOGoContactFolders.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserSettings.h>
#import <NGCards/NGVCard.h>
#import <NGCards/NGVList.h>
#import <SoObjects/Contacts/SOGoContactGCSEntry.h>
@@ -85,6 +88,31 @@
return s;
}
- (void) saveSortValue
{
NSMutableDictionary *contactSettings;
NSString *ascending, *sort;
SOGoUserSettings *us;
sort = [[context request] formValueForKey: @"sort"];
ascending = [[context request] formValueForKey: @"asc"];
if ([sort length])
{
us = [[context activeUser] userSettings];
contactSettings = [us objectForKey: @"Contact"];
// Must create if it doesn't exist
if (!contactSettings)
{
contactSettings = [NSMutableDictionary dictionary];
[us setObject: contactSettings forKey: @"Contact"];
}
[contactSettings setObject: [NSArray arrayWithObjects: [sort lowercaseString], [NSString stringWithFormat: @"%d", [ascending intValue]], nil]
forKey: @"SortingState"];
[us synchronize];
}
}
- (NSArray *) contactInfos
{
id <SOGoContactFolder> folder;
@@ -97,6 +125,8 @@
WORequest *rq;
unsigned int i;
[self saveSortValue];
if (!contactInfos)
{
folder = [self clientObject];
@@ -87,6 +87,7 @@
id info, viewer;
NSArray *parts;
NSMutableArray *renderedParts;
NSString *contentType;
NSUInteger i, max;
parts = [[self bodyInfo] objectForKey: @"parts"];
@@ -102,9 +103,13 @@
[viewer setPartPath: [self childPartPath]];
[renderedParts addObject: [viewer renderedPart]];
}
contentType = [NSString stringWithFormat: @"%@/%@",
[[self bodyInfo] objectForKey: @"type"],
[[self bodyInfo] objectForKey: @"subtype"]];
return [NSDictionary dictionaryWithObjectsAndKeys:
[self className], @"type",
contentType, @"contentType",
renderedParts, @"content",
nil];
}
+1 -3
View File
@@ -1,8 +1,6 @@
/* UIxMailPartTextViewer.h - this file is part of $PROJECT_NAME_HERE$
*
* Copyright (C) 2006 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2015 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
+19 -20
View File
@@ -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.
*/
/* UIxMailPartTextViewer.m - this file is part of $PROJECT_NAME_HERE$
*
* Copyright (C) 2006-2015 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.
*/
/*
UIxMailPartTextViewer
@@ -65,6 +65,9 @@
"Account: " = "Account: ";
"Shared Account: " = "Shared Account: ";
/* Empty right pane */
"No message selected" = "No message selected";
/* acls */
"Access rights to" = "Access rights to";
"For user" = "For user";
@@ -228,6 +231,9 @@
"Delete Message" = "Delete Message";
"Delete Selected Messages" = "Delete Selected Messages";
/* Number of selected messages in list */
"selected" = "selected";
"This Folder" = "This Folder";
/* Label popup menu */
+15 -8
View File
@@ -92,7 +92,7 @@
}
else
{
errorFormat = [self labelForKey: @"The folder with name \"%@\" could not be created."];
errorFormat = [self labelForKey: @"The folder with name \"%@\" could not be created." inContext: context];
jsonResponse = [NSDictionary dictionaryWithObject: [NSString stringWithFormat: errorFormat, folderName]
forKey: @"error"];
response = [self responseWithStatus: 500
@@ -101,7 +101,7 @@
}
else
{
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing 'name' parameter."]
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing 'name' parameter." inContext: context]
forKey: @"error"];
response = [self responseWithStatus: 500
andJSONRepresentation: jsonResponse];
@@ -149,7 +149,7 @@
if (!newFolderName || [newFolderName length] == 0)
{
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing name parameter"]
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing name parameter" inContext: context]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [message jsonRepresentation]];
@@ -160,7 +160,7 @@
error = [co renameTo: newFolderName];
if (error)
{
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to rename folder."]
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to rename folder." inContext: context]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [message jsonRepresentation]];
@@ -251,7 +251,7 @@
error = [connection moveMailboxAtURL: srcURL toURL: destURL];
if (error)
{
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder."]
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder." inContext: context]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [jsonResponse jsonRepresentation]];
@@ -283,7 +283,7 @@
}
else
{
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder."]
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder." inContext: context]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [jsonResponse jsonRepresentation]];
@@ -374,7 +374,7 @@
{
uids = [value componentsSeparatedByString: @","];
response = [co archiveUIDs: uids
inArchiveNamed: [self labelForKey: @"Saved Messages.zip"]
inArchiveNamed: [self labelForKey: @"Saved Messages.zip" inContext: context]
inContext: context];
if (!response)
response = [self responseWith204];
@@ -739,6 +739,7 @@
NSDictionary *content, *result;
BOOL addOrRemove;
NGImap4Client *client;
id flag;
int i;
@@ -751,7 +752,13 @@
// We unescape our flags
for (i = [flags count]-1; i >= 0; i--)
[flags replaceObjectAtIndex: i withObject: [[flags objectAtIndex: i] fromCSSIdentifier]];
{
flag = [flags objectAtIndex: i];
if ([flag isKindOfClass: [NSString class]])
[flags replaceObjectAtIndex: i withObject: [flag fromCSSIdentifier]];
else
[flags removeObjectAtIndex: i];
}
co = [self clientObject];
client = [[co imap4Connection] client];
+45 -17
View File
@@ -55,6 +55,7 @@
#import <Mailer/SOGoSentFolder.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSObject+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoDateFormatter.h>
#import <SOGo/SOGoUser.h>
@@ -131,7 +132,7 @@
else if ([now dayOfCommonEra] - [messageDate dayOfCommonEra] == 1)
{
// Yesterday
return [self labelForKey: @"Yesterday"];
return [self labelForKey: @"Yesterday" inContext: context];
}
else if ([now dayOfCommonEra] - [messageDate dayOfCommonEra] < 7)
{
@@ -163,13 +164,15 @@
//
// Sometimes, the MUAs don't send over the string in () so we ignore it.
//
- (NSString *) messagePriority
- (NSDictionary *) messagePriority
{
NSString *result;
NSUInteger priority;
NSString *description;
NSData *data;
data = [message objectForKey: @"header"];
result = @"";
priority = 3;
description = [self labelForKey: @"normal" inContext: context];
if (data)
{
@@ -190,15 +193,34 @@
s = [[s substringFromIndex: r.location+1]
stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([s hasPrefix: @"1"]) result = [self labelForKey: @"highest"];
else if ([s hasPrefix: @"2"]) result = [self labelForKey: @"high"];
else if ([s hasPrefix: @"4"]) result = [self labelForKey: @"low"];
else if ([s hasPrefix: @"5"]) result = [self labelForKey: @"lowest"];
if ([s hasPrefix: @"1"])
{
priority = 1;
description = [self labelForKey: @"highest" inContext: context];
}
else if ([s hasPrefix: @"2"])
{
priority = 2;
description = [self labelForKey: @"high" inContext: context];
}
else if ([s hasPrefix: @"4"])
{
priority = 4;
description = [self labelForKey: @"low" inContext: context];
}
else if ([s hasPrefix: @"5"])
{
priority = 5;
description = [self labelForKey: @"lowest" inContext: context];
}
}
}
}
return result;
return [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt: priority], @"level",
description, @"name",
nil];
}
- (NSString *) messageSubject
@@ -242,7 +264,7 @@
{
NSString *s;
s = [self labelForKey:@"View Mail Folder"];
s = [self labelForKey:@"View Mail Folder" inContext: context];
s = [s stringByAppendingString:@": "];
s = [s stringByAppendingString:[self objectTitle]];
return s;
@@ -622,11 +644,12 @@
- (NSDictionary *) getUIDsInFolder: (SOGoMailFolder *) folder
withHeaders: (BOOL) includeHeaders
{
NSMutableDictionary *data;
NSArray *uids, *threadedUids, *headers;
NSRange r;
NSMutableDictionary *data;
SOGoMailAccount *account;
id quota;
NSRange r;
int count;
data = [NSMutableDictionary dictionary];
@@ -644,10 +667,14 @@
if (includeHeaders)
{
// Also retrieve the first headers, up to 'headersPrefetchMaxSize'
count = [[uids flattenedArray] count];
if (count > headersPrefetchMaxSize) count = headersPrefetchMaxSize;
NSArray *a;
a = [uids flattenedArray];
count = [a count];
if (count > headersPrefetchMaxSize)
count = headersPrefetchMaxSize;
r = NSMakeRange(0, count);
headers = [self getHeadersForUIDs: [[uids flattenedArray] subarrayWithRange: r]
headers = [self getHeadersForUIDs: [a subarrayWithRange: r]
inFolder: folder];
[data setObject: headers forKey: @"headers"];
@@ -716,6 +743,8 @@
* @apiSuccess (Success 200) {String} headers.From.email Sender's email address
* @apiSuccess (Success 200) {Number} headers.isRead 1 if message is read
* @apiSuccess (Success 200) {String} headers.Priority Priority
* @apiSuccess (Success 200) {String} headers.Priority.level Priority number
* @apiSuccess (Success 200) {String} headers.Priority.name Priority description
* @apiSuccess (Success 200) {String} headers.RelativeDate Message date relative to now
* @apiSuccess (Success 200) {String} headers.Size Formatted message size
* @apiSuccess (Success 200) {String[]} headers.Flags Flags, such as "answered" and "seen"
@@ -744,7 +773,6 @@
noHeaders = [[[requestContent objectForKey: @"sortingAttributes"] objectForKey:@"noHeaders"] boolValue];
data = [self getUIDsInFolder: folder
withHeaders: !noHeaders];
[response appendContentString: [data jsonRepresentation]];
return response;
@@ -878,7 +906,7 @@
data = [NSDictionary dictionaryWithObjectsAndKeys:
@"No UID specified", @"error", nil];
return [self responseWithStatus: 404 /* Not Found */
andString: [data jsonRepresentation]];
andString: [data jsonRepresentation]];
}
uids = [data objectForKey: @"uids"];
+20 -3
View File
@@ -87,6 +87,11 @@
[super dealloc];
}
- (NSString *) modulePath
{
return @"Mail";
}
- (void) _setupContext
{
SOGoUser *activeUser;
@@ -118,6 +123,18 @@
return [accounts jsonRepresentation];
}
- (WOResponse *) mailAccountsAction
{
WOResponse *response;
NSString *s;
s = [self mailAccounts];
response = [self responseWithStatus: 200
andString: s];
return response;
}
- (NSString *) userNames
{
NSArray *accounts, *userNames;
@@ -374,14 +391,14 @@
- (WOResponse *) saveFoldersStateAction
{
WORequest *request;
NSString *expandedFolders;
NSArray *expandedFolders;
[self _setupContext];
request = [context request];
expandedFolders = [request formValueForKey: @"expandedFolders"];
expandedFolders = [[request contentAsString] objectFromJSONString];
[moduleSettings setObject: expandedFolders
forKey: @"ExpandedFolders"];
forKey: @"ExpandedFolders"];
[us synchronize];
+9
View File
@@ -342,10 +342,19 @@
actionClass = "UIxMailMainFrame";
actionName = "unseenCount";
};
mailAccounts = {
protectedBy = "View";
actionClass = "UIxMailMainFrame";
actionName = "mailAccounts";
};
// viewerTemplate = {
// protectedBy = "View";
// pageName = "UIxMailViewTemplate";
// };
UIxMailPopupView = {
protectedBy = "View";
pageName = "UIxMailPopupView";
};
UIxMailEditor = {
protectedBy = "View";
pageName = "UIxMailEditor";
@@ -9,6 +9,7 @@
"Connect" = "Connect";
"Authentication Failed" = "Authentication Failed";
"Wrong username or password." = "Wrong username or password.";
"cookiesNotEnabled" = "You cannot login because your browser's cookies are disabled. Please enable cookies in your browser's settings and try again.";
+7 -6
View File
@@ -183,7 +183,6 @@
SOGoWebAuthenticator *auth;
SOGoAppointmentFolders *calendars;
SOGoUserDefaults *ud;
SOGoSystemDefaults *sd;
SOGoUser *loggedInUser;
NSDictionary *params;
NSString *username, *password, *language, *domain, *remoteHost;
@@ -226,13 +225,15 @@
response = [self responseWithStatus: 200
andJSONRepresentation: json];
// We get the proper username for cookie creation. If we are using a multidomain
// environment with SOGoEnableDomainBasedUID, we could have to append the domain
// to the username. Also when SOGoEnableDomainBasedUID is enabled, we could be in
// the DomainLessLogin situation, so we would NOT add the domain. -getUIDForEmail
// has all the logic for this, so lets use it.
if ([domain isNotNull])
{
sd = [SOGoSystemDefaults sharedSystemDefaults];
if ([sd enableDomainBasedUID] &&
[username rangeOfString: @"@"].location == NSNotFound)
username = [NSString stringWithFormat: @"%@@%@", username, domain];
username = [[SOGoUserManager sharedUserManager] getUIDForEmail: username];
}
authCookie = [auth cookieWithUsername: username
+13 -9
View File
@@ -636,7 +636,7 @@
* @apiExample {curl} Example usage:
* curl -i http://localhost/SOGo/so/sogo1/foldersSearch?type=contact
*
* @apiParam {String} type Either 'calendar' or 'contact'
* @apiParam {String} type Either 'calendar' or 'contact'. If nothing is specifed, its both.
*
* @apiSuccess (Success 200) {Object[]} folders List of matching folders
* @apiSuccess (Success 200) {String} folders.name Path of folder
@@ -648,27 +648,31 @@
- (id <WOActionResults>) foldersSearchAction
{
NSString *folderType;
NSArray *folders;
NSMutableArray *folders;
NSDictionary *message;
id <WOActionResults> result;
SOGoUserFolder *userFolder;
folderType = [self queryParameterForKey: @"type"];
folders = [NSMutableArray array];
userFolder = [self clientObject];
if ([folderType length])
{
userFolder = [self clientObject];
folders = [userFolder foldersOfType: folderType
forUID: [userFolder ownerInContext: context]];
result = [self _foldersResponseForResults: folders];
[folders addObjectsFromArray: [userFolder foldersOfType: folderType
forUID: [userFolder ownerInContext: context]]];
}
else
{
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing type parameter"]
forKey: @"error"];
result = [self responseWithStatus: 400 andJSONRepresentation: message];
[folders addObjectsFromArray: [userFolder foldersOfType: @"calendar"
forUID: [userFolder ownerInContext: context]]];
[folders addObjectsFromArray: [userFolder foldersOfType: @"contact"
forUID: [userFolder ownerInContext: context]]];
}
result = [self _foldersResponseForResults: folders];
return result;
}
@@ -1,6 +1,7 @@
/* toolbar */
"Save and Close" = "Save and Close";
"Close" = "Close";
"Preferences saved" = "Preferences saved";
/* tabs */
"General" = "General";
@@ -317,6 +318,9 @@
"matches regex" = "matches regex";
"does not match regex" = "does not match regex";
/* Placeholder for the value field of a condition */
"Value" = "Value";
"Seen" = "Seen";
"Deleted" = "Deleted";
"Answered" = "Answered";
+35 -20
View File
@@ -61,7 +61,8 @@ static SoProduct *preferencesProduct = nil;
SOGoUserDefaults *defaults;
NSMutableArray *accounts;
NSArray *categoryLabels;
NSDictionary *locale;
if (!preferencesProduct)
{
preferencesProduct = [[SoProduct alloc] initWithBundle:
@@ -78,7 +79,7 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoTimeZone"])
[[defaults source] setObject: [defaults timeZoneName] forKey: @"SOGoTimeZone"];
if (![[defaults source] objectForKey: @"SOGoShortDateFormat"])
[[defaults source] setObject: [defaults shortDateFormat] forKey: @"SOGoShortDateFormat"];
@@ -90,7 +91,7 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoLoginModule"])
[[defaults source] setObject: [defaults loginModule] forKey: @"SOGoLoginModule"];
if (![[defaults source] objectForKey: @"SOGoRefreshViewCheck"])
[[defaults source] setObject: [defaults refreshViewCheck] forKey: @"SOGoRefreshViewCheck"];
@@ -99,13 +100,13 @@ static SoProduct *preferencesProduct = nil;
//
if (![[defaults source] objectForKey: @"SOGoFirstDayOfWeek"])
[[defaults source] setObject: [NSNumber numberWithInt: [defaults firstDayOfWeek]] forKey: @"SOGoFirstDayOfWeek"];
if (![[defaults source] objectForKey: @"SOGoDayStartTime"])
[[defaults source] setObject: [NSString stringWithFormat: @"%02d:00", [defaults dayStartHour]] forKey: @"SOGoDayStartTime"];
if (![[defaults source] objectForKey: @"SOGoDayEndTime"])
[[defaults source] setObject: [NSString stringWithFormat: @"%02d:00", [defaults dayEndHour]] forKey: @"SOGoDayEndTime"];
if (![[defaults source] objectForKey: @"SOGoFirstWeekOfYear"])
[[defaults source] setObject: [defaults firstWeekOfYear] forKey: @"SOGoFirstWeekOfYear"];
@@ -114,20 +115,20 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoCalendarEventsDefaultClassification"])
[[defaults source] setObject: [defaults calendarEventsDefaultClassification] forKey: @"SOGoCalendarEventsDefaultClassification"];
if (![[defaults source] objectForKey: @"SOGoCalendarTasksDefaultClassification"])
[[defaults source] setObject: [defaults calendarTasksDefaultClassification] forKey: @"SOGoCalendarTasksDefaultClassification"];
if (![[defaults source] objectForKey: @"SOGoCalendarDefaultReminder"])
[[defaults source] setObject: [defaults calendarDefaultReminder] forKey: @"SOGoCalendarDefaultReminder"];
// Populate default calendar categories, based on the user's preferred language
if (![defaults calendarCategories])
{
categoryLabels = [[[self labelForKey: @"calendar_category_labels" withResourceManager: [preferencesProduct resourceManager]]
componentsSeparatedByString: @","]
sortedArrayUsingSelector: @selector (localizedCaseInsensitiveCompare:)];
[defaults setCalendarCategories: categoryLabels];
}
if (![defaults calendarCategoriesColors])
@@ -143,7 +144,7 @@ static SoProduct *preferencesProduct = nil;
[defaults setCalendarCategoriesColors: colors];
}
//
// Default Contacts preferences
//
@@ -154,10 +155,10 @@ static SoProduct *preferencesProduct = nil;
categoryLabels = [[[self labelForKey: @"contacts_category_labels" withResourceManager: [preferencesProduct resourceManager]]
componentsSeparatedByString: @","]
sortedArrayUsingSelector: @selector (localizedCaseInsensitiveCompare:)];
if (!categoryLabels)
categoryLabels = [NSArray array];
[defaults setContactsCategories: categoryLabels];
}
@@ -172,27 +173,27 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoMailReplyPlacement"])
[[defaults source] setObject: [defaults mailReplyPlacement] forKey: @"SOGoMailReplyPlacement"];
if (![[defaults source] objectForKey: @"SOGoMailSignaturePlacement"])
[[defaults source] setObject: [defaults mailSignaturePlacement] forKey: @"SOGoMailSignaturePlacement"];
if (![[defaults source] objectForKey: @"SOGoMailComposeMessageType"])
[[defaults source] setObject: [defaults mailComposeMessageType] forKey: @"SOGoMailComposeMessageType"];
if (![[defaults source] objectForKey: @"SOGoMailDisplayRemoteInlineImages"])
[[defaults source] setObject: [defaults mailDisplayRemoteInlineImages] forKey: @"SOGoMailDisplayRemoteInlineImages"];
// Populate default mail lablels, based on the user's preferred language
if (![[defaults source] objectForKey: @"SOGoMailLabelsColors"])
{
SOGoMailLabel *label;
NSArray *labels;
id v;
int i;
v = [defaults mailLabelsColors];
labels = [SOGoMailLabel labelsFromDefaults: v component: self];
v = [NSMutableDictionary dictionary];
for (i = 0; i < [labels count]; i++)
@@ -201,7 +202,7 @@ static SoProduct *preferencesProduct = nil;
[v setObject: [NSArray arrayWithObjects: [label label], [label color], nil]
forKey: [label name]];
}
[defaults setMailLabelsColors: v];
}
@@ -214,6 +215,10 @@ static SoProduct *preferencesProduct = nil;
//
values = [[[[defaults source] values] mutableCopy] autorelease];
// Add locale code (used by CK Editor)
locale = [[preferencesProduct resourceManager] localeForLanguageNamed: [defaults language]];
[values setObject: [locale objectForKey: @"NSLocaleCode"] forKey: @"LocaleCode"];
accounts = [NSMutableArray arrayWithArray: [values objectForKey: @"AuxiliaryMailAccounts"]];
account = [[[context activeUser] mailAccounts] objectAtIndex: 0];
if (![account objectForKey: @"receipts"])
@@ -250,6 +255,16 @@ static SoProduct *preferencesProduct = nil;
forKey: @"PreventInvitationsWhitelist"];
}
// Initialize some default values
if (![settings objectForKey: @"Calendar"])
[settings setObject: [NSMutableDictionary dictionary] forKey: @"Calendar"];
if (![settings objectForKey: @"Contact"])
[settings setObject: [NSMutableDictionary dictionary] forKey: @"Contact"];
if (![settings objectForKey: @"Mail"])
[settings setObject: [NSMutableDictionary dictionary] forKey: @"Mail"];
return [self _makeResponse: [[settings source] values]];
}
+10 -1
View File
@@ -169,6 +169,11 @@ static NSArray *reminderValues = nil;
[super dealloc];
}
- (NSString *) modulePath
{
return @"Preferences";
}
// - (void) setHasChanged: (BOOL) newHasChanged
// {
// hasChanged = newHasChanged;
@@ -1064,7 +1069,7 @@ static NSArray *reminderValues = nil;
if (!capabilities)
{
if ([self _sieveClient])
if ([self isSieveScriptsEnabled] && [self _sieveClient])
capabilities = [[self _sieveClient] capabilities];
else
capabilities = [NSArray array];
@@ -2086,6 +2091,10 @@ static NSArray *reminderValues = nil;
// We convert our object into a mutable one
v = [[v mutableCopy] autorelease];
if ([[v objectForKey: @"SOGoLoginModule"] isEqualToString: @"Last"])
[v setObject: [NSNumber numberWithBool: YES] forKey: @"SOGoRememberLastModule"];
else
[v setObject: [NSNumber numberWithBool: NO] forKey: @"SOGoRememberLastModule"];
// We remove short/long date formats if they are default ones
if ([[v objectForKey: @"SOGoShortDateFormat"] isEqualToString: @"default"])
+9 -30
View File
@@ -360,40 +360,19 @@ static SoProduct *commonProduct = nil;
- (NSString *) modulePath
{
SOGoObject *currentClient, *parent;
BOOL found;
NSString *hostLessURL;
Class objectClass, userFolderClass;
// , groupFolderClass
currentClient = [self clientObject];
if (currentClient
&& [currentClient isKindOfClass: [SOGoObject class]])
if ([[self parent] respondsToSelector: @selector(modulePath)])
{
// groupFolderClass = [SOGoCustomGroupFolder class];
userFolderClass = [SOGoUserFolder class];
NSString *baseURL;
objectClass = [currentClient class];
// found = (objectClass == groupFolderClass || objectClass == userFolderClass);
found = (objectClass == userFolderClass);
while (!found && currentClient)
{
parent = [currentClient container];
objectClass = [parent class];
if (// objectClass == groupFolderClass
// ||
objectClass == userFolderClass)
found = YES;
else
currentClient = parent;
}
baseURL = [[self clientObject] baseURLInContext: context];
if ([baseURL hasSuffix: [NSString stringWithFormat: @"%@/", [[self parent] modulePath]]])
return baseURL;
return [NSString stringWithFormat: @"%@%@", baseURL, [[self parent] modulePath]];
}
else
currentClient = [WOApplication application];
hostLessURL = [[currentClient baseURLInContext: context] hostlessURL];
return [hostLessURL substringToIndex: [hostLessURL length] - 1];
return @"SOGo";
}
- (NSString *) ownPath
@@ -439,6 +439,9 @@ validate_endbeforestart = "The end date that you entered occurs before the st
"Month View" = "Month View";
"Reload" = "Reload";
/* Number of selected components in events or tasks list */
"selected" = "selected";
"eventPartStatModificationError" = "Your participation status could not be modified.";
/* menu */
+3 -3
View File
@@ -595,12 +595,12 @@
* @apiSuccess (Success 200) {Boolean} alarm.attendees Alert attendees by email if true and action is email
* @apiSuccess (Success 200) {Boolean} alarm.organizer Alert organizer by email if true and action is email
*
* @apiSuccess {_} ... _From [iCalEvent+SOGo attributes]_
* @apiSuccess {_} ... _From [iCalEvent+SOGo attributesInContext:]_
*
* @apiSuccess (Success 200) {Number} isAllDay 1 if event is all-day
* @apiSuccess (Success 200) {Number} isTransparent 1 if the event is not opaque
*
* @apiSuccess {_} .... _From [iCalEntityObject+SOGo attributes]_
* @apiSuccess {_} .... _From [iCalEntityObject+SOGo attributesInContext:]_
*
* @apiSuccess (Success 200) {Number} sendAppointmentNotifications 1 if notifications must be sent
* @apiSuccess (Success 200) {String} component "vevent"
@@ -626,7 +626,7 @@
* @apiSuccess (Success 200) {String} [attendees.delegatedTo] User that the original request was delegated to
* @apiSuccess (Success 200) {String} [attendees.delegatedFrom] User the request was delegated from
*
* @apiSuccess {_} ..... _From [iCalRepeatableEntityObject+SOGo attributes]_
* @apiSuccess {_} ..... _From [iCalRepeatableEntityObject+SOGo attributesInContext:]_
*
* @apiSuccess (Success 200) {Object} [repeat] Recurrence rule definition
* @apiSuccess (Success 200) {String} repeat.frequency Either daily, (every weekday), weekly, (bi-weekly), monthly, or yearly
+1 -1
View File
@@ -39,7 +39,7 @@
NSArray *weekDays;
NSString *currentView, *timeFormat, *currentTableHour;
NSCalendarDate *startDate, *currentTableDay;
NSMutableArray *daysToDisplay, *calendarsToDisplay, *currentCalendar, *hoursToDisplay;
NSMutableArray *daysToDisplay, *calendarsToDisplay, *currentCalendar, *hoursToDisplay;
unsigned int numberOfDays;
}
+59 -18
View File
@@ -37,6 +37,9 @@
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/WOResourceManager+SOGo.h>
#import <SoObjects/Appointments/SOGoAppointmentFolder.h>
#import <SoObjects/Appointments/SOGoAppointmentFolders.h>
#import "UIxCalDayTable.h"
@class SOGoAppointment;
@@ -184,7 +187,6 @@
{
if (!calendarsToDisplay)
{
int max=0, i;
NSArray *folders;
SOGoAppointmentFolders *co;
SOGoAppointmentFolder *folder;
@@ -292,6 +294,39 @@
return [weekDays objectAtIndex: [currentTableDay dayOfWeek]];
}
- (NSString *) labelForMonth
{
NSCalendarDate *nextDay;
NSString *calendarFormat;
BOOL isLastOrFirstDay;
isLastOrFirstDay = NO;
calendarFormat = @"%b";
if ([currentView hasSuffix: @"dayview"])
{
isLastOrFirstDay = YES;
calendarFormat = @"%B";
}
else
{
if ([currentTableDay dayOfMonth] == 1)
{
isLastOrFirstDay = YES;
}
else if ([currentTableDay dayOfMonth] > 27)
{
nextDay = [currentTableDay dateByAddingYears: 0
months: 0
days: 1];
if ([nextDay dayOfMonth] == 1)
isLastOrFirstDay = YES;
}
}
return isLastOrFirstDay? [currentTableDay descriptionWithCalendarFormat: calendarFormat locale: locale] : nil;
}
- (NSString *) labelForDate
{
return [dateFormatter shortFormattedDate: currentTableDay];
@@ -397,18 +432,23 @@
numberOfDays];
}
- (NSString *) daysViewClasses
{
NSString *daysView;
// - (NSString *) daysViewClasses
// {
// NSString *daysView;
if ([currentView isEqualToString:@"multicolumndayview"])
daysView = @"daysView daysViewForMultipleDays";
// if ([currentView isEqualToString:@"multicolumndayview"])
// daysView = @"daysView daysViewForMultipleDays";
else
daysView = [NSString stringWithFormat: @"daysView daysViewFor%dDays", numberOfDays];
// else
// daysView = [NSString stringWithFormat: @"daysView daysViewFor%dDays", numberOfDays];
return daysView;
}
// return daysView;
//}
// - (NSString *) daysViewHeaderClasses
// {
// return [NSString stringWithFormat: @"%@ daysHeader", [self daysViewClasses]];
// }
- (NSString *) dayClasses
{
@@ -416,22 +456,18 @@
unsigned int currentDayNbr, realDayOfWeek;
classes = [NSMutableString string];
if ([currentView isEqualToString:@"multicolumndayview"])
[classes appendFormat:@"day dayColumn"];
else {
currentDayNbr = [daysToDisplay indexOfObject: currentTableDay];
realDayOfWeek = [currentTableDay dayOfWeek];
currentDayNbr = [daysToDisplay indexOfObject: currentTableDay];
realDayOfWeek = [currentTableDay dayOfWeek];
[classes appendFormat: @"day day%d", currentDayNbr];
[classes appendFormat: @"day day%d", currentDayNbr];
if (numberOfDays > 1)
if (numberOfDays > 1)
{
if (realDayOfWeek == 0 || realDayOfWeek == 6)
[classes appendString: @" weekEndDay"];
if ([currentTableDay isToday])
[classes appendString: @" dayOfToday"];
}
}
return classes;
}
@@ -468,4 +504,9 @@
return NO;
}
- (BOOL) isWeekView
{
return [currentView isEqualToString:@"weekview"];
}
@end
+81 -35
View File
@@ -94,7 +94,7 @@ static NSArray *tasksFields = nil;
@"c_classification", @"c_category",
@"c_partmails", @"c_partstates", @"c_owner",
@"c_iscycle", @"c_nextalarm",
@"c_recurrence_id", @"isException", @"editable",
@"c_recurrence_id", @"isException", @"viewable", @"editable",
@"erasable", @"ownerIsOrganizer", @"c_description", nil];
[eventsFields retain];
}
@@ -104,7 +104,7 @@ static NSArray *tasksFields = nil;
@"calendarName",
@"c_status", @"c_title", @"c_enddate",
@"c_classification", @"c_location", @"c_category",
@"editable", @"erasable",
@"viewable", @"editable", @"erasable",
@"c_priority", @"c_owner",
@"c_iscycle", @"c_nextalarm",
@"c_recurrence_id", @"isException", @"c_description", nil];
@@ -258,7 +258,7 @@ static NSArray *tasksFields = nil;
labelKey = [NSString stringWithFormat: @"%@_class%@",
type, [component objectForKey: @"c_classification"]];
[component setObject: [self labelForKey: labelKey]
[component setObject: [self labelForKey: labelKey inContext: context]
forKey: @"c_title"];
}
@@ -328,16 +328,24 @@ static NSArray *tasksFields = nil;
iCalPersonPartStat stat;
NSUInteger count, max;
if ([[theRecord objectForKey: @"c_partmails"] length] > 0 &&
[[theRecord objectForKey: @"c_partstates"] length] > 0)
states = nil;
if ([[theRecord objectForKey: @"c_partmails"] length] > 0)
{
mails = [[theRecord objectForKey: @"c_partmails"] componentsSeparatedByString: @"\n"];
states = [[theRecord objectForKey: @"c_partstates"] componentsSeparatedByString: @"\n"];
// We add some robustness here, in case the quick table had c_partmails defined but
// no corresponding c_partstates entries. This can happen if folks toy around the database
// or if Funambol was used in the past, with a broken connector.
if ([[theRecord objectForKey: @"c_partstates"] length] > 0)
states = [[theRecord objectForKey: @"c_partstates"] componentsSeparatedByString: @"\n"];
max = [mails count];
statesDescription = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
stat = [[states objectAtIndex: count] intValue];
stat = (states ? [[states objectAtIndex: count] intValue] : 0);
[statesDescription addObject: [[iCalPerson descriptionForParticipationStatus: stat] lowercaseString]];
}
@@ -441,21 +449,35 @@ static NSArray *tasksFields = nil;
}
else
{
id foo;
foo = [currentFolder fetchCoreInfosFrom: startDate
currentInfos = [[currentFolder fetchCoreInfosFrom: startDate
to: endDate
title: value
component: component];
currentInfos = [foo objectEnumerator];
component: component]
objectEnumerator];
}
owner = [currentFolder ownerInContext: context];
ownerUser = [SOGoUser userWithLogin: owner];
isErasable = ([owner isEqualToString: userLogin] || [[currentFolder aclsForUser: userLogin] containsObject: SOGoRole_ObjectEraser]);
while ((newInfo = [currentInfos nextObject]))
{
if ([fields containsObject: @"viewable"])
{
if ([owner isEqualToString: userLogin])
[newInfo setObject: [NSNumber numberWithInt: 1] forKey: @"viewable"];
else
{
role = [currentFolder roleForComponentsWithAccessClass: [[newInfo objectForKey: @"c_classification"] intValue]
forUser : userLogin];
if ([role isEqualToString: @"ComponentViewer"])
[newInfo setObject: [NSNumber numberWithInt: 1] forKey: @"viewable"];
else
[newInfo setObject: [NSNumber numberWithInt: 0] forKey: @"viewable"];
}
}
if ([fields containsObject: @"editable"])
{
if (folderIsRemote)
@@ -548,19 +570,19 @@ static NSArray *tasksFields = nil;
{
// Same day
if (forAllDay)
return [self labelForKey: @"Today"];
return [self labelForKey: @"Today" inContext: context];
else
return [dateFormatter formattedTime: date];
}
else if ([now dayOfCommonEra] - [date dayOfCommonEra] == 1)
{
// Yesterday
return [self labelForKey: @"Yesterday"];
return [self labelForKey: @"Yesterday" inContext: context];
}
else if ([date dayOfCommonEra] - [now dayOfCommonEra] == 1)
{
// Tomorrow
return [self labelForKey: @"Tomorrow"];
return [self labelForKey: @"Tomorrow" inContext: context];
}
else if (abs(delta = [date dayOfCommonEra] - [now dayOfCommonEra]) < 7)
{
@@ -573,7 +595,7 @@ static NSArray *tasksFields = nil;
return dayOfWeek;
else
// With the past 7 days
return [NSString stringWithFormat: [self labelForKey: @"last %@"], dayOfWeek];
return [NSString stringWithFormat: [self labelForKey: @"last %@" inContext: context], dayOfWeek];
}
else
{
@@ -652,10 +674,11 @@ static NSArray *tasksFields = nil;
return [self _responseWithData: data];
}
- (void) saveFilterValue
- (void) saveFilterValue: (NSString *) submodule
{
NSString *filter;
SOGoUserSettings *us;
NSMutableDictionary *calendarSettings;
filter = [[context request] formValueForKey: @"filterpopup"];
if ([filter length]
@@ -663,12 +686,20 @@ static NSArray *tasksFields = nil;
&& ![filter isEqualToString: @"view_future"])
{
us = [[context activeUser] userSettings];
[us setObject: filter forKey: @"CalendarDefaultFilter"];
calendarSettings = [us objectForKey: @"Calendar"];
// Must create if it doesn't exist
if (!calendarSettings)
{
calendarSettings = [NSMutableDictionary dictionary];
[us setObject: calendarSettings forKey: @"Calendar"];
}
//[us setObject: filter forKey: submodule];
[calendarSettings setObject: filter forKey: submodule];
[us synchronize];
}
}
- (void) saveSortValue
- (void) saveSortValue: (NSString *) submodule
{
NSString *sort, *ascending;
SOGoUserSettings *us;
@@ -686,7 +717,8 @@ static NSArray *tasksFields = nil;
calendarSettings = [NSMutableDictionary dictionary];
[us setObject: calendarSettings forKey: @"Calendar"];
}
[calendarSettings setObject: [NSArray arrayWithObjects: [sort lowercaseString], [NSString stringWithFormat: @"%d", (ascending?1:0)], nil] forKey: @"SortingState"];
[calendarSettings setObject: [NSArray arrayWithObjects: [sort lowercaseString], [NSString stringWithFormat: @"%d", [ascending intValue]], nil]
forKey: submodule];
[us synchronize];
}
}
@@ -722,10 +754,11 @@ static NSArray *tasksFields = nil;
* @apiSuccess (Success 200) {String[]} events.c_partmails Participants email addresses
* @apiSuccess (Success 200) {String[]} events.c_partstates Participants states
* @apiSuccess (Success 200) {String} events.c_owner Event's owner
* @apiSuccess (Success 200) {Number} events.c_iscycle 1 if the event is cyclic
* @apiSuccess (Success 200) {Number} events.c_iscycle 1 if the event is cyclic/recurring
* @apiSuccess (Success 200) {Number} events.c_nextalarm Epoch time of next alarm
* @apiSuccess (Success 200) {String} [events.c_recurrence_id] Recurrence ID if event is cyclic
* @apiSuccess (Success 200) {Number} events.isException 1 if recurrence is an exception
* @apiSuccess (Success 200) {Number} events.viewable 1 if active user can view the event
* @apiSuccess (Success 200) {Number} events.editable 1 if active user can edit the event
* @apiSuccess (Success 200) {Number} events.erasable 1 if active user can erase the event
* @apiSuccess (Success 200) {Number} events.ownerIsOrganizer 1 if owner is also the organizer
@@ -743,8 +776,8 @@ static NSArray *tasksFields = nil;
NSString *sort, *ascending;
[self _setupContext];
[self saveFilterValue];
[self saveSortValue];
[self saveFilterValue: @"EventsFilterState"];
[self saveSortValue: @"EventsSortingState"];
newEvents = [NSMutableArray array];
events = [[self _fetchFields: eventsFields
@@ -867,10 +900,10 @@ static inline iCalPersonPartStat _userStateInEvent (NSArray *event)
states = [event objectAtIndex: eventPartStatesIndex];
count = 0;
max = [participants count];
user = [SOGoUser userWithLogin: [event objectAtIndex: eventOwnerIndex]
roles: nil];
while (state == iCalPersonPartStatOther && count < max)
{
user = [SOGoUser userWithLogin: [event objectAtIndex: eventOwnerIndex]
roles: nil];
if ([user hasEmail: [participants objectAtIndex: count]])
state = [[states objectAtIndex: count] intValue];
else
@@ -1192,7 +1225,7 @@ _computeBlocksPosition (NSArray *blocks)
SOGoAppointmentFolder *folder;
NSMutableArray *selectedCalendars;
NSArray *folders;
NSString *fUID;
NSString *fUID, *fName;
NSNumber *isActive;
unsigned int count, foldersCount;
@@ -1206,7 +1239,11 @@ _computeBlocksPosition (NSArray *blocks)
isActive = [NSNumber numberWithBool: [folder isActive]];
if ([isActive intValue] != 0) {
fUID = [folder nameInContainer];
[selectedCalendars addObject: fUID];
fName = [folder displayName];
[selectedCalendars addObject: [NSDictionary dictionaryWithObjectsAndKeys:
fUID, @"id",
fName, @"name",
nil]];
}
}
return selectedCalendars;
@@ -1244,6 +1281,7 @@ _computeBlocksPosition (NSArray *blocks)
* @apiSuccess (Success 200) {Number} events.c_nextalarm Epoch time of next alarm
* @apiSuccess (Success 200) {String} [events.c_recurrence_id] Recurrence ID if event is cyclic
* @apiSuccess (Success 200) {Number} events.isException 1 if recurrence is an exception
* @apiSuccess (Success 200) {Number} events.viewable 1 if active user can view the event
* @apiSuccess (Success 200) {Number} events.editable 1 if active user can edit the event
* @apiSuccess (Success 200) {Number} events.erasable 1 if active user can erase the event
* @apiSuccess (Success 200) {Number} events.ownerIsOrganizer 1 if owner is also the organizer
@@ -1263,9 +1301,10 @@ _computeBlocksPosition (NSArray *blocks)
{
int count, max;
NSArray *events, *event, *calendars;
NSDictionary *eventsBlocks;
NSMutableArray *allDayBlocks, *blocks, *currentDay, *eventsByCalendars, *eventsForCalendar;
NSDictionary *eventsBlocks, *calendar;
NSMutableArray *allDayBlocks, *blocks, *currentDay, *eventsForCalendar, *eventsByCalendars;
NSNumber *eventNbr;
NSString *calendarName, *calendarId;
BOOL isAllDay;
int i, j;
@@ -1279,16 +1318,21 @@ _computeBlocksPosition (NSArray *blocks)
eventsByCalendars = [NSMutableArray arrayWithCapacity:[calendars count]];
for (i = 0; i < [calendars count]; i++) // For each calendar
{
calendar = [calendars objectAtIndex:i];
calendarName =[calendar objectForKey: @"name"];
calendarId = [calendar objectForKey: @"id"];
eventsForCalendar = [NSMutableArray array];
[self _prepareEventBlocks: &blocks withAllDays: &allDayBlocks];
for (j = 0; j < [events count]; j++) {
if ([[[events objectAtIndex:j] objectAtIndex:eventFolderIndex] isEqualToString:[calendars objectAtIndex:i]]) {
if ([[[events objectAtIndex:j] objectAtIndex:eventFolderIndex] isEqualToString: calendarId]) {
// Event is in current calendar
[eventsForCalendar addObject: [events objectAtIndex:j]];
}
}
eventsBlocks = [NSDictionary dictionaryWithObjectsAndKeys:
eventsFields, @"eventsFields",
calendarId , @"id",
calendarName, @"calendarName",
eventsFields, @"eventsFields",
eventsForCalendar, @"events",
allDayBlocks, @"allDayBlocks",
blocks, @"blocks", nil];
@@ -1310,8 +1354,7 @@ _computeBlocksPosition (NSArray *blocks)
[currentDay sortUsingSelector: @selector (compareEventByStart:)];
[self _addBlocksWidth: currentDay];
}
[eventsByCalendars insertObject:eventsBlocks atIndex:i];
[eventsByCalendars addObject: eventsBlocks];
}
return [self _responseWithData: eventsByCalendars];
}
@@ -1347,7 +1390,7 @@ _computeBlocksPosition (NSArray *blocks)
[currentDay sortUsingSelector: @selector (compareEventByStart:)];
[self _addBlocksWidth: currentDay];
}
return [self _responseWithData: eventsBlocks];
return [self _responseWithData: [NSArray arrayWithObject: eventsBlocks]];
}
}
@@ -1408,6 +1451,7 @@ _computeBlocksPosition (NSArray *blocks)
* @apiSuccess (Success 200) {Number} tasks.c_classification 0: Public, 1: Private, 2: Confidential
* @apiSuccess (Success 200) {String} tasks.c_location Location
* @apiSuccess (Success 200) {String} tasks.c_category Category
* @apiSuccess (Success 200) {Number} tasks.viewable 1 if task is viewable by the active user
* @apiSuccess (Success 200) {Number} tasks.editable 1 if task is editable by the active user
* @apiSuccess (Success 200) {Number} tasks.erasable 1 if task is erasable by the active user
* @apiSuccess (Success 200) {String} tasks.c_priority Priority (0-9)
@@ -1436,6 +1480,8 @@ _computeBlocksPosition (NSArray *blocks)
filteredTasks = [NSMutableArray array];
[self _setupContext];
[self saveFilterValue: @"TasksFilterState"];
[self saveSortValue: @"TasksSortingState"];
startSecs = (unsigned int) [startDate timeIntervalSince1970];
endsSecs = (unsigned int) [endDate timeIntervalSince1970];
+9 -1
View File
@@ -33,6 +33,7 @@
#import <NGCards/iCalPerson.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
@@ -45,6 +46,11 @@
@implementation UIxCalMainView
- (NSString *) modulePath
{
return @"Calendar";
}
- (void) checkDefaultModulePreference
{
SOGoUserDefaults *ud;
@@ -261,12 +267,14 @@
- (WOResponse *) saveSelectedListAction
{
WORequest *request;
NSDictionary *params;
NSString *selectedList;
[self _setupContext];
request = [context request];
params = [[request contentAsString] objectFromJSONString];
selectedList = [request formValueForKey: @"list"];
selectedList = [params objectForKey: @"list"];
[moduleSettings setObject: selectedList
forKey: @"SelectedList"];
[us synchronize];
+1 -4
View File
@@ -1,8 +1,6 @@
/* UIxCalMonthView.h - this file is part of SOGo
*
* Copyright (C) 2006-2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2015 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
@@ -69,7 +67,6 @@
- (void) setCurrentTableDay: (NSCalendarDate *) newCurrentTableDay;
- (NSCalendarDate *) currentTableDay;
- (NSString *) labelForCurrentDayCell;
@end
+5 -33
View File
@@ -1,8 +1,6 @@
/* UIxCalMonthView.m - this file is part of SOGo
*
* Copyright (C) 2006, 2007 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2015 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
@@ -235,27 +233,6 @@
return currentWeek;
}
- (NSString *) labelForCurrentDayCell
{
NSCalendarDate *lastDayOfMonth;
NSString *label, *monthOfYear;
int dayOfMonth;
dayOfMonth = [currentTableDay dayOfMonth];
lastDayOfMonth = [currentTableDay lastDayOfMonth];
if (dayOfMonth == 1
|| [currentTableDay isDateOnSameDay: lastDayOfMonth])
{
monthOfYear
= [monthNames objectAtIndex: [currentTableDay monthOfYear]];
label = [NSString stringWithFormat: @"%d %@", dayOfMonth, monthOfYear];
}
else
label = [NSString stringWithFormat: @"%d", dayOfMonth];
return label;
}
- (NSString *) headerDayCellClasses
{
unsigned int dayOfWeek;
@@ -265,24 +242,19 @@
return [NSString stringWithFormat: @"headerDay day%d", dayOfWeek];
}
- (NSString *) dayHeaderNumber
- (NSString *) dayHeaderMonth
{
NSString *nameOfMonth, *dayHeaderNumber;
NSString *nameOfMonth = nil;
unsigned int dayOfMonth;
dayOfMonth = [currentTableDay dayOfMonth];
if (dayOfMonth == 1
|| [currentTableDay isDateOnSameDay: [currentTableDay lastDayOfMonth]])
{
nameOfMonth
= [self localizedNameForMonthOfYear: [currentTableDay monthOfYear]];
dayHeaderNumber = [NSString stringWithFormat: @"%d %@", dayOfMonth,
nameOfMonth];
nameOfMonth = [self localizedNameForMonthOfYear: [currentTableDay monthOfYear]];
}
else
dayHeaderNumber = [NSString stringWithFormat: @"%d", dayOfMonth];
return dayHeaderNumber;
return nameOfMonth;
}
- (NSString *) dayCellClasses
@@ -20,14 +20,6 @@
#import <SOGoUI/UIxComponent.h>
@class NSString;
@class SOGoAppointmentFolder;
@interface UIxCalendarFolderLinksTemplate : UIxComponent
{
SOGoAppointmentFolder *calendar;
NSString *baseCalDAVURL, *basePublicCalDAVURL;
}
@end
@@ -28,89 +28,6 @@
@implementation UIxCalendarFolderLinksTemplate
- (id) init
{
if ((self = [super init]))
{
calendar = [self clientObject];
baseCalDAVURL = nil;
basePublicCalDAVURL = nil;
}
return self;
}
- (void) dealloc
{
[baseCalDAVURL release];
[basePublicCalDAVURL release];
[super dealloc];
}
- (NSString *) _baseCalDAVURL
{
NSString *davURL;
if (!baseCalDAVURL)
{
davURL = [[calendar realDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
baseCalDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
baseCalDAVURL = davURL;
[baseCalDAVURL retain];
}
return baseCalDAVURL;
}
- (NSString *) calDavURL
{
return [NSString stringWithFormat: @"%@/", [self _baseCalDAVURL]];
}
- (NSString *) webDavICSURL
{
return [calendar webDavICSURL];
}
- (NSString *) webDavXMLURL
{
return [calendar webDavXMLURL];
}
- (NSString *) _basePublicCalDAVURL
{
NSString *davURL;
if (!basePublicCalDAVURL)
{
davURL = [[calendar publicDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
basePublicCalDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
basePublicCalDAVURL = davURL;
[basePublicCalDAVURL retain];
}
return basePublicCalDAVURL;
}
- (NSString *) publicCalDavURL
{
return [NSString stringWithFormat: @"%@/", [self _basePublicCalDAVURL]];
}
- (NSString *) publicWebDavICSURL
{
return [calendar publicWebDavICSURL];
}
- (NSString *) publicWebDavXMLURL
{
return [calendar publicWebDavXMLURL];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
@@ -118,14 +35,4 @@
enablePublicAccess];
}
- (BOOL) isWebCalendar
{
return ([calendar isKindOfClass: [SOGoWebAppointmentFolder class]]);
}
- (NSString *) webCalendarURL
{
return [calendar folderPropertyValueInCategory: @"WebCalendars"];
}
@end
Loaded 100 of 303 files, more files were not shown because too many files have changed in this diff. Show more