Compare commits

..
Author SHA1 Message Date
Luc Charland 1dd4d870e0 Changed the keyname to EventsSortingState 2015-08-06 12:38:26 -04:00
Luc Charland 31b959a7dc Added saving of Calendar Sort to jsonSettings 2015-08-06 12:17:48 -04:00
Francis Lachapelle 2a132b4e8f (css) Cleanup directives already in material 2015-08-06 11:48:42 -04:00
Francis Lachapelle ff1a1e6f59 (html) Cleanup UIxContactFoldersView.wox 2015-08-06 11:26:26 -04:00
Francis Lachapelle a4a75607fa (js,html) Improve sgSubscribe directive 2015-08-06 11:23:16 -04:00
Francis Lachapelle 6641bbded2 (js) Improve User.$filter function 2015-08-06 11:23:16 -04:00
Ludovic Marcotte 78975b9e68 (fix) add proper class to avoid displaying the chips' line 2015-08-06 09:36:43 -04:00
Ludovic Marcotte 2f122f12b6 (fix) don't show a dialog on top of a dialog for acl confirmation 2015-08-05 20:23:11 -04:00
Francis Lachapelle 5e19a889c2 (feat) Handle invitations in appointment viewer 2015-08-05 17:31:22 -04:00
Ludovic Marcotte 87aec2fc01 (feat) added batch export option to cards 2015-08-05 16:02:57 -04:00
Ludovic Marcotte 5327df4c7a (feat) added select all feature to contacts module 2015-08-05 15:04:47 -04:00
Ludovic Marcotte a220ee67b7 (feat) reworked batch menu + added save messages option 2015-08-05 14:39:58 -04:00
Ludovic Marcotte 124ca5d8cf (fix) escape the HTML content from the raw source before returning it 2015-08-05 11:08:36 -04:00
Francis Lachapelle 9dac8e55ff (js) Avoid accidentally closing the composer 2015-08-05 10:14:32 -04:00
Francis Lachapelle 4539fd4c5e (html) Internationalized labels in message viewer 2015-08-05 10:12:51 -04:00
Luc Charland 923ab97309 Added tooltips for saving/view attachments 2015-08-05 10:05:21 -04:00
Luc Charland c56644f0e5 Added tooltips to Contacts 2015-08-05 09:40:12 -04:00
Ludovic Marcotte da119ef3ad (feat) raw source support in contact module 2015-08-04 20:32:43 -04:00
Ludovic Marcotte 6e114b84be (feat) reorganized menu + added view raw source feature 2015-08-04 16:51:33 -04:00
Ludovic Marcotte dff7b59036 (fix) wrong tagging for dialog content 2015-08-04 16:39:02 -04:00
Ludovic Marcotte 7f43f894b0 (fix) update the unseenCount when reading messages 2015-08-04 16:08:17 -04:00
Ludovic Marcotte a27f106a4a (fix) pimped ical viewer 2015-08-04 13:52:31 -04:00
Francis Lachapelle 95a360dd36 (fix) Variable test in Component service 2015-08-04 13:46:55 -04:00
Luc Charland 09224f587f Added a few tooltips for the mail view 2015-08-04 12:54:58 -04:00
Francis Lachapelle ff1b9e7ed1 (fix) Draft initialization 2015-08-04 10:56:55 -04:00
Francis Lachapelle 6d260c58c7 (js) Update Gruntfile.js for changes in vendor js 2015-08-04 10:37:07 -04:00
Francis Lachapelle ba69bd4c82 (css) Improve color picker inside md-avatar 2015-08-04 08:31:40 -04:00
Francis Lachapelle 2a45a2133b (fix) Padding of mail account delegation dialog 2015-08-04 08:31:40 -04:00
Francis Lachapelle 57fbc453a6 (html) Add avatars to ACL editor 2015-08-04 08:31:39 -04:00
Francis Lachapelle f17b3638d7 (js) Fix usage of Settings.activeUser 2015-08-04 08:31:39 -04:00
extrafu 67517e0b28 Update README.md 2015-08-03 20:40:59 -04:00
extrafu 0038ce03fa Merge pull request #12 from vDevices/master
Created README & 'tutorials' subdirectory
2015-08-03 20:39:03 -04:00
Francis Lachapelle d2b2cb186e (js) Fix usage of Settings.activeUser 2015-08-03 16:18:23 -04:00
Francis Lachapelle ca8b64bd88 (js) Fix error when deleting an ACL user 2015-08-03 16:17:02 -04:00
Francis Lachapelle 732b8c9d8a (css) Fullscreen dialogs on small screens 2015-08-03 16:15:59 -04:00
Francis Lachapelle ba6418fb93 (html) Review the toolbar of dialog boxes 2015-08-03 14:44:11 -04:00
Francis Lachapelle ab1cd36c77 Review delegation dialog in Mail app module 2015-08-03 14:40:18 -04:00
Ludovic Marcotte 9f2c5b5f50 (fix) added bg icon 2015-08-03 12:48:29 -04:00
Ludovic Marcotte b6ea1de4c0 (fix) improved message editor 2015-08-03 12:43:21 -04:00
Francis Lachapelle 21f214659e (fix) Some compilation warnings 2015-08-03 12:16:16 -04:00
Francis Lachapelle 82720d341c (fix) Some compilation warnings 2015-08-03 12:03:56 -04:00
Francis Lachapelle a330ff7320 (js) Move Settings to a value in Common ng module
Also removed explicit settings and defaults that were imported in
various app modules. We now always use the Preferences ng service.
2015-08-03 11:57:04 -04:00
extrafu 375eac2be0 Merge pull request #94 from inverse-inc/feature/trailing_spaces
Feature/trailing spaces
2015-08-03 11:00:58 -04:00
Luc Charland 4cf82bd41c removed trailing space to strings 2015-08-03 10:56:24 -04:00
Luc Charland 9f202cfcdc cleaned up a few comments 2015-08-03 10:24:15 -04:00
extrafu 393321b223 Merge pull request #92 from inverse-inc/feature/remove_colon
Feature/remove colon
2015-08-03 09:54:48 -04:00
Luc Charland 6835cd5319 removed double Name translation in 5 languages 2015-08-03 09:51:31 -04:00
Ludovic Marcotte 31bc88d9d3 Updated NEWS file regarding PR93
Conflicts:

	NEWS
2015-08-03 09:19:35 -04:00
Ludovic Marcotte f515106bcc (fix) added patch from PR93 2015-08-03 09:16:55 -04:00
Ludovic Marcotte ec1420c19b (feat) now use mdDialog for message composition 2015-08-02 13:25:38 -04:00
Ludovic Marcotte f44af10cdd (feat) integrated the color picker in the pref module 2015-07-31 15:40:00 -04:00
Ludovic Marcotte e25305980f (fix) improved the account delegation dialog 2015-07-31 14:54:55 -04:00
Ludovic Marcotte 0c7bdac091 (fix) refactored code and we now return the unseenCount when fetching uids 2015-07-31 14:31:38 -04:00
Luc Charland 2f8f8c78f7 Removing colons 2015-07-31 14:14:30 -04:00
Luc Charland 90b7ce7ec3 fixed Appointments path 2015-07-31 13:57:41 -04:00
Luc Charland 6ecea9d702 cleaned up a few comments 2015-07-31 13:57:41 -04:00
Luc Charland a54b8780a8 added first draft of verify_translation 2015-07-31 13:57:41 -04:00
Ludovic Marcotte 40d269c504 (feat) unseen count support folder all accounts/mailboxes 2015-07-31 13:32:14 -04:00
Francis Lachapelle 1d52be415f (html) Improve Calendar properties dialog 2015-07-31 11:18:29 -04:00
Francis Lachapelle 3b9a989abd (js) New color picker directive 2015-07-31 11:16:34 -04:00
Ludovic Marcotte a728616bca (fix) comment typo 2015-07-31 09:05:56 -04:00
Ludovic Marcotte 65b8382020 (fix) move refresh timeout to service classes and enabled repeating timers 2015-07-30 19:47:53 -04:00
Ludovic Marcotte 85f4efab46 (fix) refactored to code 2015-07-30 14:51:14 -04:00
Ludovic Marcotte af7fb1c6ee (fix) modified the template so it shares the same l&f as the contacts one 2015-07-30 13:25:50 -04:00
Ludovic Marcotte a52d42f6b2 (feat) manual+auto refresh support for addressbooks/mailboxes 2015-07-30 13:06:28 -04:00
Francis Lachapelle 1f8edbdd1f (js) Respect SOGoCalendarDefaultReminder in editor 2015-07-30 12:05:33 -04:00
Francis Lachapelle b61eb00b99 (js) Reload lists when (en|dis)abling a calendar
Only the displayed list was refresh. Now we invalidate the hidden list.
2015-07-30 11:55:02 -04:00
Francis Lachapelle 449093c3f0 (fix) Calendar destination of new task
Also removed the possibility to use the "moveToCalendar" parameter. We
should eventually create UIxTaskActions.m as we have
UIxAppointmentActions.m for this kind of actions.
2015-07-30 11:51:02 -04:00
Ludovic Marcotte 0fc7c96924 (fix) use proper values for short/long date formats 2015-07-29 19:10:03 -04:00
Francis Lachapelle b73450e992 (fix) Label color of pseudo-input
It now matches the one of ng-material.
2015-07-29 16:45:16 -04:00
Francis Lachapelle 8e00387152 (fix) CSS padding style of toolbar 2015-07-29 16:44:18 -04:00
Francis Lachapelle b6a5eb0b60 (fix) Disable frequency when editing an occurrence 2015-07-29 16:43:10 -04:00
Francis Lachapelle d7b7824aad Improve display of attendees in event viewer 2015-07-29 16:42:20 -04:00
Francis Lachapelle 3e6c50b7dc (cleanup) Remove ionic templates 2015-07-29 15:54:13 -04:00
Ludovic Marcotte 9291dc29b6 Removed old file 2015-07-29 15:51:52 -04:00
Francis Lachapelle f5927944fa (fix) Missing header file import 2015-07-29 15:48:29 -04:00
Ludovic Marcotte 16885f4ebf (fix) properly handle subparts compilation for message/rfc822 message parts 2015-07-29 15:37:55 -04:00
Francis Lachapelle d3a233b222 (js) Improve Angular modules initialization 2015-07-29 15:16:43 -04:00
Francis Lachapelle 7e10cff395 (js) Restore user's last Calendar view
Also reviewed the handling of Angular modules dependencies.
2015-07-29 11:47:01 -04:00
Francis Lachapelle 762f305914 (cleanup) No longer used JavasScript 2015-07-29 10:11:28 -04:00
Francis Lachapelle e208ee3fde (feat) Open events from day and week views 2015-07-28 20:45:46 -04:00
Francis Lachapelle 571be37c1d (scss) Indentation 2015-07-28 15:53:48 -04:00
Francis Lachapelle 433983e7c4 (fix) DI annotation of sgIMAP controller 2015-07-28 14:37:49 -04:00
Francis Lachapelle d7aae0e667 (fix) _.allKeys doesn't exist in lodash 2015-07-28 14:21:19 -04:00
Francis Lachapelle e87a6de46b (html) Remove previous "hack" for ng-material
The md-menu positioned as a secondary action in a list can now be
properly hidden. ng-material is just getting better :)
2015-07-28 14:16:56 -04:00
Francis Lachapelle 13a779019b (html) Add toolbar to dialog boxes 2015-07-28 14:13:48 -04:00
Ludovic Marcotte 17432deaa7 (fix) reset alarm after popping it 2015-07-28 13:05:05 -04:00
Ludovic Marcotte 7365f77dd5 (feat) new Alarm service + alarms support in all modules 2015-07-28 11:54:49 -04:00
Francis Lachapelle e8dcc6df37 (js) New common ACL Controller 2015-07-28 10:53:29 -04:00
Francis Lachapelle a8765eb41b (fix) Autocompletion in mail editor 2015-07-27 20:43:43 -04:00
Francis Lachapelle c700608f0f (fix) Sidenav list items display of Preferences 2015-07-27 20:11:57 -04:00
Francis Lachapelle 86a0032cc1 Handle the edition of recurring event occurrences 2015-07-27 16:02:05 -04:00
Francis Lachapelle 049c1385d6 (fix) Event propagation when saving component 2015-07-27 13:50:14 -04:00
Francis Lachapelle a168d0227d (js) Use lodash and remove old dependencies
Replaced underscore by lodash
2015-07-27 13:27:12 -04:00
Ludovic Marcotte 40dd3e5875 (fix) reworked c_ prefixes on givenname,cn,sn,o and component.
This makes it easier to map to the quick table and avoid
fields duplications.
2015-07-27 12:47:40 -04:00
Francis Lachapelle dc9a0896e5 (js) Fix possible errors (help from jsjint) 2015-07-24 16:14:53 -04:00
Francis Lachapelle e064dc4a46 (js) Add support for local URL in sgAvatarImage
Directive was previously named sgGravatarImage.

The local URL will be used before falling back to Gravatar.
2015-07-24 15:48:28 -04:00
Francis Lachapelle 843a689094 (js) Enable jshint and uglify 2015-07-24 15:47:29 -04:00
Ludovic Marcotte f09c06f8c4 (feat) show folders stats 2015-07-24 15:35:29 -04:00
Ludovic Marcotte c365d646a2 (feat) ability to set folders as drafts/trash/sent 2015-07-24 14:40:07 -04:00
Ludovic Marcotte cbbb3dd9d4 (feat) show error message (like submission max) when sending mails 2015-07-24 11:21:04 -04:00
Ludovic Marcotte c5a35a64a2 (fix) SOGoMaximumMessageSubmissionCount should be independant from SOGoMaximumRecipientCount 2015-07-24 11:20:51 -04:00
Ludovic Marcotte a03715f74a (fix) show proper icon for shared mailboxes 2015-07-24 10:21:33 -04:00
Francis Lachapelle 51b8ce7400 (doc) Improve documentation of angular filters 2015-07-23 15:33:34 -04:00
Francis Lachapelle 2981b0af00 (js) New 'range' filter for angular 2015-07-23 15:33:33 -04:00
Ludovic Marcotte 194c5635b5 Updates for the release 2015-07-23 13:13:02 -04:00
Francis Lachapelle 73d62800e8 Update Spanish (Argentina) translation 2015-07-23 12:08:25 -04:00
Francis Lachapelle c5834e2f89 Add missing templates for Basque language 2015-07-23 12:08:15 -04:00
Francis Lachapelle ab28476e7e Add missing template for Slovenian language 2015-07-23 12:08:11 -04:00
Francis Lachapelle 538e6fa4b1 Add Chinese (Taiwan) translation 2015-07-23 12:08:04 -04:00
Francis Lachapelle c3aa601f3c (fix) Return value of [iCalPerson+SOGo uidInDomain:] 2015-07-23 12:07:57 -04:00
Francis Lachapelle 613f05ac02 Calendar module optimizations in multidomain setup
Conflicts:
	NEWS
	UI/Common/UIxUserRightsEditor.m
2015-07-23 12:06:54 -04:00
Francis Lachapelle 8f49778501 Fix .wod files for English and Basque 2015-07-23 12:03:45 -04:00
Francis Lachapelle f0b88811d6 Add Slovenian translation 2015-07-23 12:02:42 -04:00
Francis Lachapelle c1d744e47a (js) Reset "selectedCard" when cancelling new card 2015-07-23 12:01:14 -04:00
Ludovic Marcotte 12875539bb (fix) fallback to utf-8/latin1 in rare decoding error cases - this is ugly 2015-07-23 11:21:25 -04:00
Ludovic Marcotte a8dd698425 (feat) added "select all" capability, just an idea 2015-07-23 10:54:46 -04:00
Francis Lachapelle f8b6864553 (js) Remove Mailbox dependency in Preferences 2015-07-22 17:27:46 -04:00
Francis Lachapelle 01f213e65e (fix) Main toolbar of contacts module 2015-07-22 12:11:19 -04:00
Ludovic Marcotte 22ead81c48 (doc) ModuleConstraints for EAS 2015-07-22 11:55:26 -04:00
Francis Lachapelle a7afcbf4a0 (js) Use user's settings when sorting messages 2015-07-22 11:28:08 -04:00
Ludovic Marcotte a8ab01a5eb (fix) fixed EAS conversation ID issues on BB10 (#3152) 2015-07-22 10:27:24 -04:00
Ludovic Marcotte 731264d9dc (fix) make sure ActiveSync is always usable with SQL-based auth sources 2015-07-22 10:14:03 -04:00
Ludovic Marcotte 046c4db2e8 (fix) fixed computation of GlobalObjectId (#3235) 2015-07-22 10:00:28 -04:00
Ludovic Marcotte ac21649633 (feat) added support for DeletesAsMoves over EAS
Conflicts:

	ActiveSync/SOGoActiveSyncDispatcher+Sync.m
2015-07-22 09:56:36 -04:00
Ludovic Marcotte efd182f145 (fix) improved EAS email flagging handling (#3140) 2015-07-22 09:46:44 -04:00
Ludovic Marcotte b6c6e09a5a (fix) compilation warning 2015-07-22 09:26:00 -04:00
Ludovic Marcotte 83a8f388ce Android EAS Lollipop fixes (#3268 and #3269)
Conflicts:

	NEWS
2015-07-22 09:16:49 -04:00
Ludovic Marcotte 8650492989 Applied PR90 as a patch due to too-many merge conflicts when cherry-picking 2015-07-22 09:05:17 -04:00
Francis Lachapelle cc03430ac6 (fix) Don't hide "new" button on small screens 2015-07-21 15:54:26 -04:00
Francis Lachapelle 9c464c15ad (fix) Show/hide completed tasks in list 2015-07-21 15:54:26 -04:00
Ludovic Marcotte 82ed44ee31 (fix) added more proper menu activation for the mailer 2015-07-21 15:48:31 -04:00
Ludovic Marcotte d7c008f2d9 (feat) timezones are now using md-autocomplete 2015-07-21 15:21:57 -04:00
Ludovic Marcotte ea68715f59 (feat) support for drafts autosaving 2015-07-21 11:19:47 -04:00
Francis Lachapelle 601ed94d77 Update product.plist for new component templates 2015-07-21 10:15:02 -04:00
Ludovic Marcotte bee36814e5 (feat) added many missing actions on mail folders 2015-07-21 09:28:54 -04:00
Francis Lachapelle a75524af9f Review task/event viewer/editor
Components will now appear in dialogs instead of a right sidenav.

This commit also introduces read-only viewers for tasks and events.
2015-07-21 08:29:28 -04:00
Ludovic Marcotte 5eeb473fe8 (feat) initial sort/search feature for mails 2015-07-20 16:44:26 -04:00
Francis Lachapelle dfb750caf4 (fix) Resolve addressbook before rendering view 2015-07-20 15:33:51 -04:00
Ludovic Marcotte cdd449fec9 (feat) search capabilities in the addressbook module 2015-07-20 15:03:29 -04:00
Ludovic Marcotte 7c2cb259a2 (feat) sorting capabilities in the addressbook module 2015-07-20 14:24:36 -04:00
Ludovic Marcotte e7a73413d5 (fix) SOGo adjustment regarding https://github.com/angular/material/commit/dbafaa14473b41e262d55f8f6d06f818f4d88654 2015-07-19 08:39:56 -04:00
Francis Lachapelle 4c51d4702f (fix) List members auto-completion 2015-07-16 16:29:54 -04:00
Francis Lachapelle 191a1afe23 (fix) Filtering components lists 2015-07-16 16:29:00 -04:00
Francis Lachapelle 8c8070ff16 (fix) Respect order of cards list 2015-07-16 16:26:09 -04:00
Francis Lachapelle ee0e45cad6 (js) Respect user's default classification 2015-07-16 11:21:55 -04:00
Francis Lachapelle d58030a475 (css) Cleanup and fixes for latest md changes 2015-07-15 16:30:19 -04:00
Francis Lachapelle d1e1d11e4a (html) Move close button of card & msg viewer
The close button is only displayed on small devices (show-sm).
2015-07-15 15:27:51 -04:00
Francis Lachapelle f09f1a4282 (fix) Respect order in list of cards 2015-07-15 15:20:08 -04:00
Francis Lachapelle 25f364991e (fix) Creation of card/list in Contacts module 2015-07-15 15:18:23 -04:00
Francis Lachapelle 730b803ad6 (js) Replace vs-repeat by md-virtual-repeat
There're some issues with mdVirtualRepeat that required some changes to
our app:

1. Having multiple directives with md-virtual-repeat doesn't work well.
That why I added a dedicated div for the md-virtual-repeat directive.
2. ui-sref-active doesn't work well for child nodes of the
md-virtual-repeat element. The class was applied repetitively to a
visible child. I now save which message or card is selected and use the
ng-class directive to highlight the selected list item.
2015-07-15 13:44:17 -04:00
Francis Lachapelle dbb8acca9a (js) Expose 'activeUser' through navController 2015-07-14 16:12:07 -04:00
Francis Lachapelle aee229e700 (js) Make use of 'controllerAs' in Mailer module 2015-07-13 15:22:58 -04:00
Francis Lachapelle 7c6716784e (feat) Events/tasks sorting in Calendar module 2015-07-09 16:35:19 -04:00
Francis Lachapelle 9a3aeb0004 (fix) Tasks list sorting 2015-07-09 16:18:03 -04:00
Ludovic Marcotte 6f6ec58a09 (html) wrap checkbox inside md-button 2015-07-09 16:08:14 -04:00
Ludovic Marcotte 88860e2e38 (fix) don't show "end repeat" for non-repetitive events/tasks 2015-07-08 21:27:24 -04:00
Francis Lachapelle 5bbb768fe8 (html) Improve event editor 2015-07-08 21:16:33 -04:00
Francis Lachapelle 396bc3b0f6 (css) Lower icon button width to 36px 2015-07-08 21:14:25 -04:00
Francis Lachapelle 164654f784 (css) Icon modifiers (rotation) for animation 2015-07-08 21:08:51 -04:00
Francis Lachapelle 9514103031 (js) Improve search toolbar in Calendar module
- new distinct search toolbar
- restored filtering options
- honor user's settings
2015-07-08 14:49:42 -04:00
Francis Lachapelle 4d4458a183 (js) Set focus to title field in component editor 2015-07-08 14:49:42 -04:00
Francis Lachapelle 5a2eb67cb7 (fix) Alignment in bottomSheet 2015-07-08 14:49:41 -04:00
Francis Lachapelle 9bdc365bf0 (js) Allow cancelling of sgSearch 2015-07-08 14:49:41 -04:00
Francis Lachapelle 066bdc5f3f (js) New 'ready' method for Preferences service
This method returns a promise that succeeds when the user's defaults and
settings have been received from the server.
2015-07-08 14:49:40 -04:00
Francis Lachapelle 396892b0e1 (js) Simplify PreferencesController 2015-07-08 14:49:40 -04:00
Francis Lachapelle dd5b325864 (js) Simplify promises handling in User.$filter 2015-07-08 14:49:39 -04:00
Francis Lachapelle fe20cc220f (js) Improve sgSearch directive
The directive now relies on transclusion to compile its content. This
way, we don't limit the directives used inside the content.
2015-07-08 14:49:38 -04:00
Ludovic Marcotte 02112dea8a (feat) can now send out MDN responses 2015-07-08 13:19:16 -04:00
Ludovic Marcotte f369eabd06 (fix) arial labels + icon classes 2015-07-07 09:50:44 -04:00
Ludovic Marcotte 4ab2c57903 (feat) handle priority and return receipt in mail editor (needs layout work) 2015-07-07 08:35:53 -04:00
Francis Lachapelle 9015f82413 (html) Improve events/tasks lists 2015-07-06 15:16:49 -04:00
Francis Lachapelle 44b8dab9c4 (css) Left-align list items in sidenav 2015-07-06 11:14:46 -04:00
Francis Lachapelle 9814a8cd0e (css) No bg-color change in :hover of bottom sheet 2015-07-06 11:11:38 -04:00
Francis Lachapelle ecce179a2f (css) Allow .sg-child-level-[1-12] everywhere 2015-07-06 11:08:50 -04:00
Ludovic Marcotte cf0aded1a4 (feat) show the 'right' folder icons 2015-07-06 11:05:19 -04:00
Ludovic Marcotte a49687fe41 (fix) make use of floating labels 2015-07-04 20:14:32 -04:00
Ludovic Marcotte 3bfc47d105 (fix) fixed icons and controller regarding mailbox delegates 2015-07-04 07:51:20 -04:00
Ludovic Marcotte 01750fa98e (fix) small syntax error 2015-07-04 07:36:15 -04:00
Francis Lachapelle b948384ec9 (js) Preload ACL users in Calendar module 2015-07-03 21:45:59 -04:00
Francis Lachapelle 1c34e71cdc (js) Remove unused dependency on $q in controllers 2015-07-03 21:44:28 -04:00
Francis Lachapelle 2337d4057f Integrate md-icon using the Material Icons font 2015-07-03 21:42:21 -04:00
Francis Lachapelle b39dedd2d2 Add attachUrls to task editor 2015-07-03 21:42:20 -04:00
Ludovic Marcotte 05d59df68b (feat) added mailbox delegation support 2015-07-03 20:40:06 -04:00
Ludovic Marcotte 9225f2b908 (fix) layout improvements when text is folding 2015-07-03 20:07:17 -04:00
Francis Lachapelle e4e18dea5e (js) Rename variable in scope of recurrence editor 2015-07-03 16:02:47 -04:00
Francis Lachapelle b0d8148ca6 (font) Add official Material Design Icons font 2015-07-03 16:02:47 -04:00
Ludovic Marcotte 52253d8c10 (fix) fixed path handling 2015-07-03 15:17:02 -04:00
Ludovic Marcotte 94a34f41b8 (fix) some layout improvements 2015-07-03 14:46:25 -04:00
Ludovic Marcotte d1636314f1 (fix) first big pass at floating label support in all md-selects 2015-07-03 11:29:40 -04:00
Ludovic Marcotte 1ae7cddf09 Silenced NSLog calls 2015-07-03 10:22:48 -04:00
Ludovic Marcotte 5ef2db59f7 (fix) a bit more error handling 2015-07-02 16:11:11 -04:00
Ludovic Marcotte 1a78882c51 (fix) floating label for select on the login page 2015-07-02 13:20:13 -04:00
Ludovic Marcotte 6471524627 (fix) also use floating label for mail composer 2015-07-02 10:54:33 -04:00
Ludovic Marcotte bc9e1f9558 (fix) use md-select floating labels 2015-07-02 10:45:13 -04:00
Francis Lachapelle a408fdff52 (js) Minor JavaScript improvements 2015-07-01 15:21:22 -04:00
Francis Lachapelle cc7eeca2c4 (js) Improve calendar lists controller
- preload both lists (events & tasks)
- don't reload list when switching tab
2015-07-01 15:17:55 -04:00
Francis Lachapelle 35ff82710a Handle attach URLs in appointment editor 2015-07-01 15:00:12 -04:00
Francis Lachapelle ba5f6410ed Return recurrence rule only if frequency is known 2015-07-01 14:11:53 -04:00
Francis Lachapelle 763ce37f8c Allow multiple "attach" values in iCalEntityObject 2015-07-01 14:09:12 -04:00
Ludovic Marcotte f449f0456b (feat) can now copy mails 2015-06-30 17:13:55 -04:00
Francis Lachapelle 06cb06fce6 New UIxTaskEditorTemplate class 2015-06-29 21:04:41 -04:00
Ludovic Marcotte 647f052486 Fix for dfecce7381
Conflicts:

	ActiveSync/SOGoActiveSyncDispatcher+Sync.m
2015-06-29 13:55:12 -04:00
Ludovic Marcotte 013e316e7d re-enabled forwardConstraints code 2015-06-29 09:22:12 -04:00
Ludovic Marcotte 33345ed1f0 (feat) new mailbox sharing capabilities and mailbox menu 2015-06-26 13:13:25 -04:00
Ludovic Marcotte 0b5146655d (fix) do now show the subscribe options for non-GCS folders 2015-06-26 13:01:32 -04:00
Francis Lachapelle d674b33a20 (css) Review cards display in sharing dialog box 2015-06-25 11:31:53 -04:00
Francis Lachapelle c524782395 Initial task editor 2015-06-25 11:30:44 -04:00
Francis Lachapelle c144f736dc Improve JSON API documentation 2015-06-25 11:23:18 -04:00
Francis Lachapelle f4a8638fb4 (json) Add c_iscycle and c_nextalarm in tasks list 2015-06-25 11:21:24 -04:00
Francis Lachapelle 69424be245 Return attributes of iCalEntity if non-empty
Only include classification and status if theyr are not empty.
2015-06-25 11:17:53 -04:00
Francis Lachapelle c3d364558e (html) Remove line under categories in card view 2015-06-23 11:23:51 -04:00
Francis Lachapelle aa6741ee4b Move categories to top in contact editor 2015-06-23 11:22:35 -04:00
Francis Lachapelle 412dca053a (css) Fix for md-cards in ACL editor 2015-06-23 11:21:50 -04:00
Francis Lachapelle b41828a7f1 (html,js) Add reminder/alarm to event editor 2015-06-22 15:49:27 -04:00
Francis Lachapelle bb8c32a77e (html) Minor improvement to preferences module 2015-06-22 10:03:41 -04:00
Francis Lachapelle 1a5763d468 (css) Remove reference to unused font 2015-06-22 10:02:53 -04:00
Francis Lachapelle 8402152a59 Load alarm from UIxComponentEditor
The alarm attributes are now loaded from UIxComponentEditor and not
iCalEntityObject so we can determine if the component's owner is part of
the alarm's attendees.
2015-06-22 09:20:35 -04:00
Francis Lachapelle d89f02ff53 Change signature of [iCalAlarm alarmForEvent:..]
The first argument doesn't have to be a iCalRepeatableEntity; a simple
iCalEntity is enough.
2015-06-19 22:08:08 -04:00
Francis Lachapelle 94cded35d2 Revert "(fix) Adjust md-tabs for latest material"
This reverts commit 6041a9c009.
2015-06-19 14:52:00 -04:00
Francis Lachapelle ba4520da51 Use node-sass 3.x 2015-06-19 13:52:18 -04:00
Francis Lachapelle 92c4ee50fa (css) Remove reference to uninstalled font 2015-06-19 13:46:14 -04:00
Francis Lachapelle 4249d6cc5b Don't use md iconic font v2.0.0 (yet)
We need to evaluate the pros and cons of moving to font ligatures now
available with Angular Material
(https://material.angularjs.org/latest/#/api/material.components.icon/directive/mdIcon).
2015-06-19 13:43:18 -04:00
Ludovic Marcotte e65ff2105e Revert "(fix) ensure calendar objects have a (fake) filename"
This reverts commit a145fdbb53.
2015-06-17 15:57:53 -04:00
Ludovic Marcotte e5c4907aba (fix) improved memory usage 2015-06-17 08:47:15 -04:00
Ludovic Marcotte a49032a6bd (fix) show only the URL of web calendars, when asked 2015-06-15 12:03:38 -04:00
Francis Lachapelle f7f83ae1c7 (css) Review cards display in sharing dialog box 2015-06-15 11:35:20 -04:00
Francis Lachapelle 6b31e3d3b9 (fix) Remove useless ng-show directive (Contacts) 2015-06-15 11:34:48 -04:00
Francis Lachapelle 0ed67f1355 (doc) Improve Angular services documentation 2015-06-15 11:33:21 -04:00
Francis Lachapelle abe8f8f93c (doc) Improve JSON API documentation 2015-06-15 11:32:09 -04:00
Francis Lachapelle 6041a9c009 (fix) Adjust md-tabs for latest material directive 2015-06-15 11:25:32 -04:00
Francis Lachapelle da95a810d4 (fix) Avoid triggering checkbox with md-menu
When clicking on the "more" secondary button of a calendar, trick the
md-list directive to consider the 'md-secondary' class of the md-menu so
the click doesn't trigger the checkbox of the list item.
2015-06-15 11:02:59 -04:00
Ludovic Marcotte f1fc78c71b (fix) invalid scope for notification prefs 2015-06-15 10:10:58 -04:00
Ludovic Marcotte d41458dfdc (fix) hide the dialog after save 2015-06-15 10:07:56 -04:00
Ludovic Marcotte 406e9a3df2 (fix) dropped old calendar sync/tag properties 2015-06-15 10:05:39 -04:00
Ludovic Marcotte b4f9b9095d (feat) first pass at the calendar properties dialog 2015-06-15 09:53:10 -04:00
Francis Lachapelle e18b61f180 Improve API documentation /Calendar/calendarslist 2015-06-15 09:19:21 -04:00
Francis Lachapelle af6e20d627 (fix) Restore methods in UIxCalUserRightsEditor 2015-06-12 16:12:14 -04:00
Francis Lachapelle 8100dfa797 Update {variables,content}.scss for latest md 2015-06-12 15:59:26 -04:00
Ludovic Marcotte 4514d7ce32 (fix) md-content -> md-dialog-content in templates 2015-06-12 15:26:22 -04:00
Ludovic Marcotte 0612c3dff1 Fixed templates 2015-06-12 14:44:33 -04:00
Ludovic Marcotte ee9e559765 (feat) links to calendars 2015-06-12 14:24:33 -04:00
Ludovic Marcotte a89b116940 (feat) allow showing links to address books 2015-06-12 14:24:23 -04:00
Ludovic Marcotte 942c972942 (feat) re-enabled export cards feature 2015-06-12 14:23:51 -04:00
Francis Lachapelle 20914a8812 Improve and fix SASS preprocessing 2015-06-12 14:12:51 -04:00
Francis Lachapelle b85b2ed416 Update angular-material 2015-06-12 12:26:17 -04:00
Ludovic Marcotte 3a1c1226bc (feat) added menu in list of address books 2015-06-12 12:06:00 -04:00
Ludovic Marcotte 6d12c2a2c8 (feat) enabled menu and fabdial support 2015-06-12 12:06:00 -04:00
Ludovic Marcotte 23d7c06829 (feat) improved the attachments handling code for the message editor 2015-06-12 12:06:00 -04:00
Francis Lachapelle 290264262c (js) Improve autocomplete of attendees editor 2015-06-12 12:05:07 -04:00
Francis Lachapelle a95ed0f8e6 Initial ng/md version of the attendees editor 2015-06-12 12:05:06 -04:00
Francis Lachapelle a2383db454 (js) Improve recurrence editor 2015-06-12 12:05:06 -04:00
Francis Lachapelle 1d1f7445cb (html) Remove upper div from image part viewer 2015-06-12 12:05:06 -04:00
Francis Lachapelle b469045de8 (js) Simplify Dialog service 2015-06-12 12:05:05 -04:00
Francis Lachapelle a1c493358f Improve API documentation for appointment editor 2015-06-12 12:05:05 -04:00
Francis Lachapelle 4cece88a42 JSONinfy [SOGoUserHomePage readFreeBusyAction] 2015-06-12 12:05:05 -04:00
Ludovic Marcotte 8124faa6d4 (feat) "Materialization" of the IMIP handling template 2015-06-12 12:03:59 -04:00
Francis Lachapelle fb992db5ad Improve ACL editor (Scheduler & Contacts) 2015-06-12 12:03:58 -04:00
Luc Charland e385bf34d5 added simple event testing json->dav 2015-06-12 12:03:57 -04:00
Ludovic Marcotte 4f75499439 (fix) Materalized link and image templates 2015-06-12 12:03:57 -04:00
Francis Lachapelle 34140e77fc (html) Improve ACL editor 2015-06-12 12:03:56 -04:00
Francis Lachapelle 04b992e1f1 (js) Make use of 'controllerAs' in Contacts module 2015-06-12 12:03:56 -04:00
Francis Lachapelle 11cf483e2c (js) Set height/width of gravatar's image 2015-06-12 12:03:56 -04:00
Francis Lachapelle 6a835c745f (js) Fix resource URL of Acl service 2015-06-12 12:03:56 -04:00
Francis Lachapelle af353fb73f (js) Improve contact chips in card list editor
Exclude list members from auto-completion results.
2015-06-12 12:03:56 -04:00
Francis Lachapelle d159e12b3f (js) Improve contact chips in Preferences module
Exclude users already in the prevent invitations whitelist.
2015-06-12 12:03:55 -04:00
Francis Lachapelle 28477e9709 Add apidoc.json 2015-06-12 12:03:55 -04:00
Luc Charland 9a3f4080c0 test adding single and multiple phones with JSON and getting it with DAV 2015-06-12 12:03:54 -04:00
Francis Lachapelle cdd52a0b37 (css) Cleanup card.scss 2015-06-12 12:03:54 -04:00
Francis Lachapelle e226a32413 Improve login page
Used ngCloak directive to display the form only once AngularJS/Angular
Material is loaded and all components initialized.
2015-06-12 12:03:54 -04:00
Francis Lachapelle 1f83680e12 Add aria attributes in Preferences module 2015-06-12 12:03:54 -04:00
Francis Lachapelle d5f4da3934 (fix) Don't return duplicated timezones 2015-06-12 12:03:53 -04:00
Francis Lachapelle dc91be3c0d Initial recurrence editor for appointments 2015-06-12 12:03:53 -04:00
Francis Lachapelle 7e96f37809 (wox) Add classification to event editor 2015-06-12 12:03:53 -04:00
Francis Lachapelle 0ac27aab50 (js) Improve coding style of Card.service.js 2015-06-12 12:03:53 -04:00
Francis Lachapelle 3350e2fa12 (fix) Syntax of word "occurrence" 2015-06-12 12:03:52 -04:00
Francis Lachapelle e4e5106f97 Set editor mode of Sass files 2015-06-12 12:03:52 -04:00
Francis Lachapelle ed7f979635 (js) Fix dependency injection in Dialog service 2015-06-12 12:03:51 -04:00
Francis Lachapelle 46bfe6befb (fix) Resolve component data completely 2015-06-12 12:03:51 -04:00
Francis Lachapelle 22db835036 Compress production version of CSS 2015-06-12 12:03:51 -04:00
Francis Lachapelle f6f704ddd8 (js) New sgToggleGrid directive
This directive allows to transform the tiles of a md-grid-list to toggle
buttons.
2015-06-12 12:03:51 -04:00
Francis Lachapelle 9222bf80ef (js) Fix mdContactChips in preferencs module
By keeping the same array reference throughout the auto-completion, we
fix the ng-click on a list item. It also avoids constant redraws of the
list.
2015-06-12 12:03:50 -04:00
Francis Lachapelle b017cb1711 Update variables.scss for latest md version 2015-06-12 12:03:50 -04:00
Francis Lachapelle 9db7d3b85d (fix) Show/save whitelisted users for invitations 2015-06-12 12:03:50 -04:00
Francis Lachapelle 18cf3d5f9c (wox) Improve layout of Preferences module 2015-06-12 12:03:49 -04:00
Francis Lachapelle 8e286b5d15 (css) Drop unused "theme" Sass files
Only the original ng-material theme files are used in the build process
of ng-material (using gulp).
2015-06-12 12:03:07 -04:00
Francis Lachapelle ec2b52bf7b (fix) Save event to a different calendar 2015-06-12 12:03:07 -04:00
Francis Lachapelle ac705547e8 (js) Colorize the bg of calendars checkboxes
The background color was affecting only checked checkboxes. Now the
color is applied even for uncheck checkboxes.
2015-06-12 12:02:19 -04:00
Francis Lachapelle a9ad21b395 (js) Improve handling of message flags
We now call the model's methods from the template. Uncluttered the
message controller. Fixed the synchronization of the flags with the
messages list.
2015-06-12 12:02:19 -04:00
Francis Lachapelle ec123b4512 Improve tags chips of message viewer 2015-06-12 12:02:18 -04:00
Francis Lachapelle f27544c72c (js) New sgCompile directive to render mail parts 2015-06-12 12:02:18 -04:00
Francis Lachapelle 9ed65e34ad JSONify mail parts 2015-06-12 12:02:18 -04:00
Ludovic Marcotte 6cd02043af (feat) can now batch delete messages 2015-06-12 12:02:17 -04:00
Ludovic Marcotte 0edc2c1217 Added email button for selected contacts (to be implemented) 2015-06-12 12:02:17 -04:00
Francis Lachapelle 2fa0e91ca7 (js) AddressBook service: Improve documentation 2015-06-12 12:02:17 -04:00
Francis Lachapelle b37892e033 (js) Card service: Improve initialization 2015-06-12 12:02:17 -04:00
Francis Lachapelle 2b6d9db83a API documentation for batchDelete
Also return a proper JSON-payload in case of an error
2015-06-12 12:02:17 -04:00
Ludovic Marcotte 7d54bd5e86 Initial work for muliple selections + new toolbar with ops 2015-06-12 12:02:16 -04:00
Francis Lachapelle 499e66ffe9 (css) Improve contacts and messages lists 2015-06-12 12:02:16 -04:00
Francis Lachapelle 395f3585ca (js) Improve user search in AddressBook ACL editor 2015-06-12 12:02:15 -04:00
Francis Lachapelle 9f9cd68997 (fix) Partial fix for md-contact-chips
Those fixes resolve the JavaScript errors, but the behavior is wrong;
the displayed results are delayed with respect to the search string.
md-contact-chips is expected to change (for the best) soon.
2015-06-12 12:02:15 -04:00
Francis Lachapelle ab38134ba9 Improve user search auto-completion 2015-06-12 12:02:14 -04:00
Francis Lachapelle a52e6604c1 Improve contact list auto-completion 2015-06-12 12:02:14 -04:00
Francis Lachapelle b65573472e Don't use node-sass 3.0.0, yet 2015-06-12 12:02:14 -04:00
Francis Lachapelle 160055a874 Remove unused CSS files 2015-06-12 12:02:13 -04:00
Francis Lachapelle 2d46f4fd33 (js) Improve Contacts controllers 2015-06-12 12:02:13 -04:00
Francis Lachapelle 0eb2ecd2f2 (js) Improve Gravatar support 2015-06-12 12:02:13 -04:00
Francis Lachapelle 8568094266 (wox) Improve mobile display of Contacts 2015-06-12 12:02:12 -04:00
Ludovic Marcotte 5e2bdcd2ba (fix) pimped the login form and added SOGoLoginDomains support 2015-06-12 12:02:12 -04:00
Francis Lachapelle 2095df7865 Update .gitignore 2015-06-12 12:02:12 -04:00
Francis Lachapelle 618d3d9cc0 (js) Fix dependency injections 2015-06-12 12:02:12 -04:00
Francis Lachapelle 874e393948 (js) New file structure for login page (RootPage) 2015-06-12 12:02:11 -04:00
Ludovic Marcotte d056f5581a (fix) do not autocapitalize the username field 2015-06-12 12:02:11 -04:00
Ludovic Marcotte a8f52b30b8 (fix) adjusted the template after refactoring 2015-06-12 12:02:11 -04:00
Francis Lachapelle 1dc5f0d412 (js) New file structure for Angular modules
JavaScript files are now merged by the 'js' Grunt task.
2015-06-12 12:01:21 -04:00
Ludovic Marcotte b1ff1d4365 (fix) many UI improvements in the pref panel 2015-06-12 12:01:19 -04:00
Ludovic Marcotte 7e82ece74c (fix) hide the detail view when we have nothing to show 2015-06-12 11:59:05 -04:00
Francis Lachapelle a5cf7659eb (js) Fix Message service initialization 2015-06-12 11:59:04 -04:00
Luc Charland 43b6006a86 removed the unneeded sleep 2015-06-12 11:59:04 -04:00
Ludovic Marcotte 7eb58db949 (fix) also remove the Draft flag 2015-06-12 11:59:04 -04:00
Ludovic Marcotte 12c8207836 (feat) flag/unflag messages 2015-06-12 11:59:04 -04:00
Ludovic Marcotte b16c17457e (fix) disabled the toggles for priority/return receipt for now 2015-06-12 11:59:04 -04:00
Ludovic Marcotte 2d22c56efe (feat) added replied/forwarded message status support + pimped UI a bit 2015-06-12 11:59:04 -04:00
Ludovic Marcotte 8a74c9f9e0 (fix) recipients handling was broken since moving to chips 2015-06-12 11:59:03 -04:00
Ludovic Marcotte c09e76f2d1 (feat) handle show cc/bcc/attachments in mail composition 2015-06-12 11:59:03 -04:00
Luc Charland 4fa33c0bc2 All tests now pass in version 3 2015-06-12 11:59:03 -04:00
Ludovic Marcotte b0318ca7b6 (feat) chips for mail labels 2015-06-12 11:59:03 -04:00
Francis Lachapelle 8e0230488f (css) Remove Compass dependency 2015-06-12 11:59:02 -04:00
Francis Lachapelle b20707d0ed (css) Add rem function from ng's variables.scss 2015-06-12 11:59:02 -04:00
Ludovic Marcotte cc7c7f7dc7 Fixed wrong subheader name 2015-06-12 11:59:02 -04:00
Francis Lachapelle 8b483136d0 Fix compilation warning in UIxJSONPreferences.m 2015-06-12 11:59:02 -04:00
Francis Lachapelle 2888034ea6 (js) Create new appointment from Calendar module 2015-06-12 11:59:02 -04:00
Francis Lachapelle 520103cffe (js) Extend String with method 'capitalize' 2015-06-12 11:59:01 -04:00
Francis Lachapelle 12d5b57624 (js) Handle web calendars in Calendar model 2015-06-12 11:59:01 -04:00
Francis Lachapelle 523532b153 (js) Resource.post: argument 'id' is optional 2015-06-12 11:59:01 -04:00
Francis Lachapelle fc3c362515 (css) Grow width of message viewer 2015-06-12 11:59:00 -04:00
Francis Lachapelle 0c213c9e66 (js) Improve code of Calendar module
Integrated usage of 'controllerAs'.
2015-06-12 11:59:00 -04:00
Francis Lachapelle 494e2be680 (js) Save addressbook search string
This allow to show the email address that matches the search string (if
it does)
2015-06-12 11:59:00 -04:00
Francis Lachapelle d9184d2363 Format HTML + remove fab button from mail viewer 2015-06-12 11:59:00 -04:00
Francis Lachapelle d9b67a85f2 Return the proper HTTP code when saving an event 2015-06-12 11:59:00 -04:00
Francis Lachapelle e59a4b4246 Use of Dialog.prompt service in Contacts module 2015-06-12 11:58:59 -04:00
Francis Lachapelle 666d36736f Fix/improve search filter of Contacts module 2015-06-12 11:58:59 -04:00
Francis Lachapelle 33b035b360 LDIF to vCard: Handle multiple emails & phones 2015-06-12 11:58:59 -04:00
Ludovic Marcotte 41bc68946d Improved the UI of the bottom sheet content 2015-06-12 11:58:59 -04:00
Ludovic Marcotte f515543e33 (fix) many adjustments regarding auxiliary accounts 2015-06-12 11:58:58 -04:00
Francis Lachapelle 6d64242382 (fix) Restore radio-button.scss 2015-06-12 11:58:58 -04:00
Francis Lachapelle d3916e6e5e Improve API documentation 2015-06-12 11:58:57 -04:00
Francis Lachapelle f2833a2781 JSONify [UIxCalMainActions addWebCalendarAction] 2015-06-12 11:58:57 -04:00
Francis Lachapelle 9e8d991099 Display relative dates in Calendar module 2015-06-12 11:58:57 -04:00
Ludovic Marcotte 1e3cb57567 (fix) removal of mail account 2015-06-12 11:58:57 -04:00
Ludovic Marcotte bc67ffe20d (feat) Gravatar support for mail/contacts 2015-06-12 11:58:57 -04:00
Francis Lachapelle d0ee85e77a Add button to create new mailbox from account root 2015-06-12 11:58:57 -04:00
Francis Lachapelle b390a4b117 (css) Enlarge width of "detail" view 2015-06-12 11:58:56 -04:00
Francis Lachapelle bb05ce89c9 (js) Improve Dialog service 2015-06-12 11:58:56 -04:00
Francis Lachapelle 16b75b1deb Improve lists in sidenav
- the ripple effect is now visible when clicking on list items;
- the fab icon has been moved over the items list;
- colorized the checkboxes of the calendars list.
2015-06-12 11:58:56 -04:00
Ludovic Marcotte 4dbd796fd5 Added color "avatar" for mail labels 2015-06-12 11:58:55 -04:00
Ludovic Marcotte affa6ba656 (fix) new calendar category now showing color too 2015-06-12 11:58:54 -04:00
Ludovic Marcotte 8074e67b84 (fix) default calendar category colors 2015-06-12 11:58:54 -04:00
Ludovic Marcotte d43d31d144 (fix) correctly initialize arrays 2015-06-12 11:58:54 -04:00
Ludovic Marcotte 966a088a0f (fix) correctly initialize the PreventInvitationsWhitelist array 2015-06-12 11:58:53 -04:00
Ludovic Marcotte ebf4c1a9c7 (fix) also handle forwardAddress 2015-06-12 11:58:53 -04:00
Ludovic Marcotte dd13abd701 Fixed pref module 2015-06-12 11:58:53 -04:00
Ludovic Marcotte aa4dc45aa7 fix over previous commit 2015-06-12 11:50:50 -04:00
Ludovic Marcotte e1d8854d10 (fix) correctly generate the Sieve script 2015-06-12 11:50:50 -04:00
Luc Charland 243726f096 Changed preferences to use JSON for v3 2015-06-12 11:50:50 -04:00
Ludovic Marcotte f49610f058 (fix) renamed md-content -> md-dialog-content regarding changes in angular-material 2015-06-12 11:49:37 -04:00
Ludovic Marcotte a166d1b4cf Using chips for adding/viewing contact categories 2015-06-12 11:47:50 -04:00
Francis Lachapelle 8557b2a161 (fix) Reset button of contact and event editors 2015-06-12 11:47:50 -04:00
Francis Lachapelle d77f75da49 (fix) Save card in Contacts module 2015-06-12 11:47:50 -04:00
Ludovic Marcotte 0f1b0bfbbe Fixed ui routing for card editor 2015-06-12 11:47:50 -04:00
Ludovic Marcotte 91c92e3a11 added new calendar feature 2015-06-12 11:47:50 -04:00
Ludovic Marcotte 1f5248371c Disabled unuseful md-select for now 2015-06-12 11:47:49 -04:00
Ludovic Marcotte dbf614a2dc Re-enabled textures 2015-06-12 11:47:49 -04:00
Francis Lachapelle c957b4b45f Initial component editor in Scheduler module 2015-06-12 11:47:49 -04:00
Ludovic Marcotte feee156c0a Added calendar sharing capability 2015-06-12 11:47:48 -04:00
Francis Lachapelle 0d62ec0bea (js) Split Calendars list by type
The list of calendars is now splitted by type: personal calendars and
other's calendars (subscriptions).

The constructor of the Calendar model has been improved.
2015-06-12 11:47:48 -04:00
Francis Lachapelle 550fc09ee4 Add attribute isReadOnly to event/task JSON view 2015-06-12 11:47:47 -04:00
Ludovic Marcotte 5e3b3e6987 Added ACL sharing capabilities for address books 2015-06-12 11:47:47 -04:00
Ludovic Marcotte 77269e5cd6 Used the avatar for consistency with other dialogs 2015-06-12 11:47:46 -04:00
Francis Lachapelle 8c9a074688 (css) Add sticky component for subheaders 2015-06-12 11:47:46 -04:00
Francis Lachapelle 5247200bf0 (css) Improve lists and subheaders components 2015-06-12 11:47:46 -04:00
Francis Lachapelle 96ebc32d8a Split AddressBooks list by type
The list of addressbook books is now splitted by type: personal
addressbooks, other's addressbooks (subscriptions) and global
addressbooks (from sogo.conf, per domain).

The main ui-view has been splitted and therefore an additional
controller has been created.

The constructor of the AddressBook model has been improved.
2015-06-12 11:47:46 -04:00
Ludovic Marcotte 19c12add73 Added the capability to create address books 2015-06-12 11:47:46 -04:00
Ludovic Marcotte ecf30daaf3 Removed useless file 2015-06-12 11:47:45 -04:00
Ludovic Marcotte 47e7201eb6 added list avatar, make use of it 2015-06-12 11:47:45 -04:00
Ludovic Marcotte 174accdb23 Display category color 2015-06-12 11:47:45 -04:00
Francis Lachapelle 62468d21ef Improve template for sgSubscribe directive 2015-06-12 11:46:50 -04:00
Ludovic Marcotte 9571befbfd Added logoff feature 2015-06-12 11:46:50 -04:00
Ludovic Marcotte 36189e4203 Added calendar subscription 2015-06-12 11:46:50 -04:00
Ludovic Marcotte 428f2586c7 Added addressbook subscription support 2015-06-12 11:46:49 -04:00
Ludovic Marcotte 5ea57f195b Allow creation or list/contact 2015-06-12 11:46:49 -04:00
Ludovic Marcotte 11179cf27d Use a label instead of subheader, it's more sexy 2015-06-12 11:46:49 -04:00
Ludovic Marcotte 2ff6131483 Added list editor/viewer, fixed contact editor 2015-06-12 11:46:49 -04:00
Francis Lachapelle 7b874d08d4 Improve sidenav lists 2015-06-12 11:46:48 -04:00
Luc Charland 3858911007 Changed login from urlencoded to json 2015-06-12 11:46:48 -04:00
Ludovic Marcotte a25a01495f Added chips for prevent invitations 2015-06-12 11:46:48 -04:00
Francis Lachapelle 781d55cc37 Force colors of sgFolderStylesheet 2015-06-12 11:46:06 -04:00
Francis Lachapelle 5d5d8bc201 (html) Fix list of messages of selected mailbox 2015-06-12 11:46:06 -04:00
Luc Charland 0cdd19e3aa fixed a missing semi-colon 2015-06-12 11:46:05 -04:00
Francis Lachapelle 0f21c02a5f (html) Fix template of sgFolderTree directive 2015-06-12 11:46:05 -04:00
Francis Lachapelle ecad0c367f (html) Fix list of messages of selected mailbox 2015-06-12 11:46:05 -04:00
Francis Lachapelle eeca191103 (css) Cleanup sidenav component
Remove duplicated definitions from angular material source and prepare
for right sidenav.
2015-06-12 11:46:05 -04:00
Francis Lachapelle 3f27e5b775 (css) Dirty fix for vs-repeat damages 2015-06-12 11:46:04 -04:00
Ludovic Marcotte 991351999e Removed unused statement 2015-06-12 11:46:04 -04:00
Ludovic Marcotte c76bca3f68 arial label 2015-06-12 11:46:04 -04:00
Ludovic Marcotte 0529de2c1c Removed useless stuff 2015-06-12 11:46:03 -04:00
Francis Lachapelle b141031ccd Initialize arrays of new message instance 2015-06-12 11:46:03 -04:00
Francis Lachapelle 6971632165 (scss) Remove deprecated textfield component 2015-06-12 11:46:02 -04:00
Francis Lachapelle b9eb0f9a69 (scss) Integrate md-chips 2015-06-12 11:46:02 -04:00
Francis Lachapelle 0924b5154e Replace ngTagsInput by md-chips + md-autocomplete 2015-06-12 11:46:02 -04:00
Benoit Favreault 14597f8f16 Update angular-material to commit 96559458633f90dafd543d3a88f9a2165fd76ac3 2015-06-12 11:46:01 -04:00
Benoit Favreault fc69d6b1d4 Change paper palette defaults hues
Fix mobile show-hide
2015-06-12 11:46:01 -04:00
Benoit Favreault 321cfb8467 Update angular-material to commit 77504e9ff178abf02c0886d7f6cfad7d68239fca 2015-06-12 11:46:01 -04:00
Francis Lachapelle da9dbd8ad5 (fix) Restore checkbox.scss 2015-06-12 11:46:01 -04:00
Francis Lachapelle 4626039e5c (fix) JavaScript errors on Mail preferences tab 2015-06-12 11:46:01 -04:00
Benoit Favreault 41f598c9ed Reset SchdulerUI.scss to [6ee715d] 2015-06-12 11:45:38 -04:00
Benoit Favreault f080536b3f Resolve conflict 2015-06-12 11:45:38 -04:00
Benoit Favreault af57fa84e4 Update angular-material [commit 42db19dd5571eb88ba8d2a9667d183d9f3d61da7] 2015-06-12 11:45:38 -04:00
Benoit Favreault 46a262acfa Cleaning unused scss files 2015-06-12 11:45:38 -04:00
Francis Lachapelle a276ac366e Set width/position of events blocks and add colors 2015-06-12 11:45:10 -04:00
Francis Lachapelle 82d1d5e701 Review calendar views templates
Changed month view to a md-grid-list.
Added some colors.
2015-06-12 11:45:10 -04:00
Francis Lachapelle 305c13d720 Add common template UIxTopnavToolbarTemplate.wox 2015-06-12 11:45:09 -04:00
Ludovic Marcotte 0de02262d0 (fix) make sure we can always add filters 2015-06-12 11:45:09 -04:00
Ludovic Marcotte d581054214 (fix) fixed default values for many prefs 2015-06-12 11:45:09 -04:00
Francis Lachapelle e8acb225e4 Add md-grid-list and md-autocomplete components 2015-06-12 11:45:08 -04:00
Ludovic Marcotte 6e36154b1b (fix) retagged properly for latest angular-material changes 2015-06-12 11:45:08 -04:00
Francis Lachapelle 8f47e62dd2 Calendar module: add month view 2015-06-12 11:44:35 -04:00
Ludovic Marcotte a2c469d46a (fix) correctly handle PreventInvitationsWhitelist 2015-06-12 11:44:35 -04:00
Ludovic Marcotte 6f59955340 (fix) sanitize autoReplyEmailAddresses 2015-06-12 11:44:35 -04:00
Ludovic Marcotte 70e5b1d4aa (fix) pref template now looks like others 2015-06-12 11:44:34 -04:00
Ludovic Marcotte f262347149 (fix) more defaults values in the prefs 2015-06-12 11:44:34 -04:00
Ludovic Marcotte 7168779684 (feat) added password change support 2015-06-12 11:44:34 -04:00
Ludovic Marcotte d21715d8f1 (fix) right localized string + one for account locked 2015-06-12 11:43:53 -04:00
Ludovic Marcotte 1fe5e39254 Default value for short date format 2015-06-12 11:43:52 -04:00
Ludovic Marcotte 023362797b Icons and flexbox additions to the guide 2015-06-12 11:43:52 -04:00
Ludovic Marcotte 772a682655 (fix) correctly localize mail labels from common bundle 2015-06-12 11:43:52 -04:00
Francis Lachapelle 4252b66b6e Initial SOGo developer's guide 2015-06-12 11:43:52 -04:00
Francis Lachapelle 91acb9d0c5 Integrate UIxSidenavToolbarTemplate in all modules 2015-06-12 11:43:51 -04:00
Francis Lachapelle 171b5085ab Fix GNUmakefile for latest underscore package 2015-06-12 11:42:40 -04:00
Benoit Favreault c38a23d9b1 Hot-fix for build problem 2015-06-12 11:42:39 -04:00
Benoit Favreault 92ecdd52ad Hot-fix for build problem (commit/74dda0206595acb1c48f7d328fd96b64025542bb) 2015-06-12 11:42:39 -04:00
Ludovic Marcotte 8b093f9001 (fix) broken template, additional </div> closing tag 2015-06-12 11:42:39 -04:00
Benoit Favreault 5797e70358 Made bottom sheet opaque 2015-06-12 11:42:39 -04:00
Benoit Favreault a75719e151 Fix fab-button positionning 2015-06-12 11:42:38 -04:00
Benoit Favreault a5c9f41375 Fixed "fantom account" 2015-06-12 11:42:38 -04:00
Benoit Favreault 00d2154c0e fix card background color (angular-material regression)
Change views container justification
hide sg-menu components
2015-06-12 11:42:38 -04:00
Francis Lachapelle 0fc027e5e9 Set the doctype to HTML5 2015-06-12 11:42:37 -04:00
Francis Lachapelle 5f7abec980 Gruntfile.js: add postcss:dist to build task 2015-06-12 11:42:37 -04:00
Francis Lachapelle 6c4a82571c Calendar module: add day view 2015-06-12 11:42:37 -04:00
Francis Lachapelle 7ef01304ee Add debounce to sgSearch Angular directive 2015-06-12 11:42:37 -04:00
Francis Lachapelle 8d66f62cad [fix] JS syntax error on login page 2015-06-12 11:42:37 -04:00
Benoit Favreault eaeae77be4 Fix (tried to) regressions introduced by angular-material update 2015-06-12 11:42:08 -04:00
Benoit Favreault 1477bd6eae Regression fixes 2015-06-12 11:42:08 -04:00
Benoit Favreault fe8c1b927c Fix mobile transitions (sort of) 2015-06-12 11:42:07 -04:00
Benoit Favreault a554158686 Fix mobile transitions (sort of) 2015-06-12 11:42:07 -04:00
Benoit Favreault f6cb8f226a Edit scss-lint to prevent false positive warnings 2015-06-12 11:42:07 -04:00
Benoit Favreault 5224a7b304 Fix fab-button positioning (still some strange corner-cases from md sources) 2015-06-12 11:42:07 -04:00
Benoit Favreault b5bea16abb Add sassyJSON library 2015-06-12 11:42:07 -04:00
Benoit Favreault 3441a66040 Add paper texture to toolbar and sidenav 2015-06-12 11:42:06 -04:00
Benoit Favreault e69c47854a Fine-tune layout 2015-06-12 11:41:28 -04:00
Benoit Favreault 2c8d1b5db7 Alternate mobile transitions 2015-06-12 11:41:27 -04:00
Francis Lachapelle 2446677172 [fix] Add definition of UIxSidenavToolbarTemplate 2015-06-12 11:41:27 -04:00
Francis Lachapelle abe9a85c4f SOGo.SchedulerUI AngularJS module 2015-06-12 11:41:26 -04:00
Francis Lachapelle a57bf56165 [fix] Possible exception in UIxCalendarSelector.m 2015-06-12 11:41:26 -04:00
Francis Lachapelle 9a1c2b6a11 New ng directives for Calendar module
- sgCalendarDayTable
- sgCalendarDayBlock
2015-06-12 11:41:26 -04:00
Francis Lachapelle 4b47e5de83 Calendar module: initial AngularJS-based templates 2015-06-12 11:41:25 -04:00
Francis Lachapelle 22c9b61246 Extend String and Date native JS objects 2015-06-12 11:41:25 -04:00
Francis Lachapelle e9db6cda61 UIxCalListingActions: improve style and syntax 2015-06-12 11:41:25 -04:00
Francis Lachapelle 44c49abbec Calendar list: use 'name' instead of 'displayName' 2015-06-12 11:41:25 -04:00
Francis Lachapelle 8b6a3d0e3f Initial JavaScript models for calendar module 2015-06-12 11:41:25 -04:00
Francis Lachapelle d72f83ee95 Save calendar: use 'name' instead of 'displayName' 2015-06-12 11:41:24 -04:00
Benoit Favreault ba35bc801f Add a grunt task target postcss:dev (no minification) 2015-06-12 11:41:24 -04:00
Ludovic Marcotte 10a017503e Now enforce SOGoMailCustomFromEnabled 2015-06-12 11:41:24 -04:00
Ludovic Marcotte bb1977fa93 Wiped unused scss file 2015-06-12 11:40:07 -04:00
Ludovic Marcotte cf4593917f More form validation 2015-06-12 11:40:07 -04:00
Francis Lachapelle ae2990d8d6 Review core scss following ng-material changes 2015-06-12 11:40:06 -04:00
Benoit Favreault 05e58b30cc Replace mobile detail-view transitions
Minor adjustments to layout
2015-06-12 11:39:49 -04:00
Ludovic Marcotte 2bc44dec9b Improved preferences module (save/load, checks on enabled modules, etc.) 2015-06-12 11:38:08 -04:00
Benoit Favreault 48b74b0f6b Add responsiveness (First draft) 2015-06-12 11:35:54 -04:00
Francis Lachapelle 516e5fd303 Add current date in top bar 2015-06-12 11:35:54 -04:00
Ludovic Marcotte 0241cacbf9 Added some data validation 2015-06-12 11:35:53 -04:00
Benoit Favreault 78da72f3cb Replace specific list-view by generic (class-based) view 2015-06-12 11:35:52 -04:00
Benoit Favreault 660af9abfd Amend previous:
Add a view-detail class (for more generic use)
Fix the regression (scroll) problem on contact editor
2015-06-12 11:35:52 -04:00
Benoit Favreault b4f2b470dc Add a scss-lint config file 2015-06-12 11:35:52 -04:00
Benoit Favreault f99dae331d Add a view-detail class (for more generic use)
Fix the regression (scroll) problem on contact editor
2015-06-12 11:35:52 -04:00
Benoit Favreault 655fe5f731 Fix regression on Contact editor 2015-06-12 11:35:52 -04:00
Francis Lachapelle 72968f2e6e Indentation of Gruntfile.js 2015-06-12 11:35:51 -04:00
Ludovic Marcotte 0f2e3df164 More cleanups, defaults values for mail/contact/calendar labels and data validation 2015-06-12 11:35:51 -04:00
Benoit Favreault 818af41ea6 Add breakpoints management librairies
Add grunt-contrib-compass (required)
Edit gruntfile.js (add compass tasks)

--don't forget to update bower components
2015-06-12 11:34:29 -04:00
Benoit Favreault 6153e88f62 Add animation params 2015-06-12 11:34:29 -04:00
Ludovic Marcotte 6636fe3625 Disabled lots of unused code (to be removed) in v3/prefs 2015-06-12 11:34:28 -04:00
Ludovic Marcotte 96d2a4cea8 Preferences saving support 2015-06-12 11:27:17 -04:00
Francis Lachapelle 2edfd1863b Addressbook module: improve routing views handling 2015-06-12 11:26:07 -04:00
Benoit Favreault 766c421b53 Remove margin from date in msg list 2015-06-12 11:26:07 -04:00
Francis Lachapelle f909fe657f Webmail: fix cancel button of message editor 2015-06-12 11:26:07 -04:00
Francis Lachapelle 885140c92a Addressbook module: fix edit button 2015-06-12 11:26:07 -04:00
Francis Lachapelle 6bf22795f3 Improve bower.json 2015-06-12 11:26:07 -04:00
Francis Lachapelle 3864264787 JS Card model: initialize birthday as Date object 2015-06-12 11:26:06 -04:00
Francis Lachapelle a3aaf36477 Contact editor: remove extra bottom padding 2015-06-12 11:26:06 -04:00
Francis Lachapelle 4f6babcdcc Add cancel button to Webmail editor 2015-06-12 11:26:06 -04:00
Ludovic Marcotte 56763e1aca Removed dead code, support for adding calendar categories, disabled unused tab 2015-06-12 11:26:05 -04:00
Francis Lachapelle a787521977 Restore md-button in sgSearch directive 2015-06-12 11:22:46 -04:00
Benoit Favreault ca1831271b Make iconButton round to display a more elegant ripple-effect 2015-06-12 11:22:45 -04:00
Benoit Favreault d6a294b89f Fix breakpoints in scss and js (in await for a more elegant solution) 2015-06-12 11:22:45 -04:00
Francis Lachapelle 6b1fe301a6 Fix mail search criteria 2015-06-12 11:22:45 -04:00
Francis Lachapelle 8d003a7c35 Return used space of quota as a number in JSON 2015-06-12 11:22:45 -04:00
Francis Lachapelle db9e3a5401 Use sgSearch directive in Webmail module 2015-06-12 11:22:45 -04:00
Francis Lachapelle 01d4134a23 Use sgSearch directive in addressbook module 2015-06-12 11:22:44 -04:00
Francis Lachapelle 44164c0f43 Improve and document Webmail search options 2015-06-12 11:22:44 -04:00
Francis Lachapelle 68eec08de6 New sgSearch Angular directive 2015-06-12 11:22:44 -04:00
Francis Lachapelle 0e3cbe8c98 Improve "load images" button from Webmail 2015-06-12 11:22:44 -04:00
Benoit Favreault 4ab4ac0f11 Fix card.view template to prevent useless element (<ui-view="undefined">) injection 2015-06-12 11:22:43 -04:00
Benoit Favreault 8cebc00f7a Polish UI (Contact and Mail)
- Unifiy & simplify markup
- Add visual consistency
2015-06-12 11:22:43 -04:00
Benoit Favreault 01f1cbbd75 Fix minor deprecations in Preferences template 2015-06-12 11:22:43 -04:00
Benoit Favreault 6a304b3e07 Fix sidenav icon-button 2015-06-12 11:22:43 -04:00
Ludovic Marcotte 5b674abcf1 Fixed preferences module wrt recent changes 2015-06-12 11:22:43 -04:00
Benoit Favreault e756053f8b Replace bottom-bar by bottom-sheet
Adjust sidenav layout
Unify background colors
2015-06-12 11:18:00 -04:00
Benoit Favreault 69d0a3ad57 Minor adjustment to toolbar date 2015-06-12 11:17:59 -04:00
Benoit Favreault a57713523b Finish the toolbars
Add an app button on sidenav
Add a logout select on sidenav
2015-06-12 11:17:59 -04:00
Benoit Favreault 9fc713dc5f Create a 'css' grunt task (sass + postcss) 2015-06-12 11:17:59 -04:00
Benoit Favreault 31bb97aaec Update angular-material [dc2d204] master 2015-06-12 11:17:58 -04:00
Francis Lachapelle 1d8dbbfe2c Restore compose mail button 2015-06-12 11:17:58 -04:00
Francis Lachapelle fc72d3b12a Cleanup references to ZURB Foundation 2015-06-12 11:17:58 -04:00
iRouge bdbbc5be47 Fix login window according to specs 2015-06-12 11:17:57 -04:00
Francis Lachapelle f1cdb3a472 Cleanup dead code from js/Common/ui.js 2015-06-12 11:17:57 -04:00
iRouge fdb4cd71eb Fix sidenav toolbar (logo and buttons) 2015-06-12 11:17:56 -04:00
iRouge 90cd79a21b Adjust Contact view (center card in right container) 2015-06-12 11:17:55 -04:00
iRouge 90c359779e Fix Contact editor layout 2015-06-12 11:17:55 -04:00
iRouge 0cd4688e64 Update material-angular (release 0.8.3) 2015-06-12 11:17:55 -04:00
iRouge 9c42350caa Add aria-label to iconButtons 2015-06-12 11:17:55 -04:00
iRouge 1ac49415d1 Remove (useless) "close" button on sidenav 2015-06-12 11:17:54 -04:00
iRouge bed46ba2eb Correct sources to prevent angular.js deprecation Warning
Correct scss to prevent 404 server response
2015-06-12 11:17:54 -04:00
iRouge 02cdc869be Apply iconButton to Contact and Mail 2015-06-12 11:17:54 -04:00
iRouge 4264d054ca Create Sass component: iconButton 2015-06-12 11:17:53 -04:00
iRouge b6f83f3275 Update angular-material 2015-06-12 11:17:53 -04:00
iRouge 1fc846bf5c Update angular-material 2015-06-12 11:17:53 -04:00
iRouge 2d2edabb18 Add action "create new contact" to button 2015-06-12 11:17:53 -04:00
iRouge dcfa43ab28 Update angular-material 2015-06-12 11:17:53 -04:00
iRouge b0d7fff56a Add Selected-state to lists elements (Contacts and Messages) -- Amend previous 2015-06-12 11:17:52 -04:00
iRouge be4d56b7e1 Add Selected-state to lists elements (Contacts and Messages) 2015-06-12 11:17:52 -04:00
Francis Lachapelle fefd4fc7c7 Fix md-select 2015-06-12 11:17:52 -04:00
iRouge 855acccd6d Update angular-material 2015-06-12 11:17:52 -04:00
iRouge 80d6c0226a Adjust UI for Contact, Contact editor, Messages and Message editor 2015-06-12 11:17:51 -04:00
Francis Lachapelle 15dab4dfe5 Add placeholder to type selects of contact editor 2015-06-12 11:17:51 -04:00
Francis Lachapelle dbb08a762a Fix "add" buttons of contact editor 2015-06-12 11:17:51 -04:00
Francis Lachapelle af144585a5 Cleanup formatting of attributes in HTML templates 2015-06-12 11:17:50 -04:00
Francis Lachapelle 6a25ccfe96 Remove data- prefix in attributes of HTML tags 2015-06-12 11:17:50 -04:00
Francis Lachapelle dd84782427 Cleanup .gitignore 2015-06-12 11:17:49 -04:00
iRouge 012d953d65 Add an input-container to search field in Contact toolbar 2015-06-12 11:17:49 -04:00
iRouge 5819cc75d3 Style Contact Editor UI: Ready to test
Some functional bugs remaining
2015-06-12 11:17:49 -04:00
Ludovic Marcotte 5c3f0138f5 First pass at the v3 pref module
The UI needs to be styled. We also need to properly load "default"
values from the system/domain/user defaults and save them correctly
too.
2015-06-12 11:17:49 -04:00
Francis Lachapelle 575676deb8 Add search field in AddressBook module 2015-06-12 10:50:10 -04:00
Francis Lachapelle 4b9eb44790 Initial integration of md-select in contact editor 2015-06-12 10:50:10 -04:00
Francis Lachapelle dc538f969e Cleanup dependencies in bower.json 2015-06-12 10:50:10 -04:00
iRouge e32f6d3086 Update angular-material 2015-06-12 10:50:09 -04:00
Francis Lachapelle 4bce4ff988 Update angular-material 2015-06-12 10:50:09 -04:00
iRouge fe071a5435 Style contact editor (email addresses with select) 2015-06-12 10:50:09 -04:00
iRouge 6b58f3a080 Style contact editor (model for add and remove) 2015-06-12 10:50:08 -04:00
iRouge 1d61e33a51 Update angular-material (select are fixed !) 2015-06-12 10:50:08 -04:00
iRouge 79dc49b905 Style contact editor layout (needs more work) 2015-06-12 10:50:08 -04:00
iRouge b81f98236c Style contact-view layout 2015-06-12 10:50:08 -04:00
iRouge b7adf2814d Refactor:
- use lowercase and hyphen name for new component according to Sass Guidelines
- change every uses by hand (thanks to my stupid IDE WebStorm)
2015-06-12 10:50:07 -04:00
iRouge 5980f8e90e Create new Sass component: pseudo-input 2015-06-12 10:50:07 -04:00
iRouge cfed9c2058 Update angular-material 2015-06-12 10:50:07 -04:00
iRouge 16440998de Fix Messages editor:
- textarea field
- labels and placeholders color
2015-06-12 10:50:07 -04:00
iRouge 1264dbad73 Skin Contact editor according to Material Design specs (still buggy) 2015-06-12 10:50:06 -04:00
iRouge 3fb8a42856 Fix login layout ;-) (md-select are still buggy) 2015-06-12 10:50:06 -04:00
Francis Lachapelle 4a4658e29d Add missing npm modules for grunt postcss 2015-06-12 10:50:06 -04:00
Francis Lachapelle 7ff934a5cb Remove dependency of hammer.js 2015-06-12 10:50:06 -04:00
Ludovic Marcotte 6d4147fa34 fixed templates 2015-06-12 10:50:05 -04:00
iRouge b2c71ceae9 Add box-shadow to Mail editor 2015-06-12 10:50:05 -04:00
iRouge 4185cc37fe Style the plain-text Mail editor
Style Contacts
Style Contact editor (buggy)
Change postcss config
2015-06-12 10:50:05 -04:00
Ludovic Marcotte 79a0a3684c Use md-select on the root page 2015-06-12 10:50:04 -04:00
Benoit Favreault dcd6c5fd5a Add autoprefixer and its dependancies (fix the buggy flex boxes in Safari and some older browsers) 2015-06-12 10:50:04 -04:00
Francis Lachapelle cc24b9b599 Add md-select SASS component 2015-06-12 10:50:04 -04:00
Francis Lachapelle 1569306ea0 Update angular-material subrepo 2015-06-12 10:50:04 -04:00
iRouge a1f0c9fd6e Update angular-material 2015-06-12 10:50:03 -04:00
Francis Lachapelle da22cd854b Fix compilation of UIxTaskEditor.m 2015-06-12 10:50:03 -04:00
Francis Lachapelle 94105e937a Improve JSON getter/setter for todos 2015-06-12 10:50:03 -04:00
Francis Lachapelle f47a22068f JSON: fix handling of dates of all-day events 2015-06-12 10:50:03 -04:00
Francis Lachapelle 177c933af4 Lowercase the status property of vEvent & vToDo 2015-06-12 10:50:02 -04:00
Francis Lachapelle bbc5a2a048 Fix compilation warning of SOGoAppointmentObject.m 2015-06-12 10:50:02 -04:00
Ludovic Marcotte 48ffe8fb7e Minor improvement to the template 2015-06-12 10:50:01 -04:00
Ludovic Marcotte e3d4a7a373 Deleted old CSS 2015-06-12 10:50:01 -04:00
Francis Lachapelle 4ddf92aadb JSON: Return ISO8601 dates in calendar components 2015-06-12 10:50:01 -04:00
Francis Lachapelle e226825bde Add [NSCalendarDate+SOGo iso8601DateString] 2015-06-12 10:50:01 -04:00
iRouge d8a816a335 Add toolbars Icons (several bugs, some remaining) 2015-06-12 10:50:00 -04:00
Ludovic Marcotte b9e7e24344 Fixed remember login option 2015-06-12 10:50:00 -04:00
Francis Lachapelle 23ce8904a9 Restore color transistion in sidenav.scss 2015-06-12 10:49:59 -04:00
Francis Lachapelle d1271799f4 Update angular-material version in bower.json 2015-06-12 10:49:59 -04:00
Ludovic Marcotte e5726d2840 WiP - some improvements to the login form 2015-06-12 10:49:58 -04:00
iRouge fcd5abfafc Update angular-material version 0.8.0-rc1 2015-06-12 10:49:57 -04:00
Francis Lachapelle 67c62fe85c Fix compilation of UIxComponentEditor.m 2015-06-12 10:49:57 -04:00
Francis Lachapelle f9271b747c Initial JSON actions to get/set an event 2015-06-12 10:48:00 -04:00
iRouge f694f57638 Minor tweaks to Mailer UI 2015-06-11 15:57:27 -04:00
iRouge 522108bdd3 [Amend previous commit]Styled ngTags for Mail editor and propose a chip-like version of tags 2015-06-11 15:57:27 -04:00
iRouge 780d757d6b Styled ngTags for Mail editor and propose a chip-like version of tags 2015-06-11 15:57:27 -04:00
iRouge df8b4e302f Styled ngTags but no hasFocus state 2015-06-11 15:57:27 -04:00
iRouge 1bb66bbf7d Skin the Mail editor except ng-tags 2015-06-11 15:57:26 -04:00
iRouge a1299fea97 Skin the Mail editor 2015-06-11 15:57:26 -04:00
Francis Lachapelle 221e49a5d1 Improve [iCalTrigger asDictionary] 2015-06-11 15:57:26 -04:00
iRouge d123b2d5d4 Change PseudoField-label font-weight 2015-06-11 15:57:26 -04:00
iRouge 4f2fc5c99e Fixed the message headers design (markup and scss)
Create a "PseudoField" component that mimics Material Design input fields
2015-06-11 15:57:25 -04:00
Francis Lachapelle e429dc3ff1 Add [iCalByDayMask initWithDaysAndOccurences:] 2015-06-11 15:57:25 -04:00
iRouge 9e7a051d25 Update angular-material 2015-06-11 15:57:25 -04:00
iRouge 75f38173c5 Adjust Messages header view
Add ngMaterial directives tomessage composer
2015-06-11 15:57:24 -04:00
Francis Lachapelle 4cd6ffcaec Messages list: add ng directives to icons 2015-06-11 15:57:24 -04:00
iRouge 12807457d4 Update angular-material 2015-06-11 15:57:24 -04:00
Ludovic Marcotte aefb27fc5c Initial (and fugly) materialization of the login page 2015-06-11 15:57:23 -04:00
iRouge cd32bcf961 Fine-tune presentation of Mailer UI
- Colors
- Icons
- Dimmensions
- Typography
2015-06-11 15:57:22 -04:00
iRouge 16f275af52 Add SOGo logo 2015-06-11 15:57:22 -04:00
iRouge 419f1bd698 Adjust containers width according to Google's specs 2015-06-11 15:57:21 -04:00
iRouge e00416e1b8 Apply colours to components 2015-06-11 15:57:21 -04:00
iRouge daaa4284cb Add Sass version of SOGo's palettes, mixin to generate theme-class and a getter function for convenient access to colours trough any scss 2015-06-11 15:57:20 -04:00
Francis Lachapelle 33e70cd3f3 Remove dependancy to angular-material.css 2015-06-11 15:57:20 -04:00
iRouge 44e56c92d0 Update Angular-Material to 0.7.1 : HEAD origin/master origin/HEAD master 2015-06-11 15:57:19 -04:00
iRouge 6404536d19 Add md-colors module 2015-06-11 15:57:19 -04:00
iRouge e7b7e9cefe Adjust z-indexes and box-shadows 2015-06-11 15:57:19 -04:00
iRouge 174b6f365b Typograpy : Messages list subheader 2015-06-11 15:57:18 -04:00
iRouge 08a76aef97 Adapt markup to typographic styles
- Mail Messages List
2015-06-11 15:57:18 -04:00
Francis Lachapelle bd61c354f0 Fix references to font files in typography.scss 2015-06-11 15:57:17 -04:00
Francis Lachapelle 13144aad3b Preparation for build of a unique CSS 2015-06-11 15:57:17 -04:00
iRouge b163e59563 Adjust typography for autoSrollList 2015-06-11 15:57:17 -04:00
iRouge 09fa2be5bd Typography auto-scroll List 2015-06-11 15:57:17 -04:00
Francis Lachapelle 36c931b596 Preparation for build of a unique CSS 2015-06-11 15:57:16 -04:00
Francis Lachapelle 6040838396 Don't use bower package of the font Fira 2015-06-11 15:57:16 -04:00
Francis Lachapelle 1a81abebaa Remove unused files from scss directory 2015-06-11 15:57:16 -04:00
iRouge a191c7a382 Typography : Google Material Design standard styles
- Layout is broken
2015-06-11 15:57:15 -04:00
iRouge 907df81436 Typography-2 2015-06-11 15:57:14 -04:00
iRouge 8f01b0c739 Add Fira Sans to font folder 2015-06-11 15:57:14 -04:00
Francis Lachapelle 8c974e6c1a Add Fira font 2015-06-11 15:57:10 -04:00
iRouge e73ed8ae44 Add bower sass-cache to gitignore 2015-06-11 15:57:10 -04:00
iRouge f59b078faa Typography 2 2015-06-11 15:57:09 -04:00
iRouge 6319b6f531 Typography 2015-06-11 15:57:09 -04:00
Francis Lachapelle 9671fbf506 Remove .sass-cache directory 2015-06-11 15:57:09 -04:00
iRouge 670d2bc382 Generate basic font-size according to Google Material design specs 2015-06-11 15:57:08 -04:00
Francis Lachapelle 6f6338bb64 Fix font file name in SASS 2015-06-11 15:57:06 -04:00
iRouge 1528b32cc7 Update fontes icônes 2015-06-11 15:57:06 -04:00
Francis Lachapelle e1b155b455 Remove fonts managed by bower & grunt 2015-06-11 15:57:06 -04:00
iRouge 72ae466541 Embedded Icon font source has base64 data URI in CSS 2015-06-11 15:57:06 -04:00
Francis Lachapelle 5cc14bb729 Remove reference to bower_components in CSS 2015-06-11 15:57:02 -04:00
Francis Lachapelle 79a5d61efb Improve sgFolderTree
Replaced the A link by a button and renamed the childLevel# CSS class by
a more appropriate sg-child-level-# class.
2015-06-11 15:57:02 -04:00
Francis Lachapelle c664648eb6 Fix processing of SCSS with Grunt
We now use the Ruby implementation of SASS (grunt-contrib-sass) since
libsass is not compatible with Compass.
2015-06-11 15:57:01 -04:00
Francis Lachapelle df6d957721 Fix mailbox rename operation 2015-06-11 15:57:01 -04:00
Francis Lachapelle ee9ef2f0e4 Remove unused SASS files 2015-06-11 15:57:01 -04:00
Francis Lachapelle 383ccdbc82 Add CSS animation to mailboxes tree 2015-06-11 15:57:00 -04:00
Francis Lachapelle 225d039f89 Improve sgFolderTree directive (mailboxes tree) 2015-06-11 15:57:00 -04:00
Francis Lachapelle 7f74d5a3d9 New sgEnter Angular directive 2015-06-11 15:57:00 -04:00
Francis Lachapelle 862068eeb5 Improve JSON getters for events 2015-06-11 15:56:59 -04:00
iRouge 56607a1baf Refactor contact editor form element templates markup and style 2015-06-11 15:56:34 -04:00
iRouge 750a41f19a CSS files for convenience 2015-06-11 15:56:34 -04:00
iRouge 6651b02ba2 Replace valueless attributes by classes 2015-06-11 15:56:33 -04:00
iRouge 3054c04261 Add a utility to replace attributes without values by classes 2015-06-11 15:56:33 -04:00
iRouge 04132023e6 Clean-up markup in templates 2015-06-11 15:56:33 -04:00
iRouge 60bd406eac Add import to bower_components ngMaterial 2015-06-11 15:56:33 -04:00
iRouge 7dc7662adf Fix a broken tag in UIxPageEditorTemplate 2015-06-11 15:56:33 -04:00
iRouge 047138984a Fix an update version issue 2015-06-11 15:56:32 -04:00
iRouge 1a7bfee6b6 define base theme : add palette definitions to module initialization 2015-06-11 15:56:32 -04:00
iRouge ee4c1c0706 define theme 2015-06-11 15:56:32 -04:00
iRouge eea5cf8d0b Edit gitignore to ignore built API doc 2015-06-11 15:56:32 -04:00
iRouge 67076cdffb Update angular-material 2015-06-11 15:56:31 -04:00
Francis Lachapelle 80785304f1 JSONify [UIxCalendarProperties saveAction] 2015-06-11 15:56:31 -04:00
Francis Lachapelle c934b3d9c0 Improve API doc of UIxContactEditor 2015-06-11 15:54:15 -04:00
Francis Lachapelle 794c1999bc Add bower package "material-design-iconic-font"
We actually don't use "material-design-fonticons"
2015-06-11 15:54:14 -04:00
Francis Lachapelle aaed279c50 Extend JSON response of calendar properties 2015-06-11 15:54:14 -04:00
Francis Lachapelle bd6a430d3a Add bower package "material-design-fonticons" 2015-06-11 15:54:14 -04:00
iRouge 746afe0f60 Add scss devtools 2015-06-11 15:54:13 -04:00
iRouge 1b807bd6f3 Icons to the message-template toolbar 2015-06-11 15:54:13 -04:00
iRouge 6e26040e19 Icon component (zavoloklom.github.io/material-design-iconic-font/index.html) 2015-06-11 15:54:13 -04:00
iRouge 6fde77ac3f Bottombar visibility 2015-06-11 15:54:13 -04:00
iRouge 296e8f09e5 Reformat code (indent) 2015-06-11 15:54:12 -04:00
iRouge 3b43cabc68 Revert [a5ba017bc3] (Regression fix) 2015-06-11 15:54:12 -04:00
Francis Lachapelle cc34d56a0e FIx compilation of UIxMailUserRightsEditor.m 2015-06-11 15:54:11 -04:00
Francis Lachapelle 6080e800d7 Improve API documentation 2015-06-11 15:54:11 -04:00
Francis Lachapelle f2587aa385 JSON-based getter operations for Calendar module 2015-06-11 15:54:11 -04:00
Francis Lachapelle cf8c4c482d Add static method in iCalPerson for partstate 2015-06-11 15:54:11 -04:00
Francis Lachapelle 8986a18dac Review mail user rights editor 2015-06-11 15:54:10 -04:00
Francis Lachapelle 9829cd4027 Minor improvements to Contacts user rights editor 2015-06-11 15:54:10 -04:00
Francis Lachapelle 2e2da46d84 JSON-based Contacts list: don't escape HTML
HTML is now sanitized on the client-side by AngularJS.
2015-06-11 15:54:10 -04:00
Francis Lachapelle 37e91733ca Improve API documentation 2015-06-11 15:54:09 -04:00
Francis Lachapelle c29719f49d Remove newAction from UIx{Appointment,Task}Editor
We now rely on [UIxFolderActions newguidAction]
2015-06-11 15:54:09 -04:00
iRouge a5a2028da7 Contact Form 2015-06-11 15:54:08 -04:00
Francis Lachapelle b8ad85b2e2 Rename ng-templates to match names of .wox files 2015-06-11 15:54:08 -04:00
iRouge 50b45678e3 Regression fix 2015-06-11 15:54:08 -04:00
iRouge bcce9ac76a Report body inline style in Sass
Create bottombar component
2015-06-11 15:54:08 -04:00
Francis Lachapelle 137e8e7290 Add [iCal(Repeatable)EntityObject attribtues]
Those methods are used to get the common attributes of a iCalendar
component (vEvent or vTodo).
2015-06-11 15:54:07 -04:00
Francis Lachapelle a22195beec Add method [iCalTrigger asDictionary]
This method is used to get a JSON representation of an alarm trigger.
2015-06-11 15:54:07 -04:00
Francis Lachapelle 99af190e29 Remove used .ttf font files 2015-06-11 15:54:07 -04:00
Francis Lachapelle 01736318d8 Expose [iCalRecurrenceRule frequencyForValue:]
This method was missing from its header file.
2015-06-11 15:49:38 -04:00
Francis Lachapelle f0108a5069 Add method [iCalByDayMask asRuleArray]
This method is used to get a JSON representation of a BYDAY attribute of
a recurrence rule.
2015-06-11 15:49:37 -04:00
iRouge f4f53ec21e Report body inline style in Sass
Create bottombar component
2015-06-11 15:49:37 -04:00
iRouge 8e76c699dd Restructure Sass files and folders for proper application Sass development 2015-06-11 15:49:37 -04:00
iRouge 256d5e1bac Restructure Sass files and folders for proper application Sass development 2015-06-11 15:49:37 -04:00
iRouge 5217c0ac64 Restructure Sass files and folders for proper application Sass development 2015-06-11 15:49:36 -04:00
Francis Lachapelle a87bbc2de5 Enable toggleCtrl in Webmail 2015-06-11 15:49:33 -04:00
Francis Lachapelle d3f0abf3ef Simplify links to modules in UIxPageFrame.wox 2015-06-11 15:49:32 -04:00
Francis Lachapelle 9b73d4e9a5 Add user identification in sgSettings constant 2015-06-11 15:49:32 -04:00
Francis Lachapelle 4f632174ce Initial integration of ngMaterial in Contacts 2015-06-11 15:49:32 -04:00
Francis Lachapelle 263ff962a1 Update .gitignore 2015-06-11 15:49:32 -04:00
Francis Lachapelle b9414a77ab Move md theming config to ui.js (SOGo.UI) 2015-06-11 15:49:31 -04:00
iRouge f180617a3f Update Angular-material 2015-06-11 15:49:31 -04:00
iRouge 5919580b5d MODULE-TYPO
- Ajustements, message-view
2015-06-11 15:49:31 -04:00
iRouge 25213605bb MODULE-TYPO
- Sass set-up
- md-list
- md-theming (install)
2015-06-11 15:49:31 -04:00
iRouge c7309d7dc8 Views tweaking and todos 2015-06-11 15:49:28 -04:00
iRouge 7e2c60e495 Should had been include with preceeding 2015-06-11 15:49:28 -04:00
iRouge a98320783f Fought with ms-layouts (and they won) 2015-06-11 15:49:27 -04:00
iRouge c196e11fde Correct Bottom-Bar Buttons url 2015-06-11 15:49:27 -04:00
iRouge 414f0fbb26 Rows adjustments 2015-06-11 15:49:27 -04:00
Francis Lachapelle 8286fb68a4 Improve JSON API documentation 2015-06-11 15:49:27 -04:00
Francis Lachapelle 64615ea6fa Update .gitignore 2015-06-11 15:49:26 -04:00
iRouge aa64e86a43 Theme css & fonts 2015-06-11 15:49:26 -04:00
Francis Lachapelle 2cd3920532 Add angular-material as a git module 2015-06-11 15:49:22 -04:00
Benoit Favreault 23bd56a854 Edit gitignore (add .idea) 2015-06-11 15:49:21 -04:00
Francis Lachapelle 307d38c13d Populate bottom navigation bar 2015-06-11 15:49:21 -04:00
Francis Lachapelle f1490e7b92 Initial integration of ngMaterial in Webmail 2015-06-11 15:49:21 -04:00
Francis Lachapelle 8148209fc8 Improve instanciation of Resource in Card model 2015-06-11 15:49:21 -04:00
Francis Lachapelle f3597616ea Minor improvements to SOGo.ContactsUI ng module 2015-06-11 15:49:20 -04:00
Francis Lachapelle 34d7a3bf52 Improve error handling in JavaScript promises 2015-06-11 15:49:20 -04:00
Francis Lachapelle 1a1d51f427 Contacts module: optimize ng-repeat directives 2015-06-11 15:49:20 -04:00
Francis Lachapelle 0d9c78c60b Contacts module: add tooltips to icon buttons 2015-06-11 15:49:20 -04:00
Francis Lachapelle 2644dc9305 Webmail: add possibility to rename a mailbox 2015-06-11 15:49:20 -04:00
Francis Lachapelle c00e09f40b JSONify [UIxMailFolderActions renameFolderAction]
This commit also introduces an initial effort to have a documentation
for the JSON-based Web API following the apiDoc format
(http://apidocjs.com/).
2015-06-11 15:49:19 -04:00
Francis Lachapelle 3526cadc96 Webmail: add "load images" button to mail viewer 2015-06-11 15:49:19 -04:00
Francis Lachapelle c514cb64bc Webmail: Add forward button to messages 2015-06-11 15:49:19 -04:00
Francis Lachapelle 225c998a97 Add CC field to mail editor 2015-06-11 15:49:18 -04:00
Francis Lachapelle 90f07c28f9 JSONify [UIxMailActions forwardAction] 2015-06-11 15:49:18 -04:00
Francis Lachapelle ad012cfd60 Save message to IMAP server before saving plist
By first appending the message to the mailbox, we obtain the IMAP ID and
save it to the local plist. Having the message ID, we can delete it when
saving/appending a new version of the draft to the mailbox.
2015-06-11 15:49:18 -04:00
Francis Lachapelle 5bbdecbec2 Webmail: add reply button 2015-06-11 15:48:15 -04:00
Francis Lachapelle 9065a8038a JSONify [UIxMailActions replyToAll:] 2015-06-11 15:48:14 -04:00
Francis Lachapelle 8c2bcc92b5 Update JavaScript dependencies
Upgrading to AngularJS 1.3.
2015-06-11 15:48:14 -04:00
Francis Lachapelle bc86ff1e04 Fix position of new or renamed addressbook 2015-06-11 15:48:14 -04:00
Francis Lachapelle 4248cbcb16 Webmail: new option to create subfolder 2015-06-11 15:48:14 -04:00
Francis Lachapelle 2d076358d3 Improve Mail module
- JSONinfied mail folder create and delete actions
- restored sgFolderTree directive
- extended folder types with "shared" and "otherUsers"
- added mailbox creation at the account level
2015-06-11 15:48:14 -04:00
Francis Lachapelle 2d026b5ca5 JSONify [UIxMailFolderActions createFolderAction] 2015-06-11 15:48:13 -04:00
Francis Lachapelle b552882011 Fix template reference when creating new card 2015-06-11 15:42:35 -04:00
Francis Lachapelle de273973b4 Add wox templates to Mail and Contacts modules 2015-06-11 15:42:35 -04:00
Francis Lachapelle 3aec2828b4 Improve JavaScript of Mail module
- activated file uploader of mail editor
- renamed method $update to $reload
- automatically mark messages as read
- use ui-sref-active from ui-router instead of ng-class
2015-06-11 15:42:34 -04:00
Francis Lachapelle 533d7110c7 Improve webmail editor
- Fixed handling of saving drafts
- Fixed handling of message type (HTML/plain)
- Added primitive handling of attachments
2015-06-11 15:42:34 -04:00
Francis Lachapelle 32196b56db Fix short formatting of cards for mail editor 2015-06-11 15:42:34 -04:00
Francis Lachapelle 5db79ce218 Mail draft: use JSON in edit action 2015-06-11 15:42:33 -04:00
Francis Lachapelle 9e97c11401 Don't store draft on disk when not required 2015-06-11 15:42:33 -04:00
Francis Lachapelle af09691964 Auto-completion of mail addresses in mail editor 2015-06-11 15:42:33 -04:00
Francis Lachapelle fe41798b19 Fix handling of recipient addresses in mail editor 2015-06-11 15:42:33 -04:00
Francis Lachapelle 09d469927f Add tags input to edit mail recipients 2015-06-11 15:42:32 -04:00
Francis Lachapelle e01e0adac8 Fix mailto links in mail viewer 2015-06-11 15:42:32 -04:00
Francis Lachapelle fb63689573 Initial mail editor 2015-06-11 15:42:32 -04:00
Francis Lachapelle 74177e4d04 Change key for envelope addresses metadata 2015-06-11 15:40:27 -04:00
Francis Lachapelle ac06a4d4ce Fix detection of mailbox type 2015-06-11 15:40:26 -04:00
Francis Lachapelle fd40cb71ac Fix premature nil in JSON dict of UIxMailView.m 2015-06-11 15:40:26 -04:00
Francis Lachapelle df019f515f Webmail: normalize format of addresses in JSON 2015-06-11 15:40:26 -04:00
Francis Lachapelle 5a106e1613 Address formatter returns metadata as a dictionary 2015-06-11 15:40:26 -04:00
Francis Lachapelle 6d899008ca Improve SOGoDraftObject to avoid crash when saving 2015-06-11 15:40:25 -04:00
Francis Lachapelle ee1f56ad27 Improve sgFolderTree directive for mobile 2015-06-11 15:40:25 -04:00
Ludovic Marcotte 61e25184fc Added Delete and Export features for mailboxes. Initial work for the mobile version. 2015-06-11 15:40:24 -04:00
Francis Lachapelle 4e9a187c85 Fix resolution of mailboxes with a special purpose 2015-06-11 15:40:24 -04:00
Francis Lachapelle 60ae5f0eb2 Improve CSS of AddressBook module 2015-06-11 15:40:24 -04:00
Francis Lachapelle 28f7811e86 Improve CSS of AddressBook module 2015-06-11 15:40:24 -04:00
Francis Lachapelle 876b21437f Fix JavaScript of login page of mobile version 2015-06-11 15:40:23 -04:00
Francis Lachapelle 1ca1a8c249 Add mobile version to webmail and improvements 2015-06-11 15:40:23 -04:00
Francis Lachapelle cdd1131699 Add sgFocus directive + factory 2015-06-11 15:40:23 -04:00
Francis Lachapelle 6ce64828e8 AngularJS module SOGo.MailerUI 2015-06-11 15:40:23 -04:00
Francis Lachapelle 1fc15061ed Webmail: Use a relative date in messages list 2015-06-11 15:40:23 -04:00
Francis Lachapelle 0ee725b945 Contacts JS models: improve documentation 2015-06-11 15:40:22 -04:00
Francis Lachapelle 2f17e94fef Initial AngularJS version of Webmail (desktop) 2015-06-11 15:40:22 -04:00
Francis Lachapelle 1c9da902d3 Add JSON payload to webmail "headers" action 2015-06-11 15:40:22 -04:00
Ludovic Marcotte 7e11806bf9 We can now show links to the address book 2015-06-11 15:40:21 -04:00
Francis Lachapelle f2efd3fb49 Add new AngularJS modules
- external module for recursion
- external module for virtual scrolling
- internal module for common UI services
2015-06-11 15:40:21 -04:00
Francis Lachapelle 54444ddf3b AngularJS SOGo.UICommon module 2015-06-11 15:40:21 -04:00
Francis Lachapelle a64a8172be Webmail: include all accounts metadata 2015-06-11 15:40:20 -04:00
Francis Lachapelle 3b4731d6b7 Add virtual scrolling AngularJS module
Integrated virtual scrolling in contacts.
2015-06-11 15:40:20 -04:00
Francis Lachapelle 2cb5aecdcb Add sgFolderTree directive 2015-06-11 15:40:20 -04:00
Francis Lachapelle 277bdd2b1c Mailer: replace HTML view by JSON object 2015-06-11 15:40:20 -04:00
Francis Lachapelle bd9da51e83 Mailer: remove HTML from list of messages in JSON 2015-06-11 15:40:20 -04:00
Francis Lachapelle e5ce053a62 Convert allFoldersMetadata to a tree structure
When parsing the mailboxes list, we now generated a hierarchical
structure of the metadata. Each mailbox (a dictionary) can now have a
'children' key that is an array of mailboxes.
2015-06-11 15:40:20 -04:00
Francis Lachapelle ec56079635 Address Book module: Fix selection of card 2015-06-11 15:38:16 -04:00
Francis Lachapelle 78a349fecd Improve CSS of addressbooks list 2015-06-11 15:38:16 -04:00
Francis Lachapelle 8c7846a1fa Propagate theme in modules relative URLs 2015-06-11 15:38:16 -04:00
Francis Lachapelle 2af7e85f81 Move SOGoContactGCSFolder->save to SOGoGCSFolder 2015-06-11 15:38:16 -04:00
Francis Lachapelle 441834edbf Mobile version: move ng template 'menu'
The 'menu.html' AngularJS template is moved to UIxPageFrame.wox to be
used by all modules.
2015-06-11 15:38:15 -04:00
Ludovic Marcotte 28f9963f87 Adjusted the minimum version of Foundation 2015-06-11 15:38:15 -04:00
Ludovic Marcotte e964496759 Revert "Added required patch for previous commit"
This reverts commit 18a363a07b.
2015-06-11 15:38:15 -04:00
Francis Lachapelle ca14a0d4b3 Improve folder subscription widget 2015-06-11 15:38:14 -04:00
Francis Lachapelle 989cb0bca2 Fix overflow of subscriptions dropdown 2015-06-11 15:38:14 -04:00
Francis Lachapelle 25b0d872c0 Add addressbook subscriptions widget 2015-06-11 15:38:14 -04:00
Francis Lachapelle 4016fdd430 New AngularJS directives
sgDropdownContentToggle, sgUserTypeahead, sgSubscribe
2015-06-11 15:38:13 -04:00
Francis Lachapelle 55f0d5a959 Add verification of parameter for "foldersSearch" 2015-06-11 15:38:13 -04:00
Francis Lachapelle 6395d7cd6a New Resource.prototype.userResource function 2015-06-11 15:38:13 -04:00
Francis Lachapelle 7812e3757d Return properties of folder upon subscribe (JSON) 2015-06-11 15:38:12 -04:00
Francis Lachapelle 93540b4fc6 Improve ACL editor template 2015-06-11 15:38:12 -04:00
Francis Lachapelle c063832d0d Move ACL editor into a separate template
The user rights remain dynamic, based on the object type (addressbook vs
mailbox vs calendar).
2015-06-11 15:38:12 -04:00
Francis Lachapelle 97ce3e6b72 Multiple improvements regarding ACLs 2015-06-11 15:38:12 -04:00
Francis Lachapelle bd39312b19 Fix compilation warnings regarding ACLs
Also change the data structure of the JSON returned by [UIxAclEditor
aclsAction]. The user's displayName has been removed and replace by the
"cn" and "c_email" attributes. This will probably affect the Thunderbird
extensions.
2015-06-11 15:38:12 -04:00
Alexandre Cloutier 22faf5c285 refactor codes and applied comments 2015-06-11 15:38:11 -04:00
Alexandre Cloutier c82143fd2e Refactor acl object and applied comments 2015-06-11 15:38:11 -04:00
Alexandre Cloutier 9b026ebbcd Contacts - export addressBook 2015-06-11 15:38:10 -04:00
Alexandre Cloutier 4e8d3ce900 add dialog window for public and allusers access 2015-06-11 15:38:10 -04:00
Alexandre Cloutier df3ad5445c Modification for ContactsUserRightsEditor 2015-06-11 15:38:10 -04:00
Alexandre Cloutier 9113ca54f7 Created user-modal.js, code refactoring
Conflicts:

	UI/Common/UIxAclEditor.m
	UI/Common/UIxUserRightsEditor.m
	UI/Common/product.plist
	UI/Contacts/UIxContactsUserRightsEditor.m
	UI/Templates/ContactsUI/UIxContactFoldersView.wox
	UI/WebServerResources/js/Common/resource.js
	UI/WebServerResources/js/ContactsUI.js
2015-06-11 15:38:10 -04:00
Alexandre Cloutier f3c26671af GUI for mobile version of AclUsers
Conflicts:

	UI/Common/UIxAclEditor.m
	UI/Common/UIxUserRightsEditor.m
	UI/WebServerResources/js/Common/acl-model.js
2015-06-11 15:38:09 -04:00
Francis Lachapelle e4b1c0d5b9 Fix buttons in card editor 2015-06-11 15:27:09 -04:00
Francis Lachapelle 3364263247 Improve handling of contact lists 2015-06-11 15:27:09 -04:00
Francis Lachapelle 61c48d37e4 Fix creation of new contacts when editing a list 2015-06-11 15:27:09 -04:00
Alexandre Cloutier 5b83b0c7b6 Enable search by fullname and email 2015-06-11 15:27:09 -04:00
Alexandre Cloutier 7e43b13f21 GUI contact list creation 2015-06-11 15:27:08 -04:00
Francis Lachapelle 57791e384b Revert "Creation of aclUsers window"
This reverts commit ee5ef02f50.
2015-06-11 15:27:08 -04:00
Francis Lachapelle 63b20c4ba2 Revert "rebase modification"
This reverts commit 45c5bbd36a.
2015-06-11 15:27:08 -04:00
Francis Lachapelle e11eea7548 Revert "file lost through rebasing"
This reverts commit a952402c39.
2015-06-11 15:27:08 -04:00
Francis Lachapelle f7df4c1fa0 Allow reverting edition of addressbook name w/ESC 2015-06-11 15:27:07 -04:00
Francis Lachapelle 4457997276 AngularJS sgEscape directive for SOGo.UIDesktop 2015-06-11 15:27:07 -04:00
Francis Lachapelle 16628ab104 Fix installation of ionic JS in Gruntfile.js 2015-06-11 15:27:07 -04:00
Alexandre Cloutier 56b765c6a5 delete unused .png 2015-06-11 15:27:06 -04:00
Alexandre Cloutier 3bf1224c9b file lost through rebasing 2015-06-11 15:27:06 -04:00
Alexandre Cloutier edbd661104 rebase modification 2015-06-11 15:27:05 -04:00
Alexandre Cloutier 8ef222ab44 Creation of aclUsers window 2015-06-11 15:27:05 -04:00
Francis Lachapelle 654c8d26b7 Contact editor: fix cancel and reset buttons 2015-06-11 15:27:05 -04:00
Francis Lachapelle 31ea7a5ab5 Improve contact module models
- Fixed reset behavior
- Created an abstract state for the card viewer and editor
- Moved the card instance from the $rootScope.addressbook to the $scope
2015-06-11 15:27:05 -04:00
Francis Lachapelle 342441ecf8 Improve JavaScript coding style
To comitted code now passes the Airbnb pattern using jscs
2015-06-11 15:27:03 -04:00
Francis Lachapelle df37fd8b92 AddressBook JS model: rename $all to $findAll 2015-06-11 15:27:03 -04:00
Francis Lachapelle 32d732caf0 Update GNUmakefile for WebServerResources 2015-06-11 15:27:02 -04:00
Francis Lachapelle 3e2173db7a Improve JavaScript coding style
To comitted code now passes the Airbnb pattern using jscs
2015-06-11 15:27:02 -04:00
Francis Lachapelle 17ee62f4de Improve usage of ui.router with "resolve" 2015-06-11 15:27:02 -04:00
Francis Lachapelle f363257e51 Use angularModule variable in mobile templates 2015-06-11 15:27:02 -04:00
Francis Lachapelle 865bf096f5 Don't add an empty category to a contact 2015-06-11 15:27:01 -04:00
Francis Lachapelle c4802df024 Remove debugging output 2015-06-11 15:27:01 -04:00
Francis Lachapelle 130194a740 Update GNUmakefile for WebServerResources 2015-06-11 15:27:01 -04:00
Francis Lachapelle 21f63c15fc Cleanup of Web server resources 2015-06-11 15:27:00 -04:00
Francis Lachapelle bb9e8c0348 Remove trailing slash from template theme name 2015-06-11 15:26:38 -04:00
Francis Lachapelle 16cd231ce3 New .button.outline CSS style 2015-06-11 15:26:37 -04:00
Francis Lachapelle 20497e49e3 Add creation & deletion of addressbooks 2015-06-11 15:26:37 -04:00
Francis Lachapelle b97bf2b25f JSON'ed [UIxParentFolderActions createFolder] 2015-06-11 15:26:37 -04:00
Francis Lachapelle 4f7a5d6ef5 Rename ng module SOGo.RootPage to SOGo.MainUI 2015-06-11 15:26:37 -04:00
Francis Lachapelle f321abc63a Desktop list editor, mobile contact editor 2015-06-11 15:26:36 -04:00
Francis Lachapelle 4b0ef6086a Cleanup contact and list viewers 2015-06-11 15:26:36 -04:00
Francis Lachapelle b57a8163fe Add link to switch to mobile version 2015-06-11 15:26:36 -04:00
Francis Lachapelle 86a6f5c67b Add Angular module assignation in page frame 2015-06-11 15:26:35 -04:00
Francis Lachapelle e40b42c213 Move setAttributes from NGVCard to UIx*Editors 2015-06-11 15:26:35 -04:00
Francis Lachapelle 2f6bc1c4ff Update .gitignore 2015-06-11 15:25:31 -04:00
Francis Lachapelle 958f0b4958 Contacts: Add possibility to add/delete objects 2015-06-11 15:25:31 -04:00
Francis Lachapelle 0c720bb902 New ng-module for common desktop widgets 2015-06-11 15:25:30 -04:00
Francis Lachapelle 34b3f2ac0c Improve build scripts of Web frontend 2015-06-11 15:25:30 -04:00
Francis Lachapelle ef4de28ed4 ContactView: lowercase the card tag 2015-06-11 15:25:30 -04:00
Francis Lachapelle c98d580b29 Return contact UID when saving contact 2015-06-11 15:25:30 -04:00
Francis Lachapelle a2460f9368 Add "newguid" action accessible from any folder 2015-06-11 15:25:29 -04:00
Francis Lachapelle c0eac04437 Adapt SOGoContactGCSFolder contact representation
To match [UIxContactViewer dataAction].
2015-06-11 15:25:29 -04:00
Francis Lachapelle 7f9be26541 Improve localization func to support placeholders 2015-06-11 15:25:28 -04:00
Francis Lachapelle c8ea3da5bc Improve detection of theme-specific JavaScript 2015-06-11 15:25:28 -04:00
Francis Lachapelle a70287870b Improve build scripts of Web frontend 2015-06-11 15:25:28 -04:00
Francis Lachapelle e4ec5313db Improve grunt tasks to copy fonts files 2015-06-11 15:25:27 -04:00
Francis Lachapelle a07d918fbf Add category to CardElement
New method to return a JSON representation of the element.
2015-06-11 15:25:27 -04:00
Francis Lachapelle 8da98367db Fix path to ionic scss files 2015-06-11 15:25:26 -04:00
Francis Lachapelle af5489bf57 Fix redirection URLs in JavaScript 2015-06-11 15:25:26 -04:00
Francis Lachapelle 54103124e8 Restore/move missing images 2015-06-11 15:25:25 -04:00
Francis Lachapelle 459c040d8a Initial mobile theme using ionic 2015-06-11 15:25:25 -04:00
Francis Lachapelle f248f81583 Improve template of Contacts module 2015-06-11 15:25:25 -04:00
Francis Lachapelle a6d8dcc5b6 Common JavaScript utlity functions 2015-06-11 15:25:25 -04:00
Francis Lachapelle b4d6667f88 Remove duplicated method 2015-06-11 15:25:24 -04:00
Francis Lachapelle 77b80e27c6 Add support for themes 2015-06-11 15:25:24 -04:00
Francis Lachapelle 16d04e998f Improve Sass of SOGoRootPage & Contacts modules 2015-06-11 15:25:24 -04:00
Francis Lachapelle d52d7582ca Improve build scripts of Web frontend 2015-06-11 15:25:23 -04:00
Francis Lachapelle e817a3b3d0 Improve JavaScript of SOGoRootPage module 2015-06-11 15:25:23 -04:00
Francis Lachapelle 2d6f8e4ea6 Improve JavaScript for Contacts module 2015-06-11 15:25:23 -04:00
Francis Lachapelle fbe412db08 Allow js/img/css subfolders in WebServerResources 2015-06-11 15:25:22 -04:00
Francis Lachapelle 24f01e72c2 JSON-based operations for root page 2015-06-11 15:25:22 -04:00
Francis Lachapelle 6970acfaa4 AngularJS-based templates 2015-06-11 15:25:22 -04:00
Francis Lachapelle 0a49031b01 JSON-based operations on contacts 2015-06-11 15:23:27 -04:00
Francis Lachapelle 29e147ee86 Initial Sass and JavaScript files 2015-06-11 15:19:22 -04:00
Francis Lachapelle c21ec86365 Icons font 2015-06-11 15:19:21 -04:00
Francis Lachapelle 825c45f872 Initial JavaScript models for addressbooks module 2015-06-11 15:19:21 -04:00
Francis Lachapelle 3ad4352ffe Initial cleanup of Web server resources 2015-06-11 15:19:21 -04:00
Alexandre Cloutier a45dc96885 applied comments 2015-06-11 15:17:26 -04:00
Alexandre Cloutier b041ec6e65 activeTasks updates dynamically 2015-06-11 15:15:39 -04:00
Alexandre Cloutier 257d731e04 multiColumns print view + bugFixes 2015-06-11 15:15:39 -04:00
Alexandre Cloutier 720a946d39 The multiColumns view 2015-06-11 15:15:39 -04:00
Pablo Carranza 2d5f2d88fa Revert "Started Ubuntu w/PostgreSQL tutorial"
This reverts commit 51d2372976.
2013-11-05 22:32:35 -06:00
Pablo Carranza eb5ca2cba6 Revert "Update ubuntu_postgresql.md"
This reverts commit 92d68de332.
2013-11-05 22:31:54 -06:00
Pablo Carranza 54cd55ebef Update README.md 2013-10-19 13:15:13 -05:00
Pablo Carranza 5f4ab6eaaf Update README.md 2013-10-19 13:11:36 -05:00
Pablo Carranza 1d8e6b6295 Update README.md 2013-10-19 13:03:40 -05:00
Pablo Carranza aa791b2571 Create README.md 2013-10-19 12:42:31 -05:00
Pablo Carranza 92d68de332 Update ubuntu_postgresql.md 2013-10-19 12:25:19 -05:00
Pablo Carranza 51d2372976 Started Ubuntu w/PostgreSQL tutorial 2013-10-19 12:17:39 -05:00
1067 changed files with 31153 additions and 70659 deletions

No files matched your search

+27 -12
View File
@@ -1,18 +1,33 @@
config.make
tags
*._*
*/obj/
*._*
*/*/obj/
*/*/*/obj/
*/*/*.SOGo/
*.css
*.pyc
*.sax
*.wox/
*.swp
*.wox/
*/*/*.SOGo/
*/*/*/obj/
*/*/obj/
*/obj/
.scss-lint-config.yml_
Documentation/*.docbook
Documentation/*.pdf
SoObjects/SOGo/SOGo.framework/
SoObjects/SOGo/derived_src/
Tests/*/config.py
*.pyc
._*
Documentation/*.docbook
Documentation/*.pdf
UI/WebServerResources/.bowerrc
UI/WebServerResources/.sass-cache/
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/vendor/
UI/WebServerResources/node_modules/
UI/WebServerResources/scss/.sass-cache/
config.make
doc
tags
+3
View File
@@ -0,0 +1,3 @@
[submodule "UI/WebServerResources/angular-material"]
path = UI/WebServerResources/angular-material
url = https://github.com/angular/material
+9 -46
View File
@@ -133,7 +133,6 @@ 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];
@@ -160,7 +159,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSString *nameInCache;
if (theFolderType == ActiveSyncMailFolder)
nameInCache = [imapFolderGUIDS objectForKey: [theCollection nameInContainer]];
nameInCache= [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]];
else
{
NSString *component_name;
@@ -171,7 +170,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;
@@ -674,7 +673,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSDictionary *component;
NSArray *allComponents;
BOOL updated, initialLoadInProgress;
BOOL updated;
int deleted, return_count;
if (theFolderType == ActiveSyncContactFolder)
@@ -684,20 +683,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
component_name = @"vtodo";
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 = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
allComponents = [allComponents sortedArrayUsingDescriptors: [NSArray arrayWithObjects: [[NSSortDescriptor alloc] initWithKey: @"c_lastmodified" ascending:YES], nil]];
@@ -861,35 +847,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSMutableArray *allCacheObjects, *sortedBySequence;
SOGoMailObject *mailObject;
NSArray *allMessages, *a;
NSArray *allMessages;
int j, k, return_count, highestmodseq;
BOOL found_in_cache, initialLoadInProgress;
int j, k, return_count;
BOOL found_in_cache;
initialLoadInProgress = NO;
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];
allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
max = [allMessages count];
allCacheObjects = [NSMutableArray array];
@@ -948,7 +911,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSString *lastSequence;
more_available = YES;
lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? [NSString stringWithFormat:@"%d", highestmodseq] : [aCacheObject sequence]);
lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? @"1" : [aCacheObject sequence]);
*theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", [aCacheObject uid], lastSequence];
//NSLog(@"Reached windowSize - lastUID will be: %@", *theLastServerKey);
DESTROY(pool);
-1
View File
@@ -37,7 +37,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@interface SOGoActiveSyncDispatcher : NSObject
{
NSURL *folderTableURL;
NSDictionary *imapFolderGUIDS;
id context;
}
+11 -20
View File
@@ -151,14 +151,12 @@ static BOOL debugOn = NO;
debugOn = [[SOGoSystemDefaults sharedSystemDefaults] easDebugEnabled];
folderTableURL = nil;
imapFolderGUIDS = nil;
return self;
}
- (void) dealloc
{
RELEASE(folderTableURL);
RELEASE(imapFolderGUIDS);
[super dealloc];
}
@@ -231,27 +229,23 @@ static BOOL debugOn = NO;
SOGoMailAccounts *accountsFolder;
SOGoMailAccount *accountFolder;
SOGoUserFolder *userFolder;
NSDictionary *imapGUIDs;
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] ;
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] ;
}
return theIdToTranslate;
}
//
//
//
@@ -944,7 +938,6 @@ static BOOL debugOn = NO;
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
[o save];
command_count++;
@@ -1030,7 +1023,6 @@ static BOOL debugOn = NO;
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
}
[o save];
@@ -1054,7 +1046,6 @@ static BOOL debugOn = NO;
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
}
[o save];
@@ -1213,7 +1204,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 initialLoad: NO];
allMessages = [currentCollection syncTokenFieldsWithProperties: nil matchingSyncToken: syncKey fromDate: filter];
count = [allMessages count];
-1103
View File
File diff suppressed because it is too large. Load diff
-5655
View File
File diff suppressed because it is too large. Load diff
+157
View File
@@ -0,0 +1,157 @@
https://wiki.inverse.ca/focus/SOGo/CodingStyle
https://wiki.inverse.ca/focus/SOGo/D%C3%A9veloppement
https://wiki.inverse.ca/focus/PacketFence/Devel/BestPractices
UI and UX
---------
* http://www.google.com/design/spec/material-design/
* http://goodui.org/
* Try Repeating Your Primary Action instead of showing it just once.
* Try Distinct Clickable/Selected Styles instead of blurring them.
* Try Undos instead of prompting for confirmation.
* Try More Contrast instead of similarity.
* Try Exposing Options instead of hiding them.
* Try Showing State instead of being state agnostic.
* Try Direct Manipulation instead of contextless menus.
* Try Exposing Fields instead of creating extra pages.
* Try Transitions instead of showing changes instantly.
* Try Fewer Borders instead of wasting attention.
* Try Designing For Zero Data instead of just data heavy cases.
* Try Consistency instead of making people relearn.
* Try Conventions instead of reinventing the wheel.
* Try Visual Hierarchy instead of dullness.
* Try Grouping Related Items instead of disordering.
* Try Inline Validation instead of delaying errors.
* Try Forgiving Inputs instead of being strict with data.
* Try Bigger Click Areas instead of tiny ones.
* Try Faster Load Times instead of making people wait.
* Try Keyboard Shortcuts instead of buttons only.
* Try Upfront Progress instead of starting with a blank.
* Try Softer Prompts instead of modal windows.
* Try Multifunctional Controls instead of more parts.
* Try Icon Labels instead of opening for interpretation.
* http://designmodo.com/create-style-guides/
* http://uxmyths.com/
Frameworks & Tools
------------------
GNUstep
~~~~~~~
SOPE
~~~~
AngularJS
~~~~~~~~~
* https://angularjs.org/
* http://angular-ui.github.io/ui-router/
* https://www.airpair.com/angularjs/posts/angularjs-tutorial
Angular Material
~~~~~~~~~~~~~~~~
* https://material.angularjs.org/
Lo-Dash
~~~~~~~
* http://lodash.com/
* http://underscorejs.org/
Code Style
----------
Objective-C
~~~~~~~~~~~
* To document the Web APIs, we follow the http://apidocjs.com/[APIDOC] standards.
apidoc -f ".*\\.m" -i UI -o Documentation/JSON-API
HTML
~~~~
* Localize your strings:
<var:string label:value="I'm localized!"/>
* Localize your attributes:
<input type="text" label:placeholder="I'm localized!"/>
* Reuse existing localized strings as much as possible. Otherwise don't forget to update the English Localizable.strings file of the appropriate module.
JavaScript
~~~~~~~~~~
* http://eslint.org/
* https://github.com/toddmotto/angularjs-styleguide
* https://github.com/angular/material/blob/master/docs/guides/CODING.md
* We conform to https://github.com/airbnb/javascript[Airbnb coding standards]. Use https://github.com/jscs-dev/node-jscs[jscs] with the 'airbnb' preset to validate your code style:
jscs -p airbnb *.js
* For documentation, we follow the http://usejsdoc.org/[JSDoc3] standards:
jsdoc UI/WebServerResources/js/{Appointments,Common,Contacts,Mailer}/*.js -d Documentation/JavaScript-API
Models (facades)
^^^^^^^^^^^^^^^^
////
http://trochette.github.io/Angular-Design-Patterns-Best-Practices/
////
* Move business logic into models and share them.
* Keep it simple, separate server interaction and error handling from the model. That way model only handle data processing and code is easier to maintain.
SASS/CSS
~~~~~~~~
* http://componentcss.com/
* https://github.com/styleguide/css
* https://smacss.com/
* For documentation, we follow http://sassdoc.com/[SassDoc] annotations.
* We need the SASS files of Angular Material to build our CSS. The git repository of Angular Material is included as a submodule of SOGo:
git submodule init
git submodule update
* FlexBox compatibility http://caniuse.com/#feat=flexbox
Fonts
^^^^^
* We build a custom icons font using the online app https://icomoon.io/app/#/select[icomoon].
* http://www.carrois.com/fira-4-0/
* Material Design Iconic Font http://zavoloklom.github.io/material-design-iconic-font/icons.html
Version Control
---------------
* https://devcharm.com/articles/46/improve-your-git-workflow/
* Each commit should cover only one thing;
* Begin the commit message with a single short (less than 50 characters) line summarizing the change, followed by a blank line and then a more thorough description;
* When fixing a bug, commit to the devel branch as well as the maintenance branch of the latest release version (named maintenance/x.y). When a ticket is associated to the bug, add to the description a line saying *Fixes #1234*.
* `git pull` may introduce http://stackoverflow.com/questions/15316601/in-what-cases-could-git-pull-be-harmful[inconsistencies and problems]. Replace it with the following alias:
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
Testing
-------
* https://github.com/angular/protractor
* http://karma-runner.github.io/
JSON API
~~~~~~~~
One practical way to test the JSON API is to use `curl`. To do so, you need to enable `SOGoTrustProxyAuthentication` and configure HTTP authentifcation in Apache. You can pipe the result to [jq](http://stedolan.github.io/jq/) to nicely format and filter the output:
curl -u username:password http://localhost/SOGo/so/username/Calendar/calendarslist | jq '.'
curl -u username:password -H 'Content-Type: application/json' -d '{}' http://localhost/SOGo/so/francis/Calendar/personal/71B6-54904400-1-7C308500.ics/save
@@ -233,7 +233,6 @@ 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -317,7 +316,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)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -396,7 +395,7 @@ samba-tool user setexpiry administrator --noexpiry
----
You might consider changing the realm and domain used, to suit your
environment.
enviroment.
You might also have to
remove `/etc/samba/smb.conf` prior running this command.
@@ -442,7 +441,7 @@ Your Samba 4 configuration file should look like this:
OpenChange Configuration
~~~~~~~~~~~~~~~~~~~~~~~~
Since v2.2, OpenChange stores its metadata in MySQL so you need to have it installed.
OpenChange 2.2 stores its metadata in MySQL so you need to have it installed.
First, create the OpenChange MySQL user:
@@ -513,11 +512,9 @@ 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:
@@ -548,11 +545,9 @@ 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`.
@@ -568,10 +563,8 @@ 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
@@ -587,12 +580,10 @@ 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
@@ -822,7 +813,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:
`openchange_user_cleanup username`. See the usage output for additional options.
`python 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
+3 -3
View File
@@ -1,7 +1,7 @@
<!-- TODO have the build system take care of this -->
<releaseinfo>Version 2.3.2 - September 2015</releaseinfo>
<subtitle>for version 2.3.</subtitle>
<date>2015-09-16</date>
<releaseinfo>Version 2.3.1 - July 2015</releaseinfo>
<subtitle>for version 2.3.1</subtitle>
<date>2015-07-23</date>
<legalnotice>
<para>Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".</para>
@@ -13,6 +13,6 @@
// TODO have the build system take care of this
:release_version: 2.3.2
:release_version: 2.3.1
// vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab:
+5 -20
View File
@@ -1,26 +1,12 @@
2.3.2 (2015-09-16)
2.3.x (2015-MM-DD)
------------------
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)
- 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)
- 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)
2.3.1 (2015-07-23)
------------------
@@ -33,7 +19,6 @@ 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;
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
- (int) setReadFlag: (uint8_t) flag
{
return MAPISTORE_SUCCESS;
}
-136
View File
@@ -22,7 +22,6 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSData.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
@@ -35,7 +34,6 @@
#import "MAPIStoreDBFolder.h"
#import "MAPIStoreDBMessage.h"
#import "MAPIStoreTypes.h"
#import "NSData+MAPIStore.h"
#import "NSObject+MAPIStore.h"
#import "NSString+MAPIStore.h"
@@ -106,105 +104,6 @@
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?
//
@@ -267,9 +166,6 @@
[properties setObject: [NSNumber numberWithUnsignedLongLong: newVersion]
forKey: @"version"];
/* Update PredecessorChangeList accordingly */
[self _updatePredecessorChangeList];
[self logWithFormat: @"%d props in dict", [properties count]];
[sogoObject save];
@@ -313,36 +209,4 @@
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,7 +121,6 @@
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;
+8 -20
View File
@@ -642,7 +642,6 @@ 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
{
@@ -670,18 +669,15 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
[sourceMsg copyToMessage: destMsg inMemCtx: memCtx];
if (targetPredecessorChangeList)
if (targetChangeKey)
{
property.ulPropTag = PidTagPredecessorChangeList;
property.value.bin = *targetPredecessorChangeList;
property.ulPropTag = PidTagChangeKey;
property.value.bin = *targetChangeKey;
aRow.cValues = 1;
aRow.lpProps = &property;
rc = [destMsg addPropertiesFromRow: &aRow];
if (rc != MAPISTORE_SUCCESS)
{
[self errorWithFormat: @"Cannot add PredecessorChangeList on move"];
goto end;
}
goto end;
}
[destMsg save: memCtx];
if (!wantCopy)
@@ -700,7 +696,6 @@ 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
{
@@ -710,7 +705,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
NSString *oldMessageURL;
MAPIStoreMapping *mapping;
SOGoUser *ownerUser;
struct Binary_r *targetChangeKey, *targetPredecessorChangeList;
struct Binary_r *targetChangeKey;
//TALLOC_CTX *memCtx;
//memCtx = talloc_zero (NULL, TALLOC_CTX);
@@ -731,21 +726,14 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
if (oldMessageURL)
{
[oldMessageURLs addObject: oldMessageURL];
if (targetChangeKeys && targetPredecessorChangeList)
{
targetChangeKey = targetChangeKeys[count];
targetPredecessorChangeList = targetPredecessorChangeLists[count];
}
if (targetChangeKeys)
targetChangeKey = targetChangeKeys[count];
else
{
targetChangeKey = NULL;
targetPredecessorChangeList = NULL;
}
targetChangeKey = NULL;
rc = [self _moveCopyMessageWithMID: srcMids[count]
fromFolder: sourceFolder
withMID: targetMids[count]
andChangeKey: targetChangeKey
andPredecessorChangeList: targetPredecessorChangeList
wantCopy: wantCopy
inMemCtx: memCtx];
}
+1 -2
View File
@@ -42,8 +42,7 @@
/* synchronisation */
- (BOOL) synchroniseCache;
- (void) updateVersionsForMessageWithKey: (NSString *) messageKey
withChangeKey: (NSData *) oldChangeKey
andPredecessorChangeList: (NSData *) pcl;
withChangeKey: (NSData *) newChangeKey;
- (NSNumber *) lastModifiedFromMessageChangeNumber: (NSString *) changeNumber;
- (NSString *) changeNumberForMessageWithKey: (NSString *) messageKey;
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey;
+18 -85
View File
@@ -75,7 +75,6 @@ static Class NSNumberK;
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
inContainer: dbFolder]);
[versionsMessage setObjectType: MAPIInternalCacheObject];
[versionsMessage reloadIfNeeded];
}
- (void) dealloc
@@ -262,6 +261,7 @@ static Class NSNumberK;
*/
- (void) _setChangeKey: (NSData *) changeKey
forMessageEntry: (NSMutableDictionary *) messageEntry
inChangeListOnly: (BOOL) inChangeListOnly
{
struct XID *xid;
NSString *guid;
@@ -270,15 +270,19 @@ static Class NSNumberK;
NSMutableDictionary *changeList;
xid = [changeKey asXIDInMemCtx: NULL];
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
guid = [NSString stringWithGUID: &xid->GUID];
globCnt = [NSData dataWithBytes: xid->Data length: xid->Size];
talloc_free (xid);
/* 1. set change key association */
changeKeyDict = [NSDictionary dictionaryWithObjectsAndKeys: guid, @"GUID",
globCnt, @"LocalId",
nil];
[messageEntry setObject: changeKeyDict forKey: @"ChangeKey"];
if (!inChangeListOnly)
{
/* 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"];
@@ -292,77 +296,6 @@ 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)
@@ -532,7 +465,8 @@ 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];
[self _setChangeKey: changeKey forMessageEntry: messageEntry
inChangeListOnly: NO];
}
now = [NSCalendarDate date];
@@ -549,13 +483,12 @@ static Class NSNumberK;
}
- (void) updateVersionsForMessageWithKey: (NSString *) messageKey
withChangeKey: (NSData *) oldChangeKey
andPredecessorChangeList: (NSData *) pcl
withChangeKey: (NSData *) newChangeKey
{
NSMutableDictionary *messages, *messageEntry;
[self synchroniseCache];
if (oldChangeKey || pcl)
if (newChangeKey)
{
messages = [[versionsMessage properties] objectForKey: @"Messages"];
messageEntry = [messages objectForKey: messageKey];
@@ -563,8 +496,8 @@ static Class NSNumberK;
[NSException raise: @"MAPIStoreIOException"
format: @"no version record found for message '%@'",
messageKey];
[self _updatePredecessorChangeList: pcl forMessageEntry: messageEntry
withOldChangeKey: oldChangeKey];
[self _setChangeKey: newChangeKey forMessageEntry: messageEntry
inChangeListOnly: YES];
[versionsMessage save];
}
}
+3 -6
View File
@@ -209,16 +209,13 @@
- (void) updateVersions
{
/* Update ChangeKey and PredecessorChangeList on message's save */
NSData *newChangeKey, *predecessorChangeList;
NSData *newChangeKey;
newChangeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
predecessorChangeList = [properties objectForKey: MAPIPropertyKey (PR_PREDECESSOR_CHANGE_LIST)];
[(MAPIStoreGCSFolder *) container
updateVersionsForMessageWithKey: [self nameInContainer]
withChangeKey: newChangeKey
andPredecessorChangeList: predecessorChangeList];
updateVersionsForMessageWithKey: [self nameInContainer]
withChangeKey: newChangeKey];
}
@end
+15 -4
View File
@@ -30,7 +30,6 @@
#import <SOGo/NSArray+Utilities.h>
#import <Mailer/SOGoMailBodyPart.h>
#import <Mailer/SOGoMailObject.h>
#import <Mailer/NSDictionary+Mail.h>
#import "MAPIStoreTypes.h"
#import "MAPIStoreMailMessage.h"
@@ -109,7 +108,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];
@@ -118,7 +117,19 @@
- (NSString *) _fileName
{
return [bodyInfo 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;
}
- (int) getPidTagAttachLongFilename: (void **) data
@@ -167,7 +178,7 @@
- (int) getPidTagAttachContentId: (void **) data
inMemCtx: (TALLOC_CTX *) memCtx
{
{
*data = [[bodyInfo objectForKey: @"bodyId"]
asUnicodeInMemCtx: memCtx];
-2
View File
@@ -52,8 +52,6 @@
- (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;
+10 -98
View File
@@ -68,8 +68,6 @@
static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
#include <gen_ndr/exchange.h>
#undef DEBUG
#include <util/attr.h>
#include <libmapi/libmapi.h>
@@ -111,7 +109,6 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
inContainer: dbFolder]);
[versionsMessage setObjectType: MAPIInternalCacheObject];
[versionsMessage reloadIfNeeded];
}
- (BOOL) ensureFolderExists
@@ -519,44 +516,6 @@ _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
{
@@ -567,8 +526,8 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
NSMutableDictionary *changeList;
xid = [changeKey asXIDInMemCtx: NULL];
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
guid = [NSString stringWithGUID: &xid->GUID];
globCnt = [NSData dataWithBytes: xid->Data length: xid->Size];
talloc_free (xid);
/* 1. set change key association */
@@ -965,7 +924,8 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
return changeNumber;
}
- (NSMutableDictionary *) _messageEntryFromMessageKey: (NSString *) messageKey
- (void) setChangeKey: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey
{
NSMutableDictionary *messages, *messageEntry;
NSString *messageUid;
@@ -976,7 +936,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
messageEntry = [messages objectForKey: messageUid];
if (!messageEntry)
{
[self warnWithFormat: @"attempting to synchronise to get the message entry for "
[self warnWithFormat: @"attempting to synchronise to set the change key for "
@"this message %@", messageKey];
synced = [self synchroniseCacheForUID: messageUid];
if (synced)
@@ -987,57 +947,11 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
abort ();
}
}
return messageEntry;
}
- (void) setChangeKey: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey
{
[self _setChangeKey: changeKey
forMessageEntry: [self _messageEntryFromMessageKey: messageKey]];
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
[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;
@@ -1303,7 +1217,6 @@ _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
@@ -1318,13 +1231,12 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
NSDictionary *result;
NSUInteger count;
NSArray *a;
NSData *changeList;
NSData *changeKey;
if (![sourceFolder isKindOfClass: [MAPIStoreMailFolder class]])
return [super moveCopyMessagesWithMIDs: srcMids andCount: midCount
fromFolder: sourceFolder withMIDs: targetMids
andChangeKeys: targetChangeKeys
andPredecessorChangeLists: targetPredecessorChangeLists
wantCopy: wantCopy
inMemCtx: memCtx];
@@ -1413,11 +1325,11 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
[self synchroniseCache];
for (count = 0; count < midCount; count++)
{
changeList = [NSData dataWithBinary: targetPredecessorChangeLists[count]];
changeKey = [NSData dataWithBinary: targetChangeKeys[count]];
messageKey = [NSString stringWithFormat: @"%@.eml",
[destUIDs objectAtIndex: count]];
[self _updatePredecessorChangeListWith: changeList
forMessageEntry: [self _messageEntryFromMessageKey: messageKey]];
[self setChangeKey: changeKey
forMessageWithKey: messageKey];
}
}
+26 -38
View File
@@ -40,7 +40,6 @@
#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"
@@ -197,7 +196,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"];
@@ -530,7 +529,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
else
stringValue = @"";
*data = [stringValue asUnicodeInMemCtx: memCtx];
return MAPISTORE_SUCCESS;
}
@@ -625,7 +624,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
NSDictionary *coreInfos;
NSArray *flags;
unsigned int v = 0;
coreInfos = [sogoObject fetchCoreInfos];
flags = [coreInfos objectForKey: @"flags"];
@@ -637,7 +636,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
if ([[self attachmentKeys]
count] > 0)
v |= MSGFLAG_HASATTACH;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -657,7 +656,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
v = 2;
else
v = 0;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -669,15 +668,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;
@@ -756,7 +755,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.
@@ -810,7 +809,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
entryId = MAPIStoreExternalEntryId (cn, email);
*data = [entryId asBinaryInMemCtx: memCtx];
rc = MAPISTORE_SUCCESS;
}
else
@@ -967,15 +966,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;
@@ -1164,14 +1163,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;
@@ -1518,7 +1517,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++;
@@ -1526,7 +1525,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++;
@@ -1534,7 +1533,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++;
@@ -1567,9 +1566,12 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
withPrefix: (NSString *) keyPrefix
{
NSArray *parts;
NSDictionary *parameters;
NSUInteger count, max;
if ([[bodyInfo filename] length] > 0)
parameters = [[bodyInfo objectForKey: @"disposition"]
objectForKey: @"parameterList"];
if ([[parameters objectForKey: @"filename"] length] > 0)
{
if ([keyPrefix length] == 0)
keyPrefix = @"0";
@@ -1643,29 +1645,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
return attachment;
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
- (int) 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];
modified = alreadyRead;
}
[sogoObject removeFlags: imapFlag];
else
{
[sogoObject addFlags: imapFlag];
modified = !alreadyRead;
}
if (modified)
[(MAPIStoreMailFolder *)[self container] synchroniseCache];
[sogoObject addFlags: imapFlag];
return MAPISTORE_SUCCESS;
}
@@ -1706,7 +1694,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
return nil;
}
- (void) save: (TALLOC_CTX *) memCtx
- (void) save: (TALLOC_CTX *) memCtx
{
NSNumber *value;
+7 -22
View File
@@ -161,30 +161,15 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK;
//[self logWithFormat: @"change number from oxcfxics: %.16lx", [value unsignedLongLongValue]];
//[self logWithFormat: @" modseq: %.16lx", [modseq unsignedLongLongValue]];
if (modseq)
{
if (res->relop == RELOP_GT)
modseq = [NSNumber numberWithUnsignedLongLong:
[modseq unsignedLongLongValue] + 1];
}
modseq = [NSNumber numberWithUnsignedLongLong:
[modseq unsignedLongLongValue] + 1];
else
modseq = [NSNumber numberWithUnsignedLongLong: 0];
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;
}
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"MODSEQ"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
value: modseq];
[*qualifier autorelease];
rc = MAPIRestrictionStateNeedsEval;
}
break;
+11 -25
View File
@@ -34,7 +34,6 @@
#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>
@@ -286,7 +285,7 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" };
version = [properties objectForKey: @"version"];
return (version
? [version unsignedLongLongValue]
? exchange_globcnt ([version unsignedLongLongValue])
: ULLONG_MAX);
}
@@ -1111,8 +1110,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
- (void) save: (TALLOC_CTX *) memCtx
{
BOOL updatedMetadata;
NSString *folderName, *flag, *newIdString, *messageKey, *changeNumber;
NSString *folderName, *flag, *newIdString, *messageKey;
NSData *changeKey, *messageData;
NGImap4Connection *connection;
NGImap4Client *client;
@@ -1148,33 +1146,21 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
[sogoObject setNameInContainer: messageKey];
[mapping registerURL: [self url] withID: mid];
/* 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 */
/* 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 */
[client unselect];
[client select: folderName];
[(MAPIStoreMailFolder *) container synchroniseCache];
changeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
if (changeKey)
{
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"];
[(MAPIStoreMailFolder *) container
setChangeKey: changeKey
forMessageWithKey: messageKey];
}
}
+1 -1
View File
@@ -63,7 +63,7 @@
withAID: (uint32_t) aid;
- (int) getAttachmentTable: (MAPIStoreAttachmentTable **) tablePtr
andRowCount: (uint32_t *) countPtr;
- (enum mapistore_error) setReadFlag: (uint8_t) flag;
- (int) setReadFlag: (uint8_t) flag;
- (enum mapistore_error) saveMessage: (TALLOC_CTX *) memCtx;
- (NSArray *) activeContainerMessageTables;
+2 -3
View File
@@ -549,12 +549,11 @@ rtf2html (NSData *compressedRTF)
}
[self save: memCtx];
/* We make sure that any change-related properties are removed from the
/* We make sure that any change-related properties are removes 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])
{
@@ -919,7 +918,7 @@ rtf2html (NSData *compressedRTF)
return [self getNo: data inMemCtx: memCtx];;
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
- (int) setReadFlag: (uint8_t) flag
{
// [self subclassResponsibility: _cmd];
+1 -3
View File
@@ -674,7 +674,6 @@ 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;
@@ -699,7 +698,6 @@ 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)
@@ -1120,7 +1118,7 @@ sogo_message_set_read_flag (void *message_object, uint8_t flag)
struct MAPIStoreTallocWrapper *wrapper;
NSAutoreleasePool *pool;
MAPIStoreMessage *message;
enum mapistore_error rc;
int rc;
if (message_object)
{
+1 -1
View File
@@ -531,7 +531,7 @@
}
[vToDo setTimeStampAsDate: now];
[sogoObject saveCalendar: vCalendar];
[sogoObject saveComponent: vCalendar];
[self updateVersions];
}
-2
View File
@@ -41,8 +41,6 @@
+ (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;
+6 -40
View File
@@ -22,7 +22,6 @@
#import <NGExtensions/NSObject+Logs.h>
#import "MAPIStoreTypes.h"
#import "NSObject+MAPIStore.h"
#import "NSString+MAPIStore.h"
@@ -30,7 +29,6 @@
#undef DEBUG
#include <stdbool.h>
#include <libmapi/libmapi.h>
#include <talloc.h>
#include <util/time.h>
#include <gen_ndr/exchange.h>
@@ -138,11 +136,11 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
NSMutableData *xidData;
struct FlatUID_r flatUID;
_fillFlatUIDWithGUID (&flatUID, &xid->NameSpaceGuid);
_fillFlatUIDWithGUID (&flatUID, &xid->GUID);
xidData = [NSMutableData dataWithCapacity: 16 + xid->LocalId.length];
xidData = [NSMutableData dataWithCapacity: 16 + xid->Size];
[xidData appendBytes: flatUID.ab length: 16];
[xidData appendBytes: xid->LocalId.data length: xid->LocalId.length];
[xidData appendBytes: xid->Data length: xid->Size];
return xidData;
}
@@ -158,12 +156,12 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
{
xid = talloc_zero (memCtx, struct XID);
[self _extractGUID: &xid->NameSpaceGuid];
[self _extractGUID: &xid->GUID];
xid->LocalId.length = max - 16;
xid->Size = max - 16;
bytes = (uint8_t *) [self bytes];
xid->LocalId.data = talloc_memdup (xid, (bytes+16), xid->LocalId.length);
xid->Data = talloc_memdup (xid, (bytes+16), xid->Size);
}
else
{
@@ -174,38 +172,6 @@ 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;
{
+27
View File
@@ -0,0 +1,27 @@
[![SOGo Logo](http://www.sogo.nu/fileadmin/sogo/logos/sogo.logo.png)](http://www.sogo.nu/)
Share your calendars, address books and mails in your community with a completely free and open source solution.
Let your Mozilla Thunderbird/Lightning, Microsoft Outlook, Apple iCal/iPhone, Android and BlackBerry users collaborate using a modern platform.
## What is SOGo?
SOGo is [fully supported](http://www.sogo.nu/support/commercial_support.html) and [trusted](http://www.sogo.nu/tour/testimonials.html) groupware server with a focus on scalability and open standards. SOGo is released under the GNU GPL/LGPL v2 and above.
SOGo provides a rich AJAX-based Web interface and supports multiple native clients through the use of standard protocols such as CalDAV, CardDAV and GroupDAV.
SOGo is the missing component of your infrastructure; it sits in the middle of your servers to offer your users an uniform and complete interface to access their information. It has been deployed in production environments where thousands of users are involved.
[![Sogo diagram](http://www.sogo.nu/uploads/pics/sogo.png)](http://www.sogo.nu/)
[Learn more...](http://www.sogo.nu/about/overview.html)
## Microsoft Outlook Compatible
SOGo offers native Microsoft Outlook compatibility using the [OpenChange](http://www.openchange.org/) backend developed by [Inverse](http://www.inverse.ca/). This means Microsoft Outlook 2003, 2007, 2010 and 2013 can talk directly to SOGo &ndash; just like if it was a Microsoft Exchange server. **No plugins are required in Outlook to make this work**.
The following screencast shows Outlook 2010 connected to SOGo as if it was an Exchange server, along the Web interface of SOGo.
[![SOGo Screencast](http://img.youtube.com/vi/1hcBSB4Kxww/0.jpg)](http://www.youtube.com/watch?v=1hcBSB4Kxww)
[![SOGo on GitHub](http://www.sogo.nu/fileadmin/images/icon.github.png)](https://github.com/inverse-inc/sogo/) [![SOGo Ohloh](http://www.sogo.nu/fileadmin/images/icon.ohloh.png)](https://www.ohloh.net/p/sogo) [![SOGo on Twitter](http://www.sogo.nu/fileadmin/images/icon.twitter.png)](http://twitter.com/scalableogo) [![SOGo on SourceForge](http://www.sogo.nu/fileadmin/images/icon.sourceforge.png)](http://sourceforge.net/projects/sogo-zeg) [![SOGo on free code](http://www.sogo.nu/fileadmin/images/icon.freecode.png)](https://freecode.com/projects/sogo-inverse) [![SOGo on LinkedIn](http://www.sogo.nu/fileadmin/images/icon.linkedin.png)](http://www.linkedin.com/groups/SOGo-Groupware-4164805)
+4 -3
View File
@@ -1,8 +1,6 @@
/* iCalByDayMask.h - this file is part of SOPE
*
* Copyright (C) 2010 Wolfgang Sourdeau
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2010-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
@@ -55,6 +53,8 @@ typedef iCalWeekOccurrence iCalWeekOccurrences[7];
- (id) initWithDays: (iCalWeekOccurrences) theDays;
+ (id) byDayMaskWithRuleString: (NSString *) byDayRule;
- (id) initWithRuleString: (NSString *) byDayRule;
+ (id) byDayMaskWithDaysAndOccurrences: (NSArray *) values;
- (id) initWithDaysAndOccurrences: (NSArray *) values;
- (BOOL) occursOnDay: (iCalWeekDay) weekDay;
- (BOOL) occursOnDay: (iCalWeekDay) weekDay
@@ -71,6 +71,7 @@ typedef iCalWeekOccurrence iCalWeekOccurrences[7];
- (NSString *) asRuleString;
- (NSString *) asRuleStringWithIntegers;
- (NSArray *) asRuleArray;
@end
+159 -20
View File
@@ -1,8 +1,6 @@
/* iCalByDayMask.m - this file is part of SOPE
*
* Copyright (C) 2010 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2010-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
@@ -21,7 +19,9 @@
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
#import "iCalByDayMask.h"
@@ -39,10 +39,10 @@
+ (id) byDayMaskWithDays: (iCalWeekOccurrences) theDays
{
id o;
o = [[self alloc] initWithDays: theDays];
AUTORELEASE(o);
return o;
}
@@ -62,7 +62,7 @@
{
id o;
iCalWeekOccurrences d;
d[iCalWeekDaySunday] = 0;
d[iCalWeekDayMonday] = iCalWeekOccurrenceAll;
d[iCalWeekDayTuesday] = iCalWeekOccurrenceAll;
@@ -72,7 +72,7 @@
d[iCalWeekDaySaturday] = 0;
o = [[self alloc] initWithDays: d];
AUTORELEASE(o);
return o;
}
@@ -102,7 +102,7 @@
if (self)
{
memset(days, 0, 7 * sizeof(iCalWeekOccurrence));
if ([byDayRule length] > 0)
{
values = [byDayRule componentsSeparatedByString: @","];
@@ -120,7 +120,7 @@
[value getCharacters: chars
range: NSMakeRange(valueLength - 2, 2)];
switch (chars[0])
{
case 'M': day = iCalWeekDayMonday;
@@ -142,7 +142,7 @@
day = iCalWeekDaySunday;
break;
}
if (day != iCalWeekDayUnknown)
{
c = [value characterAtIndex: 0];
@@ -157,15 +157,15 @@
digitStart = 1;
c = [value characterAtIndex: 1];
}
i = digitStart;
while (i < valueLength && isdigit(c))
{
i++;
c = [value characterAtIndex: i];
}
if (i != digitStart)
order = [[value substringWithRange: NSMakeRange(digitStart, (i - digitStart))] intValue];
@@ -186,7 +186,123 @@
}
}
}
return self;
}
+ (id) byDayMaskWithDaysAndOccurrences: (NSArray *) values
{
id o;
o = [[self alloc] initWithDaysAndOccurrences: values];
AUTORELEASE(o);
return o;
}
- (id) initWithDaysAndOccurrences: (NSArray *) values
{
unsigned int count, max;
NSString *value;
unichar c, chars[2];
unsigned int valueLength, i, digitStart, order;
iCalWeekDay day;
id mask;
BOOL reverse;
self = [super init];
if (self)
{
memset(days, 0, 7 * sizeof(iCalWeekOccurrence));
max = [values count];
for (count = 0; count < max; count++)
{
mask = [values objectAtIndex: count];
if (![mask isKindOfClass: [NSDictionary class]])
continue;
value = [[mask objectForKey: @"day"] uppercaseString];
valueLength = [value length];
if (valueLength > 1)
{
day = iCalWeekDayUnknown;
reverse = NO;
digitStart = 0;
order = 0;
[value getCharacters: chars
range: NSMakeRange(0, valueLength)];
switch (chars[0])
{
case 'M': day = iCalWeekDayMonday;
break;
case 'W': day = iCalWeekDayWednesday;
break;
case 'F': day = iCalWeekDayFriday;
break;
case 'T':
if (chars[1] == 'U')
day = iCalWeekDayTuesday;
else if (chars[1] == 'H')
day = iCalWeekDayThursday;
break;
case 'S':
if (chars[1] == 'A')
day = iCalWeekDaySaturday;
else if (chars[1] == 'U')
day = iCalWeekDaySunday;
break;
}
if (day != iCalWeekDayUnknown)
{
value = [mask objectForKey: @"occurrence"];
valueLength = [value length];
if (valueLength > 0)
{
c = [value characterAtIndex: 0];
if (c == '-')
{
digitStart = 1;
reverse = YES;
}
else if (c == '+')
{
digitStart = 1;
}
i = digitStart;
while (i < valueLength)
{
c = [value characterAtIndex: i];
i++;
if (!isdigit(c))
break;
}
if (i != digitStart)
order = [[value substringWithRange: NSMakeRange(digitStart, (i - digitStart))] intValue];
}
if (order > 0 && order < 6)
{
order = pow (2, order - 1);
if (reverse)
order = order << 5;
days[day] |= order;
//NSLog(@"*** iCalByDayMask [%i] %@ : day = %i, order = %i, result = %i", count, byDayRule, day, order, days[day]);
}
else
{
days[day] = iCalWeekOccurrenceAll;
}
}
}
}
}
return self;
}
@@ -214,7 +330,7 @@
withWeekNumber: (int) week
{
unsigned int absWeek, order;
absWeek = abs (week);
order = 0;
if (absWeek > 0 && absWeek < 6)
@@ -223,7 +339,7 @@
if (week < 0)
order = order << 5;
}
return ((days[weekDay] & order) > 0);
}
@@ -249,7 +365,7 @@
if (days[i])
day = i;
}
return day;
}
@@ -260,10 +376,10 @@
occurrence = 0;
day = [self firstDay];
if (day > -1 && days[day] != iCalWeekOccurrenceAll)
occurrence = [self _iCalWeekOccurrenceIntValue: days[day]];
return occurrence;
}
@@ -323,7 +439,7 @@
[rules addObject: rule];
}
}
return [rules componentsJoinedByString: @","];
}
@@ -344,4 +460,27 @@
return s;
}
- (NSArray *) asRuleArray
{
NSMutableArray *rules;
NSMutableDictionary *rule;
int i;
rules = [NSMutableArray array];
for (i = 0; i < 7; i++)
{
if (days[i])
{
rule = [NSMutableDictionary dictionary];
if (days[i] != iCalWeekOccurrenceAll)
[rule setObject: [NSNumber numberWithInt: [self _iCalWeekOccurrenceIntValue: days[i]]]
forKey: @"occurrence"];
[rule setObject: iCalWeekDayString[i]
forKey: @"day"];
[rules addObject: rule];
}
}
return rules;
}
@end /* iCalByDayMask */
+2 -2
View File
@@ -94,8 +94,8 @@ typedef enum
- (void) increaseSequence;
/* url can either be set as NSString or NSURL */
- (void) setAttach: (id) _value;
- (NSURL *) attach;
- (void) setAttach: (NSArray *) _value;
- (NSArray *) attach;
- (void) setUrl: (id) _value;
- (NSURL *) url;
+32 -15
View File
@@ -400,32 +400,49 @@
return [self childrenWithTag: @"valarm"];
}
- (void) setAttach: (id) _value
- (void) setAttach: (NSArray *) _value
{
NSString *aString;
id anObject;
int count, max;
if ([_value isKindOfClass: [NSString class]])
aString = _value;
else if ([_value isKindOfClass: [NSURL class]])
aString = [_value absoluteString];
else
aString = @"";
[[self uniqueChildWithTag: @"attach"] setSingleValue: aString forKey: @""];
max = [_value count];
for (count = 0; count < max; count++)
{
anObject = [_value objectAtIndex: count];
if ([anObject isKindOfClass: [NSURL class]])
{
aString = [anObject absoluteString];
}
else
aString = anObject;
[self addChild: [CardElement simpleElementWithTag: @"attach" value: aString]];
}
}
- (NSURL *) attach
- (NSArray *) attach
{
NSArray *elements;
NSMutableArray *attachUrls;
NSString *stringAttach;
NSURL *url;
int count, max;
stringAttach = [[self uniqueChildWithTag: @"attach"] flattenedValuesForKey: @""];
url = [NSURL URLWithString: stringAttach];
elements = [self childrenWithTag: @"attach"];
max = [elements count];
attachUrls = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
stringAttach = [[elements objectAtIndex: count] flattenedValuesForKey: @""];
url = [NSURL URLWithString: stringAttach];
if (!url && [stringAttach length] > 0)
url = [NSURL URLWithString: [NSString stringWithFormat: @"http://%@", stringAttach]];
if (!url && [stringAttach length] > 0)
url = [NSURL URLWithString: [NSString stringWithFormat: @"http://%@", stringAttach]];
[attachUrls addObject: [url absoluteString]];
}
return url;
return attachUrls;
}
- (void) setUrl: (id) _value
+2
View File
@@ -44,6 +44,8 @@ typedef enum {
@interface iCalPerson : CardElement
+ (NSString *) descriptionForParticipationStatus: (iCalPersonPartStat) _status;
/* accessors */
- (void)setCn:(NSString *)_s;
+45 -34
View File
@@ -25,6 +25,48 @@
@implementation iCalPerson
+ (NSString *) descriptionForParticipationStatus: (iCalPersonPartStat) _status
{
NSString *stat;
switch (_status) {
case iCalPersonPartStatUndefined:
stat = @"";
break;
case iCalPersonPartStatAccepted:
stat = @"ACCEPTED";
break;
case iCalPersonPartStatDeclined:
stat = @"DECLINED";
break;
case iCalPersonPartStatTentative:
stat = @"TENTATIVE";
break;
case iCalPersonPartStatDelegated:
stat = @"DELEGATED";
break;
case iCalPersonPartStatCompleted:
stat = @"COMPLETED";
break;
case iCalPersonPartStatInProcess:
stat = @"IN-PROCESS";
break;
case iCalPersonPartStatExperimental:
case iCalPersonPartStatOther:
// [NSException raise:NSInternalInconsistencyException
// format:@"Attempt to set meaningless "
// @"participationStatus (%d)!", _status];
stat = nil; /* keep compiler happy */
break;
default:
stat = @"NEEDS-ACTION";
break;
}
return stat;
}
/* accessors */
- (void) setCn: (NSString *) _s
@@ -132,44 +174,13 @@
{
NSString *stat;
switch (_status) {
case iCalPersonPartStatUndefined:
stat = @"";
break;
case iCalPersonPartStatAccepted:
stat = @"ACCEPTED";
break;
case iCalPersonPartStatDeclined:
stat = @"DECLINED";
break;
case iCalPersonPartStatTentative:
stat = @"TENTATIVE";
break;
case iCalPersonPartStatDelegated:
stat = @"DELEGATED";
break;
case iCalPersonPartStatCompleted:
stat = @"COMPLETED";
break;
case iCalPersonPartStatInProcess:
stat = @"IN-PROCESS";
break;
case iCalPersonPartStatExperimental:
case iCalPersonPartStatOther:
// [NSException raise:NSInternalInconsistencyException
// format:@"Attempt to set meaningless "
// @"participationStatus (%d)!", _status];
stat = nil; /* keep compiler happy */
break;
default:
stat = @"NEEDS-ACTION";
break;
}
stat = [iCalPerson descriptionForParticipationStatus: _status];
if (stat)
[self setPartStat:stat];
}
- (iCalPersonPartStat)participationStatus {
- (iCalPersonPartStat) participationStatus {
NSString *stat;
stat = [[self partStat] uppercaseString];
+1
View File
@@ -75,6 +75,7 @@ extern NSString *iCalWeekDayString[];
- (void) setFrequency: (iCalRecurrenceFrequency) _frequency;
- (iCalRecurrenceFrequency) frequency;
- (iCalRecurrenceFrequency) valueForFrequency: (NSString *) value;
- (NSString *) frequencyForValue: (iCalRecurrenceFrequency) freq;
- (void) setRepeatInterval: (int) _repeatInterval;
- (int) repeatInterval;
+3 -2
View File
@@ -1,6 +1,6 @@
/* iCalTimeZone.m - this file is part of SOPE
*
* Copyright (C) 2006-2014 Inverse inc.
* 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
@@ -147,7 +147,8 @@ static NSArray *knownTimeZones;
if (ext.location != NSNotFound)
{
zone = [zone substringToIndex: ext.location];
[timeZoneNames addObject: zone];
if (![timeZoneNames containsObject: zone])
[timeZoneNames addObject: zone];
}
}
}
+4 -36
View File
@@ -161,11 +161,8 @@
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
@@ -200,41 +197,12 @@
END:STANDARD
END:VTIMEZONE
The time changes occur on a Sunday, but in March, the 1st is a Sunday itself and in November
The time changes occure 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 && pos >= 0)
if (dayOfWeek == dateDayOfWeek)
pos--;
offset = (dayOfWeek - dateDayOfWeek) + (pos * 7);
+1
View File
@@ -39,6 +39,7 @@
- (NSString *) percentComplete;
- (void) setDue: (NSCalendarDate *) _date;
- (void) setAllDayDue: (NSCalendarDate *) _date;
- (NSCalendarDate *) due;
- (void) setCompleted: (NSCalendarDate *) _date;
+6
View File
@@ -65,6 +65,12 @@
setDateTime: newDueDate];
}
- (void) setAllDayDue: (NSCalendarDate *) newDueDate
{
[(iCalDateTime *) [self uniqueChildWithTag: @"due"]
setDate: newDueDate];
}
- (NSCalendarDate *) due
{
return [(iCalDateTime *) [self uniqueChildWithTag: @"due"]
+2
View File
@@ -34,6 +34,8 @@
- (NSCalendarDate *) nextAlarmDate;
- (NSDictionary *) asDictionary;
@end
#endif /* __NGCards_iCalTrigger_H__ */
+74
View File
@@ -20,7 +20,9 @@
*/
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
#import "iCalEvent.h"
#import "iCalTrigger.h"
@@ -52,6 +54,78 @@
return [self value: 0 ofAttribute: @"related"];
}
- (NSDictionary *) asDictionary
{
NSDictionary *data;
NSString *duration, *relation, *reference, *quantity, *unit;
NSUInteger i;
unichar c;
data = nil;
if (![[self valueType] length] ||
[[self valueType] caseInsensitiveCompare: @"DURATION"] == NSOrderedSame)
{
relation = [[self relationType] uppercaseString];
duration = [self flattenedValuesForKey: @""];
i = 0;
c = [duration characterAtIndex: i];
if (c == '-')
{
reference = @"BEFORE";
i++;
}
else
{
reference = @"AFTER";
}
c = [duration characterAtIndex: i];
if (c == 'P')
{
quantity = @"";
unit = @"";
// Parse duration -- ignore first character (P)
for (i++; i < [duration length]; i++)
{
c = [duration characterAtIndex: i];
if (c == 't' || c == 'T')
// time -- ignore character
continue;
else if (isdigit (c))
quantity = [quantity stringByAppendingFormat: @"%c", c];
else
{
switch (c)
{
case 'D': /* day */
unit = @"DAYS";
break;
case 'H': /* hour */
unit = @"HOURS";
break;
case 'M': /* min */
unit = @"MINUTES";
break;
case 'W': /* week */
unit = @"WEEKS";
break;
default:
NSLog(@"Cannot process duration unit: '%c'", c);
break;
}
}
}
}
data = [NSDictionary dictionaryWithObjectsAndKeys:
relation, @"relation",
reference, @"reference",
[NSNumber numberWithInt: [quantity intValue]], @"quantity",
unit, @"unit",
nil];
}
return data;
}
- (NSCalendarDate *) nextAlarmDate
{
NSCalendarDate *relationDate, *nextAlarmDate;
@@ -65,6 +65,7 @@ typedef enum {
NSMutableDictionary *uidToFilename;
NSMutableDictionary *aclMatrix;
NSMutableArray *stripFields;
NSString *baseCalDAVURL, *basePublicCalDAVURL;
int davCalendarStartTimeLimit;
int davTimeLimitSeconds;
int davTimeHalfLimitSeconds;
@@ -73,6 +74,7 @@ typedef enum {
}
- (BOOL) isActive;
- (BOOL) isWebCalendar;
- (NSString *) calendarColor;
- (void) setCalendarColor: (NSString *) newColor;
@@ -129,6 +131,13 @@ typedef enum {
- (NSString *) baseURLForAptWithUID: (NSString *) _uid
inContext: (id) _ctx;
- (NSString *) calDavURL;
- (NSString *) webDavICSURL;
- (NSString *) webDavXMLURL;
- (NSString *) publicCalDavURL;
- (NSString *) publicWebDavICSURL;
- (NSString *) publicWebDavXMLURL;
/* folder management */
- (id) lookupHomeFolderForUID: (NSString *) _uid
@@ -157,12 +166,6 @@ typedef enum {
- (BOOL) showCalendarTasks;
- (void) setShowCalendarTasks: (BOOL) new;
- (NSString *) syncTag;
- (void) setSyncTag: (NSString *) newSyncTag;
- (BOOL) synchronizeCalendar;
- (void) setSynchronizeCalendar: (BOOL) new;
- (BOOL) includeInFreeBusy;
- (void) setIncludeInFreeBusy: (BOOL) newInclude;
+77 -67
View File
@@ -84,6 +84,7 @@
#import "SOGoAppointmentFolders.h"
#import "SOGoFreeBusyObject.h"
#import "SOGoTaskObject.h"
#import "SOGoWebAppointmentFolder.h";
#import "SOGoAppointmentFolder.h"
@@ -332,13 +333,15 @@ static Class iCalEventK = nil;
memset (userCanAccessObjectsClassifiedAs, NO,
iCalAccessClassCount * sizeof (BOOL));
davCalendarStartTimeLimit
= [[user domainDefaults] davCalendarStartTimeLimit];
davCalendarStartTimeLimit = [[user domainDefaults] davCalendarStartTimeLimit];
davTimeLimitSeconds = davCalendarStartTimeLimit * 86400;
/* 86400 / 2 = 43200. We hardcode that value in order to avoid
integer and float confusion. */
davTimeHalfLimitSeconds = davCalendarStartTimeLimit * 43200;
componentSet = nil;
baseCalDAVURL = nil;
basePublicCalDAVURL = nil;
}
return self;
@@ -350,6 +353,8 @@ static Class iCalEventK = nil;
[stripFields release];
[uidToFilename release];
[componentSet release];
[baseCalDAVURL release];
[basePublicCalDAVURL release];
[super dealloc];
}
@@ -432,61 +437,6 @@ static Class iCalEventK = nil;
inCategory: @"FolderShowTasks"];
}
- (NSString *) syncTag
{
NSString *syncTag;
syncTag = [self folderPropertyValueInCategory: @"FolderSyncTags"];
if (!syncTag)
syncTag = @"";
return syncTag;
}
- (void) setSyncTag: (NSString *) newSyncTag
{
// Check for duplicated tags
SOGoUserSettings *settings;
NSDictionary *syncTags;
NSArray *values;
if ([newSyncTag length])
{
settings = [[context activeUser] userSettings];
syncTags = [[settings objectForKey: @"Calendar"]
objectForKey: @"FolderSyncTags"];
values = [syncTags allValues];
if (![values containsObject: newSyncTag])
[self setFolderPropertyValue: newSyncTag
inCategory: @"FolderSyncTags"];
}
else
[self setFolderPropertyValue: nil
inCategory: @"FolderSyncTags"];
}
- (BOOL) synchronizeCalendar
{
NSNumber *synchronize;
synchronize = [self folderPropertyValueInCategory: @"FolderSynchronize"];
return [synchronize boolValue];
}
- (void) setSynchronizeCalendar: (BOOL) new
{
NSNumber *synchronize;
if (new)
synchronize = [NSNumber numberWithBool: YES];
else
synchronize = nil;
[self setFolderPropertyValue: synchronize
inCategory: @"FolderSynchronize"];
}
//
// If the user is the owner of the calendar, by default we include the freebusy information.
//
@@ -2869,6 +2819,70 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
return [url stringByAppendingString:_uid];
}
- (NSString *) _baseCalDAVURL
{
NSString *davURL;
if (!baseCalDAVURL)
{
davURL = [[self realDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
baseCalDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
baseCalDAVURL = davURL;
[baseCalDAVURL retain];
}
return baseCalDAVURL;
}
- (NSString *) _basePublicCalDAVURL
{
NSString *davURL;
if (!basePublicCalDAVURL)
{
davURL = [[self publicDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
basePublicCalDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
basePublicCalDAVURL = davURL;
[basePublicCalDAVURL retain];
}
return basePublicCalDAVURL;
}
- (NSString *) calDavURL
{
return [NSString stringWithFormat: @"%@/", [self _baseCalDAVURL]];
}
- (NSString *) webDavICSURL
{
return [NSString stringWithFormat: @"%@.ics", [self _baseCalDAVURL]];
}
- (NSString *) webDavXMLURL
{
return [NSString stringWithFormat: @"%@.xml", [self _baseCalDAVURL]];
}
- (NSString *) publicCalDavURL
{
return [NSString stringWithFormat: @"%@/", [self _basePublicCalDAVURL]];
}
- (NSString *) publicWebDavICSURL
{
return [NSString stringWithFormat: @"%@.ics", [self _basePublicCalDAVURL]];
}
- (NSString *) publicWebDavXMLURL
{
return [NSString stringWithFormat: @"%@.xml", [self _basePublicCalDAVURL]];
}
/* folder management */
- (BOOL) create
{
@@ -2914,12 +2928,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
refArray = [moduleSettings objectForKey: @"InactiveFolders"];
[refArray removeObject: nameInContainer];
refDict = [moduleSettings objectForKey: @"FolderSyncTags"];
[refDict removeObjectForKey: reference];
refDict = [moduleSettings objectForKey: @"FolderSynchronize"];
[refDict removeObjectForKey: reference];
[super removeFolderSettings: moduleSettings
withReference: reference];
@@ -2980,10 +2988,7 @@ 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]))
@@ -3135,6 +3140,11 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
return (![inactiveFolders containsObject: nameInContainer]);
}
- (BOOL) isWebCalendar
{
return ([self isKindOfClass: [SOGoWebAppointmentFolder class]]);
}
- (NSString *) importComponent: (iCalEntityObject *) event
timezone: (iCalTimeZone *) timezone
{
@@ -39,6 +39,7 @@
#import <NGCards/iCalEventChanges.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalRecurrenceCalculator.h>
#import <NGCards/iCalToDo.h>
#import <NGCards/NSCalendarDate+NGCards.h>
#import <SaxObjC/XMLNamespaces.h>
+13 -3
View File
@@ -1,6 +1,6 @@
/* iCalEntityObject+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2007-2014 Inverse inc.
* 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
@@ -26,6 +26,7 @@
@class iCalAlarm;
@class NSMutableDictionary;
@class SOGoUser;
@class WOContext;
extern NSCalendarDate *iCalDistantFuture;
extern NSNumber *iCalDistantFutureNumber;
@@ -34,12 +35,15 @@ extern NSNumber *iCalDistantFutureNumber;
+ (void) initializeSOGoExtensions;
- (NSDictionary *) attributesInContext: (WOContext *) context;
- (void) setAttributes: (NSDictionary *) data
inContext: (WOContext *) context;
- (BOOL) userIsAttendee: (SOGoUser *) user;
- (BOOL) userIsOrganizer: (SOGoUser *) user;
- (iCalPerson *) userAsAttendee: (SOGoUser *) user;
- (NSArray *) attendeeUIDs;
- (BOOL) isStillRelevant;
- (id) itipEntryWithMethod: (NSString *) method;
@@ -47,7 +51,13 @@ extern NSNumber *iCalDistantFutureNumber;
- (NSArray *) attendeesWithoutUser: (SOGoUser *) user;
- (int) priorityNumber;
- (NSString *) createdBy;
- (NSDictionary *) createdBy;
- (NSCalendarDate *) dateFromString: (NSString *) dateString
inContext: (WOContext *) context;
- (void) adjustDate: (NSCalendarDate **) date
withTimeString: (NSString *) timeString
inContext: (WOContext *) context;
- (NSNumber *) quickRecordDateAsNumber: (NSCalendarDate *) _date
withOffset: (int) offset
+380 -21
View File
@@ -1,6 +1,6 @@
/* iCalEntityObject+SOGo.m - this file is part of SOGo
*
* Copyright (C) 2007-2014 Inverse inc.
* 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
@@ -20,19 +20,24 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSTimeZone.h>
#import <Foundation/NSURL.h>
#import <NGCards/iCalAlarm.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalTrigger.h>
#import <NGCards/iCalRepeatableEntityObject.h>
#import <NGCards/NSString+NGCards.h>
#import <NGExtensions/NGCalendarDateRange.h>
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import "SOGoAppointmentFolder.h"
@@ -40,11 +45,13 @@
#import <NGObjWeb/WOContext+SoObjects.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/SOGoSource.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/SOGoUserManager.h>
#import "iCalPerson+SOGo.h"
#import "iCalAlarm+SOGo.h"
#import "iCalCalendar+SOGo.h"
#import "iCalEntityObject+SOGo.h"
@@ -63,6 +70,336 @@ NSNumber *iCalDistantFutureNumber = nil;
}
}
/**
* @see [UIxAppointmentEditor viewAction]
* @see [UIxTaskEditor viewAction]
*/
- (NSDictionary *) attributesInContext: (WOContext *) context
{
NSArray *elements;
NSMutableArray *attendees;
NSDictionary *contactData;
NSMutableDictionary *data, *organizerData, *attendeeData;
NSEnumerator *attendeesList;
NSString *uid, *domain, *sentBy;
NSObject <SOGoSource> *source;
SOGoUserManager *um;
iCalPerson *organizer, *currentAttendee;
id value;
um = [SOGoUserManager sharedUserManager];
data = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[[self tag] lowercaseString], @"component",
[self summary], @"summary",
nil];
value = [self priority];
if ([value length]) [data setObject: value forKey: @"priority"];
value = [self location];
if ([value length]) [data setObject: value forKey: @"location"];
value = [self comment];
if ([value length]) [data setObject: value forKey: @"comment"];
value = [self accessClass];
if ([value length]) [data setObject: [value lowercaseString] forKey: @"classification"];
value = [self status];
if ([value length]) [data setObject: [value lowercaseString] forKey: @"status"];
value = [self createdBy];
if (value) [data setObject: value forKey: @"createdBy"];
// Categories
elements = [self categories];
if ([elements count])
[data setObject: elements forKey: @"categories"];
// Send appointment notifications when the custom tag is *not* set
value = [self firstChildWithTag: @"X-SOGo-Send-Appointment-Notifications"];
[data setObject: [NSNumber numberWithBool: (value? 0:1)] forKey: @"sendAppointmentNotifications"];
// Organizer
organizer = [self organizer];
if ([[organizer email] length])
{
organizerData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[organizer rfc822Email], @"email",
[organizer cnWithoutQuotes], @"name",
nil];
uid = [organizer uid];
if ([uid length]) [organizerData setObject: uid forKey: @"uid"];
sentBy = [organizer sentBy];
if ([sentBy length]) [organizerData setObject: sentBy forKey: @"sentBy"];
[data setObject: organizerData forKey: @"organizer"];
}
// Attendees
attendees = [NSMutableArray array];
attendeesList = [[self attendees] objectEnumerator];
while ((currentAttendee = [attendeesList nextObject]))
{
attendeeData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[currentAttendee rfc822Email], @"email",
[currentAttendee cnWithoutQuotes], @"name",
nil];
if ((uid = [currentAttendee uid]))
{
[attendeeData setObject: uid forKey: @"uid"];
}
else
{
// Search for attendee in global contacts sources that are associated with a MS Exchange server
domain = [[context activeUser] domain];
elements = [um fetchContactsMatching: [currentAttendee rfc822Email] inDomain: domain];
if ([elements count] == 1)
{
contactData = [elements lastObject];
source = [contactData objectForKey: @"source"];
if ([source conformsToProtocol: @protocol (SOGoDNSource)] &&
[[(NSObject <SOGoDNSource>*) source MSExchangeHostname] length])
{
uid = [NSString stringWithFormat: @"%@:%@", [[context activeUser] login],
[contactData valueForKey: @"c_uid"]];
[attendeeData setObject: uid forKey: @"uid"];
}
}
}
[attendeeData setObject: [[currentAttendee partStat] lowercaseString] forKey: @"status"];
[attendeeData setObject: [[currentAttendee role] lowercaseString] forKey: @"role"];
if ([[currentAttendee delegatedTo] length])
[attendeeData setObject: [[currentAttendee delegatedTo] rfc822Email] forKey: @"delegatedTo"];
if ([[currentAttendee delegatedFrom] length])
[attendeeData setObject: [[currentAttendee delegatedFrom] rfc822Email] forKey: @"delegatedFrom"];
[attendees addObject: attendeeData];
}
if ([attendees count])
[data setObject: attendees forKey: @"attendees"];
return data;
}
// From [UIxDatePicker takeValuesFromRequest:inContext:]
- (NSCalendarDate *) dateFromString: (NSString *) dateString
inContext: (WOContext *) context
{
NSInteger dateTZOffset, userTZOffset;
NSTimeZone *systemTZ, *userTZ;
SOGoUserDefaults *ud;
NSCalendarDate *date;
date = [NSCalendarDate dateWithString: dateString
calendarFormat: @"%Y-%m-%d"];
if (!date)
[self warnWithFormat: @"Could not parse dateString: '%@'", dateString];
// We must adjust the date timezone because "dateWithString:..." uses the
// system timezone, which can be different from the user's. */
ud = [[context activeUser] userDefaults];
systemTZ = [date timeZone];
dateTZOffset = [systemTZ secondsFromGMTForDate: date];
userTZ = [ud timeZone];
userTZOffset = [userTZ secondsFromGMTForDate: date];
if (dateTZOffset != userTZOffset)
date = [date dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: (dateTZOffset - userTZOffset)];
[date setTimeZone: userTZ];
return date;
}
// From [UIxTimeDatePicker takeValuesFromRequest:inContext:]
- (void) adjustDate: (NSCalendarDate **) date
withTimeString: (NSString *) timeString
inContext: (WOContext *) context
{
unsigned _year, _month, _day, _hour, _minute;
SOGoUserDefaults *ud;
NSArray *_time;
_year = [*date yearOfCommonEra];
_month = [*date monthOfYear];
_day = [*date dayOfMonth];
_time = [timeString componentsSeparatedByString: @":"];
_hour = [[_time objectAtIndex: 0] intValue];
_minute = [[_time objectAtIndex: 1] intValue];
ud = [[context activeUser] userDefaults];
*date = [NSCalendarDate dateWithYear: _year month: _month day: _day
hour: _hour minute: _minute second: 0
timeZone: [ud timeZone]];
}
- (void) _setAttendees: (NSArray *) attendees
{
NSMutableArray *newAttendees;
NSUInteger count, max;
NSString *currentEmail;
iCalPerson *currentAttendee;
NSString *role, *partstat;
NSDictionary *currentData;
if (attendees)
{
newAttendees = [NSMutableArray array];
max = [attendees count];
for (count = 0; count < max; count++)
{
currentData = [attendees objectAtIndex: count];
currentEmail = [currentData objectForKey: @"email"];
if ([currentEmail length] > 0)
{
role = [[currentData objectForKey: @"role"] uppercaseString];
if (!role)
role = @"REQ-PARTICIPANT";
if ([role isEqualToString: @"NON-PARTICIPANT"])
partstat = @"";
else
{
partstat = [[currentData objectForKey: @"partstat"] uppercaseString];
if (!partstat)
partstat = @"NEEDS-ACTION";
}
currentAttendee = [self findAttendeeWithEmail: currentEmail];
if (!currentAttendee)
{
currentAttendee = [iCalPerson elementWithTag: @"attendee"];
[currentAttendee setCn: [currentData objectForKey: @"name"]];
[currentAttendee setEmail: currentEmail];
}
[currentAttendee
setRsvp: ([role isEqualToString: @"NON-PARTICIPANT"]
? @"FALSE"
: @"TRUE")];
[currentAttendee setRole: role];
[currentAttendee setPartStat: partstat];
[newAttendees addObject: currentAttendee];
}
}
[self setAttendees: newAttendees];
}
}
- (void) _appendAttendeesToEmailAlarm: (iCalAlarm *) alarm
{
NSArray *attendees;
NSMutableArray *aAttendees;
int count, max;
iCalPerson *currentAttendee, *aAttendee;
attendees = [self attendees];
max = [attendees count];
aAttendees = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
currentAttendee = [attendees objectAtIndex: count];
aAttendee = [iCalPerson elementWithTag: @"attendee"];
[aAttendee setCn: [currentAttendee cn]];
[aAttendee setEmail: [currentAttendee rfc822Email]];
[aAttendees addObject: aAttendee];
}
[alarm setAttendees: aAttendees];
}
- (void) _setAlarm: (NSDictionary *) alarm
forOwner: (NSString *) owner
{
iCalAlarm *anAlarm;
NSString *reminderAction, *reminderUnit, *reminderQuantity, *reminderReference, *reminderRelation;
BOOL reminderEmailAttendees, reminderEmailOrganizer;
reminderAction = [alarm objectForKey: @"action"];
reminderUnit = [alarm objectForKey: @"unit"];
reminderQuantity = [alarm objectForKey: @"quantity"];
reminderReference = [alarm objectForKey: @"reference"];
reminderRelation = [alarm objectForKey: @"relation"];
reminderEmailAttendees = [[alarm objectForKey: @"attendees"] boolValue];
reminderEmailOrganizer = [[alarm objectForKey: @"organizer"] boolValue];
anAlarm = [iCalAlarm alarmForEvent: self
owner: owner
action: reminderAction
unit: reminderUnit
quantity: reminderQuantity
reference: reminderReference
reminderRelation: reminderRelation
emailAttendees: reminderEmailAttendees
emailOrganizer: reminderEmailOrganizer];
// If there was an unsupported alarm defined in the event, it will be deleted.
[self removeAllAlarms];
if (anAlarm)
{
[self addToAlarms: anAlarm];
}
}
/**
* @see [UIxAppointmentEditor saveAction]
*/
- (void) setAttributes: (NSDictionary *) data
inContext: (WOContext *) context
{
NSString *owner;
id o, sendAppointmentNotifications;
o = [data objectForKey: @"summary"];
if ([o isKindOfClass: [NSString class]])
[self setSummary: o];
o = [data objectForKey: @"priority"];
if ([o isKindOfClass: [NSNumber class]])
[self setPriority: [NSString stringWithFormat: @"%d", [o intValue]]];
o = [data objectForKey: @"location"];
if ([o isKindOfClass: [NSString class]])
[self setLocation: o];
o = [data objectForKey: @"comment"];
if ([o isKindOfClass: [NSString class]])
[self setComment: [o stringByReplacingString: @"\r\n" withString: @"\n"]];
o = [data objectForKey: @"classification"];
if ([o isKindOfClass: [NSString class]])
[self setAccessClass: [o uppercaseString]];
o = [data objectForKey: @"status"];
if ([o isKindOfClass: [NSString class]])
[self setStatus: [o uppercaseString]];
o = [data objectForKey: @"categories"];
if ([o isKindOfClass: [NSArray class]])
[self setCategories: o];
o = [data objectForKey: @"sendAppointmentNotifications"];
if ([o isKindOfClass: [NSNumber class]])
{
sendAppointmentNotifications = [self firstChildWithTag: @"X-SOGo-Send-Appointment-Notifications"];
if (!sendAppointmentNotifications && ![o boolValue])
[self addChild: [CardElement simpleElementWithTag: @"X-SOGo-Send-Appointment-Notifications" value: @"NO"]];
else if (sendAppointmentNotifications && [o boolValue])
[self removeChild: sendAppointmentNotifications];
}
o = [data objectForKey: @"attendees"];
if ([o isKindOfClass: [NSArray class]])
[self _setAttendees: o];
o = [data objectForKey: @"alarm"];
if ([o isKindOfClass: [NSDictionary class]])
{
owner = [data objectForKey: @"owner"];
[self _setAlarm: o forOwner: owner];
}
// Other attributes depend on the client object and therefore are set in [UIxComponentEditor setAttributes:]:
// - organizer & "created-by"
// - timestamps (creation/modification)
}
- (BOOL) userIsAttendee: (SOGoUser *) user
{
NSEnumerator *attendees;
@@ -113,10 +450,11 @@ NSNumber *iCalDistantFutureNumber = nil;
if ([mail length])
return [user hasEmail: mail];
return NO;
}
/*
- (NSArray *) attendeeUIDs
{
NSEnumerator *attendees;
@@ -136,6 +474,7 @@ NSNumber *iCalDistantFutureNumber = nil;
return uids;
}
*/
#warning this method should be implemented in a category of iCalToDo
- (BOOL) isStillRelevant
@@ -152,11 +491,11 @@ NSNumber *iCalDistantFutureNumber = nil;
NSArray *events;
int i, count;
newCalendar = [parent mutableCopy];
[newCalendar autorelease];
[newCalendar setMethod: method];
events = [newCalendar childrenWithTag: tag];
count = [events count];
if (count > 1)
@@ -250,19 +589,39 @@ NSNumber *iCalDistantFutureNumber = nil;
return priorityNumber;
}
- (NSString *) createdBy
- (NSDictionary *) createdBy
{
NSString *created_by;
NSString *created_by, *created_by_name, *login;
NSDictionary *createdByData;
SOGoUser *user;
created_by_name = nil;
createdByData = nil;
created_by = [[self firstChildWithTag: @"X-SOGo-Component-Created-By"] flattenedValuesForKey: @""];
// We consider "SENT-BY" in case our custom header isn't found
if (![created_by length])
{
// We consider "SENT-BY" in case our custom header isn't found
created_by = [[self organizer] sentBy];
}
return created_by;
if ([created_by length])
{
login = [[SOGoUserManager sharedUserManager] getUIDForEmail: created_by];
if (login)
{
user = [SOGoUser userWithLogin: login];
if (user)
created_by_name = [user cn];
}
createdByData = [NSDictionary dictionaryWithObjectsAndKeys: created_by, @"email",
created_by_name, @"name",
nil];
}
return createdByData;
;
}
//
@@ -349,20 +708,20 @@ NSNumber *iCalDistantFutureNumber = nil;
NSCalendarDate *start, *end;
NGCalendarDateRange *range;
NSMutableArray *alarms;
alarms = [NSMutableArray array];
start = [NSCalendarDate date];
end = [start addYear:1 month:0 day:0 hour:0 minute:0 second:0];
range = [NGCalendarDateRange calendarDateRangeWithStartDate: start
endDate: end];
// Always check if container is defined. If not, that means this method
// call was reentrant.
if (theContainer)
{
NSTimeInterval now;
int i, v, delta, c_startdate, c_nextalarm;
//
// Here is the logic:
//
@@ -377,12 +736,12 @@ NSNumber *iCalDistantFutureNumber = nil;
// If we don't have a match, we pick the event for which its trigger is the closest to the c_nextalarm.
//
nextAlarmDate = nil;
[theContainer flattenCycleRecord: (id)row
forRange: range
intoArray: alarms
withCalendar: [self parent]];
// We pickup the closest one from now. We remove the actual reminder (ie., 15 mins before - plus 1 minute for roundups)
// so we don't pickup the same alarm over and over. This could happen if our alarm popups and the user clicks on "Cancel".
// In case of a repetitive event, we want to pickup the next one (next day for example), and not the one that has just
@@ -394,11 +753,11 @@ NSNumber *iCalDistantFutureNumber = nil;
c_startdate = [[[alarms objectAtIndex: i] objectForKey: @"c_startdate"] intValue];
c_nextalarm = [[[alarms objectAtIndex: i] objectForKey: @"c_nextalarm"] intValue];
delta = (c_startdate - now - (c_startdate - c_nextalarm)) - 60;
// If value is not initialized, we grab it right away
if (!v && delta > 0)
v = delta;
// If we found a smaller delta than before, use it.
if (v > 0 && delta > 0 && delta <= v)
{
@@ -409,10 +768,10 @@ NSNumber *iCalDistantFutureNumber = nil;
o = [[self parent] eventWithRecurrenceID: [[alarms objectAtIndex: i] objectForKey: @"c_recurrence_id"]];
else
o = [[self parent] todoWithRecurrenceID: [[alarms objectAtIndex: i] objectForKey: @"c_recurrence_id"]];
if (!o)
o = self;
if ([[o alarms] count])
{
anAlarm = [self firstDisplayOrAudioAlarm];
@@ -422,7 +781,7 @@ NSNumber *iCalDistantFutureNumber = nil;
if (!webstatus
|| ([webstatus caseInsensitiveCompare: @"TRIGGERED"]
!= NSOrderedSame))
v = delta;
nextAlarmDate = [NSDate dateWithTimeIntervalSince1970: [[[alarms objectAtIndex: i] objectForKey: @"c_nextalarm"] intValue]];
}
@@ -436,7 +795,7 @@ NSNumber *iCalDistantFutureNumber = nil;
} // if (theContainer)
}
}
if ([nextAlarmDate isNotNull])
[row setObject: [NSNumber numberWithInt: [nextAlarmDate timeIntervalSince1970]]
forKey: @"c_nextalarm"];
+2 -2
View File
@@ -1,6 +1,6 @@
/* iCalEvent+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2007-2014 Inverse inc.
* 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
@@ -28,7 +28,7 @@
@interface iCalEvent (SOGoExtensions)
- (BOOL) isStillRelevant;
- (unsigned int) occurenceInterval;
- (NSTimeInterval) occurenceInterval;
- (void) updateRecurrenceRulesUntilDate: (NSCalendarDate *) previousEndDate;
@end
+153 -13
View File
@@ -1,6 +1,6 @@
/* iCalEvent+SOGo.m - this file is part of SOGo
*
* Copyright (C) 2007-2014 Inverse inc.
* 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
@@ -22,12 +22,14 @@
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSTimeZone.h>
#import <Foundation/NSValue.h>
#import <NGExtensions/NGCalendarDateRange.h>
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalTimeZone.h>
#import <NGCards/iCalEvent.h>
@@ -36,6 +38,12 @@
#import <NGCards/iCalTrigger.h>
#import <NGCards/NSString+NGCards.h>
#import <SoObjects/SOGo/SOGoUser.h>
#import <SoObjects/SOGo/SOGoUserDefaults.h>
#import <SoObjects/SOGo/WOContext+SOGo.h>
#import <SOGo/NSCalendarDate+SOGo.h>
#import "SOGoAppointmentFolder.h"
#import "iCalRepeatableEntityObject+SOGo.h"
@@ -47,7 +55,7 @@
{
NSCalendarDate *now, *lastRecurrence;
BOOL isStillRelevent;
now = [NSCalendarDate calendarDate];
if ([self isRecurrent])
@@ -57,7 +65,7 @@
}
else
isStillRelevent = ([[self endDate] earlierDate: now] == now);
return isStillRelevent;
}
@@ -114,8 +122,8 @@
row = [NSMutableDictionary dictionaryWithCapacity:8];
[row setObject: @"vevent" forKey: @"c_component"];
if ([uid isNotNull])
if ([uid isNotNull])
[row setObject:uid forKey: @"c_uid"];
else
[self logWithFormat: @"WARNING: could not extract a uid from event!"];
@@ -168,11 +176,11 @@
forAllDay: isAllDay]
forKey: @"c_enddate"];
}
if ([self isRecurrent])
{
NSCalendarDate *date;
date = [self lastPossibleRecurrenceStartDate];
if (!date)
{
@@ -194,7 +202,7 @@
if ([status isNotNull])
{
int code = 1;
if ([status isEqualToString: @"TENTATIVE"])
code = 2;
else if ([status isEqualToString: @"CANCELLED"])
@@ -214,12 +222,12 @@
if (organizer)
{
NSString *email;
email = [organizer valueForKey: @"rfc822Email"];
if (email)
[row setObject:email forKey: @"c_orgmail"];
}
/* construct partstates */
count = [attendees count];
partstates = [[NSMutableString alloc] initWithCapacity:count * 2];
@@ -227,7 +235,7 @@
{
iCalPerson *p;
iCalPersonPartStat stat;
p = [attendees objectAtIndex:i];
stat = [p participationStatus];
if (i)
@@ -263,7 +271,7 @@
}
/**
* Shift the "until dates" of the recurrence rules of the event
* Shift the "until dates" of the recurrence rules of the event
* with respect to the previous end date of the event.
* @param previousEndDate the previous end date of the event
*/
@@ -312,5 +320,137 @@
}
}
}
/**
* @see [iCalRepeatableEntityObject+SOGo attributes]
* @see [iCalEntityObject+SOGo attributes]
* @see [UIxAppointmentEditor viewAction]
*/
- (NSDictionary *) attributesInContext: (WOContext *) context
{
BOOL isAllDay;
NSCalendarDate *eventStartDate, *eventEndDate;
NSMutableDictionary *data;
NSTimeZone *timeZone;
SOGoUserDefaults *ud;
isAllDay = [self isAllDay];
ud = [[context activeUser] userDefaults];
timeZone = [ud timeZone];
eventStartDate = [self startDate];
eventEndDate = [self endDate];
if (!isAllDay)
{
[eventStartDate setTimeZone: timeZone];
[eventEndDate setTimeZone: timeZone];
}
data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]];
[data setObject: [eventStartDate iso8601DateString] forKey: @"startDate"];
[data setObject: [eventEndDate iso8601DateString] forKey: @"endDate"];
[data setObject: [NSNumber numberWithBool: isAllDay] forKey: @"isAllDay"];
[data setObject: [NSNumber numberWithBool: ![self isOpaque]] forKey: @"isTransparent"];
return data;
}
/**
* @see [iCalRepeatableEntityObject+SOGo setAttributes:inContext:]
* @see [iCalEntityObject+SOGo setAttributes:inContext:]
* @see [UIxAppointmentEditor saveAction]
*/
- (void) setAttributes: (NSDictionary *) data
inContext: (WOContext *) context
{
NSCalendarDate *aptStartDate, *aptEndDate, *allDayStartDate;
NSTimeZone *timeZone;
NSInteger offset, nbrDays;
iCalDateTime *startDate;
iCalTimeZone *tz;
SOGoUserDefaults *ud;
BOOL isAllDay;
id o;
[super setAttributes: data inContext: context];
aptStartDate = aptEndDate = nil;
// Handle start/end dates
o = [data objectForKey: @"startDate"];
if ([o isKindOfClass: [NSString class]] && [o length])
aptStartDate = [self dateFromString: o inContext: context];
o = [data objectForKey: @"startTime"];
if ([o isKindOfClass: [NSString class]] && [o length])
[self adjustDate: &aptStartDate withTimeString: o inContext: context];
o = [data objectForKey: @"endDate"];
if ([o isKindOfClass: [NSString class]] && [o length])
aptEndDate = [self dateFromString: o inContext: context];
o = [data objectForKey: @"endTime"];
if ([o isKindOfClass: [NSString class]] && [o length])
[self adjustDate: &aptEndDate withTimeString: o inContext: context];
o = [data objectForKey: @"isTransparent"];
if ([o isKindOfClass: [NSNumber class]])
[self setTransparency: ([o boolValue]? @"TRANSPARENT" : @"OPAQUE")];
isAllDay = [[data objectForKey: @"isAllDay"] boolValue];
if (aptStartDate && aptEndDate)
{
if (isAllDay)
{
nbrDays = ((float) abs ([aptEndDate timeIntervalSinceDate: aptStartDate]) / 86400) + 1;
// Convert all-day start date to GMT (floating date)
ud = [[context activeUser] userDefaults];
timeZone = [ud timeZone];
offset = [timeZone secondsFromGMTForDate: aptStartDate];
allDayStartDate = [aptStartDate dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: offset];
[self setAllDayWithStartDate: allDayStartDate
duration: nbrDays];
}
else
{
[self setStartDate: aptStartDate];
[self setEndDate: aptEndDate];
}
}
if (!isAllDay)
{
// Make sure there's a vTimeZone associated to the event unless it
// is an all-day event.
startDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"];
if (![startDate timeZone])
{
ud = [[context activeUser] userDefaults];
tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]];
if ([[self parent] addTimeZone: tz])
{
[startDate setTimeZone: tz];
[(iCalDateTime *)[self uniqueChildWithTag: @"dtend"] setTimeZone: tz];
}
}
}
else // if (![[self clientObject] isNew])
{
// Remove the vTimeZone when dealing with an all-day event.
startDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"];
tz = [startDate timeZone];
if (tz)
{
[startDate setTimeZone: nil];
[(iCalDateTime *)[self uniqueChildWithTag: @"dtend"] setTimeZone: nil];
[[self parent] removeChild: tz];
}
}
}
@end
@@ -1,6 +1,6 @@
/* iCalRepeatableEntityObject+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2007-2014 Inverse inc.
* 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,6 +1,6 @@
/* iCalRepeatableEntityObject+SOGo.m - this file is part of SOGo
*
* Copyright (C) 2007-2014 Inverse inc.
* 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
@@ -25,8 +25,10 @@
#import <Foundation/NSTimeZone.h>
#import <Foundation/NSValue.h>
#import <NGCards/iCalByDayMask.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalEvent.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalRecurrenceRule.h>
#import <NGCards/iCalRecurrenceCalculator.h>
#import <NGCards/iCalTimeZone.h>
@@ -35,6 +37,12 @@
#import <NGExtensions/NGCalendarDateRange.h>
#import <SoObjects/SOGo/SOGoUser.h>
#import <SoObjects/SOGo/SOGoUserDefaults.h>
#import <SoObjects/SOGo/WOContext+SOGo.h>
#import <SOGo/NSCalendarDate+SOGo.h>
#import "iCalRepeatableEntityObject+SOGo.h"
@implementation iCalRepeatableEntityObject (SOGoExtensions)
@@ -69,6 +77,152 @@
return ma;
}
/**
* @see [iCalEntityObject+SOGo attributes]
* @see [iCalEvent+SOGo attributes]
* @see [UIxAppointmentEditor viewAction]
*/
- (NSDictionary *) attributesInContext: (WOContext *) context
{
NSArray *rules;
NSCalendarDate *untilDate;
NSMutableDictionary *data, *repeat;
NSString *frequency;
NSTimeZone *timeZone;
SOGoUserDefaults *ud;
iCalRecurrenceRule *rule;
data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]];
rules = [self recurrenceRules];
if ([rules count] > 0)
{
rule = [rules objectAtIndex: 0];
frequency = [rule frequencyForValue: [rule frequency]];
repeat = [NSMutableDictionary dictionary];
[repeat setObject: [frequency lowercaseString] forKey: @"frequency"];
[repeat setObject: [NSNumber numberWithInt: [rule repeatInterval]] forKey: @"interval"];
if ([rule repeatCount])
[repeat setObject: [NSNumber numberWithInt: [rule repeatCount]] forKey: @"count"];
if ((untilDate = [rule untilDate]))
{
ud = [[context activeUser] userDefaults];
timeZone = [ud timeZone];
[untilDate setTimeZone: timeZone];
[repeat setObject: [untilDate iso8601DateString] forKey: @"until"];
}
if ([[rule byDay] length])
[repeat setObject: [[rule byDayMask] asRuleArray] forKey: @"days"];
if ([[rule byMonthDay] count])
[repeat setObject: [rule byMonthDay] forKey: @"monthdays"];
if ([[rule byMonth] count])
[repeat setObject: [rule byMonth] forKey: @"months"];
[data setObject: repeat forKey: @"repeat"];
}
return data;
}
/**
* @see [iCalEntityObject+SOGo setAttributes:inContext:]
* @see [UIxAppointmentEditor saveAction]
*/
- (void) setAttributes: (NSDictionary *) data
inContext: (WOContext *) context
{
iCalRecurrenceRule *rule;
iCalRecurrenceFrequency frequency;
NSCalendarDate *date;
SOGoUserDefaults *ud;
id repeat, o;
[super setAttributes: data inContext: context];
if ([self recurrenceId])
// Occurrence of a recurrent object can't have a recurrence rule
return;
repeat = [data objectForKey: @"repeat"];
if ([repeat isKindOfClass: [NSDictionary class]])
{
rule = [iCalRecurrenceRule new];
[rule setInterval: @"1"];
frequency = NSNotFound;
o = [repeat objectForKey: @"frequency"];
if ([o isKindOfClass: [NSString class]])
{
frequency = [rule valueForFrequency: o];
if ((NSUInteger) frequency == NSNotFound)
{
if ([o caseInsensitiveCompare: @"BI-WEEKLY"] == NSOrderedSame)
{
frequency = iCalRecurrenceFrequenceWeekly;
[rule setInterval: @"2"];
}
else if ([o caseInsensitiveCompare: @"EVERY WEEKDAY"] == NSOrderedSame)
{
frequency = iCalRecurrenceFrequenceDaily;
[rule setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]];
}
}
else
{
o = [repeat objectForKey: @"interval"];
if ([o isKindOfClass: [NSNumber class]])
[rule setInterval: [NSString stringWithFormat: @"%i", [o intValue]]];
o = [repeat objectForKey: @"count"];
if ([o isKindOfClass: [NSNumber class]])
[rule setRepeatCount: [o intValue]];
o = [repeat objectForKey: @"until"];
if ([o isKindOfClass: [NSString class]])
{
date = [NSCalendarDate dateWithString: o
calendarFormat: @"%Y-%m-%d"];
if (date)
{
// Adjust timezone
ud = [[context activeUser] userDefaults];
date = [NSCalendarDate dateWithYear: [date yearOfCommonEra]
month: [date monthOfYear]
day: [date dayOfMonth]
hour: 0 minute: 0 second: 0
timeZone: [ud timeZone]];
[rule setUntilDate: date];
}
}
o = [repeat objectForKey: @"days"];
if ([o isKindOfClass: [NSArray class]])
[rule setByDayMask: [iCalByDayMask byDayMaskWithDaysAndOccurrences: o]];
o = [repeat objectForKey: @"monthdays"];
if ([o isKindOfClass: [NSArray class]])
[rule setValues: o atIndex: 0 forKey: @"bymonthday"];
o = [repeat objectForKey: @"months"];
if ([o isKindOfClass: [NSArray class]])
[rule setValues: o atIndex: 0 forKey: @"bymonth"];
}
if ((NSUInteger) frequency != NSNotFound)
{
[rule setFrequency: frequency];
[self setRecurrenceRules: [NSArray arrayWithObject: rule]];
}
[rule release];
}
}
else if ([self hasRecurrenceRules])
{
[self removeAllRecurrenceRules];
}
}
- (NSString *) cycleInfo
{
NSArray *rules;
@@ -83,7 +237,7 @@
rules = [self _indexedRules: [self recurrenceRules]];
if (rules)
[cycleInfo setObject: rules forKey: @"rules"];
rules = [self _indexedRules: [self exceptionRules]];
if (rules)
[cycleInfo setObject: rules forKey: @"exRules"];
@@ -122,7 +276,7 @@
firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: start
endDate: end];
}
return firstRange;
}
+192 -10
View File
@@ -27,10 +27,19 @@
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGCards/iCalAlarm.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalTrigger.h>
#import <NGCards/NSString+NGCards.h>
#import <NGCards/iCalAlarm.h>
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalTimeZone.h>
#import <NGCards/iCalTrigger.h>
#import <SoObjects/SOGo/WOContext+SOGo.h>
#import <SOGo/NSCalendarDate+SOGo.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import "iCalRepeatableEntityObject+SOGo.h"
@@ -38,6 +47,179 @@
@implementation iCalToDo (SOGoExtensions)
- (NSDictionary *) attributesInContext: (WOContext *) context
{
BOOL isAllDayStartDate, isAllDayDueDate;
NSCalendarDate *startDate, *dueDate, *completedDate;
NSMutableDictionary *data;
NSTimeZone *timeZone;
SOGoUserDefaults *ud;
ud = [[context activeUser] userDefaults];
timeZone = [ud timeZone];
startDate = [self startDate];
isAllDayStartDate = [(iCalDateTime *) [self uniqueChildWithTag: @"dtstart"] isAllDay];
if (!isAllDayStartDate)
[startDate setTimeZone: timeZone];
dueDate = [self due];
isAllDayDueDate = [(iCalDateTime *) [self uniqueChildWithTag: @"due"] isAllDay];
if (!isAllDayDueDate)
[dueDate setTimeZone: timeZone];
completedDate = [self completed];
[completedDate setTimeZone: timeZone];
data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]];
if (startDate)
[data setObject: [startDate iso8601DateString] forKey: @"startDate"];
if (dueDate)
[data setObject: [dueDate iso8601DateString] forKey: @"dueDate"];
if (completedDate)
[data setObject: [completedDate iso8601DateString] forKey: @"completedDate"];
if ([[self percentComplete] length])
[data setObject: [NSNumber numberWithInt: [[self percentComplete] intValue]] forKey: @"percentComplete"];
return data;
}
/**
* @see [iCalRepeatableEntityObject+SOGo setAttributes:inContext:]
* @see [iCalEntityObject+SOGo setAttributes:inContext:]
* @see [UIxAppointmentEditor saveAction]
*/
- (void) setAttributes: (NSDictionary *) data
inContext: (WOContext *) context
{
BOOL isAllDayStartDate, isAllDayDueDate;
NSCalendarDate *startDate, *dueDate, *completedDate;
NSInteger percent;
SOGoUserDefaults *ud;
iCalDateTime *todoStartDate, *todoDueDate;
iCalTimeZone *tz;
id o;
[super setAttributes: data inContext: context];
startDate = dueDate = completedDate = nil;
// Handle start date
isAllDayStartDate = YES;
o = [data objectForKey: @"startDate"];
if ([o isKindOfClass: [NSString class]] && [o length])
startDate = [self dateFromString: o inContext: context];
o = [data objectForKey: @"startTime"];
if ([o isKindOfClass: [NSString class]] && [o length])
{
isAllDayStartDate = NO;
[self adjustDate: &startDate withTimeString: o inContext: context];
}
if (startDate)
[self setStartDate: startDate];
else
{
[self setStartDate: nil];
[self removeAllAlarms];
}
// Handle due date
isAllDayDueDate = YES;
o = [data objectForKey: @"dueDate"];
if ([o isKindOfClass: [NSString class]] && [o length])
dueDate = [self dateFromString: o inContext: context];
o = [data objectForKey: @"dueTime"];
if ([o isKindOfClass: [NSString class]] && [o length])
{
isAllDayDueDate = NO;
[self adjustDate: &dueDate withTimeString: o inContext: context];
}
if (dueDate)
if (isAllDayDueDate)
[self setAllDayDue: dueDate];
else
[self setDue: dueDate];
else
[self setDue: nil];
// Handle time zone
todoStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"];
todoDueDate = (iCalDateTime *)[self uniqueChildWithTag: @"due"];
tz = [todoStartDate timeZone];
if (!tz)
tz = [todoDueDate timeZone];
if (isAllDayStartDate && isAllDayDueDate)
{
if (tz)
[[self parent] removeChild: tz];
[todoStartDate setTimeZone: nil];
[todoDueDate setTimeZone: nil];
}
else
{
if (!tz)
{
ud = [[context activeUser] userDefaults];
tz = [iCalTimeZone timeZoneForName: [ud timeZoneName]];
}
if (tz)
{
[[self parent] addTimeZone: tz];
if (todoStartDate)
{
if (isAllDayStartDate)
[todoStartDate setTimeZone: nil];
else if (![todoStartDate timeZone])
[todoStartDate setTimeZone: tz];
}
if (todoDueDate)
{
if (isAllDayDueDate)
[todoDueDate setTimeZone: nil];
else if (![todoDueDate timeZone])
[todoDueDate setTimeZone: tz];
}
}
}
// Handle completed date
o = [data objectForKey: @"completedDate"];
if ([o isKindOfClass: [NSString class]] && [o length])
completedDate = [self dateFromString: o inContext: context];
o = [data objectForKey: @"completedTime"];
if ([o isKindOfClass: [NSString class]] && [o length])
[self adjustDate: &completedDate withTimeString: o inContext: context];
o = [self status];
if ([o length])
{
if ([o isEqualToString: @"COMPLETED"] && completedDate)
// As specified in RFC5545, the COMPLETED property must use UTC time
[self setCompleted: completedDate];
else
[self setCompleted: nil];
}
// Percent complete
o = [data objectForKey: @"percentComplete"];
if ([o isKindOfClass: [NSNumber class]])
{
percent = [o intValue];
if (percent >= 0 && percent <= 100)
[self setPercentComplete: [NSString stringWithFormat: @"%i", percent]];
}
else
[self setPercentComplete: @""];
}
- (NSMutableDictionary *) quickRecordFromContent: (NSString *) theContent
container: (id) theContainer
{
@@ -53,7 +235,7 @@
iCalAccessClass accessClass;
/* extract values */
startDate = [self startDate];
dueDate = [self due];
uid = [self uid];
@@ -78,7 +260,7 @@
[row setObject: @"vtodo" forKey: @"c_component"];
if ([uid isNotNull])
if ([uid isNotNull])
[row setObject:uid forKey: @"c_uid"];
else
[self logWithFormat: @"WARNING: could not extract a uid from event!"];
@@ -96,7 +278,7 @@
[row setObject: title forKey: @"c_title"];
if ([location isNotNull]) [row setObject: location forKey: @"c_location"];
if ([sequence isNotNull]) [row setObject: sequence forKey: @"c_sequence"];
if ([startDate isNotNull])
date = [self quickRecordDateAsNumber: startDate
withOffset: 0 forAllDay: NO];
@@ -146,12 +328,12 @@
if (organizer)
{
NSString *email;
email = [organizer valueForKey: @"rfc822Email"];
if (email)
[row setObject:email forKey: @"c_orgmail"];
}
/* construct partstates */
count = [attendees count];
partstates = [[NSMutableString alloc] initWithCapacity:count * 2];
@@ -159,7 +341,7 @@
{
iCalPerson *p;
iCalPersonPartStat stat;
p = [attendees objectAtIndex:i];
stat = [p participationStatus];
if(i != 0)
@@ -171,7 +353,7 @@
/* handle alarms */
[self updateNextAlarmDateInRow: row forContainer: theContainer];
categories = [self categories];
if ([categories count] > 0)
[row setObject: [categories componentsJoinedByString: @","]
+47 -17
View File
@@ -1,6 +1,6 @@
/* NGVCard+SOGo.m - 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
@@ -187,29 +187,59 @@ convention:
return element;
}
- (void) addElementWithTag: (NSString *) elementTag
ofType: (NSString *) type
withValue: (id) value
{
NSArray *allValues;
NSEnumerator *list;
CardElement *element;
// value is either an array or a string
if ([value isKindOfClass: [NSString class]])
allValues = [NSArray arrayWithObject: value];
else
allValues = value;
// Add all values as separate elements
list = [allValues objectEnumerator];
while ((value = [list nextObject]))
{
element = [CardElement simpleElementWithTag: elementTag
singleType: type
value: value];
[self addChild: element];
}
}
- (void) _setPhoneValues: (NSDictionary *) ldifRecord
{
CardElement *phone;
phone = [self elementWithTag: @"tel" ofType: @"work"];
[phone setSingleValue: [ldifRecord objectForKey: @"telephonenumber"] forKey: @""];
phone = [self elementWithTag: @"tel" ofType: @"home"];
[phone setSingleValue: [ldifRecord objectForKey: @"homephone"] forKey: @""];
phone = [self elementWithTag: @"tel" ofType: @"cell"];
[phone setSingleValue: [ldifRecord objectForKey: @"mobile"] forKey: @""];
phone = [self elementWithTag: @"tel" ofType: @"fax"];
[phone setSingleValue: [ldifRecord objectForKey: @"facsimiletelephonenumber"]
forKey: @""];
phone = [self elementWithTag: @"tel" ofType: @"pager"];
[phone setSingleValue: [ldifRecord objectForKey: @"pager"] forKey: @""];
[self addElementWithTag: @"tel"
ofType: @"work"
withValue: [ldifRecord objectForKey: @"telephonenumber"]];
[self addElementWithTag: @"tel"
ofType: @"home"
withValue: [ldifRecord objectForKey: @"homephone"]];
[self addElementWithTag: @"tel"
ofType: @"cell"
withValue: [ldifRecord objectForKey: @"mobile"]];
[self addElementWithTag: @"tel"
ofType: @"fax"
withValue: [ldifRecord objectForKey: @"facsimiletelephonenumber"]];
[self addElementWithTag: @"tel"
ofType: @"pager"
withValue: [ldifRecord objectForKey: @"pager"]];
}
- (void) _setEmails: (NSDictionary *) ldifRecord
{
CardElement *mail, *homeMail;
CardElement *homeMail;
mail = [self elementWithTag: @"email" ofType: @"work"];
[mail setSingleValue: [ldifRecord objectForKey: @"mail"] forKey: @""];
// Emails from the configured mail fields of the source have already been extracted in
// [LDAPSource _fillEmailsOfEntry:intoLDIFRecord:]
[self addElementWithTag: @"email"
ofType: @"work"
withValue: [ldifRecord objectForKey: @"c_emails"]];
homeMail = [self elementWithTag: @"email" ofType: @"home"];
[homeMail setSingleValue: [ldifRecord objectForKey: @"mozillasecondemail"] forKey: @""];
[[self uniqueChildWithTag: @"x-mozilla-html"]
+39 -6
View File
@@ -209,10 +209,26 @@ static NSArray *folderListingFields = nil;
return qualifier;
}
/**
* Normalize keys of dictionary representing a contact.
* @param contactRecord a dictionary with pairs from the quick table
* @see [UIxContactView dataAction]
*/
- (void) fixupContactRecord: (NSMutableDictionary *) contactRecord
{
NSString *data;
// c_categories => categories
data = [contactRecord objectForKey: @"c_categories"];
if ([data length])
[contactRecord setObject: data forKey: @"categories"];
// c_name => id
data = [contactRecord objectForKey: @"c_name"];
if ([data length])
[contactRecord setObject: data forKey: @"id"];
// c_cn => fn
data = [contactRecord objectForKey: @"c_cn"];
if (![data length])
{
@@ -224,17 +240,34 @@ static NSArray *folderListingFields = nil;
else
{
data = [contactRecord objectForKey: @"c_o"];
[contactRecord setObject: data forKey: @"c_cn"];
[contactRecord setObject: data forKey: @"c_cn"];
}
}
if (![contactRecord objectForKey: @"c_mail"])
[contactRecord setObject: @"" forKey: @"c_mail"];
// c_screenname => X-AIM
if (![contactRecord objectForKey: @"c_screenname"])
[contactRecord setObject: @"" forKey: @"c_screenname"];
if (![contactRecord objectForKey: @"c_o"])
[contactRecord setObject: @"" forKey: @"c_o"];
if (![contactRecord objectForKey: @"c_telephonenumber"])
// c_mail => emails[]
data = [contactRecord objectForKey: @"c_mail"];
if ([data length])
{
NSDictionary *email;
email = [NSDictionary dictionaryWithObjectsAndKeys: @"pref", @"type", data, @"value", nil];
[contactRecord setObject: [NSArray arrayWithObject: email] forKey: @"emails"];
}
else
[contactRecord setObject: @"" forKey: @"c_mail"];
// c_telephonenumber => phones
data = [contactRecord objectForKey: @"c_telephonenumber"];
if ([data length])
{
NSDictionary *phonenumber;
phonenumber = [NSDictionary dictionaryWithObjectsAndKeys: @"pref", @"type", data, @"value", nil];
[contactRecord setObject: [NSArray arrayWithObject: phonenumber] forKey: @"phones"];
}
else
[contactRecord setObject: @"" forKey: @"c_telephonenumber"];
}
+1 -1
View File
@@ -92,7 +92,7 @@
[vcard setProdID: [NSString
stringWithFormat: @"-//Inverse inc./SOGo %@//EN",
SOGoVersion]];
[vcard updateFromLDIFRecord: [self simplifiedLDIFRecord]];
[vcard updateFromLDIFRecord: [self ldifRecord]];
return vcard;
}
-1
View File
@@ -38,7 +38,6 @@
- (NSDictionary *) simplifiedLDIFRecord;
- (NSException *) save;
- (NSException *) delete;
@end
+57 -17
View File
@@ -1,6 +1,6 @@
/* SOGoContactSourceFolder.m - this file is part of SOGo
*
* Copyright (C) 2006-2013 Inverse inc.
* 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
@@ -217,6 +217,11 @@
return [source removeContactEntryWithID: [ldifEntry nameInContainer]];
}
/**
* Normalize keys of dictionary representing a contact.
* @param oldRecord a dictionary with pairs from the source folder (LDAP or SQL)
* @see [SOGoContactGCSFolder _fixupContactRecord]
*/
- (NSDictionary *) _flattenedRecord: (NSDictionary *) oldRecord
{
NSMutableDictionary *newRecord;
@@ -226,26 +231,54 @@
newRecord = [NSMutableDictionary dictionaryWithCapacity: 8];
[newRecord setObject: [oldRecord objectForKey: @"c_uid"]
forKey: @"c_uid"];
// c_name => id
[newRecord setObject: [oldRecord objectForKey: @"c_name"]
forKey: @"c_name"];
[newRecord setObject: [oldRecord objectForKey: @"c_name"]
forKey: @"id"];
// displayname || c_cn => fn
data = [oldRecord objectForKey: @"displayname"];
if (!data)
data = [oldRecord objectForKey: @"c_cn"];
if (!data)
if (data)
[newRecord setObject: data forKey: @"fn"];
else
data = @"";
[newRecord setObject: data forKey: @"c_cn"];
data = [oldRecord objectForKey: @"mail"];
if (!data)
data = @"";
else if ([data isKindOfClass: [NSArray class]])
// mail => emails[]
data = [oldRecord objectForKey: @"c_emails"];
if (data)
{
if ([data count] > 0)
data = [data objectAtIndex: 0];
if ([data isKindOfClass: [NSArray class]])
{
if ([data count] > 0)
{
NSEnumerator *emails;
NSMutableArray *recordEmails;
NSString *email;
emails = [(NSArray *)data objectEnumerator];
recordEmails = [NSMutableArray arrayWithCapacity: [data count]];
while ((email = [emails nextObject]))
{
[recordEmails addObject: [NSDictionary dictionaryWithObject: email forKey: @"value"]];
}
[newRecord setObject: recordEmails forKey: @"emails"];
}
}
else if (data)
{
NSDictionary *email;
email = [NSDictionary dictionaryWithObjectsAndKeys: @"pref", @"type", data, @"value", nil];
[newRecord setObject: [NSArray arrayWithObject: email] forKey: @"emails"];
}
else
data = @"";
}
else
data = @"";
[newRecord setObject: data forKey: @"c_mail"];
data = [oldRecord objectForKey: @"nsaimid"];
@@ -255,25 +288,28 @@
data = @"";
[newRecord setObject: data forKey: @"c_screenname"];
// o => org
data = [oldRecord objectForKey: @"o"];
if (!data)
if (data)
[newRecord setObject: data forKey: @"org"];
else
data = @"";
[newRecord setObject: data forKey: @"c_o"];
// telephonenumber || cellphone || homephone => phones[]
data = [oldRecord objectForKey: @"telephonenumber"];
if (![data length])
data = [oldRecord objectForKey: @"cellphone"];
if (![data length])
data = [oldRecord objectForKey: @"homephone"];
if (![data length])
data = @"";
else if ([data isKindOfClass: [NSArray class]])
if (data)
{
if ([data count] > 0)
data = [data objectAtIndex: 0];
else
data = @"";
NSDictionary *phonenumber;
phonenumber = [NSDictionary dictionaryWithObjectsAndKeys: @"pref", @"type", data, @"value", nil];
[newRecord setObject: [NSArray arrayWithObject: phonenumber] forKey: @"phones"];
}
else
data = @"";
[newRecord setObject: data forKey: @"c_telephonenumber"];
// Custom attribute for group-lookups. See LDAPSource.m where
@@ -290,9 +326,13 @@
[newRecord setObject: @"vcard" forKey: @"c_component"];
}
// c_info => note
data = [oldRecord objectForKey: @"c_info"];
if ([data length] > 0)
[newRecord setObject: data forKey: @"contactInfo"];
{
[newRecord setObject: data forKey: @"note"];
[newRecord setObject: data forKey: @"contactInfo"];
}
recordSource = [oldRecord objectForKey: @"source"];
if ([recordSource conformsToProtocol: @protocol (SOGoDNSource)] &&
+13 -15
View File
@@ -63,10 +63,12 @@
#import <SOGo/SOGoDomainDefaults.h>
#import <SOGo/SOGoMailer.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserFolder.h>
#import <SOGo/SOGoUserDefaults.h>
#import <NGCards/NGVCard.h>
#import <Contacts/SOGoContactFolder.h>
#import <Contacts/SOGoContactFolders.h>
#import <Contacts/SOGoContactGCSEntry.h>
@@ -313,19 +315,19 @@ static NSString *userAgent = nil;
{
// newHeaders come from Web form; convert priority to MIME header representation
priority = [newHeaders objectForKey: @"priority"];
if (!priority || [priority isEqualToString: @"NORMAL"])
if (!priority || [priority intValue] == 3)
{
[headers removeObjectForKey: @"X-Priority"];
}
else if ([priority isEqualToString: @"HIGHEST"])
else if ([priority intValue] == 1)
{
[headers setObject: @"1 (Highest)" forKey: @"X-Priority"];
}
else if ([priority isEqualToString: @"HIGH"])
else if ([priority intValue] == 2)
{
[headers setObject: @"2 (High)" forKey: @"X-Priority"];
}
else if ([priority isEqualToString: @"LOW"])
else if ([priority intValue] == 4)
{
[headers setObject: @"4 (Low)" forKey: @"X-Priority"];
}
@@ -355,7 +357,7 @@ static NSString *userAgent = nil;
else
{
receipt = [newHeaders objectForKey: @"receipt"];
if ([receipt isEqualToString: @"true"])
if ([receipt boolValue])
{
[headers setObject: receipt forKey: @"receipt"];
pureSender = [[newHeaders objectForKey: @"from"] pureEMailAddress];
@@ -641,7 +643,6 @@ static NSString *userAgent = nil;
[imap4URL release];
imap4URL = nil;
}
[self storeInfo];
}
else
error = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
@@ -927,8 +928,6 @@ static NSString *userAgent = nil;
[self setText: [sourceMail contentForEditing]];
[self setIMAP4ID: [[sourceMail nameInContainer] intValue]];
[self storeInfo];
}
//
@@ -1007,11 +1006,11 @@ static NSString *userAgent = nil;
withMetadata: attachment];
}
[self storeInfo];
// Save the message to the IMAP store so the user can eventually view the attached file(s)
// from the Web interface
[self save];
[self storeInfo];
}
/* accessors */
@@ -1597,17 +1596,16 @@ static NSString *userAgent = nil;
{
NSString *s, *dateString;
NGMutableHashMap *map;
NSArray *emails;
id from, replyTo;
id emails, from, replyTo;
map = [[[NGMutableHashMap alloc] initWithCapacity:16] autorelease];
/* add recipients */
if ((emails = [headers objectForKey: @"to"]) != nil)
if ((emails = [headers objectForKey: @"to"]) != nil && [emails isKindOfClass: [NSArray class]])
[map setObjects: [self _quoteSpecialsInArray: emails] forKey: @"to"];
if ((emails = [headers objectForKey: @"cc"]) != nil)
if ((emails = [headers objectForKey: @"cc"]) != nil && [emails isKindOfClass: [NSArray class]])
[map setObjects: [self _quoteSpecialsInArray: emails] forKey: @"cc"];
if ((emails = [headers objectForKey: @"bcc"]) != nil)
if ((emails = [headers objectForKey: @"bcc"]) != nil && [emails isKindOfClass: [NSArray class]])
[map setObjects: [self _quoteSpecialsInArray: emails] forKey: @"bcc"];
/* add senders */
+119 -73
View File
@@ -300,7 +300,7 @@ static NSString *inboxFolderName = @"INBOX";
quota = quota * [(NSNumber*)[inboxQuota objectForKey: @"maxQuota"] intValue];
inboxQuota = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithLong: (long)(quota+0.5)], @"maxQuota",
[inboxQuota objectForKey: @"usedSpace"], @"usedSpace",
[NSNumber numberWithLong: [[inboxQuota objectForKey: @"usedSpace"] longLongValue]], @"usedSpace",
nil];
}
}
@@ -400,56 +400,147 @@ static NSString *inboxFolderName = @"INBOX";
{
NSString *folderType;
if ([folderName isEqualToString: [NSString stringWithFormat: @"/%@", inboxFolderName]])
if ([folderName isEqualToString: inboxFolderName])
folderType = @"inbox";
else if ([folderName isEqualToString: [NSString stringWithFormat: @"/%@", [self draftsFolderNameInContext: context]]])
else if ([folderName isEqualToString: [self draftsFolderNameInContext: context]])
folderType = @"draft";
else if ([folderName isEqualToString: [NSString stringWithFormat: @"/%@", [self sentFolderNameInContext: context]]])
else if ([folderName isEqualToString: [self sentFolderNameInContext: context]])
folderType = @"sent";
else if ([folderName isEqualToString: [NSString stringWithFormat: @"/%@", [self trashFolderNameInContext: context]]])
else if ([folderName isEqualToString: [self trashFolderNameInContext: context]])
folderType = @"trash";
else if ([folderName isEqualToString: otherUsersFolderName])
folderType = @"otherUsers";
else if ([folderName isEqualToString: sharedFoldersName])
folderType = @"shared";
else
folderType = @"folder";
return folderType;
}
- (NSString *) _parentForFolder: (NSString *) folderName
foldersList: (NSArray *) theFolders
- (NSMutableDictionary *) _insertFolder: (NSString *) folderPath
foldersList: (NSMutableArray *) theFolders
{
NSArray *pathComponents;
NSString *s;
int i;
NSMutableArray *folders;
NSMutableDictionary *currentFolder, *parentFolder, *folder;
NSString *currentFolderName, *currentPath, *fullName, *folderType;
SOGoUserManager *userManager;
int i, j, count;
BOOL last, isOtherUsersFolder, parentIsOtherUsersFolder;
pathComponents = [folderName pathComponents];
s = [[[pathComponents subarrayWithRange: NSMakeRange(0,[pathComponents count]-1)] componentsJoinedByString: @"/"] substringFromIndex: 1];
parentFolder = nil;
parentIsOtherUsersFolder = NO;
pathComponents = [folderPath pathComponents];
count = [pathComponents count];
for (i = 0; i < [theFolders count]; i++)
// Make sure all ancestors exist.
// The variable folderPath is something like '/INBOX/Junk' so pathComponents becomes ('/', 'INBOX', 'Junk').
// That's why we always ignore the first element
for (i = 1; i < count; i++)
{
if ([s isEqualToString: [theFolders objectAtIndex: i]])
return s;
last = ((count - i) == 1);
folder = nil;
currentPath = [[[pathComponents subarrayWithRange: NSMakeRange(0,i+1)] componentsJoinedByString: @"/"] substringFromIndex: 2];
// Search for the current path in the children of the parent folder.
// For the first iteration, take the parent folder passed as argument.
if (parentFolder)
{
folders = [parentFolder objectForKey: @"children"];
}
else
{
folders = theFolders;
}
for (j = 0; j < [folders count]; j++)
{
currentFolder = [folders objectAtIndex: j];
if ([currentPath isEqualToString: [currentFolder objectForKey: @"path"]])
{
folder = currentFolder;
// Make sure all branches are ready to receive children
if (!last && ![folder objectForKey: @"children"])
{
[folder setObject: [NSMutableArray array] forKey: @"children"];
}
break;
}
}
// Check if the current folder is the "Other users" folder (shared mailboxes)
currentFolderName = [[pathComponents objectAtIndex: i] stringByDecodingImap4FolderName];
if (otherUsersFolderName
&& [currentFolderName caseInsensitiveCompare: otherUsersFolderName] == NSOrderedSame)
{
isOtherUsersFolder = YES;
}
else
{
isOtherUsersFolder = NO;
}
if (folder == nil)
{
// Folder was not found; create it and add it to the folders list
if (parentIsOtherUsersFolder)
{
// Parent folder is the "Other users" folder; translate the user's mailbox name
// to the full name of the person
userManager = [SOGoUserManager sharedUserManager];
fullName = [userManager getCNForUID: currentFolderName];
if (fullName)
currentFolderName = fullName;
}
else if (isOtherUsersFolder)
{
currentFolderName = [self labelForKey: @"OtherUsersFolderName"];
}
else if (sharedFoldersName
&& [currentFolderName caseInsensitiveCompare: sharedFoldersName] == NSOrderedSame)
{
currentFolderName = [self labelForKey: @"SharedFoldersName"];
}
if (last)
folderType = [self _folderType: currentPath];
else
folderType = @"additional";
folder = [NSMutableDictionary dictionaryWithObjectsAndKeys:
currentPath, @"path",
folderType, @"type",
currentFolderName, @"name",
[NSMutableArray array], @"children",
nil];
// Either add this new folder to its parent or the list of root folders
[folders addObject: folder];
}
parentFolder = folder;
parentIsOtherUsersFolder = isOtherUsersFolder;
}
return nil;
return parentFolder;
}
//
//
// Return a tree representation of the mailboxes
//
- (NSArray *) allFoldersMetadata
{
NSString *currentFolder, *currentDecodedFolder, *currentDisplayName, *currentFolderType, *login, *fullName, *parent;
NSMutableArray *pathComponents, *folders;
SOGoUserManager *userManager;
NSString *currentFolder;
NSMutableArray *folders;
NSEnumerator *rawFolders;
NSDictionary *folderData;
NSAutoreleasePool *pool;
NSArray *allFolderPaths;
allFolderPaths = [self allFolderPaths];
rawFolders = [allFolderPaths objectEnumerator];
folders = [NSMutableArray array];
while ((currentFolder = [rawFolders nextObject]))
{
// Using a local pool to avoid using too many file descriptors. This could
@@ -458,55 +549,10 @@ static NSString *inboxFolderName = @"INBOX";
// lots of LDAP connections during its execution.
pool = [[NSAutoreleasePool alloc] init];
currentDecodedFolder = [currentFolder stringByDecodingImap4FolderName];
currentFolderType = [self _folderType: currentFolder];
// Insert folder into folders tree
[self _insertFolder: currentFolder
foldersList: folders];
// We translate the "Other Users" and "Shared Folders" namespaces.
// While we're at it, we also translate the user's mailbox names
// to the full name of the person.
if (otherUsersFolderName && [currentDecodedFolder hasPrefix: [NSString stringWithFormat: @"/%@", otherUsersFolderName]])
{
// We have a string like /Other Users/lmarcotte/... under Cyrus, but we could
// also have something like /shared under Dovecot. So we swap the username only
// if we have one, of course.
pathComponents = [NSMutableArray arrayWithArray: [currentDecodedFolder pathComponents]];
if ([pathComponents count] > 2)
{
login = [pathComponents objectAtIndex: 2];
userManager = [SOGoUserManager sharedUserManager];
fullName = [userManager getCNForUID: login];
[pathComponents removeObjectsInRange: NSMakeRange(0,3)];
currentDisplayName = [NSString stringWithFormat: @"/%@/%@/%@",
[self labelForKey: @"OtherUsersFolderName"],
(fullName != nil ? fullName : login),
[pathComponents componentsJoinedByString: @"/"]];
}
else
{
currentDisplayName = [NSString stringWithFormat: @"/%@%@",
[self labelForKey: @"OtherUsersFolderName"],
[currentDecodedFolder substringFromIndex:
[otherUsersFolderName length]+1]];
}
}
else if (sharedFoldersName && [currentDecodedFolder hasPrefix: [NSString stringWithFormat: @"/%@", sharedFoldersName]])
currentDisplayName = [NSString stringWithFormat: @"/%@%@", [self labelForKey: @"SharedFoldersName"],
[currentDecodedFolder substringFromIndex: [sharedFoldersName length]+1]];
else
currentDisplayName = currentDecodedFolder;
parent = [self _parentForFolder: currentFolder foldersList: allFolderPaths];
folderData = [NSDictionary dictionaryWithObjectsAndKeys:
currentFolder, @"path",
currentFolderType, @"type",
currentDisplayName, @"displayName",
parent, @"parent",
nil];
[folders addObject: folderData];
[pool release];
}
@@ -689,7 +735,7 @@ static NSString *inboxFolderName = @"INBOX";
result = [client lstatus: @"*" flags: [NSArray arrayWithObjects: @"x-guid", nil]];
e = [folderList objectEnumerator];
while ((object = [e nextObject]))
{
if (hasAnnotatemore)
@@ -799,12 +845,12 @@ static NSString *inboxFolderName = @"INBOX";
accounts = [user mailAccounts];
accountIdx = [nameInContainer intValue];
account = [accounts objectAtIndex: accountIdx];
folderName = [[account objectForKey: @"mailboxes"]
folderName = [[account objectForKey: @"specialMailboxes"]
objectForKey: purpose];
if (!folderName && accountIdx > 0)
{
account = [accounts objectAtIndex: 0];
folderName = [[account objectForKey: @"mailboxes"]
folderName = [[account objectForKey: @"specialMailboxes"]
objectForKey: purpose];
}
+4 -4
View File
@@ -56,6 +56,7 @@
- (NSArray *) toManyRelationshipKeys;
- (NSDictionary *) statusForFlags: (NSArray *) flags;
- (unsigned int) unseenCount;
- (NSException *) deleteUIDs: (NSArray *) uids
useTrashFolder: (BOOL *) withTrash
@@ -96,10 +97,9 @@
- (NSString *) davCollectionTag;
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
matchingSyncToken: (NSString *) syncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress;
- (NSArray *) syncTokenFieldsWithProperties: (NSArray *) theProperties
matchingSyncToken: (NSString *) theSyncToken
fromDate: (NSCalendarDate *) theStartDate;
/* flags */
+30 -5
View File
@@ -57,6 +57,7 @@
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserFolder.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/WORequest+SOGo.h>
#import <SOGo/WOResponse+SOGo.h>
@@ -64,6 +65,7 @@
#import "EOQualifier+MailDAV.h"
#import "SOGoMailObject.h"
#import "SOGoMailAccount.h"
#import "SOGoMailAccounts.h"
#import "SOGoMailManager.h"
#import "SOGoMailFolder.h"
#import "SOGoTrashFolder.h"
@@ -801,6 +803,33 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
return status;
}
- (unsigned int) unseenCount
{
NSDictionary *imapResult;
NGImap4Connection *connection;
NGImap4Client *client;
EOQualifier *searchQualifier;
NSArray *searchResult;
unsigned int unseen;
connection = [self imap4Connection];
client = [connection client];
if ([connection selectFolder: [self imap4URL]])
{
searchQualifier
= [EOQualifier qualifierWithQualifierFormat: @"flags = %@ AND not flags = %@",
@"unseen", @"deleted"];
imapResult = [client searchWithQualifier: searchQualifier];
searchResult = [[imapResult objectForKey: @"RawResponse"] objectForKey: @"search"];
unseen = [searchResult count];
}
else
unseen = 0;
return unseen;
}
- (NSArray *) fetchUIDsMatchingQualifier: (id) _q
sortOrdering: (id) _so
{
@@ -2090,7 +2119,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
- (NSArray *) syncTokenFieldsWithProperties: (NSArray *) theProperties
matchingSyncToken: (NSString *) theSyncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress
{
EOQualifier *searchQualifier;
NSMutableArray *allTokens;
@@ -2167,9 +2195,6 @@ _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];
@@ -2180,7 +2205,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
if (highestmodseq == 0)
highestmodseq = 1;
if (highestmodseq > 0 && !initialLoadInProgress)
if (highestmodseq > 0)
{
id uid;
+2 -2
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2013 Inverse inc.
Copyright (C) 2013-2015 Inverse inc.
This file is part of SOGo.
@@ -43,7 +43,7 @@
- (NSString *) color;
+ (NSArray *) labelsFromDefaults: (NSDictionary *) theDefaults
component: (UIxComponent *) theComponent;
component: (id) theComponent;
@end
+2 -3
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2007-2013 Inverse inc.
Copyright (C) 2007-2015 Inverse inc.
This file is part of SOGo.
@@ -65,7 +65,7 @@
+ (NSArray *) labelsFromDefaults: (NSDictionary *) theDefaults
component: (UIxComponent *) theComponent
component: (id) theComponent
{
NSMutableArray *allLabels, *allKeys;
NSString *key, *name;
@@ -81,7 +81,6 @@
key = [allKeys objectAtIndex: i];
values = [theDefaults objectForKey: key];
name = [theComponent commonLabelForKey: [values objectAtIndex: 0]];
label = [[self alloc] initWithName: key
label: name
color: [values objectAtIndex: 1]];
+2 -1
View File
@@ -766,6 +766,7 @@ static BOOL debugSoParts = NO;
[part objectForKey: @"subtype"]];
if (!filename)
{
// We might end up here because of MUA that actually strips the
// Content-Disposition (and thus, the filename) when mails containing
// attachments have been forwarded. Thunderbird (2.x) does just that
@@ -777,7 +778,7 @@ static BOOL debugSoParts = NO;
filename = [NSString stringWithFormat: @"unknown_%@", path];
else if ([mimeType isEqualToString: @"message/rfc822"])
filename = [NSString stringWithFormat: @"email_%@.eml", path];
}
if (filename)
{
+32
View File
@@ -0,0 +1,32 @@
/* CardElement+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef CARDELEMENT_SOGO_H
#define CARDELEMENT_SOGO_H
#import <NGCards/CardElement.h>
@interface CardElement (SOGoExtensions)
- (NSString *) jsonRepresentation;
@end
#endif /* CARDELEMENT_SOGO_H */
+54
View File
@@ -0,0 +1,54 @@
/* CardElement+SOGo.m - this file is part of SOGo
*
* Copyright (C) 2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import "NSDictionary+Utilities.h"
#import "CardElement+SOGo.h"
@implementation CardElement (SOGoExtensions)
/**
* Return the JSON representation of the element as a dictionary with two keys:
* - type: the type of the card element, if it exists;
* - value: the value of the card element.
* @return a JSON string representation of the element.
*/
- (NSString *) jsonRepresentation
{
NSString *v;
NSMutableDictionary *attrs;
attrs = [NSMutableDictionary dictionary];
v = [self value: 0 ofAttribute: @"type"];
if ([v length])
[attrs setObject: v
forKey: @"type"];
[attrs setObject: [self flattenedValuesForKey: @""]
forKey: @"value"];
return [attrs jsonRepresentation];
}
@end /* NGVCard */
+3 -1
View File
@@ -72,6 +72,7 @@ SOGo_HEADER_FILES = \
SOGoGroup.h \
SOGoUser.h \
\
CardElement+SOGo.h \
DOMNode+SOGo.h \
\
WORequest+SOGo.h \
@@ -85,7 +86,7 @@ all::
@touch SOGoBuild.m
SOGo_OBJC_FILES = \
SOGoBuild.m \
SOGoBuild.m \
SOGoProductLoader.m \
\
BSONCodec.m \
@@ -150,6 +151,7 @@ SOGo_OBJC_FILES = \
SOGoGroup.m \
SOGoUser.m \
\
CardElement+SOGo.m \
DOMNode+SOGo.m \
\
WORequest+SOGo.m \
+1
View File
@@ -37,6 +37,7 @@
- (NSString *) shortDateString;
- (NSString *) rfc822DateString;
- (NSString *) iso8601DateString;
+ (id) distantFuture;
+ (id) distantPast;
+30
View File
@@ -21,6 +21,8 @@
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSTimeZone.h>
#import <Foundation/NSValue.h>
#import <NGExtensions/NSCalendarDate+misc.h>
#import "NSCalendarDate+SOGo.h"
@@ -113,6 +115,34 @@ static NSString *rfc822Months[] = {@"", @"Jan", @"Feb", @"Mar", @"Apr",
timeZoneShift];
}
- (NSString *) iso8601DateString
{
char buf[22];
int timeZoneHourShift, timeZoneMinuteShift, tzSeconds;
NSNumber *day, *month, *year, *hour, *minute;
day = [NSNumber numberWithInt: [self dayOfMonth]];
month = [NSNumber numberWithInt: [self monthOfYear]];
year = [NSNumber numberWithInt: [self yearOfCommonEra]];
hour = [NSNumber numberWithInt: [self hourOfDay]];
minute = [NSNumber numberWithInt: [self minuteOfHour]];
tzSeconds = [[self timeZone] secondsFromGMT];
timeZoneHourShift = (tzSeconds / 3600);
tzSeconds -= timeZoneHourShift * 3600;
timeZoneMinuteShift = tzSeconds / 60;
sprintf(buf, "%04d-%02d-%02dT%02d:%02d%+.2d:%02d",
[year intValue],
[month intValue],
[day intValue],
[hour intValue],
[minute intValue],
timeZoneHourShift,
timeZoneMinuteShift);
return [NSString stringWithCString:buf];
}
#define secondsOfDistantFuture 1073741823.0
#define secondsOfDistantPast -1073741823.0
+2 -8
View File
@@ -373,7 +373,6 @@ static int cssEscapingCount;
- (NSString *) asCSSIdentifier
{
NSCharacterSet *numericSet;
NSMutableString *cssIdentifier;
unichar currentChar;
int count, max, idx;
@@ -382,12 +381,10 @@ static int cssEscapingCount;
[self _setupCSSEscaping];
cssIdentifier = [NSMutableString string];
numericSet = [NSCharacterSet decimalDigitCharacterSet];
max = [self length];
if (max > 0)
{
if ([numericSet characterIsMember: [self characterAtIndex: 0]])
if (isdigit([self characterAtIndex: 0]))
// A CSS identifier can't start with a digit; we add an underscore
[cssIdentifier appendString: @"_"];
for (count = 0; count < max; count++)
@@ -418,7 +415,6 @@ static int cssEscapingCount;
- (NSString *) fromCSSIdentifier
{
NSCharacterSet *numericSet;
NSMutableString *newString;
NSString *currentString;
int count, length, max, idx;
@@ -427,14 +423,12 @@ static int cssEscapingCount;
if (!cssEscapingStrings)
[self _setupCSSEscaping];
numericSet = [NSCharacterSet decimalDigitCharacterSet];
newString = [NSMutableString string];
max = [self length];
count = 0;
if (max > 0
&& [self characterAtIndex: 0] == '_'
&& [numericSet characterIsMember: [self characterAtIndex: 1]])
&& isdigit([self characterAtIndex: 1]))
{
/* If the identifier starts with an underscore followed by a digit,
we remove the underscore */
+9 -1
View File
@@ -51,6 +51,8 @@
SOGoTimeFormat = "%H:%M";
SOGoFirstDayOfWeek = 0;
SOGoFirstWeekOfYear = "January1";
SOGoShortDateFormat = "%d-%b-%y";
SOGoLongDateFormat = "%A, %B %d, %Y";
SOGoIMAPServer = "localhost";
SOGoMailDomain = "localhost";
@@ -67,6 +69,8 @@
SOGoSentFolderName = "Sent";
SOGoDraftsFolderName = "Drafts";
SOGoTrashFolderName = "Trash";
SOGoMailComposeMessageType = "html";
SOGoMailDisplayRemoteInlineImages = "never";
SOGoMailAutoSave = "5";
@@ -74,9 +78,11 @@
SOGoCalendarEventsDefaultClassification = "PUBLIC";
SOGoCalendarTasksDefaultClassification = "PUBLIC";
SOGoCalendarDefaultReminder = "NONE";
SOGoDefaultCalendar = "selected";
SOGoFreeBusyDefaultInterval = ( 7, 7 );
SOGoSelectedAddressBook = "personal";
SOGoSearchMinimumWordLength = 2;
SOGoMailLabelsColors = {
@@ -87,6 +93,8 @@
$label5 = ("Later", "#993399");
};
SOGoCalendarCategories = ("Customer", "Calls", "Favorites", "Meeting", "Ideas", "Miscellaneous", "Birthday", "Anniversary", "Vacation", "Travel", "Projects", "Suppliers", "Gifts", "Clients", "Issues", "Business", "Holidays", "Personal", "Status", "Competition", "Follow up", "Public Holiday");
SOGoCalendarCategoriesColors = { "Customer" = "#F0F0F0"; "Calls" = "#F0F0F0"; "Favorites" = "#F0F0F0"; "Meeting" = "#F0F0F0"; "Ideas" = "#F0F0F0"; "Miscellaneous" = "#F0F0F0"; "Birthday" = "#F0F0F0"; "Anniversary" = "#F0F0F0"; "Vacation" = "#F0F0F0"; "Travel" = "#F0F0F0"; "Projects" = "#F0F0F0"; "Suppliers" = "#F0F0F0"; "Gifts" = "#F0F0F0"; "Clients" = "#F0F0F0"; "Issues" = "#F0F0F0"; "Business" = "#F0F0F0"; "Holidays" = "#F0F0F0"; "Personal" = "#F0F0F0"; "Status" = "#F0F0F0"; "Competition" = "#F0F0F0"; "Follow up" = "#F0F0F0"; "Public Holiday" = "#F0F0F0"; };
SOGoSubscriptionFolderFormat = "%{FolderName} (%{UserName} <%{Email}>)";
+1 -3
View File
@@ -1,8 +1,6 @@
/* SOGoDefaultsSource.h - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* 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
+1 -3
View File
@@ -1,8 +1,6 @@
/* SOGoDefaultsSource.m - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* 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
+2 -2
View File
@@ -128,8 +128,8 @@
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
matchingSyncToken: (NSString *) syncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress;
fromDate: (NSCalendarDate *) theStartDate;
/* multiget helper */
- (WOResponse *) performMultigetInContext: (WOContext *) queryContext
+8 -15
View File
@@ -1151,7 +1151,6 @@ static NSArray *childRecordFields = nil;
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
matchingSyncToken: (NSString *) syncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress
{
/* TODO:
- validation:
@@ -1201,17 +1200,13 @@ static NSArray *childRecordFields = nil;
mRecords = [NSMutableArray arrayWithArray: [self _fetchFields: fields
withQualifier: qualifier
ignoreDeleted: YES]];
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]];
}
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]];
records = mRecords;
}
else
@@ -1504,9 +1499,7 @@ static NSArray *childRecordFields = nil;
properties = [self parseDAVRequestedProperties: propElement];
records = [self syncTokenFieldsWithProperties: properties
matchingSyncToken: syncToken
fromDate: nil
initialLoad: NO];
fromDate: nil];
[self _appendComponentProperties: [properties allKeys]
fromRecords: records
matchingSyncToken: [syncToken intValue]
+1 -3
View File
@@ -1,8 +1,6 @@
/* SOGoSQLUserProfile.h - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* 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
+1 -3
View File
@@ -1,8 +1,6 @@
/* SOGoSQLUserProfile.m - this file is part of SOGo
*
* Copyright (C) 2009 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* 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
+1
View File
@@ -99,6 +99,7 @@
- (NSString *) cn;
- (SOGoDateFormatter *) dateFormatterInContext: (WOContext *) context;
- (NSDictionary *) currentDay;
/* defaults */
- (SOGoUserDefaults *) userDefaults;
+59 -3
View File
@@ -394,6 +394,39 @@
return dateFormatter;
}
- (NSDictionary *) currentDay
{
NSCalendarDate *now;
NSDictionary *description, *abbr;
NSDictionary *locale;
SOGoUserDefaults *ud;
WOResourceManager *resMgr;
NSUInteger seconds;
now = [NSCalendarDate calendarDate];
ud = [self userDefaults];
resMgr = [[WOApplication application] resourceManager];
locale = [resMgr localeForLanguageNamed: [ud language]];
[now setTimeZone: [ud timeZone]];
seconds = [now hourOfDay]*3600 + [now minuteOfHour]*60 + [now secondOfMinute];
abbr = [NSDictionary dictionaryWithObjectsAndKeys:
[now descriptionWithCalendarFormat: @"%a" locale: locale], @"weekday",
[now descriptionWithCalendarFormat: @"%b" locale: locale], @"month",
nil];
description = [NSDictionary dictionaryWithObjectsAndKeys:
[now descriptionWithCalendarFormat: @"%A" locale: locale], @"weekday",
[now descriptionWithCalendarFormat: @"%B" locale: locale], @"month",
[now descriptionWithCalendarFormat: @"%d" locale: locale], @"day",
[now descriptionWithCalendarFormat: @"%Y" locale: locale], @"year",
abbr, @"abbr",
[NSNumber numberWithInt: (24*3600 - seconds)], @"secondsBeforeTomorrow",
nil];
return description;
}
- (SOGoUserDefaults *) userDefaults
{
if (!_defaults)
@@ -594,10 +627,10 @@
{
NSString *fullName, *replyTo, *imapLogin, *imapServer, *cImapServer, *signature,
*encryption, *scheme, *action, *query, *customEmail, *defaultEmail, *sieveServer;
NSMutableDictionary *mailAccount, *identity, *mailboxes, *receipts;
NSMutableDictionary *mailAccount, *identity, *mailboxes, *receipts, *mailSettings;
NSNumber *port;
NSMutableArray *identities;
NSArray *mails;
NSArray *mails, *delegates;
NSURL *url, *cUrl;
unsigned int count, max, default_identity;
NSInteger defaultPort;
@@ -786,11 +819,34 @@
forKey: @"Sent"];
[mailboxes setObject: [_defaults trashFolderName]
forKey: @"Trash"];
[mailAccount setObject: mailboxes forKey: @"mailboxes"];
[mailAccount setObject: mailboxes forKey: @"specialMailboxes"];
[mailboxes release];
[mailAccounts addObject: mailAccount];
[mailAccount release];
/* delegates */
mailSettings = [[self userSettings] objectForKey: @"Mail"];
delegates = [mailSettings objectForKey: @"DelegateTo"];
if (!delegates)
delegates = [NSArray array];
else
{
NSMutableArray *allDelegates;
SOGoUser *delegate;
allDelegates = [NSMutableArray array];
for (count = 0; count < [delegates count]; count++)
{
delegate = [SOGoUser userWithLogin: [delegates objectAtIndex: count]];
[allDelegates addObject: [NSDictionary dictionaryWithObjectsAndKeys: [delegates objectAtIndex: count], @"uid",
[delegate cn], @"cn",
[delegate systemEmail], @"c_email", nil]];
}
delegates = allDelegates;
}
[mailAccount setObject: delegates forKey: @"delegates"];
}
- (NSArray *) mailAccounts
+1 -1
View File
@@ -1,6 +1,6 @@
/* SOGoUserDefaults.h - this file is part of SOGo
*
* Copyright (C) 2011-2014 Inverse inc.
* Copyright (C) 2011-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 -1
View File
@@ -1,6 +1,6 @@
/* SOGoUserDefaults.m - 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
+1 -6
View File
@@ -449,8 +449,6 @@ static Class NSNullK;
NSEnumerator *authIDs;
NSString *currentID;
BOOL checkOK;
SOGoSystemDefaults *sd;
NSRange r;
checkOK = NO;
@@ -780,10 +778,7 @@ static Class NSNullK;
NSRange r;
r = [theUID rangeOfString: [NSString stringWithFormat: @"@%@", theDomain]];
// We check if the range is ok here since we could be using DomainLessLogin
if (r.location != NSNotFound)
theUID = [theUID substringToIndex: r.location];
theUID = [theUID substringToIndex: r.location];
}
sogoSources = [[self authenticationSourceIDsInDomain: theDomain] objectEnumerator];
+2 -2
View File
@@ -1,6 +1,5 @@
/*
Copyright (C) 2008-2009 Inverse inc.
Copyright (C) 2005 SKYRIX Software AG
Copyright (C) 2008-2015 Inverse inc.
This file is part of SOGo.
@@ -81,6 +80,7 @@ typedef enum _SOGoUserProfileType {
/* saving changes */
- (BOOL) dirty;
- (BOOL) synchronize;
@end
+5 -2
View File
@@ -1,6 +1,5 @@
/*
Copyright (C) 2008-2009 Inverse inc.
Copyright (C) 2005 SKYRIX Software AG
Copyright (C) 2008-2015 Inverse inc.
This file is part of SOGo.
@@ -323,6 +322,10 @@
}
/* saving changes */
- (BOOL) dirty
{
return defFlags.modified;
}
- (BOOL) synchronize
{
+14 -9
View File
@@ -38,10 +38,23 @@
NSMutableArray *languages;
NSArray *browserLanguages;
SOGoUser *user;
NSString *language;
NSString *language, *theme;
languages = [NSMutableArray array];
user = [self activeUser];
theme = [[self request] formValueForKey: @"theme"];
if ([theme length] > 0)
{
if ([theme hasSuffix: @"/"])
{
// Remove trailing slash
theme = [theme substringToIndex: [theme length] - 1];
}
[languages addObject: [NSString stringWithFormat: @"theme_%@", theme]];
[self setObject: theme forKey: @"theme"];
}
if (!user || [[user login] isEqualToString: @"anonymous"])
{
browserLanguages = [[self request] browserLanguages];
@@ -55,14 +68,6 @@
[languages addObject: language];
}
// if (activeUser && [activeUser language])
// [languages addObject: [activeUser language]];
// if ([self hasSession])
// [languages addObjectsFromArray: [[self session] languages]];
// else
// [languages addObjectsFromArray: [[self request] browserLanguages]];
return languages;
}
+129
View File
@@ -0,0 +1,129 @@
from config import hostname, port, username, password
import webdavlib
import simplejson
import sogoLogin
DEBUG=True
DEBUG=False
class HTTPPreferencesPOST (webdavlib.HTTPPOST):
cookie = None
def prepare_headers (self):
headers = webdavlib.HTTPPOST.prepare_headers(self)
if self.cookie:
headers["Cookie"] = self.cookie
return headers
class HTTPPreferencesGET (webdavlib.HTTPGET):
cookie = None
def prepare_headers (self):
headers = webdavlib.HTTPGET.prepare_headers(self)
if self.cookie:
headers["Cookie"] = self.cookie
return headers
class Carddav:
login = username
passw = password
def __init__(self, otherLogin = None, otherPassword = None):
if otherLogin and otherPassword:
self.login = otherLogin
self.passw = otherPassword
self.client = webdavlib.WebDAVClient(hostname, port)
authCookie = sogoLogin.getAuthCookie(hostname, port, self.login, self.passw)
self.cookie = authCookie
self.cards = None
self.calendars = None
self.fields = None
self.events = None
#- If this is not set, we CAN'T save preferences
self.preferences = None
def _get(self, url):
get = HTTPPreferencesGET(url)
get.cookie = self.cookie
self.client.execute(get)
if DEBUG: print "(url):", url
if DEBUG: print "(status):", get.response["status"]
if DEBUG: print "(body):", get.response['body']
content = simplejson.loads(get.response['body'])
return content
def _post(self, url, data, errormsg="failure POST"):
if DEBUG: print "URL:", url
post = HTTPPreferencesPOST(url, simplejson.dumps(data))
post.content_type = "application/json"
post.cookie = self.cookie
self.client.execute(post)
# Raise an exception if the pref wasn't properly set
if post.response["status"] != 200:
raise Exception ("%s, (code = %d)" % (errormsg, post.response["status"]))
def load_cards(self):
if not self.cards:
url = "/SOGo/so/%s/Contacts/personal/view" % (self.login)
content = self._get(url)
self.cards = content['cards']
return self.cards
def get_cards(self, pattern):
self.load_cards()
return [a for a in self.cards if pattern in a.values()]
def get_card(self, idstr):
url = "/SOGo/so/%s/Contacts/personal/%s/view" % (self.login, idstr)
content = self._get(url)
return content
def save_card(self, card):
url = "/SOGo/so/%s/Contacts/personal/%s/saveAsContact" % (self.login, card['id'])
self._post(url, card, "failure saving card")
def load_calendars(self):
if not self.calendars:
url = "/SOGo/so/%s/Calendar/calendarslist" % (self.login)
content = self._get(url)
self.calendars = content['calendars']
return self.calendars
def get_calendars(self, pattern):
self.load_calendars()
return [a for a in self.calendars if pattern in a.values()]
def get_calendar(self, idstr):
self.load_calendars()
callist = [a for a in self.calendars if a['id'] == idstr]
if len(callist):
return callist[0]
return None
def save_calendar(self, calendar):
url = "/SOGo/so/%s/Contacts/personal/%s/saveAsContact" % (self.login, calendar['id'])
self._post(url, card, "failure saving calendar")
def load_events(self):
if not self.events:
url = "/SOGo/so/%s/Calendar/eventslist" % (self.login)
content = self._get(url)
tmp_events = content['events']
self.fields = content['fields']
self.events = [dict(zip(self.fields, event)) for event in tmp_events]
return self.events
def newguid(self, folderpath):
url = "/SOGo/so/%s/%s/newguid" % (self.login, folderpath)
content = self._get(url)
return content['id']
def save_event(self, event, folder, gid):
#url = "/SOGo/so/%s/%s/%s/save" % (self.login, event['c_folder'], event['c_name'])
url = "/SOGo/so/%s/%s/%s/saveAsAppointment" % (self.login, folder, gid)
self._post(url, event, "failure saving event")
+119 -96
View File
@@ -7,6 +7,9 @@ import simplejson
import sogoLogin
DEBUG=False
#DEBUG=True
# must be kept in sync with SoObjects/SOGo/SOGoDefaults.plist
# this should probably be fetched magically...
@@ -18,122 +21,142 @@ SOGoSupportedLanguages = [ "Arabic", "Basque", "Catalan", "ChineseTaiwan", "Czec
daysBetweenResponseList=[1,2,3,5,7,14,21,30]
class HTTPPreferencesPOST (webdavlib.HTTPPOST):
cookie = None
def prepare_headers (self):
headers = webdavlib.HTTPPOST.prepare_headers(self)
if self.cookie:
headers["Cookie"] = self.cookie
return headers
cookie = None
def prepare_headers (self):
headers = webdavlib.HTTPPOST.prepare_headers(self)
if self.cookie:
headers["Cookie"] = self.cookie
return headers
class HTTPPreferencesGET (webdavlib.HTTPGET):
cookie = None
cookie = None
def prepare_headers (self):
headers = webdavlib.HTTPGET.prepare_headers(self)
if self.cookie:
headers["Cookie"] = self.cookie
return headers
def prepare_headers (self):
headers = webdavlib.HTTPGET.prepare_headers(self)
if self.cookie:
headers["Cookie"] = self.cookie
return headers
class preferences:
login = username
passw = password
login = username
passw = password
def __init__(self, otherLogin = None, otherPassword = None):
if otherLogin and otherPassword:
self.login = otherLogin
self.passw = otherPassword
def __init__(self, otherLogin = None, otherPassword = None):
if otherLogin and otherPassword:
self.login = otherLogin
self.passw = otherPassword
self.client = webdavlib.WebDAVClient(hostname, port)
self.client = webdavlib.WebDAVClient(hostname, port)
authCookie = sogoLogin.getAuthCookie(hostname, port, self.login, self.passw)
self.cookie = authCookie
authCookie = sogoLogin.getAuthCookie(hostname, port, self.login, self.passw)
self.cookie = authCookie
# map between preferences/jsonDefaults and the webUI names
# should probably be unified...
self.preferencesMap = {
"SOGoLanguage": "language",
"SOGoTimeZone": "timezone",
"SOGoSieveFilters": "sieveFilters",
#- If this is not set, we CAN'T save preferences
self.preferences = None
# Vacation stuff
"Vacation": "enableVacation", # to disable, don't specify it
"autoReplyText": "autoReplyText", # string
"autoReplyEmailAddresses": "autoReplyEmailAddresses", # LIST
"daysBetweenResponse": "daysBetweenResponsesList",
"ignoreLists": "ignoreLists", #bool
def find_key(self, d, key):
if key in d:
return d
subdicts = [a[1] for a in d.iteritems() if type(a[1]) == dict]
for subd in subdicts:
ret = self.find_key(subd, key)
if ret:
return ret
return None
# forward stuff
"Forward": "enableForward", # to disable, don't specify it
"forwardAddress": "forwardAddress",
"keepCopy": "forwardKeepCopy",
def load_preferences(self):
defaults = self.get_defaults()
settings = self.get_settings()
self.preferences = {'defaults': defaults, 'settings': settings}
#print "LOAD PREFS:", self.preferences
# Calendar stuff
"enablePreventInvitations": "preventInvitations",
"PreventInvitations": "PreventInvitations",
"whiteList": "whiteList",
}
def get(self, preference=None):
if not self.preferences:
self.load_preferences()
#- Want the whole thing
if not preference:
return self.preferences
else:
tmpdict = self.find_key(self.preferences, preference)
if tmpdict:
return tmpdict[preference]
else:
return None
def _get(self, subtype='jsonDefault', preference=None):
url = "/SOGo/so/%s/%s" % (self.login, subtype)
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']
content = simplejson.loads(get.response['body'])
result = None
try:
if preference:
result = content[preference]
else:
result = content
except:
pass
return result
def set(self, preference, value=None):
# if preference is a dict, set all prefs found in the dict
content=""
if isinstance(preference, dict):
for k,v in preference.items():
content+="%s=%s&" % (self.preferencesMap[k], urllib.quote(v))
else:
# assume it is a str
formKey = self.preferencesMap[preference]
content = "%s=%s&hasChanged=1" % (formKey, urllib.quote(value))
def get_defaults(self, preference=None):
return self._get('jsonDefaults', preference)
def get_settings(self, preference=None):
return self._get('jsonSettings', preference)
url = "/SOGo/so/%s/preferences" % self.login
def set_nosave(self, preference, value=None):
# First check if we did a get, if not, must get first
if not self.preferences:
self.load_preferences()
post = HTTPPreferencesPOST (url, content)
post.content_type = "application/x-www-form-urlencoded"
post.cookie = self.cookie
# 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
subdict[preference] = value
self.client.execute (post)
def set(self, preference, value=None):
self.set_nosave(preference, value)
self.save()
# Raise an exception if the pref wasn't properly set
if post.response["status"] != 200:
raise Exception ("failure setting prefs, (code = %d)" \
def set_multiple(self, preferences={}):
for key, value in preferences.iteritems():
self.set_nosave(key, value)
self.save()
def set_or_create(self, preference, value, paths=['defaults']):
if not self.preferences:
self.load_preferences()
subdict = self.find_key(self.preferences, preference)
#- Pref is not set
if not subdict:
subdict = self.preferences
for path in paths:
subdict = subdict.setdefault(path, {})
subdict[preference] = value
def save(self):
url = "/SOGo/so/%s/Preferences/save" % self.login
post = HTTPPreferencesPOST(url, simplejson.dumps(self.preferences))
post.content_type = "application/json"
post.cookie = self.cookie
self.client.execute(post)
# Raise an exception if the pref wasn't properly set
if post.response["status"] != 200:
raise Exception ("failure setting prefs, (code = %d)" \
% post.response["status"])
def get(self, preference=None):
url = "/SOGo/so/%s/preferences/jsonDefaults" % self.login
get = HTTPPreferencesGET (url)
get.cookie = self.cookie
self.client.execute (get)
content = simplejson.loads(get.response['body'])
result = None
try:
if preference:
result = content[preference]
else:
result = content
except:
pass
return result
def get_settings(self, preference=None):
url = "/SOGo/so/%s/preferences/jsonSettings" % self.login
get = HTTPPreferencesGET (url)
get.cookie = self.cookie
self.client.execute (get)
content = simplejson.loads(get.response['body'])
result = None
try:
if preference:
result = content[preference]
else:
result = content
except:
pass
return result
# Simple main to test this class
if __name__ == "__main__":
p = preferences ()
print p.get ("SOGoLanguage")
p.set ("SOGoLanguage", SOGoSupportedLanguages.index("French"))
print p.get ("SOGoLanguage")
p = preferences ()
print p.get ("SOGoLanguage")
p.set ("SOGoLanguage", SOGoSupportedLanguages.index("French"))
print p.get ("SOGoLanguage")
+3 -2
View File
@@ -14,8 +14,9 @@ def getAuthCookie(hostname, port, username, password) :
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cjar))
urllib2.install_opener(opener)
creds = urllib.urlencode([("userName",username), ("password", password)])
req = urllib2.Request("http://%s:%s/SOGo/connect" % (hostname, port), creds)
creds2 = simplejson.dumps({"userName":username, "password": password})
req = urllib2.Request("http://%s:%s/SOGo/connect" % (hostname, port), creds2,
{'Content-Type': 'application/json'})
fd = urllib2.urlopen(req)
+156
View File
@@ -0,0 +1,156 @@
#!/usr/bin/python
from config import hostname, port, username, password
import carddav
import sogotests
import unittest
import webdavlib
import time
class JsonDavEventTests(unittest.TestCase):
def setUp(self):
self._connect_as_user()
def _connect_as_user(self, newuser=username, newpassword=password):
self.dv = carddav.Carddav(newuser, newpassword)
def _create_new_event(self, path):
gid = self.dv.newguid(path)
event = {'startDate': "2015-12-25",
'startTime': "10:00",
'endDate': "2015-12-25",
'endTime': "23:00",
'isTransparent': 0,
'sendAppointmentNotifications': 0,
'summary': "Big party",
'alarm': {'action': 'display',
'quantity': 10,
'unit': "MINUTES",
'reference': "BEFORE",
'relation': "START",
'email': "sogo1@example.com"},
'organizer': {'name': u"Balthazar C\xe9sar",
'email': "sogo2@example.com"},
'c_name': gid,
'c_folder': path
}
return (event, path, gid)
def _get_dav_data(self, filename, user=username, passwd=password):
w = webdavlib.WebDAVClient(hostname, port, user, passwd)
query = webdavlib.HTTPGET("http://localhost/SOGo/dav/%s/Calendar/personal/%s" % (username, filename))
w.execute(query)
self.assertEquals(query.response['status'], 200)
return query.response['body'].split("\r\n")
def _get_dav_field(self, davdata, fieldname):
try:
data = [a.split(':')[1] for a in davdata if fieldname in a][0]
except IndexError:
data = ''
return data
def test_create_new_event(self):
path = 'Calendar/personal'
(event, folder, gid) = self._create_new_event(path)
#print "Saving Event to:", folder, gid
self.dv.save_event(event, folder, gid)
#- Get the event back with JSON
self._connect_as_user()
self.dv.load_events()
elist = [e for e in self.dv.events if e['c_name'] == gid]
#- MUST have this event -- only once
self.assertEquals(len(elist), 1)
strdate = "%d-%.02d-%.02d" % time.gmtime(elist[0]['c_startdate'])[0:3]
self.assertEquals(strdate, event['startDate'])
#- Get the event back with DAV
dav = self._get_dav_data(gid, username, password)
self.assertEquals(self._get_dav_field(dav, 'SUMMARY:'), event['summary'])
class JsonDavPhoneTests(unittest.TestCase):
def setUp(self):
self._connect_as_user()
self.newphone = [{'type': 'home', 'value': '123.456.7890'}]
self.newphones_difftype = [{'type': 'home', 'value': '123.456.7890'},
{'type': 'work', 'value': '987.654.3210'},
{'type': 'fax', 'value': '555.666.7777'}]
self.newphones_sametype = [{'type': 'work', 'value': '123.456.7890'},
{'type': 'work', 'value': '987.654.3210'}]
# Easier to erase them all in tearDown
self.allphones = list(self.newphone)
self.allphones.extend(self.newphones_difftype)
self.allphones.extend(self.newphones_sametype)
def tearDown(self):
self._connect_as_user()
self._get_card()
#- Remove the phones we just added
for phone in self.allphones:
try:
self.card['phones'].pop(self.card['phones'].index(phone))
except ValueError:
#print "Can't find", phone
pass
self._save_card()
def _connect_as_user(self, newuser=username, newpassword=password):
self.dv = carddav.Carddav(newuser, newpassword)
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'])
def _save_card(self, card=None):
if card:
self.dv.save_card(card)
else:
self.dv.save_card(self.card)
def _get_dav_data(self, filename, user=username, passwd=password):
w = webdavlib.WebDAVClient(hostname, port, user, passwd)
query = webdavlib.HTTPGET("http://localhost/SOGo/dav/%s/Contacts/personal/%s" % (username, filename))
w.execute(query)
self.assertEquals(query.response['status'], 200)
return query.response['body'].split("\r\n")
def _phone_to_dav_str(self, phonedict):
return "TEL;TYPE=%s:%s" % (phonedict['type'], phonedict['value'])
def _testMultiplePhones(self, phones):
""" Add Multiple Phones to Contact JSON and verify with DAV """
#- Use JSON to get CARD and add a phone and save it back
self._get_card()
oldphones = self.card['phones']
oldphones.extend(phones)
self._save_card()
#- Make sure that the phone is there when using JSON
self._connect_as_user()
self._get_card()
#print "C:::", self.card
testphones = self.card['phones']
#print "P1:", oldphones
#print "P2:", testphones
self.assertEquals(sorted(oldphones), sorted(testphones))
#- Verify that DAV has the same values
dav = self._get_dav_data(self.card['id'], username, password)
for phone in phones:
found = dav.index(self._phone_to_dav_str(phone))
self.assertTrue(found > 0)
def testSinglePhone(self):
self._testMultiplePhones(self.newphone)
def testMultipleDifferentPhones(self):
self._testMultiplePhones(self.newphones_difftype)
def testMultipleSameTypePhones(self):
self._testMultiplePhones(self.newphones_sametype)
if __name__ == "__main__":
sogotests.runTests()
+17 -13
View File
@@ -9,6 +9,17 @@ import utilities
class preferencesTest(unittest.TestCase):
def setUp(self):
self.prefs = preferences.preferences()
# because if not set in vacation will not be found later
# we must make sure they are there at the start
self.prefs.set_or_create("autoReplyText", '', ["defaults", "Vacation"])
self.prefs.set_or_create("PreventInvitations", 0, ["settings", "Calendar"])
self.prefs.set_or_create("PreventInvitationsWhitelist", [], ["settings", "Calendar"])
def tearDown(self):
self.prefs.set("autoReplyText", "")
def _setTextPref(self, prefText = None ):
""" set a text preference to a known value """
self.prefs.set("autoReplyText", prefText)
@@ -19,15 +30,8 @@ class preferencesTest(unittest.TestCase):
self.assertEqual(prefData["autoReplyText"], prefText,
"%s != %s" % (prefData["autoReplyText"], prefText))
def setUp(self):
self.prefs = preferences.preferences()
def tearDown(self):
self.prefs.set("autoReplyText", "")
def testSetTextPreferences(self):
""" Set/get a text preference - normal characters"""
self._setTextPref("defaultText")
def testSetTextPreferencesWeirdChars(self):
@@ -37,17 +41,17 @@ class preferencesTest(unittest.TestCase):
def testSetPreventInvitation(self):
""" Set/get the PreventInvitation pref"""
self.prefs.set('PreventInvitations', '0')
notset = self.prefs.get_settings('')['Calendar']['PreventInvitations']
self.prefs.set('PreventInvitations', 0)
notset = self.prefs.get('Calendar')['PreventInvitations']
self.assertEqual(notset, 0)
self.prefs.set('enablePreventInvitations', '0')
isset = self.prefs.get_settings('')['Calendar']['PreventInvitations']
self.prefs.set('PreventInvitations', 1)
isset = self.prefs.get('Calendar')['PreventInvitations']
self.assertEqual(isset, 1)
def testPreventInvitationsWhiteList(self):
"""Add to the PreventInvitations Whitelist"""
self.prefs.set("whiteList", simplejson.dumps(white_listed_attendee))
whitelist = self.prefs.get_settings('Calendar')['PreventInvitationsWhitelist']
self.prefs.set("PreventInvitationsWhitelist", white_listed_attendee)
whitelist = self.prefs.get('Calendar')['PreventInvitationsWhitelist']
self.assertEqual(whitelist, white_listed_attendee)
+15 -7
View File
@@ -26,18 +26,26 @@ class preventInvitationsTest(unittest.TestCase):
def setUp(self):
self.prefs = preferences.preferences(attendee1, attendee1_password)
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)
def tearDown(self):
self.prefs.set("autoReplyText", "")
self.prefs.set('PreventInvitations', '0')
self.prefs.set("whiteList", "")
self.prefs.set('PreventInvitations', 0)
self.prefs.set("PreventInvitationsWhitelist", None)
#- Manual Cleanup, not called because classs is not derived from unittest
self.caldav.tearDown()
def not_test_empty_string_instead_of_null(self):
self.prefs.set('PreventInvitationsWhitelist', "")
def testDontPreventInvitation(self):
""" Set/get the PreventInvitation pref"""
#- First accept the invitation
self.prefs.set('PreventInvitations', '0')
self.prefs.set('PreventInvitations', 0)
notset = self.prefs.get_settings('')['Calendar']['PreventInvitations']
self.assertEqual(notset, 0)
self.caldav.AddAttendee()
@@ -46,7 +54,7 @@ class preventInvitationsTest(unittest.TestCase):
def testPreventInvitation(self):
""" Set PreventInvitation and don't accept the Invitation"""
#- Second, enable PreventInviation and refuse it
self.prefs.set('enablePreventInvitations', '0')
self.prefs.set('PreventInvitations', 1)
isset = self.prefs.get_settings('')['Calendar']['PreventInvitations']
self.assertEqual(isset, 1)
self.caldav.AddAttendee(409)
@@ -55,13 +63,13 @@ class preventInvitationsTest(unittest.TestCase):
def testPreventInvitationWhiteList(self):
""" Set PreventInvitation add to WhiteList and accept the Invitation"""
#- First, add the Organiser to the Attendee's whitelist
self.prefs.set('enablePreventInvitations', '0')
self.prefs.set("whiteList", simplejson.dumps(white_listed_attendee))
self.prefs.set('PreventInvitations', 1)
self.prefs.set("PreventInvitationsWhitelist", simplejson.dumps(white_listed_attendee))
whitelist = self.prefs.get_settings('Calendar')['PreventInvitationsWhitelist']
self.assertEqual(whitelist, white_listed_attendee)
#- Second, try again to invite, it should work
self.prefs.set('enablePreventInvitations', '0')
self.prefs.set('PreventInvitations', 1)
isset = self.prefs.get_settings('')['Calendar']['PreventInvitations']
self.assertEqual(isset, 1)
self.caldav.AddAttendee()
Regular → Executable
+46 -36
View File
@@ -16,17 +16,18 @@ sieve_simple_filter="""require ["fileinto"];\r\nif anyof (header :contains "subj
class sieveTest(unittest.TestCase):
def _killFilters(self):
filtersKill={}
# kill existing filters
filtersKill["SOGoSieveFilters"]= "[]"
# vacation filters
filtersKill["autoReplyText"] = ""
filtersKill["autoReplyEmailAddresses"] = ""
# forwarding filters
filtersKill["forwardAddress"] = ""
self.prefs=preferences.preferences()
self.prefs.set(filtersKill)
# kill existing filters
self.prefs.set_or_create("SOGoSieveFilters", [], ["defaults"])
# vacation filters
self.prefs.set_or_create("autoReplyText", "", ["defaults", "Vacation"])
self.prefs.set_or_create("autoReplyEmailAddresses", [], ["defaults", "Vacation"])
self.prefs.set_or_create("daysBetweenResponse", 7, ["defaults", "Vacation"])
self.prefs.set_or_create("ignoreLists", 0, ["defaults", "Vacation"])
self.prefs.set_or_create("enabled", 0, ["defaults", "Vacation"])
# forwarding filters
self.prefs.set_or_create("forwardAddress", [], ["defaults", "Forward"])
self.prefs.set_or_create("keepCopy", 0, ["defaults", "Forward"])
def setUp(self):
ret = ""
@@ -54,7 +55,7 @@ class sieveTest(unittest.TestCase):
for (script, isActive) in sieveScriptList:
if (script == "sogo"):
sieveFoundsogo=1
sieveFoundsogo=1
self.assertEqual(isActive, True, "sogo sieve script is not active!")
(ret, createdSieveScript) = self.ms.getscript(script)
self.assertEqual(ret, "OK", "Couldn't get sogo sieve script")
@@ -73,13 +74,16 @@ class sieveTest(unittest.TestCase):
"days": preferences.daysBetweenResponseList[daysSelect],
}
filterAdd = {"Vacation":"1",
"autoReplyText": vacation_msg,
"daysBetweenResponse": "%d" % daysSelect,
"autoReplyEmailAddresses": self.user_email,
}
# Enabling Vacation now is an 'enabled' setting in the subdict Vacation
# We need to get that subdict first -- next save/set will also save this
vacation = self.prefs.get("Vacation")
vacation['enabled'] = 1
self.prefs.set_nosave("autoReplyText", vacation_msg)
self.prefs.set_nosave("daysBetweenResponse", "%d" % preferences.daysBetweenResponseList[daysSelect])
self.prefs.set_nosave("autoReplyEmailAddresses", [self.user_email])
self.prefs.save()
self.prefs.set(filterAdd)
createdSieveScript=self._getSogoSieveScript()
self.assertEqual(sieveScript, createdSieveScript)
@@ -94,14 +98,17 @@ class sieveTest(unittest.TestCase):
"days": preferences.daysBetweenResponseList[daysSelect],
}
filterAdd = {"Vacation":"1",
"autoReplyText": vacation_msg,
"daysBetweenResponse": "%d" % daysSelect,
"autoReplyEmailAddresses": self.user_email,
"ignoreLists": "1",
}
# Enabling Vacation now is an 'enabled' setting in the subdict Vacation
# We need to get that subdict first -- next save/set will also save this
vacation = self.prefs.get("Vacation")
vacation['enabled'] = 1
self.prefs.set_nosave("autoReplyText", vacation_msg)
self.prefs.set_nosave("daysBetweenResponse", "%d" % preferences.daysBetweenResponseList[daysSelect])
self.prefs.set_nosave("autoReplyEmailAddresses", [self.user_email])
self.prefs.set_nosave("ignoreLists", 1)
self.prefs.save()
self.prefs.set(filterAdd)
createdSieveScript=self._getSogoSieveScript()
self.assertEqual(sieveScript, createdSieveScript)
@@ -112,11 +119,13 @@ class sieveTest(unittest.TestCase):
sieveScript = sieve_simple_forward % { "redirect_mailaddr": redirect_mailaddr }
filterAdd = { "Forward": "1",
"forwardAddress": redirect_mailaddr,
}
# Enabling Forward now is an 'enabled' setting in the subdict Forward
# We need to get that subdict first -- next save/set will also save this
forward = self.prefs.get("Forward")
forward['enabled'] = 1
self.prefs.set("forwardAddress", [redirect_mailaddr])
self.prefs.set(filterAdd)
createdSieveScript = self._getSogoSieveScript()
self.assertEqual(sieveScript, createdSieveScript)
@@ -126,12 +135,15 @@ class sieveTest(unittest.TestCase):
sieveScript = sieve_forward_keep % { "redirect_mailaddr": redirect_mailaddr }
filterAdd = { "Forward": "1",
"forwardAddress": redirect_mailaddr,
"keepCopy": "1",
}
# Enabling Forward now is an 'enabled' setting in the subdict Forward
# We need to get that subdict first -- next save/set will also save this
forward = self.prefs.get("Forward")
forward['enabled'] = 1
self.prefs.set_nosave("forwardAddress", [redirect_mailaddr])
self.prefs.set_nosave("keepCopy", 1)
self.prefs.save()
self.prefs.set(filterAdd)
createdSieveScript = self._getSogoSieveScript()
self.assertEqual(sieveScript, createdSieveScript)
@@ -142,10 +154,8 @@ class sieveTest(unittest.TestCase):
sieveScript=sieve_simple_filter % { "subject": subject, "folderName": folderName }
filterAdd = { "SOGoSieveFilters": """[{"active": true, "actions": [{"method": "fileinto", "argument": "Sent"}], "rules": [{"operator": "contains", "field": "subject", "value": "%s"}], "match": "any", "name": "%s"}]""" % (subject, folderName)
}
self.prefs.set("SOGoSieveFilters", [{"active": True, "actions": [{"method": "fileinto", "argument": "Sent"}], "rules": [{"operator": "contains", "field": "subject", "value": subject}], "match": "any", "name": folderName}])
self.prefs.set(filterAdd)
createdSieveScript = self._getSogoSieveScript()
self.assertEqual(sieveScript, createdSieveScript)
-1
View File
@@ -110,7 +110,6 @@ class WebDAVClient:
query.set_response(self.conn.getresponse());
except httplib.BadStatusLine, e:
print e
time.sleep(3)
query.set_response(self.conn.getresponse());
query.duration = time.time() - query.start
-2
View File
@@ -22,8 +22,6 @@ $(TEST_TOOL)_OBJC_FILES += \
TestSBJsonParser.m \
\
TestNGMimeAddressHeaderFieldGenerator.m \
TestNGMimeMessageGenerator.m \
\
TestNSData+Crypto.m \
TestNSString+Crypto.m \
TestNSString+URLEscaping.m \
-4
View File
@@ -25,8 +25,6 @@
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSData.h>
#import <Foundation/NSValue.h>
@class NSArray;
@@ -54,8 +52,6 @@
- (BOOL) run;
- (NSString*) stringFromDiffBetween: (NSString*) str1
and: (NSString*) str2;
@end
#define test(c) { \
-75
View File
@@ -185,79 +185,4 @@ 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
@@ -1,134 +0,0 @@
/* 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
Loaded 100 of 1067 files, more files were not shown because too many files have changed in this diff. Show more