Compare commits

...
Author SHA1 Message Date
Luc Charland 42eb9e51b4 Reworked saving Events filter and added Tasks filter 2015-08-06 14:28:54 -04:00
Francis Lachapelle 8690cee221 Merge pull request #96 from inverse-inc/feature/saveSortEventsTasks
Feature/save sort events tasks
2015-08-06 14:26:33 -04:00
Luc Charland 7bb551a4ad Added saving of sort order for Events and Tasks 2015-08-06 13:48:39 -04:00
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
1236 changed files with 38593 additions and 69737 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
+22
View File
@@ -25,8 +25,10 @@ trans.pl = UI/MailerUI/Polish.lproj/Localizable.strings
trans.pt_BR = UI/MailerUI/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/MailerUI/Russian.lproj/Localizable.strings
trans.sk = UI/MailerUI/Slovak.lproj/Localizable.strings
trans.sl_SI = UI/MailerUI/Slovenian.lproj/Localizable.strings
trans.sv = UI/MailerUI/Swedish.lproj/Localizable.strings
trans.uk = UI/MailerUI/Ukrainian.lproj/Localizable.strings
trans.zh_TW = UI/MailerUI/ChineseTaiwan.lproj/Localizable.strings
[sogo.ui-preferencesui]
source_file = UI/PreferencesUI/English.lproj/Localizable.strings
@@ -52,8 +54,10 @@ trans.pl = UI/PreferencesUI/Polish.lproj/Localizable.strings
trans.pt_BR = UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/PreferencesUI/Russian.lproj/Localizable.strings
trans.sk = UI/PreferencesUI/Slovak.lproj/Localizable.strings
trans.sl_SI = UI/PreferencesUI/Slovenian.lproj/Localizable.strings
trans.sv = UI/PreferencesUI/Swedish.lproj/Localizable.strings
trans.uk = UI/PreferencesUI/Ukrainian.lproj/Localizable.strings
trans.zh_TW = UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings
[sogo.ui-scheduler]
source_file = UI/Scheduler/English.lproj/Localizable.strings
@@ -79,8 +83,10 @@ trans.pl = UI/Scheduler/Polish.lproj/Localizable.strings
trans.pt_BR = UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/Scheduler/Russian.lproj/Localizable.strings
trans.sk = UI/Scheduler/Slovak.lproj/Localizable.strings
trans.sl_SI = UI/Scheduler/Slovenian.lproj/Localizable.strings
trans.sv = UI/Scheduler/Swedish.lproj/Localizable.strings
trans.uk = UI/Scheduler/Ukrainian.lproj/Localizable.strings
trans.zh_TW = UI/Scheduler/ChineseTaiwan.lproj/Localizable.strings
[sogo.ui-contacts]
source_file = UI/Contacts/English.lproj/Localizable.strings
@@ -106,8 +112,10 @@ trans.pl = UI/Contacts/Polish.lproj/Localizable.strings
trans.pt_BR = UI/Contacts/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/Contacts/Russian.lproj/Localizable.strings
trans.sk = UI/Contacts/Slovak.lproj/Localizable.strings
trans.sl_SI = UI/Contacts/Slovenian.lproj/Localizable.strings
trans.sv = UI/Contacts/Swedish.lproj/Localizable.strings
trans.uk = UI/Contacts/Ukrainian.lproj/Localizable.strings
trans.zh_TW = UI/Contacts/ChineseTaiwan.lproj/Localizable.strings
[sogo.ui-mainui]
source_file = UI/MainUI/English.lproj/Localizable.strings
@@ -133,8 +141,10 @@ trans.pl = UI/MainUI/Polish.lproj/Localizable.strings
trans.pt_BR = UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/MainUI/Russian.lproj/Localizable.strings
trans.sk = UI/MainUI/Slovak.lproj/Localizable.strings
trans.sl_SI = UI/MainUI/Slovenian.lproj/Localizable.strings
trans.sv = UI/MainUI/Swedish.lproj/Localizable.strings
trans.uk = UI/MainUI/Ukrainian.lproj/Localizable.strings
trans.zh_TW = UI/MainUI/ChineseTaiwan.lproj/Localizable.strings
[sogo.ui-common]
source_file = UI/Common/English.lproj/Localizable.strings
@@ -160,8 +170,10 @@ trans.pl = UI/Common/Polish.lproj/Localizable.strings
trans.pt_BR = UI/Common/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/Common/Russian.lproj/Localizable.strings
trans.sk = UI/Common/Slovak.lproj/Localizable.strings
trans.sl_SI = UI/Common/Slovenian.lproj/Localizable.strings
trans.sv = UI/Common/Swedish.lproj/Localizable.strings
trans.uk = UI/Common/Ukrainian.lproj/Localizable.strings
trans.zh_TW = UI/Common/ChineseTaiwan.lproj/Localizable.strings
[sogo.ui-administrationui]
source_file = UI/AdministrationUI/English.lproj/Localizable.strings
@@ -187,8 +199,10 @@ trans.pl = UI/AdministrationUI/Polish.lproj/Localizable.strings
trans.pt_BR = UI/AdministrationUI/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/AdministrationUI/Russian.lproj/Localizable.strings
trans.sk = UI/AdministrationUI/Slovak.lproj/Localizable.strings
trans.sl_SI = UI/AdministrationUI/Slovenian.lproj/Localizable.strings
trans.sv = UI/AdministrationUI/Swedish.lproj/Localizable.strings
trans.uk = UI/AdministrationUI/Ukrainian.lproj/Localizable.strings
trans.zh_TW = UI/AdministrationUI/ChineseTaiwan.lproj/Localizable.strings
[sogo.soobjects-appointments]
source_file = SoObjects/Appointments/English.lproj/Localizable.strings
@@ -214,8 +228,10 @@ trans.pl = SoObjects/Appointments/Polish.lproj/Localizable.strings
trans.pt_BR = SoObjects/Appointments/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = SoObjects/Appointments/Russian.lproj/Localizable.strings
trans.sk = SoObjects/Appointments/Slovak.lproj/Localizable.strings
trans.sl_SI = SoObjects/Appointments/Slovenian.lproj/Localizable.strings
trans.sv = SoObjects/Appointments/Swedish.lproj/Localizable.strings
trans.uk = SoObjects/Appointments/Ukrainian.lproj/Localizable.strings
trans.zh_TW = SoObjects/Appointments/ChineseTaiwan.lproj/Localizable.strings
[sogo.soobjects-contacts]
source_file = SoObjects/Contacts/English.lproj/Localizable.strings
@@ -241,8 +257,10 @@ trans.pl = SoObjects/Contacts/Polish.lproj/Localizable.strings
trans.pt_BR = SoObjects/Contacts/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = SoObjects/Contacts/Russian.lproj/Localizable.strings
trans.sk = SoObjects/Contacts/Slovak.lproj/Localizable.strings
trans.sl_SI = SoObjects/Contacts/Slovenian.lproj/Localizable.strings
trans.sv = SoObjects/Contacts/Swedish.lproj/Localizable.strings
trans.uk = SoObjects/Contacts/Ukrainian.lproj/Localizable.strings
trans.zh_TW = SoObjects/Contacts/ChineseTaiwan.lproj/Localizable.strings
[sogo.soobjects-mailer]
source_file = SoObjects/Mailer/English.lproj/Localizable.strings
@@ -268,8 +286,10 @@ trans.pl = SoObjects/Mailer/Polish.lproj/Localizable.strings
trans.pt_BR = SoObjects/Mailer/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = SoObjects/Mailer/Russian.lproj/Localizable.strings
trans.sk = SoObjects/Mailer/Slovak.lproj/Localizable.strings
trans.sl_SI = SoObjects/Mailer/Slovenian.lproj/Localizable.strings
trans.sv = SoObjects/Mailer/Swedish.lproj/Localizable.strings
trans.uk = SoObjects/Mailer/Ukrainian.lproj/Localizable.strings
trans.zh_TW = SoObjects/Mailer/ChineseTaiwan.lproj/Localizable.strings
[sogo.ui-mailpartviewers]
source_file = UI/MailPartViewers/English.lproj/Localizable.strings
@@ -295,5 +315,7 @@ trans.pl = UI/MailPartViewers/Polish.lproj/Localizable.strings
trans.pt_BR = UI/MailPartViewers/BrazilianPortuguese.lproj/Localizable.strings
trans.ru = UI/MailPartViewers/Russian.lproj/Localizable.strings
trans.sk = UI/MailPartViewers/Slovak.lproj/Localizable.strings
trans.sl_SI = UI/MailPartViewers/Slovenian.lproj/Localizable.strings
trans.sv = UI/MailPartViewers/Swedish.lproj/Localizable.strings
trans.uk = UI/MailPartViewers/Ukrainian.lproj/Localizable.strings
trans.zh_TW = UI/MailPartViewers/ChineseTaiwan.lproj/Localizable.strings
+5 -3
View File
@@ -131,10 +131,12 @@ static NSArray *easCommandParameters = nil;
}
}
self = [[[NSString alloc] initWithBytes:buf length:(j) * sizeof(wchar_t) encoding:NSUTF32StringEncoding] autorelease];
self = [[NSString alloc] initWithBytesNoCopy: buf
length: (j*sizeof(wchar_t))
encoding: NSUTF32StringEncoding
freeWhenDone: YES];
if (buf) free(buf);
return self;
return [self autorelease];
}
- (NSString *) sanitizedServerIdWithType: (SOGoMicrosoftActiveSyncFolderType) folderType
+80 -35
View File
@@ -283,7 +283,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
serverId = [NSString stringWithFormat: @"%@.ics", [theCollection globallyUniqueObjectId]];
sogoObject = [[SOGoTaskObject alloc] initWithName: serverId
inContainer: theCollection];
o = [sogoObject component: YES secure: NO];
o = [sogoObject component: YES secure: NO];
}
break;
case ActiveSyncMailFolder:
@@ -311,10 +311,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syncCache = [folderMetadata objectForKey: @"SyncCache"];
dateCache = [folderMetadata objectForKey: @"DateCache"];
[syncCache setObject: [folderMetadata objectForKey: @"SyncKey"] forKey: serverId];
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
[dateCache setObject: [NSCalendarDate date] forKey: serverId];
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
}
}
@@ -365,6 +365,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSString *serverId;
NSArray *changes;
id aChange, o, sogoObject;
NSMutableDictionary *folderMetadata, *syncCache;
int i;
@@ -372,6 +373,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ([changes count])
{
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
syncCache = [folderMetadata objectForKey: @"SyncCache"];
for (i = 0; i < [changes count]; i++)
{
aChange = [changes objectAtIndex: i];
@@ -398,6 +402,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
o = [sogoObject vCard];
[o takeActiveSyncValues: allChanges inContext: context];
[sogoObject saveComponent: o];
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
}
break;
case ActiveSyncEventFolder:
@@ -406,15 +413,30 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
o = [sogoObject component: NO secure: NO];
[o takeActiveSyncValues: allChanges inContext: context];
[sogoObject saveComponent: o];
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
}
break;
case ActiveSyncMailFolder:
default:
{
NSDictionary *result;
NSString *modseq;
[sogoObject takeActiveSyncValues: allChanges inContext: context];
result = [sogoObject fetchParts: [NSArray arrayWithObject: @"MODSEQ"]];
modseq = [[[result objectForKey: @"RawResponse"] objectForKey: @"fetch"] objectForKey: @"modseq"];
if (modseq)
[syncCache setObject: modseq forKey: serverId];
}
}
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
[theBuffer appendString: @"<Change>"];
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
[theBuffer appendFormat: @"<Status>%d</Status>", 1];
@@ -453,16 +475,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
withType: (SOGoMicrosoftActiveSyncFolderType) theFolderType
inBuffer: (NSMutableString *) theBuffer
{
id aDelete, sogoObject, value;
NSArray *deletions;
NSString *serverId;
id aDelete, sogoObject;
BOOL deletesAsMoves, useTrash;
int i;
deletions = (id)[theDocumentElement getElementsByTagName: @"Delete"];
if ([deletions count])
{
// From the documention, if DeletesAsMoves is missing, we must assume it's a YES.
// See https://msdn.microsoft.com/en-us/library/gg675480(v=exchg.80).aspx for all details.
value = [theDocumentElement getElementsByTagName: @"DeletesAsMoves"];
deletesAsMoves = YES;
useTrash = YES;
if ([value count] && [[[value lastObject] textValue] length])
deletesAsMoves = [[[value lastObject] textValue] boolValue];
for (i = 0; i < [deletions count]; i++)
{
aDelete = [deletions objectAtIndex: i];
@@ -474,7 +507,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
acquire: NO];
if (![sogoObject isKindOfClass: [NSException class]])
[sogoObject delete];
{
// FIXME: handle errors here
if (deletesAsMoves && theFolderType == ActiveSyncMailFolder)
[(SOGoMailFolder *)[sogoObject container] deleteUIDs: [NSArray arrayWithObjects: serverId, nil] useTrashFolder: &useTrash inContext: context];
else
[sogoObject delete];
}
[theBuffer appendString: @"<Delete>"];
[theBuffer appendFormat: @"<ServerId>%@</ServerId>", serverId];
@@ -893,6 +932,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[syncCache removeObjectForKey: [aCacheObject uid]];
[dateCache removeObjectForKey: [aCacheObject uid]];
return_count++;
}
else
{
@@ -902,17 +943,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
inContext: context
acquire: 0];
[s appendString: @"<Change xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
[s appendString: [mailObject activeSyncRepresentationInContext: context]];
[s appendString: @"</ApplicationData>"];
[s appendString: @"</Change>"];
if (![[aCacheObject sequence] isEqual: [syncCache objectForKey: [aCacheObject uid]]])
{
[s appendString: @"<Change xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
[s appendString: [mailObject activeSyncRepresentationInContext: context]];
[s appendString: @"</ApplicationData>"];
[s appendString: @"</Change>"];
return_count++;
}
[syncCache setObject: [aCacheObject sequence] forKey: [aCacheObject uid]];
}
return_count++;
}
else
{
@@ -1187,22 +1231,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
// We generate the commands, if any, for the response. We might also have
// generated some in processSyncCommand:inResponse: as we could have
// received a Fetch command
if (getChanges && !first_sync)
{
[self processSyncGetChanges: theDocumentElement
inCollection: collection
withWindowSize: windowSize
withMaxSyncResponseSize: theMaxSyncResponseSize
withSyncKey: syncKey
withFolderType: folderType
withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]]
inBuffer: changeBuffer
lastServerKey: &lastServerKey];
}
//
// We process the commands from the request
//
@@ -1220,12 +1248,29 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
inBuffer: s
processed: &processed];
// Windows phons don't empty Responses tags - such as: <Responses></Responses>.
// We onnly generate this tag when the command has generated a response.
// Windows phones don't like empty Responses tags - such as: <Responses></Responses>.
// We only generate this tag when there is a response
if (processed && [s length])
[commandsBuffer appendFormat: @"<Responses>%@</Responses>", s];
}
// We generate the commands, if any, for the response. We might also have
// generated some in processSyncCommand:inResponse: as we could have
// received a Fetch command
if (getChanges && !first_sync)
{
[self processSyncGetChanges: theDocumentElement
inCollection: collection
withWindowSize: windowSize
withMaxSyncResponseSize: theMaxSyncResponseSize
withSyncKey: syncKey
withFolderType: folderType
withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]]
inBuffer: changeBuffer
lastServerKey: &lastServerKey];
}
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
// If we got any changes or if we have applied any commands
@@ -1250,7 +1295,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
// Make sure that client is updated with the right syncKey. - This keeps vtodo's and vevent's syncKey in sync.
syncKeyInCache = [folderMetadata objectForKey: @"SyncKey"];
if (syncKeyInCache && !([davCollectionTag isEqualToString:syncKeyInCache]) && ![davCollectionTag isEqualToString: @"-1"])
if (syncKeyInCache && !([davCollectionTag isEqualToString:syncKeyInCache]) && !first_sync)
{
davCollectionTag = syncKeyInCache;
*changeDetected = YES;
+44 -6
View File
@@ -699,7 +699,7 @@ static BOOL debugOn = NO;
SOGoMailAccount *accountFolder;
NSMutableString *s, *commands;
SOGoUserFolder *userFolder;
NSMutableArray *folders;
NSMutableArray *folders, *processedFolders;
SoSecurityManager *sm;
SOGoCacheGCSObject *o;
id currentFolder;
@@ -719,6 +719,8 @@ static BOOL debugOn = NO;
command_count = 0;
commands = [NSMutableString string];
processedFolders = [NSMutableArray array];
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
@@ -727,7 +729,7 @@ static BOOL debugOn = NO;
first_sync = YES;
syncKey = @"1";
}
else if (![syncKey isEqualToString: [metadata objectForKey: @"FolderSyncKey"]])
else if (![metadata objectForKey: @"FolderSyncKey"])
{
// Synchronization key mismatch or invalid synchronization key
//NSLog(@"FolderSync syncKey mismatch %@ <> %@", syncKey, metadata);
@@ -862,7 +864,13 @@ static BOOL debugOn = NO;
serverId = [NSString stringWithFormat: @"mail/%@", [[imapGUIDs objectForKey: nameInCache] substringFromIndex: 6]];
name = [folderMetadata objectForKey: @"displayName"];
// avoid duplicate folders if folder is returned by different imap namespaces
if ([processedFolders indexOfObject: serverId] == NSNotFound)
[processedFolders addObject: serverId];
else
continue;
if ([name hasPrefix: @"/"])
name = [name substringFromIndex: 1];
@@ -874,7 +882,10 @@ static BOOL debugOn = NO;
if ([folderMetadata objectForKey: @"parent"])
{
parentId = [NSString stringWithFormat: @"mail/%@", [[imapGUIDs objectForKey: [NSString stringWithFormat: @"folder%@", [[folderMetadata objectForKey: @"parent"] substringFromIndex: 1]]] substringFromIndex: 6]];
// make sure that parent of main-folders is always 0
if (type == 12)
parentId = [NSString stringWithFormat: @"mail/%@", [[imapGUIDs objectForKey: [NSString stringWithFormat: @"folder%@", [[folderMetadata objectForKey: @"parent"] substringFromIndex: 1]]] substringFromIndex: 6]];
name = [[name pathComponents] lastObject];
}
@@ -2565,7 +2576,7 @@ static BOOL debugOn = NO;
NGMimeFileData *fdata;
NSException *error;
NSArray *attachmentKeys;
NSMutableArray *attachments;
NSMutableArray *attachments, *references;
id body, bodyFromSmartForward, htmlPart, textPart;
NSString *fullName, *email, *charset, *s;
@@ -2604,7 +2615,25 @@ static BOOL debugOn = NO;
[map setObject: email forKey: @"from"];
if ([mailObject messageId])
[map setObject: [mailObject messageId] forKey: @"in-reply-to"];
{
[map setObject: [mailObject messageId] forKey: @"in-reply-to"];
references = [[[[[mailObject mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] mutableCopy] autorelease];
if ([references count] > 0)
{
// If there are more than ten identifiers listed, we eliminate the second one.
if ([references count] >= 10)
[references removeObjectAtIndex: 1];
[references addObject: [mailObject messageId]];
[map setObject: [references componentsJoinedByString:@" "] forKey: @"references"];
}
else
{
[map setObject: [mailObject messageId] forKey: @"references"];
}
}
messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
body = [[[NGMimeMultipartBody alloc] initWithPart: messageToSend] autorelease];
@@ -2862,6 +2891,7 @@ static BOOL debugOn = NO;
NSAutoreleasePool *pool;
id builder, dom;
SEL aSelector;
id activeUser;
NSString *cmdName, *deviceId;
NSData *d;
@@ -2870,6 +2900,14 @@ static BOOL debugOn = NO;
ASSIGN(context, theContext);
activeUser = [context activeUser];
if (![activeUser canAccessModule: @"ActiveSync"])
{
[(WOResponse *)theResponse setStatus: 403];
[self logWithFormat: @"EAS - Forbidden access for user %@", [activeUser loginInDomain]];
return nil;
}
// Get the device ID, device type and "stash" them
deviceId = [[theRequest uri] deviceId];
[context setObject: deviceId forKey: @"DeviceId"];
+81 -17
View File
@@ -77,6 +77,41 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <Appointments/SOGoAptMailNotification.h>
unsigned char strToChar(char a, char b) {
char encoder[3] = {'\0','\0','\0'};
encoder[0] = a;
encoder[1] = b;
return (char) strtol(encoder,NULL,16);
}
@interface NSString (NSStringExtensions)
- (NSData *) decodeFromHexidecimal;
@end
@implementation NSString (NSStringExtensions)
- (NSData *) decodeFromHexidecimal;
{
const char * bytes = [self cStringUsingEncoding: NSUTF8StringEncoding];
NSUInteger length = strlen(bytes);
unsigned char * r = (unsigned char *) malloc(length / 2 + 1);
unsigned char * index = r;
while ((*bytes) && (*(bytes +1))) {
*index = strToChar(*bytes, *(bytes +1));
index++;
bytes+=2;
}
*index = '\0';
NSData * result = [NSData dataWithBytes: r length: length / 2];
free(r);
return result;
}
@end
typedef struct {
uint32_t dwLowDateTime;
uint32_t dwHighDateTime;
@@ -91,7 +126,7 @@ struct GlobalObjectId {
FILETIME CreationTime;
uint8_t X[8];
uint32_t Size;
uint8_t* Data;
uint8_t Data[0];
};
@implementation SOGoMailObject (ActiveSync)
@@ -118,39 +153,40 @@ struct GlobalObjectId {
//
// The GlobalObjId is documented here: http://msdn.microsoft.com/en-us/library/ee160198(v=EXCHG.80).aspx
//
- (NSData *) _computeGlobalObjectIdFromEvent: (iCalEvent *) event
{
NSData *binPrefix, *globalObjectId, *uidAsASCII;
NSString *prefix, *uid;
struct GlobalObjectId newGlobalId;
struct GlobalObjectId *newGlobalId;
const char *bytes;
uid = [event uid];
uidAsASCII = [uid decodeFromHexidecimal];
newGlobalId = (struct GlobalObjectId*)calloc(sizeof(uint8_t), sizeof(struct GlobalObjectId) + 0x0c + [uidAsASCII length]);
prefix = @"040000008200e00074c5b7101a82e008";
// dataPrefix is "vCal-Uid %x01 %x00 %x00 %x00"
uint8_t dataPrefix[] = { 0x76, 0x43, 0x61, 0x6c, 0x2d, 0x55, 0x69, 0x64, 0x01, 0x00, 0x00, 0x00 };
uid = [event uid];
binPrefix = [prefix convertHexStringToBytes];
[binPrefix getBytes: &newGlobalId.ByteArrayID];
[self _setInstanceDate: &newGlobalId
[binPrefix getBytes: &newGlobalId->ByteArrayID];
[self _setInstanceDate: newGlobalId
fromDate: [event recurrenceId]];
uidAsASCII = [uid dataUsingEncoding: NSASCIIStringEncoding];
bytes = [uidAsASCII bytes];
// 0x0c is the size of our dataPrefix
newGlobalId.Size = 0x0c + [uidAsASCII length];
newGlobalId.Data = malloc(newGlobalId.Size * sizeof(uint8_t));
memcpy(newGlobalId.Data, dataPrefix, 0x0c);
memcpy(newGlobalId.Data + 0x0c, bytes, newGlobalId.Size - 0x0c);
newGlobalId->Size = 0x0c + [uidAsASCII length];
memcpy(newGlobalId->Data, dataPrefix, 0x0c);
memcpy(newGlobalId->Data + 0x0c, bytes, newGlobalId->Size - 0x0c);
globalObjectId = [[NSData alloc] initWithBytes: &newGlobalId length: 40 + newGlobalId.Size*sizeof(uint8_t)];
free(newGlobalId.Data);
globalObjectId = [[NSData alloc] initWithBytes: newGlobalId length: 40 + newGlobalId->Size*sizeof(uint8_t)];
free(newGlobalId);
return [globalObjectId autorelease];
}
//
// For debugging purposes...
//
@@ -514,6 +550,28 @@ struct GlobalObjectId {
return d;
}
- (NSString *) _getNormalizedSubject
{
NSString *subject;
NSUInteger colIdx;
NSString *stringValue;
subject = [[self subject] decodedHeader];
colIdx = [subject rangeOfString: @":" options:NSBackwardsSearch].location;
if (colIdx != NSNotFound && colIdx + 1 < [subject length])
stringValue = [[subject substringFromIndex: colIdx + 1] stringByTrimmingLeadSpaces];
else
stringValue = subject;
if (!stringValue)
stringValue = @"";
return stringValue;
}
//
//
//
@@ -627,7 +685,7 @@ struct GlobalObjectId {
if (value)
{
[s appendFormat: @"<Subject xmlns=\"Email:\">%@</Subject>", [value activeSyncRepresentationInContext: context]];
[s appendFormat: @"<ThreadTopic xmlns=\"Email:\">%@</ThreadTopic>", [value activeSyncRepresentationInContext: context]];
[s appendFormat: @"<ThreadTopic xmlns=\"Email:\">%@</ThreadTopic>", [[self _getNormalizedSubject] activeSyncRepresentationInContext: context]];
}
// DateReceived
@@ -1013,7 +1071,13 @@ struct GlobalObjectId {
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.0"] ||
[[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.1"])
{
if ([self inReplyTo])
NSString *reference;
reference = [[[[self mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] objectAtIndex: 0];
if ([reference length] > 0)
[s appendFormat: @"<ConversationId xmlns=\"Email2:\">%@</ConversationId>", [[reference dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
else if ([self inReplyTo])
[s appendFormat: @"<ConversationId xmlns=\"Email2:\">%@</ConversationId>", [[[self inReplyTo] dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
else if ([self messageId])
[s appendFormat: @"<ConversationId xmlns=\"Email2:\">%@</ConversationId>", [[[self messageId] dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
+1132
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
+2 -2
View File
@@ -1035,8 +1035,8 @@ repository
|ModulesConstraints (optional)
|Limits the access of any module through a constraint based on an LDAP
attribute; must be a dictionary with keys `Mail`, and/or `Calendar`, for
example:
attribute; must be a dictionary with keys `Mail`, and/or `Calendar`,
and/or `ActiveSync` for example:
----
ModulesConstraints = {
+3 -3
View File
@@ -1,7 +1,7 @@
<!-- TODO have the build system take care of this -->
<releaseinfo>Version 2.3.0 - June 2015</releaseinfo>
<subtitle>for version 2.3.0</subtitle>
<date>2015-06-01</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.0
:release_version: 2.3.1
// vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab:
+25 -1
View File
@@ -1,10 +1,26 @@
2.3.1 (2015-06-XX)
2.3.x (2015-MM-DD)
------------------
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)
2.3.1 (2015-07-23)
------------------
Enhancements
- improved EAS speed, especially when fetching big attachments
- now always enforce the organizer's default identity in appointments
- improved the handling of default calendar categories/colors (#3200)
- added support for DeletesAsMoves over EAS
- 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
- added Slovenian translation - thanks to Jens Riecken
- added Chinese (Taiwan) translation
Bug fixes
- EAS's GetItemEstimate/ItemOperations now support fetching mails and empty folders
@@ -14,6 +30,14 @@ Bug fixes
- properly support big characters in EAS and fix encoding QP EAS error for Outlook (#3082)
- properly encode id of DOM elements in Address Book module (#3239, #3245)
- fixed multi-domain support for sogo-tool backup/restore (#2600)
- fixed data ordering in events list of Calendar module (#3261)
- fixed data ordering in tasks list of Calendar module (#3267)
- Android EAS Lollipop fixes (#3268 and #3269)
- improved EAS email flagging handling (#3140)
- fixed computation of GlobalObjectId (#3235)
- fixed EAS conversation ID issues on BB10 (#3152)
- fixed CR/LF printing in event's description (#3228)
- optimized Calendar module in multidomain configurations
2.3.0 (2015-06-01)
-------------------
+2 -1
View File
@@ -33,8 +33,9 @@
}
- (id) authenticatorInContext: (id) context;
- (MAPIStoreUserContext *) userContext;
- (void) setUserContext: (MAPIStoreUserContext *) newContext;
- (void) cleanup;
@end
+10
View File
@@ -76,6 +76,11 @@ MAPIApplication *MAPIApp = nil;
return NO;
}
- (MAPIStoreUserContext *) userContext
{
return userContext;
}
- (void) setUserContext: (MAPIStoreUserContext *) newContext
{
/* user contexts must not be retained here ad their holder (mapistore)
@@ -88,4 +93,9 @@ MAPIApplication *MAPIApp = nil;
return [userContext authenticator];
}
- (void) cleanup
{
[userContext deactivate];
}
@end
+1 -5
View File
@@ -63,19 +63,15 @@
SOGoAppointmentObject *newEntry;
NSString *name;
//[self logWithFormat: @"METHOD '%s' (%d)", __FUNCTION__, __LINE__];
[[self userContext] activate];
name = [NSString stringWithFormat: @"%@.ics",
[SOGoObject globallyUniqueObjectId]];
newEntry = [SOGoAppointmentObject objectWithName: name
inContainer: sogoObject];
[newEntry setIsNew: YES];
/* the WOContext is required here for resolving notification pages */
[newEntry setContext: [[self userContext] woContext]];
newMessage = [MAPIStoreCalendarMessage mapiStoreObjectWithSOGoObject: newEntry
inContainer: self];
return newMessage;
}
+3 -2
View File
@@ -494,6 +494,7 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
folder = [sogoObject container];
/* reinstantiate the old sogo object and attach it to self */
[[self userContext] activate];
woContext = [[self userContext] woContext];
if (isNew)
{
@@ -577,7 +578,7 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
}
}
- (void) save: (TALLOC_CTX *) memCtx
- (void) save: (TALLOC_CTX *) memCtx
{
// iCalCalendar *vCalendar;
// NSCalendarDate *now;
@@ -634,7 +635,7 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
withActiveUser: activeUser
inMemCtx: memCtx];
[self _updateAttachedEvents];
[[self userContext] activateWithUser: activeUser];
[[self userContext] activate];
[sogoObject updateContentWithCalendar: calendar
fromRequest: nil];
[self updateVersions];
+5 -2
View File
@@ -28,6 +28,7 @@
#import <Contacts/SOGoContactFolders.h>
#import "MAPIApplication.h"
#import "MAPIStoreUserContext.h"
#import "MAPIStoreContactsContext.h"
#import "MAPIStoreContactsMessage.h"
#import "MAPIStoreContactsMessageTable.h"
@@ -58,10 +59,12 @@
SOGoContactGCSEntry *newEntry;
NSString *name;
[[self userContext] activate];
name = [NSString stringWithFormat: @"%@.vcf",
[SOGoObject globallyUniqueObjectId]];
newEntry = [SOGoContactGCSEntry objectWithName: name
inContainer: sogoObject];
inContainer: sogoObject];
[newEntry setIsNew: YES];
newMessage = [MAPIStoreContactsMessage mapiStoreObjectWithSOGoObject: newEntry
inContainer: self];
@@ -100,7 +103,7 @@
rights |= RightsReadItems;
if (rights != 0)
rights |= RoleNone; /* actually "folder visible" */
return rights;
}
+28 -70
View File
@@ -33,7 +33,6 @@
#import <SOGo/SOGoUser.h>
#import "MAPIStoreAttachment.h"
// #import "MAPIStoreAttachmentTable.h"
#import "MAPIStoreFallbackContext.h"
#import "MAPIStoreFolder.h"
#import "MAPIStoreFolderTable.h"
@@ -89,32 +88,31 @@ static NSMutableDictionary *contextClassMapping;
currentClass = [classes objectAtIndex: count];
moduleName = [currentClass MAPIModuleName];
if (moduleName)
{
[contextClassMapping setObject: currentClass
{
[contextClassMapping setObject: currentClass
forKey: moduleName];
NSLog (@" registered class '%@' as handler of '%@' contexts",
NSStringFromClass (currentClass), moduleName);
}
NSLog (@" registered class '%@' as handler of '%@' contexts",
NSStringFromClass (currentClass), moduleName);
}
}
MAPIStoreFallbackContextK = [MAPIStoreFallbackContext class];
}
+ (struct mapistore_contexts_list *) listAllContextsForUser: (NSString *) userName
withIndexing: (struct indexing_context *) indexing
withIndexing: (struct indexing_context *) indexing
inMemCtx: (TALLOC_CTX *) memCtx
{
struct mapistore_contexts_list *list, *current;
NSArray *classes;
Class currentClass;
NSUInteger count, max;
MAPIStoreUserContext *userContext;
list = NULL;
userContext = [MAPIStoreUserContext userContextWithUsername: userName
andTDBIndexing: indexing];
[userContext activateWithUser: [userContext sogoUser]];
// User context is activated on initialization
[MAPIStoreUserContext userContextWithUsername: userName
andTDBIndexing: indexing];
classes = GSObjCAllSubclassesOfClass (self);
max = [classes count];
@@ -122,7 +120,7 @@ static NSMutableDictionary *contextClassMapping;
{
currentClass = [classes objectAtIndex: count];
current = [currentClass listContextsForUser: userName
withIndexing: indexing
withIndexing: indexing
inMemCtx: memCtx];
if (current)
DLIST_CONCATENATE(list, current, void);
@@ -132,7 +130,7 @@ static NSMutableDictionary *contextClassMapping;
}
+ (struct mapistore_contexts_list *) listContextsForUser: (NSString *) userName
withIndexing: (struct indexing_context *) indexing
withIndexing: (struct indexing_context *) indexing
inMemCtx: (TALLOC_CTX *) memCtx
{
return NULL;
@@ -225,8 +223,6 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
NSURL *baseURL;
int rc = MAPISTORE_ERR_NOT_FOUND;
//NSLog (@"METHOD '%s' (%d) -- uri: '%s'", __FUNCTION__, __LINE__, newUri);
context = nil;
baseURL = CompleteURLFromMapistoreURI (newUri);
@@ -298,13 +294,6 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
ASSIGN (userContext,
[MAPIStoreUserContext userContextWithUsername: username
andTDBIndexing: indexing]);
#if 0
mapistore_mgmt_backend_register_user (newConnInfo,
"SOGo",
[username UTF8String]);
#endif
connInfo = newConnInfo;
username = [NSString stringWithUTF8String: newConnInfo->username];
ASSIGN (activeUser, [SOGoUser userWithLogin: username]);
@@ -322,12 +311,6 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
- (void) dealloc
{
#if 0
mapistore_mgmt_backend_unregister_user ([self connectionInfo], "SOGo",
[[userContext username]
UTF8String]);
#endif
[contextUrl release];
[userContext release];
[containersBag release];
@@ -355,27 +338,16 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
return activeUser;
}
// - (void) logRestriction: (struct mapi_SRestriction *) res
// withState: (MAPIRestrictionState) state
// {
// NSString *resStr;
// resStr = MAPIStringForRestriction (res);
// [self logWithFormat: @"%@ --> %@", resStr, MAPIStringForRestrictionState (state)];
// }
- (int) getPath: (char **) path
ofFMID: (uint64_t) fmid
inMemCtx: (TALLOC_CTX *) memCtx
{
int rc;
NSString *objectURL, *url;
// TDB_DATA key, dbuf;
url = [contextUrl absoluteString];
// FIXME transform percent escapes but not for user part of the url
//stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
//[xxxx stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
objectURL = [[userContext mapping] urlFromID: fmid];
if (objectURL)
{
@@ -396,14 +368,6 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
else
{
[self errorWithFormat: @"%s: you should *never* get here", __PRETTY_FUNCTION__];
// /* attempt to populate our mapping dict with data from indexing.tdb */
// key.dptr = (unsigned char *) talloc_asprintf (memCtx, "0x%.16llx",
// (long long unsigned int )fmid);
// key.dsize = strlen ((const char *) key.dptr);
// dbuf = tdb_fetch (memCtx->indexing_list->index_ctx->tdb, key);
// talloc_free (key.dptr);
// uri = talloc_strndup (memCtx, (const char *)dbuf.dptr, dbuf.dsize);
*path = NULL;
rc = MAPISTORE_SUCCESS;
}
@@ -426,7 +390,7 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
NSArray *pathComponents;
NSUInteger count, max;
[userContext activateWithUser: activeUser];
[userContext activate];
woContext = [userContext woContext];
[self ensureContextFolder];
@@ -488,7 +452,7 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
/* utils */
- (NSString *) extractChildNameFromURL: (NSString *) objectURL
andFolderURLAt: (NSString **) folderURL;
andFolderURLAt: (NSString **) folderURL;
{
NSString *childKey;
NSRange lastSlash;
@@ -497,15 +461,15 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
if ([objectURL hasSuffix: @"/"])
objectURL = [objectURL substringToIndex: [objectURL length] - 2];
lastSlash = [objectURL rangeOfString: @"/"
options: NSBackwardsSearch];
options: NSBackwardsSearch];
if (lastSlash.location != NSNotFound)
{
slashPtr = NSMaxRange (lastSlash);
childKey = [objectURL substringFromIndex: slashPtr];
if ([childKey length] == 0)
childKey = nil;
childKey = nil;
if (folderURL)
*folderURL = [objectURL substringToIndex: slashPtr];
*folderURL = [objectURL substringToIndex: slashPtr];
}
else
childKey = nil;
@@ -516,11 +480,10 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
- (uint64_t) idForObjectWithKey: (NSString *) key
inFolderURL: (NSString *) folderURL
{
NSString *childURL, *owner;
NSString *childURL;
MAPIStoreMapping *mapping;
uint64_t mappingId;
uint32_t contextId;
void *rootObject;
enum mapistore_error ret;
if (key)
childURL = [NSString stringWithFormat: @"%@%@", folderURL,
@@ -531,17 +494,13 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
mappingId = [mapping idFromURL: childURL];
if (mappingId == NSNotFound)
{
//[self warnWithFormat: @"no id exist yet for '%@', requesting one...",
// childURL];
mapistore_indexing_get_new_folderID (connInfo->mstore_ctx, &mappingId);
[mapping registerURL: childURL withID: mappingId];
contextId = 0;
mapistore_search_context_by_uri (connInfo->mstore_ctx, [folderURL UTF8String],
&contextId, &rootObject);
owner = [userContext username];
mapistore_indexing_record_add_mid (connInfo->mstore_ctx, contextId,
[owner UTF8String], mappingId);
[self logWithFormat: @"No id exist yet for '%@', requesting one", childURL];
ret = mapistore_indexing_get_new_folderID (connInfo->mstore_ctx, &mappingId);
if (ret == MAPISTORE_SUCCESS)
[mapping registerURL: childURL withID: mappingId];
else
[self errorWithFormat: @"Error trying to get new folder id (%d): %s",
ret, mapistore_errstr (ret)];
}
return mappingId;
@@ -595,15 +554,14 @@ static inline NSURL *CompleteURLFromMapistoreURI (const char *uri)
memCtx = talloc_zero(NULL, TALLOC_CTX);
newFMIDs = [NSMutableArray arrayWithCapacity: max];
if (mapistore_indexing_get_new_folderIDs (connInfo->mstore_ctx,
memCtx, max, &numbers)
!= MAPISTORE_SUCCESS || numbers->cValues != max)
abort ();
for (count = 0; count < max; count++)
{
newNumber
= [NSString stringWithUnsignedLongLong: numbers->lpui8[count]];
newNumber = [NSString stringWithUnsignedLongLong: numbers->lpui8[count]];
[newFMIDs addObject: newNumber];
}
+14 -8
View File
@@ -161,7 +161,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
else
[dbFolder changePathTo: [NSString stringWithFormat: @"/fallback/%@", pathComponent]
intoNewContainer: nil];
mapping = [self mapping];
if (targetFolder)
@@ -193,6 +193,8 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
SOGoMAPIDBMessage *fsObject;
NSString *newKey;
[[self userContext] activate];
newKey = [NSString stringWithFormat: @"%@.plist",
[SOGoObject globallyUniqueObjectId]];
fsObject = [SOGoMAPIDBMessage objectWithName: newKey
@@ -214,10 +216,13 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
ownerUser = [[self userContext] sogoUser];
if ([[context activeUser] isEqual: ownerUser]
|| [self subscriberCanReadMessages])
keys = [(SOGoCacheGCSFolder *) sogoObject childKeysOfType: MAPIMessageCacheObject
includeDeleted: NO
matchingQualifier: qualifier
andSortOrderings: sortOrderings];
{
[[self userContext] activate];
keys = [(SOGoCacheGCSFolder *) sogoObject childKeysOfType: MAPIMessageCacheObject
includeDeleted: NO
matchingQualifier: qualifier
andSortOrderings: sortOrderings];
}
else
keys = [NSArray array];
@@ -227,6 +232,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
- (NSArray *) folderKeysMatchingQualifier: (EOQualifier *) qualifier
andSortOrderings: (NSArray *) sortOrderings
{
[[self userContext] activate];
return [dbFolder childKeysOfType: MAPIFolderCacheObject
includeDeleted: NO
matchingQualifier: qualifier
@@ -262,7 +268,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
if ([date laterDate: fileDate] == fileDate)
{
//[self logWithFormat: @"current date: %@", date];
date = fileDate;
}
}
@@ -326,7 +332,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
rights |= RightsFolderContact;
if (rights != 0)
rights |= RoleNone; /* actually "folder visible" */
return rights;
}
@@ -361,7 +367,7 @@ static NSString *MAPIStoreRightFolderContact = @"RightsFolderContact";
subscribed to read an open contained messages in order to enable them to
find the "LocalFreebusy" message */
[sogoObject reloadIfNeeded];
displayName = [[sogoObject properties]
objectForKey: MAPIPropertyKey (PidTagDisplayName)];
+32 -29
View File
@@ -120,6 +120,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
folderName = [folderURL host];
userContext = [self userContext];
[userContext activate];
[userContext ensureFolderTableExists];
ASSIGN (dbFolder,
@@ -231,30 +232,19 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
- (id) lookupFolder: (NSString *) folderKey
{
MAPIStoreFolder *childFolder;
MAPIStoreFolder *childFolder = nil;
SOGoFolder *sogoFolder;
WOContext *woContext;
childFolder = nil;
if ([[self folderKeys] containsObject: folderKey])
{
woContext = [[self userContext] woContext];
/* We activate the user for the context using the root folder
context as there are times where the active user is not
matching with the one stored in the application context
and SOGo object is storing cached data with the wrong user */
[[self userContext] activateWithUser: [woContext activeUser]];
sogoFolder = [sogoObject lookupName: folderKey inContext: woContext
[[self userContext] activate];
sogoFolder = [sogoObject lookupName: folderKey
inContext: nil
acquire: NO];
if (sogoFolder && ![sogoFolder isKindOfClass: NSExceptionK])
{
[sogoFolder setContext: woContext];
childFolder = [isa mapiStoreObjectWithSOGoObject: sogoFolder
inContainer: self];
}
childFolder = [isa mapiStoreObjectWithSOGoObject: sogoFolder
inContainer: self];
}
else
childFolder = nil;
return childFolder;
}
@@ -285,6 +275,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
if (messageKey)
{
[[self userContext] activate];
msgObject = [sogoObject lookupName: messageKey
inContext: nil
acquire: NO];
@@ -314,6 +305,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
if (messageKey)
{
[[self userContext] activate];
if ([[self faiMessageKeys] containsObject: messageKey])
{
msgObject = [dbFolder lookupName: messageKey
@@ -546,6 +538,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
context = [self context];
ownerUser = [[self userContext] sogoUser];
[[self userContext] activate];
if ([[context activeUser] isEqual: ownerUser]
|| (!isAssociated && [self subscriberCanCreateMessages]))
@@ -758,9 +751,9 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
}
else
rc = MAPISTORE_ERR_DENIED;
//talloc_free (memCtx);
return rc;
}
@@ -931,7 +924,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
NSArray *newIDs;
uint64_t idNbr;
bool softDeleted;
baseURL = [self url];
mapping = [self mapping];
@@ -1060,9 +1053,10 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
{
static enum MAPITAGS bannedProps[] = { PR_MID, PR_FID, PR_PARENT_FID,
PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY,
PR_CHANGE_KEY, 0x00000000 };
PR_CHANGE_KEY, PidTagChangeNumber, 0x00000000 };
enum MAPITAGS *currentProp;
NSMutableDictionary *propsCopy;
uint64_t cn;
/* TODO: this should no longer be required once mapistore v2 API is in
place, when we can then do this from -dealloc below */
@@ -1080,6 +1074,12 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
}
[properties addEntriesFromDictionary: propsCopy];
/* Update change number after setting the properties */
cn = [[self context] getNewChangeNumber];
[properties setObject: [NSNumber numberWithUnsignedLongLong: cn]
forKey: MAPIPropertyKey (PidTagChangeNumber)];
[dbFolder save];
}
@@ -1105,6 +1105,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
- (NSArray *) faiMessageKeysMatchingQualifier: (EOQualifier *) qualifier
andSortOrderings: (NSArray *) sortOrderings
{
[[self userContext] activate];
return [dbFolder childKeysOfType: MAPIFAICacheObject
includeDeleted: NO
matchingQualifier: qualifier
@@ -1213,7 +1214,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
/*
Possible values are:
0x00000001 Modify
0x00000002 Read
0x00000004 Delete
@@ -1244,7 +1245,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
access |= 0x10;
if (userIsOwner)
access |= 0x20;
*data = MAPILongValue (memCtx, access);
return MAPISTORE_SUCCESS;
@@ -1273,7 +1274,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
rights |= RightsCreateSubfolders;
if (userIsOwner)
rights |= RightsFolderOwner | RightsFolderContact;
*data = MAPILongValue (memCtx, rights);
return MAPISTORE_SUCCESS;
@@ -1309,7 +1310,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
inMemCtx: (TALLOC_CTX *) memCtx
{
*data = MAPIBoolValue (memCtx, [self supportsSubFolders] && [[self folderKeys] count] > 0);
return MAPISTORE_SUCCESS;
}
@@ -1317,7 +1318,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
inMemCtx: (TALLOC_CTX *) memCtx
{
*data = MAPILongValue (memCtx, [[self folderKeys] count]);
return MAPISTORE_SUCCESS;
}
@@ -1419,7 +1420,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
[dbObject setIsNew: YES];
newMessage = [MAPIStoreFAIMessageK mapiStoreObjectWithSOGoObject: dbObject
inContainer: self];
return newMessage;
}
@@ -1428,6 +1429,8 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
MAPIStoreMessage *newMessage;
WOContext *woContext;
[[self userContext] activate];
if (isAssociated)
newMessage = [self _createAssociatedMessage];
else
@@ -1627,7 +1630,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
permissionUser = nil;
permissionRoles = nil;
if (currentPermission->PermissionDataFlags == ROW_ADD)
isAdd = YES;
else if (currentPermission->PermissionDataFlags == ROW_MODIFY)
@@ -1778,7 +1781,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
{
[self subclassResponsibility: _cmd];
return nil;
return nil;
}
- (NSArray *) getDeletedKeysFromChangeNumber: (uint64_t) changeNum
-2
View File
@@ -121,7 +121,6 @@
userContext = [MAPIStoreUserContext userContextWithUsername: userName
andTDBIndexing: NULL];
[MAPIApp setUserContext: userContext];
moduleName = [self MAPIModuleName];
parentFolder = [[userContext rootFolders] objectForKey: moduleName];
nameInContainer = nil;
@@ -134,7 +133,6 @@
moduleName, nameInContainer];
else
mapistoreURI = nil;
[MAPIApp setUserContext: nil];
return mapistoreURI;
}
+1 -3
View File
@@ -67,7 +67,7 @@ MakeDisplayFolderName (NSString *folderName)
NSArray *parts;
NSString *lastFolder;
NSUInteger max;
parts = [folderName componentsSeparatedByString: @"/"];
max = [parts count];
if (max > 1)
@@ -198,7 +198,6 @@ MakeDisplayFolderName (NSString *folderName)
userContext = [MAPIStoreUserContext userContextWithUsername: userName
andTDBIndexing: NULL];
[MAPIApp setUserContext: userContext];
accountFolder = [[userContext rootFolders] objectForKey: @"mail"];
folderName = [NSString stringWithFormat: @"folder%@",
[newFolderName asCSSIdentifier]];
@@ -213,7 +212,6 @@ MakeDisplayFolderName (NSString *folderName)
[[folderName stringByEncodingImap4FolderName] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
else
mapistoreURI = nil;
[MAPIApp setUserContext: nil];
return mapistoreURI;
}
+17 -19
View File
@@ -139,7 +139,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
[[self mapping] updateID: fid withURL: [self url]];
[dbFolder setNameInContainer: newNameInContainer];
[self cleanupCaches];
propsCopy = [newProperties mutableCopy];
[propsCopy removeObjectForKey: key];
[propsCopy autorelease];
@@ -186,10 +186,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
nameInContainer = [NSString stringWithFormat: @"folder%@",
[[folderName stringByEncodingImap4FolderName] asCSSIdentifier]];
/* it may be the operation is interleaved with operations
from other users having cached information in the thread
with the other user, so it'd better activate the user again here... */
[[self userContext] activateWithUser: [[[self userContext] woContext] activeUser]];
[[self userContext] activate];
newFolder = [SOGoMailFolderK objectWithName: nameInContainer
inContainer: sogoObject];
@@ -245,7 +242,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
sortOrdering: nil]
count];
*data = MAPILongValue (memCtx, longValue);
return MAPISTORE_SUCCESS;
}
@@ -253,7 +250,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
inMemCtx: (TALLOC_CTX *) memCtx
{
*data = [@"IPF.Note" asUnicodeInMemCtx: memCtx];
return MAPISTORE_SUCCESS;
}
@@ -265,7 +262,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
if (!nonDeletedQualifier)
{
deletedQualifier
= [[EOKeyValueQualifier alloc]
= [[EOKeyValueQualifier alloc]
initWithKey: @"FLAGS"
operatorSelector: EOQualifierOperatorContains
value: [NSArray arrayWithObject: @"Deleted"]];
@@ -378,7 +375,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
[self warnWithFormat: @"qualifier is only used for folders with name"];
if (sortOrderings)
[self errorWithFormat: @"sort orderings are not used for folders"];
/* FIXME: Flush any cache before retrieving the hierarchy, this
slows things down but it is safer */
if (!qualifier)
@@ -472,7 +469,7 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
}
else
supportsSubFolders = YES;
return supportsSubFolders;
}
@@ -641,7 +638,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
fetchResults
= [fetchResults sortedArrayUsingFunction: _compareFetchResultsByMODSEQ
context: NULL];
for (count = 0; count < max; count++)
{
result = [fetchResults objectAtIndex: count];
@@ -951,7 +948,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
}
}
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
[versionsMessage save];
}
@@ -1178,7 +1175,7 @@ _parseIMAPRange (const unichar *uniString, NSArray **UIDsP)
count++;
}
*UIDsP = UIDs;
return count;
}
@@ -1456,7 +1453,7 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
[uids addObject: [self messageUIDFromMessageKey: childKey]];
}
result = [client copyUids: uids
result = [client copyUids: uids
toFolder: newFolderIMAPName];
if ([[result objectForKey: @"result"] boolValue])
{
@@ -1495,9 +1492,10 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
{
SOGoCacheObject *childObject;
childObject = [SOGoCacheObject objectWithName: [SOGoCacheObject
globallyUniqueObjectId]
inContainer: sogoObject];
[[[self context] userContext] activate];
childObject = [SOGoCacheObject objectWithName: [SOGoCacheObject globallyUniqueObjectId]
inContainer: sogoObject];
return [MAPIStoreMailVolatileMessage
mapiStoreObjectWithSOGoObject: childObject
inContainer: self];
@@ -1573,7 +1571,7 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
rights |= RoleNone; /* actually "folder visible" */
// [self logWithFormat: @"rights for roles (%@) = %.8x", roles, rights];
return rights;
}
@@ -1624,7 +1622,7 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
}
}
}
client = [[(SOGoMailFolder *) sogoObject imap4Connection] client];
[client select: [sogoObject absoluteImap4Name]];
response = [client fetchUids: [keyAssoc allKeys]
+2
View File
@@ -1614,6 +1614,8 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
max = [keyParts count];
if (max > 0)
{
[[self userContext] activate];
currentPart = [sogoObject lookupName: [keyParts objectAtIndex: 0]
inContext: nil
acquire: NO];
@@ -771,6 +771,12 @@ FillMessageHeadersFromProperties (NGMutableHashMap *headers,
subjectData = [mailProperties objectForKey: MAPIPropertyKey (PR_NORMALIZED_SUBJECT_UNICODE)];
if (subjectData)
[subject appendString: subjectData];
if ([subject length] == 0)
{
subjectData = [mailProperties objectForKey: MAPIPropertyKey (PR_SUBJECT_UNICODE)];
if (subjectData)
[subject appendString: subjectData];
}
[headers setObject: [subject asQPSubjectString: @"utf-8"] forKey: @"subject"];
messageId = [mailProperties objectForKey: MAPIPropertyKey (PR_INTERNET_MESSAGE_ID_UNICODE)];
@@ -1077,6 +1083,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
dd = [activeUser domainDefaults];
from = [[activeUser allEmails] objectAtIndex: 0];
[[self userContext] activate];
woContext = [[self userContext] woContext];
authenticator = [sogoObject authenticatorInContext: woContext];
error = [[SOGoMailer mailerWithDomainDefaults: dd]
+72 -27
View File
@@ -178,56 +178,101 @@ rtf2html (NSData *compressedRTF)
andColumns: (struct SPropTagArray *) columns
{
NSMutableDictionary *recipientProperties;
enum MAPITAGS prop_tag;
char *displayName = NULL;
char *email = NULL;
enum MAPI_OBJTYPE object_type = 0;
char *smtpAddress = NULL;
SOGoUser *recipientUser;
NSUInteger count;
NSString *recipientEmail = nil;
NSString *recipientFullName = nil;
id value;
recipientProperties = [NSMutableDictionary dictionaryWithCapacity: columns->cValues + 2];
for (count = 0; count < columns->cValues; count++)
{
prop_tag = columns->aulPropTag[count];
switch(prop_tag) {
case PidTagDisplayName:
displayName = recipient->data[count];
break;
case PidTagEmailAddress:
email = recipient->data[count];
break;
case PidTagObjectType:
if (recipient->data[count])
object_type = *((uint8_t*) recipient->data[count]);
break;
case PidTagSmtpAddress:
smtpAddress = recipient->data[count];
break;
default:
break;
}
if (recipient->data[count])
{
value = NSObjectFromValuePointer (prop_tag,
recipient->data[count]);
if (value)
[recipientProperties setObject: value
forKey: MAPIPropertyKey (prop_tag)];
}
}
if (recipient->username)
{
value = [NSString stringWithUTF8String: recipient->username];
[recipientProperties setObject: value forKey: @"x500dn"];
}
if (object_type == MAPI_MAILUSER && recipient->username)
{
/* values from user object have priority over data sent by the client */
recipientUser = [SOGoUser userWithLogin: [value lowercaseString]];
if (recipientUser)
{
value = [recipientUser cn];
if ([value length] > 0)
[recipientProperties setObject: value forKey: @"fullName"];
recipientFullName = value;
value = [[recipientUser allEmails] objectAtIndex: 0];
if ([value length] > 0)
[recipientProperties setObject: value forKey: @"email"];
}
}
else
{
if (recipient->data[0])
{
value = [NSString stringWithUTF8String: recipient->data[0]];
if ([value length] > 0)
[recipientProperties setObject: value forKey: @"fullName"];
}
if (recipient->data[1])
{
value = [NSString stringWithUTF8String: recipient->data[1]];
if ([value length] > 0)
[recipientProperties setObject: value forKey: @"email"];
}
recipientEmail = value;
}
}
for (count = 0; count < columns->cValues; count++)
/* If we do not have values from the user object we try to get them from the parameters */
if (!recipientFullName && displayName)
{
if (recipient->data[count])
{
value = NSObjectFromValuePointer (columns->aulPropTag[count],
recipient->data[count]);
if (value)
[recipientProperties setObject: value
forKey: MAPIPropertyKey (columns->aulPropTag[count])];
}
value = [NSString stringWithUTF8String: displayName];
if ([value length] > 0)
recipientFullName = value;
}
if (!recipientEmail && email)
{
value = [NSString stringWithUTF8String: email];
if ([value length] > 0)
recipientEmail = value;
}
if (!recipientEmail && smtpAddress)
{
value = [NSString stringWithUTF8String: smtpAddress];
if ([value length] > 0)
recipientEmail = value;
}
/* Now we can set the properties if we have them */
if (recipientFullName)
[recipientProperties setObject: recipientFullName forKey: @"fullName"];
if (recipientEmail)
[recipientProperties setObject: recipientEmail forKey: @"email"];
return recipientProperties;
}
+34 -116
View File
@@ -61,20 +61,25 @@ static BOOL initialization_done = NO;
if (!initialization_done) { \
OC_DEBUG(5, "[SOGo] You should call sogo_backend_init() first. Current thread: %p, pid: %d\n", \
GSCurrentThread(), getpid()); \
}
} \
OC_DEBUG(5, "[SOGo] --->");
#define NS_CURRENT_THREAD_TRY_UNREGISTER() \
if (__nsrct_thread_registered) { \
GSUnregisterCurrentThread(); \
}
} \
OC_DEBUG(6, "[SOGo] <---");
#define TRYCATCH_START @try {
#define TRYCATCH_END(pool) \
} @catch (NSException * e) { \
enum mapistore_error ret = sogo_backend_handle_objc_exception(e, __PRETTY_FUNCTION__, __LINE__); \
mapiapp_cleanup(); \
[pool release]; \
NS_CURRENT_THREAD_TRY_UNREGISTER(); \
return ret; \
}
} \
mapiapp_cleanup();
static enum mapistore_error
@@ -196,12 +201,28 @@ sogo_backend_init (void)
return MAPISTORE_SUCCESS;
}
/**
\details Cleanup operation to execute after an action has been performed
so there won't be any conflicts with future calls.
In practice this will deactivate the current user context set on MAPIApp
(which is the current WOApplication), this means two things: (1) set nil
as current user context on MAPIApp and (2) remove woContext from current
thread dictionary (this is used on WOContext.m).
*/
static void mapiapp_cleanup(void)
{
Class MAPIApplicationK;
MAPIApplicationK = NSClassFromString (@"MAPIApplication");
if (MAPIApplicationK)
[[MAPIApplicationK application] cleanup];
}
/**
\details Create a connection context to the sogo backend
\param mem_ctx pointer to the memory context
\param uri pointer to the sogo path
\param private_data pointer to the private backend context
\param private_data pointer to the private backend context
*/
static enum mapistore_error
@@ -214,8 +235,6 @@ sogo_backend_create_context(TALLOC_CTX *mem_ctx,
MAPIStoreContext *context;
int rc;
OC_DEBUG(5, "[SOGo]");
NS_CURRENT_THREAD_REGISTER();
pool = [NSAutoreleasePool new];
@@ -251,8 +270,6 @@ sogo_backend_create_root_folder (const char *username,
NSString *mapistoreUri;
int rc;
OC_DEBUG(5, "[SOGo]");
NS_CURRENT_THREAD_REGISTER();
pool = [NSAutoreleasePool new];
@@ -288,8 +305,6 @@ sogo_backend_list_contexts(const char *username, struct indexing_context *indexi
NSString *userName;
int rc;
OC_DEBUG(5, "[SOGo]");
NS_CURRENT_THREAD_REGISTER();
pool = [NSAutoreleasePool new];
@@ -336,8 +351,6 @@ sogo_context_get_path(void *backend_object, TALLOC_CTX *mem_ctx,
MAPIStoreContext *context;
int rc;
OC_DEBUG(5, "[SOGo]");
if (backend_object)
{
wrapper = backend_object;
@@ -370,8 +383,6 @@ sogo_context_get_root_folder(void *backend_object, TALLOC_CTX *mem_ctx,
MAPIStoreFolder *folder;
int rc;
OC_DEBUG(5, "[SOGo]");
if (backend_object)
{
wrapper = backend_object;
@@ -413,8 +424,6 @@ sogo_folder_open_folder(void *folder_object, TALLOC_CTX *mem_ctx, uint64_t fid,
MAPIStoreFolder *folder, *childFolder;
int rc;
OC_DEBUG(5, "[SOGo]");
if (folder_object)
{
wrapper = folder_object;
@@ -442,7 +451,7 @@ sogo_folder_open_folder(void *folder_object, TALLOC_CTX *mem_ctx, uint64_t fid,
/**
\details Create a folder in the sogo backend
\param private_data pointer to the current sogo context
\return MAPISTORE_SUCCESS on success, otherwise MAPISTORE_ERROR
@@ -457,8 +466,6 @@ sogo_folder_create_folder(void *folder_object, TALLOC_CTX *mem_ctx,
MAPIStoreFolder *folder, *childFolder;
int rc;
OC_DEBUG(5, "[SOGo]");
if (folder_object)
{
wrapper = folder_object;
@@ -500,8 +507,6 @@ sogo_folder_delete(void *folder_object)
MAPIStoreFolder *folder;
int rc;
OC_DEBUG(5, "[SOGo]");
if (folder_object)
{
wrapper = folder_object;
@@ -532,8 +537,6 @@ sogo_folder_get_child_count(void *folder_object, enum mapistore_table_type table
MAPIStoreFolder *folder;
int rc;
OC_DEBUG(5, "[SOGo]");
if (folder_object)
{
wrapper = folder_object;
@@ -568,8 +571,6 @@ sogo_folder_open_message(void *folder_object,
MAPIStoreMessage *message;
int rc;
OC_DEBUG(5, "[SOGo]");
if (folder_object)
{
wrapper = folder_object;
@@ -610,8 +611,6 @@ sogo_folder_create_message(void *folder_object,
MAPIStoreMessage *message;
int rc;
OC_DEBUG(5, "[SOGo]");
if (folder_object)
{
wrapper = folder_object;
@@ -622,7 +621,7 @@ sogo_folder_create_message(void *folder_object,
TRYCATCH_START
rc = [folder createMessage: &message
withMID: mid
isAssociated: associated];
isAssociated: associated];
if (rc == MAPISTORE_SUCCESS)
*message_object = [message tallocWrapper: mem_ctx];
TRYCATCH_END(pool)
@@ -646,8 +645,6 @@ sogo_folder_delete_message(void *folder_object, uint64_t mid, uint8_t flags)
MAPIStoreFolder *folder;
int rc;
OC_DEBUG(5, "[SOGo]");
if (folder_object)
{
wrapper = folder_object;
@@ -684,8 +681,6 @@ sogo_folder_move_copy_messages(void *folder_object,
struct MAPIStoreTallocWrapper *wrapper;
int rc;
OC_DEBUG(5, "[SOGo]");
if (folder_object)
{
wrapper = folder_object;
@@ -728,8 +723,6 @@ sogo_folder_move_folder(void *folder_object, void *target_folder_object,
struct MAPIStoreTallocWrapper *wrapper;
int rc;
OC_DEBUG(5, "[SOGo]");
if (folder_object)
{
wrapper = folder_object;
@@ -778,8 +771,6 @@ sogo_folder_copy_folder(void *folder_object, void *target_folder_object, TALLOC_
struct MAPIStoreTallocWrapper *wrapper;
int rc;
OC_DEBUG(5, "[SOGo]");
if (folder_object)
{
wrapper = folder_object;
@@ -822,8 +813,6 @@ sogo_folder_get_deleted_fmids(void *folder_object, TALLOC_CTX *mem_ctx,
MAPIStoreFolder *folder;
int rc;
OC_DEBUG(5, "[SOGo]");
if (folder_object)
{
wrapper = folder_object;
@@ -861,8 +850,6 @@ sogo_folder_open_table(void *folder_object, TALLOC_CTX *mem_ctx,
MAPIStoreTable *table;
int rc;
OC_DEBUG(5, "[SOGo]");
if (folder_object)
{
wrapper = folder_object;
@@ -900,8 +887,6 @@ sogo_folder_modify_permissions(void *folder_object, uint8_t flags,
MAPIStoreFolder *folder;
int rc;
OC_DEBUG(5, "[SOGo]");
if (folder_object)
{
wrapper = folder_object;
@@ -934,8 +919,6 @@ sogo_folder_preload_message_bodies(void *folder_object, enum mapistore_table_typ
MAPIStoreFolder *folder;
int rc;
OC_DEBUG(5, "[SOGo]");
if (folder_object)
{
wrapper = folder_object;
@@ -969,8 +952,6 @@ sogo_message_get_message_data(void *message_object,
MAPIStoreMessage *message;
int rc;
OC_DEBUG(5, "[SOGo]");
if (message_object)
{
wrapper = message_object;
@@ -1004,8 +985,6 @@ sogo_message_create_attachment (void *message_object, TALLOC_CTX *mem_ctx, void
MAPIStoreAttachment *attachment;
int rc;
OC_DEBUG(5, "[SOGo]");
if (message_object)
{
wrapper = message_object;
@@ -1017,7 +996,6 @@ sogo_message_create_attachment (void *message_object, TALLOC_CTX *mem_ctx, void
rc = [message createAttachment: &attachment inAID: aidp];
if (rc == MAPISTORE_SUCCESS)
*attachment_object = [attachment tallocWrapper: mem_ctx];
// [context tearDownRequest];
TRYCATCH_END(pool)
[pool release];
@@ -1041,8 +1019,6 @@ sogo_message_open_attachment (void *message_object, TALLOC_CTX *mem_ctx,
MAPIStoreAttachment *attachment;
int rc;
OC_DEBUG(5, "[SOGo]");
if (message_object)
{
wrapper = message_object;
@@ -1054,7 +1030,6 @@ sogo_message_open_attachment (void *message_object, TALLOC_CTX *mem_ctx,
rc = [message getAttachment: &attachment withAID: aid];
if (rc == MAPISTORE_SUCCESS)
*attachment_object = [attachment tallocWrapper: mem_ctx];
// [context tearDownRequest];
TRYCATCH_END(pool)
[pool release];
@@ -1077,8 +1052,6 @@ sogo_message_get_attachment_table (void *message_object, TALLOC_CTX *mem_ctx, vo
MAPIStoreAttachmentTable *table;
int rc;
OC_DEBUG(5, "[SOGo]");
if (message_object)
{
wrapper = message_object;
@@ -1091,7 +1064,6 @@ sogo_message_get_attachment_table (void *message_object, TALLOC_CTX *mem_ctx, vo
andRowCount: row_count];
if (rc == MAPISTORE_SUCCESS)
*table_object = [table tallocWrapper: mem_ctx];
// [context tearDownRequest];
TRYCATCH_END(pool)
[pool release];
@@ -1116,9 +1088,6 @@ sogo_message_modify_recipients (void *message_object,
MAPIStoreMessage *message;
int rc;
OC_DEBUG(5, "[SOGo]");
if (message_object)
{
wrapper = message_object;
@@ -1130,7 +1099,6 @@ sogo_message_modify_recipients (void *message_object,
rc = [message modifyRecipientsWithRecipients: recipients
andCount: count
andColumns: columns];
// [context tearDownRequest];
TRYCATCH_END(pool)
[pool release];
@@ -1152,9 +1120,6 @@ sogo_message_set_read_flag (void *message_object, uint8_t flag)
MAPIStoreMessage *message;
int rc;
OC_DEBUG(5, "[SOGo]");
if (message_object)
{
wrapper = message_object;
@@ -1164,7 +1129,6 @@ sogo_message_set_read_flag (void *message_object, uint8_t flag)
TRYCATCH_START
rc = [message setReadFlag: flag];
// [context tearDownRequest];
TRYCATCH_END(pool)
[pool release];
@@ -1186,9 +1150,6 @@ sogo_message_save (void *message_object, TALLOC_CTX *mem_ctx)
MAPIStoreMessage *message;
int rc;
OC_DEBUG(5, "[SOGo]");
if (message_object)
{
wrapper = message_object;
@@ -1198,7 +1159,6 @@ sogo_message_save (void *message_object, TALLOC_CTX *mem_ctx)
TRYCATCH_START
rc = [message saveMessage: mem_ctx];
// [context tearDownRequest];
TRYCATCH_END(pool)
[pool release];
@@ -1220,9 +1180,6 @@ sogo_message_submit (void *message_object, enum SubmitFlags flags)
MAPIStoreMailVolatileMessage *message;
int rc;
OC_DEBUG(5, "[SOGo]");
if (message_object)
{
wrapper = message_object;
@@ -1232,7 +1189,6 @@ sogo_message_submit (void *message_object, enum SubmitFlags flags)
TRYCATCH_START
rc = [message submitWithFlags: flags];
// [context tearDownRequest];
TRYCATCH_END(pool)
[pool release];
@@ -1259,9 +1215,6 @@ sogo_message_attachment_open_embedded_message (void *attachment_object,
MAPIStoreEmbeddedMessage *message;
int rc;
OC_DEBUG(5, "[SOGo]");
if (attachment_object)
{
wrapper = attachment_object;
@@ -1301,9 +1254,6 @@ sogo_message_attachment_create_embedded_message (void *attachment_object,
MAPIStoreEmbeddedMessage *message;
int rc;
OC_DEBUG(5, "[SOGo]");
if (attachment_object)
{
wrapper = attachment_object;
@@ -1338,9 +1288,6 @@ static enum mapistore_error sogo_table_get_available_properties(void *table_obje
MAPIStoreTable *table;
int rc;
OC_DEBUG(5, "[SOGo]");
if (table_object)
{
wrapper = table_object;
@@ -1371,9 +1318,6 @@ sogo_table_set_columns (void *table_object, uint16_t count, enum MAPITAGS *prope
MAPIStoreTable *table;
int rc;
OC_DEBUG(5, "[SOGo]");
if (table_object)
{
wrapper = table_object;
@@ -1405,9 +1349,6 @@ sogo_table_set_restrictions (void *table_object, struct mapi_SRestriction *restr
MAPIStoreTable *table;
int rc;
OC_DEBUG(5, "[SOGo]");
if (table_object)
{
wrapper = table_object;
@@ -1441,9 +1382,6 @@ sogo_table_set_sort_order (void *table_object, struct SSortOrderSet *sort_order,
MAPIStoreTable *table;
int rc;
OC_DEBUG(5, "[SOGo]");
if (table_object)
{
wrapper = table_object;
@@ -1479,9 +1417,6 @@ sogo_table_get_row (void *table_object, TALLOC_CTX *mem_ctx,
MAPIStoreTable *table;
int rc;
OC_DEBUG(5, "[SOGo]");
if (table_object)
{
wrapper = table_object;
@@ -1515,9 +1450,6 @@ sogo_table_get_row_count (void *table_object,
MAPIStoreTable *table;
int rc;
OC_DEBUG(5, "[SOGo]");
if (table_object)
{
wrapper = table_object;
@@ -1549,9 +1481,6 @@ sogo_table_handle_destructor (void *table_object, uint32_t handle_id)
MAPIStoreTable *table;
int rc;
OC_DEBUG(5, "[SOGo]");
if (table_object)
{
wrapper = table_object;
@@ -1584,9 +1513,6 @@ static enum mapistore_error sogo_properties_get_available_properties(void *objec
MAPIStoreObject *propObject;
int rc;
OC_DEBUG(5, "[SOGo]");
if (object)
{
wrapper = object;
@@ -1620,9 +1546,6 @@ sogo_properties_get_properties (void *object,
MAPIStoreObject *propObject;
int rc;
OC_DEBUG(5, "[SOGo]");
if (object)
{
wrapper = object;
@@ -1655,9 +1578,6 @@ sogo_properties_set_properties (void *object, struct SRow *aRow)
MAPIStoreObject *propObject;
int rc;
OC_DEBUG(5, "[SOGo]");
if (object)
{
wrapper = object;
@@ -1681,18 +1601,16 @@ sogo_properties_set_properties (void *object, struct SRow *aRow)
}
static enum mapistore_error
sogo_manager_generate_uri (TALLOC_CTX *mem_ctx,
const char *user,
const char *folder,
const char *message,
sogo_manager_generate_uri (TALLOC_CTX *mem_ctx,
const char *user,
const char *folder,
const char *message,
const char *rootURI,
char **uri)
{
NSAutoreleasePool *pool;
NSString *partialURLString, *username, *directory;
OC_DEBUG(5, "[SOGo]");
if (uri == NULL) return MAPISTORE_ERR_INVALID_PARAMETER;
/* This fixes a crash occurring during the instantiation of the
@@ -1738,9 +1656,9 @@ sogo_manager_generate_uri (TALLOC_CTX *mem_ctx,
*/
int mapistore_init_backend(void)
{
struct mapistore_backend backend;
int ret;
static BOOL registered = NO;
struct mapistore_backend backend;
int ret;
static BOOL registered = NO;
if (registered)
ret = MAPISTORE_SUCCESS;
+4 -2
View File
@@ -31,6 +31,7 @@
#import <Appointments/SOGoTaskObject.h>
#import "MAPIApplication.h"
#import "MAPIStoreUserContext.h"
#import "MAPIStoreTasksContext.h"
#import "MAPIStoreTasksMessage.h"
#import "MAPIStoreTasksMessageTable.h"
@@ -61,6 +62,7 @@
SOGoTaskObject *newEntry;
NSString *name;
[[[self context] userContext] activate];
name = [NSString stringWithFormat: @"%@.ics",
[SOGoObject globallyUniqueObjectId]];
newEntry = [SOGoTaskObject objectWithName: name
@@ -69,7 +71,7 @@
newMessage = [MAPIStoreTasksMessage mapiStoreObjectWithSOGoObject: newEntry
inContainer: self];
return newMessage;
}
@@ -116,7 +118,7 @@
rights |= RightsReadItems;
if (rights != 0)
rights |= RoleNone; /* actually "folder visible" */
return rights;
}
+2
View File
@@ -86,6 +86,8 @@
/* SOGo hacky magic */
- (void) activateWithUser: (SOGoUser *) activeUser;
- (void) activate;
- (void) deactivate;
- (MAPIStoreAuthenticator *) authenticator;
- (WOContext *) woContext;
+35 -7
View File
@@ -39,6 +39,7 @@
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserFolder.h>
#import <SOGo/NSString+Utilities.h>
#import <NGExtensions/NSObject+Logs.h>
#import <Mailer/SOGoMailAccount.h>
#import <Mailer/SOGoMailAccounts.h>
@@ -158,6 +159,8 @@ static NSMapTable *contextsTable = nil;
if ([userPassword length] == 0)
userPassword = username;
[authenticator setPassword: userPassword];
// Activate the profile on initialization
[self activate];
}
return self;
@@ -214,7 +217,7 @@ static NSMapTable *contextsTable = nil;
if (!userFolder)
{
userFolder = [SOGoUserFolder objectWithName: username
inContainer: MAPIApp];
inContainer: nil];
[userFolder retain];
}
@@ -232,7 +235,8 @@ static NSMapTable *contextsTable = nil;
if (!rootFolders)
{
rootFolders = [NSMutableDictionary new];
[self userFolder];
[self activate];
[self userFolder]; // force lazy initialization
[woContext setClientObject: userFolder];
/* Calendar */
@@ -257,10 +261,10 @@ static NSMapTable *contextsTable = nil;
acquire: NO];
[containersBag addObject: accountsFolder];
[woContext setClientObject: accountsFolder];
currentFolder = [accountsFolder lookupName: @"0"
inContext: woContext
acquire: NO];
[rootFolders setObject: currentFolder
forKey: @"mail"];
connection = [currentFolder imap4Connection];
@@ -268,8 +272,7 @@ static NSMapTable *contextsTable = nil;
/* ensure the folder cache is filled */
[currentFolder toManyRelationshipKeysWithNamespaces: YES];
hierarchy = [connection
cachedHierarchyResultsForURL: [currentFolder imap4URL]];
hierarchy = [connection cachedHierarchyResultsForURL: [currentFolder imap4URL]];
flags = [[hierarchy objectForKey: @"list"] objectForKey: @"/INBOX"];
inboxHasNoInferiors = [flags containsObject: @"noinferiors"];
}
@@ -338,7 +341,7 @@ static NSMapTable *contextsTable = nil;
cm = [GCSChannelManager defaultChannelManager];
channel = [cm acquireOpenChannelForURL: folderTableURL];
/* FIXME: make use of [EOChannelAdaptor describeTableNames] instead */
tableName = [[folderTableURL path] lastPathComponent];
if ([channel evaluateExpressionX:
@@ -355,7 +358,7 @@ static NSMapTable *contextsTable = nil;
[channel cancelFetch];
[cm releaseChannel: channel];
[cm releaseChannel: channel];
}
/* SOGo context objects */
@@ -369,6 +372,11 @@ static NSMapTable *contextsTable = nil;
return authenticator;
}
- (void) activate
{
[self activateWithUser: [self sogoUser]];
}
- (void) activateWithUser: (SOGoUser *) activeUser;
{
NSMutableDictionary *info;
@@ -379,4 +387,24 @@ static NSMapTable *contextsTable = nil;
[info setObject: woContext forKey: @"WOContext"];
}
- (void) deactivate
{
NSMutableDictionary *info;
if (self == [MAPIApp userContext])
[MAPIApp setUserContext: nil];
else
[self errorWithFormat: @"Error: Tried to deactivate an user context "
@"not enabled (%@ vs %@)",
[self username], [[MAPIApp userContext] username]];
info = [[NSThread currentThread] threadDictionary];
if (woContext == [info objectForKey: @"WOContext"])
[info removeObjectForKey: @"WOContext"];
else
[self errorWithFormat: @"Error: Tried to deactivate a WOContext "
@"not enabled (%@ vs %@)",
woContext, [info objectForKey: @"WOContext"]];
}
@end
+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];
}
}
}
+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;
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env python
import sys
import os
import re
DEBUG=False
dir_mappings = {"../UI/Templates":"../UI/Common",
"../UI/Templates/AdministrationUI":"../UI/AdministrationUI",
"../UI/Templates/Appointments":"../SoObjects/Appointments",
"../UI/Templates/ContactsUI":"../UI/Contacts",
"../UI/Templates/MailerUI":"../UI/MailerUI",
"../UI/Templates/MailPartViewers":"../UI/MailPartViewers",
"../UI/Templates/MainUI":"../UI/MainUI",
"../UI/Templates/PreferencesUI":"../UI/PreferencesUI",
"../UI/Templates/SchedulerUI":"../UI/Scheduler"
}
def get_translations(path):
try:
transpath = dir_mappings.get(path, path)
transname = transpath + '/English.lproj/Localizable.strings'
transall = open(transname).read()
except:
transall = ""
return transall
def find_missing_translations(rootdir='.', extention='', recomp=None, greylist=()):
for path, dirs, files in os.walk(rootdir):
filelist = [f for f in files if f[(-1 * len(extention)):] == extention]
if filelist:
for filename in filelist:
if filename in greylist:
print "%s is greylisted -- SKIPPING" % filename
continue
pathname = path + '/' + filename
lines = open(pathname).read().split("\n")
values = [r.groups()[0] for r in [recomp.search(l) for l in lines] if r]
if values:
#- Get the current english translations for the path
transgood = get_translations(path)
if not transgood:
print "No translation file found for %s, skipping %s" % (path, pathname)
continue
notfound = list()
if DEBUG:print pathname
for value in values:
#- Try to find the value from the source file in the translation file
escaped_value = re.escape(value)
found = re.search('(%s|"%s")\s*=\s*"(.*?)";' % (escaped_value, escaped_value), transgood)
if found:
if DEBUG: print "\t", '[%s] FOUND -- "%s"' % found.groups()
else:
notfound.append("-->\t[%s] ==== Not Found ====" % value)
if notfound:
if not DEBUG:print pathname
print "\n".join(notfound)
def main():
#- Only one option...
if len(sys.argv) > 1:
if sys.argv[1] == '-g':
global DEBUG
DEBUG = True
else:
print 'Usage:', sys.argv[0], '[-g]\n\t\t-g: debug will show matching also'
sys.exit(1)
greylist = ('UIxFilterEditor.wox')
#- Get only the label:value from all lines
recomp = re.compile(' label:[^=]*="(.*?)"')
find_missing_translations('../UI', 'wox', recomp, greylist)
#- [self labelForKey: @"Issuer"]
recomp = re.compile('\[self labelForKey: @"(.*?)"\]')
find_missing_translations('../UI', 'm', recomp, ())
main()
@@ -11,16 +11,16 @@ vtodo_class2 = "(مهمة سرية)";
"The event \"%{Summary}\" was created" = "أُنشِئ الحدث \"%{Summary}\"";
"The event \"%{Summary}\" was deleted" = "حُذِفَ الحدث \"%{Summary}\"";
"The event \"%{Summary}\" was updated" = "حُدِّث الحدث \"%{Summary}\"";
"The following attendees(s) were notified:" = "تم ابلاغ المدعو (المدعويين) الأتي أسماءهم:";
"The following attendees(s) were added:" = "تم أضافة المدعو (المدعويين) الأتي أسماءهم:";
"The following attendees(s) were removed:" = "تم مسح المدعو (المدعويين) الأتي أسماءهم:";
"The following attendees(s) were notified" = "تم ابلاغ المدعو (المدعويين) الأتي أسماءهم";
"The following attendees(s) were added" = "تم أضافة المدعو (المدعويين) الأتي أسماءهم";
"The following attendees(s) were removed" = "تم مسح المدعو (المدعويين) الأتي أسماءهم";
/* IMIP messages */
"calendar_label" = "التقويم:";
"startDate_label" = "البداية:";
"endDate_label" = "النهاية:";
"calendar_label" = "التقويم";
"startDate_label" = "البداية";
"endDate_label" = "النهاية";
"due_label" = "تاريخ الاستحقاق:";
"location_label" = "المكان:";
"location_label" = "المكان";
"summary_label" = "الملخص:";
"comment_label" = "التعليق:";
@@ -12,16 +12,16 @@ vtodo_class2 = "(Isilpeko egitekoa)";
"The event \"%{Summary}\" was created" = "\"%{Summary}\" ekitaldia sortu da";
"The event \"%{Summary}\" was deleted" = "\"%{Summary}\" ekitaldia ezabatu da";
"The event \"%{Summary}\" was updated" = "\"%{Summary}\" ekitaldia eguneratu da";
"The following attendees(s) were notified:" = "Ondorengo partaidea(k) jakinarazi d(ir)a:";
"The following attendees(s) were added:" = "Ondorengo partaidea(k) gehitu d(ir)a:";
"The following attendees(s) were removed:" = "Ondorengo partaidea(k) ezabatu d(ir)a:";
"The following attendees(s) were notified" = "Ondorengo partaidea(k) jakinarazi d(ir)a";
"The following attendees(s) were added" = "Ondorengo partaidea(k) gehitu d(ir)a";
"The following attendees(s) were removed" = "Ondorengo partaidea(k) ezabatu d(ir)a";
/* IMIP messages */
"calendar_label" = "Egutegia";
"startDate_label" = "Hasi:";
"endDate_label" = "Amaitu:";
"startDate_label" = "Hasi";
"endDate_label" = "Amaitu";
"due_label" = "Epemuga:";
"location_label" = "Kokapena:";
"location_label" = "Kokapena";
"summary_label" = "Laburpena:";
"comment_label" = "Iruzkina:";
@@ -12,16 +12,16 @@ vtodo_class2 = "(Tarefa Confidencial)";
"The event \"%{Summary}\" was created" = "O evento \"%{Summary}\" foi criado";
"The event \"%{Summary}\" was deleted" = "O evento \"%{Summary}\" foi removido";
"The event \"%{Summary}\" was updated" = "O evento \"%{Summary}\" foi atualizado";
"The following attendees(s) were notified:" = "Estes participantes foram notificados:";
"The following attendees(s) were added:" = "Estes participantes foram adicionados:";
"The following attendees(s) were removed:" = "Estes participantes foram removidos:";
"The following attendees(s) were notified" = "Estes participantes foram notificados";
"The following attendees(s) were added" = "Estes participantes foram adicionados";
"The following attendees(s) were removed" = "Estes participantes foram removidos";
/* IMIP messages */
"calendar_label" = "Calendário:";
"startDate_label" = "Início:";
"endDate_label" = "Fim:";
"calendar_label" = "Calendário";
"startDate_label" = "Início";
"endDate_label" = "Fim";
"due_label" = "Data de Vencimento:";
"location_label" = "Local:";
"location_label" = "Local";
"summary_label" = "Resumo:";
"comment_label" = "Comentário:";
@@ -12,16 +12,16 @@ vtodo_class2 = "(Tasca confidencial)";
"The event \"%{Summary}\" was created" = "Es va crear l'esdeveniment \"%{Summary}\" ";
"The event \"%{Summary}\" was deleted" = "Es va esborrar l'esdeveniment \"%{Summary}\" ";
"The event \"%{Summary}\" was updated" = "Es va modificar l'esdeveniment \"%{Summary}\" ";
"The following attendees(s) were notified:" = "I va notificar als següents participants: ";
"The following attendees(s) were added:" = "Es van agregar els següents participants: ";
"The following attendees(s) were removed:" = "Heu suprimit els assistents següents:";
"The following attendees(s) were notified" = "I va notificar als següents participants ";
"The following attendees(s) were added" = "Es van agregar els següents participants ";
"The following attendees(s) were removed" = "Heu suprimit els assistents següents";
/* IMIP messages */
"calendar_label" = "Calendari:";
"startDate_label" = "Inici:";
"endDate_label" = "Final:";
"calendar_label" = "Calendari";
"startDate_label" = "Inici";
"endDate_label" = "Final";
"due_label" = "Data límit:";
"location_label" = "Lloc:";
"location_label" = "Lloc";
"summary_label" = "Resum:";
"comment_label" = "Comentari:";
@@ -0,0 +1,67 @@
"Inviting the following persons is prohibited:" = "禁止邀請以下人士:";
"Personal Calendar" = "個人行事曆";
vevent_class0 = "(公開事件)";
vevent_class1 = "(私人事件)";
vevent_class2 = "(機密事件)";
vtodo_class0 = "(公開任務)";
vtodo_class1 = "(私人任務)";
vtodo_class2 = "(機密任務)";
/* Receipts */
"The event \"%{Summary}\" was created" = "事件 \"%{Summary}\" 已建立";
"The event \"%{Summary}\" was deleted" = "事件\"%{Summary}\" 已刪除";
"The event \"%{Summary}\" was updated" = "事件\"%{Summary}\" 已異動";
"The following attendees(s) were notified" = "已通知下列出席者";
"The following attendees(s) were added" = "已增加下列出席者";
"The following attendees(s) were removed" = "已移除下列出席者";
/* IMIP messages */
"calendar_label" = "行事曆";
"startDate_label" = "開始";
"endDate_label" = "結束";
"due_label" = "截止日期:";
"location_label" = "地點";
"summary_label" = "事件主題:";
"comment_label" = "備註:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "邀請事件: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(來自 %{SentBy})";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText} 邀請您參加 %{Summary}。\n\n起始日期: %{StartDate}\n結束日期: %{EndDate}\n備註說明: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} 邀請您參加 %{Summary}。\n\n起始時間: %{StartDate} %{StartTime}\n結束時間: %{EndDate} %{EndTime}\n備註說明: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "取消事件 : \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} 己取消事件: %{Summary}。\n\n起始日期: %{StartDate}\n結束日期: %{EndDate}\n備註說明: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} 己取消了事件: %{Summary}。\n\n起始時間: %{StartDate} %{StartTime}\n結束時間: %{EndDate} %{EndTime}\n備註說明: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= " 安排於%{OldStartDate} 的事件 \"%{Summary}\" 已變更";
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
= "安排於 %{OldStartDate} %{OldStartTime} 的事件 \"%{Summary}\" 已變更";
"The following parameters have changed in the \"%{Summary}\" meeting:"
= "已變更會議 \"%{Summary}\" 下列參數:";
"Please accept or decline those changes."
= "請接受或拒絶這些變更。";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "接受邀請: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "拒絶邀請: \"%{Summary}\"";
"Delegated invitation: \"%{Summary}\"" = "\"委派出席: \"%{Summary}\"";
"Not yet decided on invitation: \"%{Summary}\"" = "未處理的邀請: \"%{Summary}\"";
"%{Attendee} %{SentByText}has accepted your event invitation."
= "\"%{Attendee} %{SentByText}已接受了您的邀請。";
"%{Attendee} %{SentByText}has declined your event invitation."
= "\"%{Attendee} %{SentByText}已拒絶了您的邀請。";
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
= "\"%{Attendee} %{SentByText}已將您的邀請委任給%{Delegate}。";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}還沒有決定是否接受您的邀請。";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "無法讀取資源: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "\"%{Cn} %{SystemEmail}\" 的衝突事件己達上限 ( %{NumberOfSimultaneousBookings} )。衝突事件 \"%{EventTitle}\" 的起始日期為 %{StartDate}。";
@@ -12,16 +12,16 @@ vtodo_class2 = "(Skrytý úkol)";
"The event \"%{Summary}\" was created" = "Událost \"%{Summary}\" byla vytvořena";
"The event \"%{Summary}\" was deleted" = "Událost \"%{Summary}\" byla smazána";
"The event \"%{Summary}\" was updated" = "Událost \"%{Summary}\" byla upravena";
"The following attendees(s) were notified:" = "Následující účastníci byli upozorněni:";
"The following attendees(s) were added:" = "Následující účastníci byli přidáni:";
"The following attendees(s) were removed:" = "Následující účastníci byli odebráni:";
"The following attendees(s) were notified" = "Následující účastníci byli upozorněni";
"The following attendees(s) were added" = "Následující účastníci byli přidáni";
"The following attendees(s) were removed" = "Následující účastníci byli odebráni";
/* IMIP messages */
"calendar_label" = "Kalendář:";
"startDate_label" = "Začátek:";
"endDate_label" = "Konec:";
"calendar_label" = "Kalendář";
"startDate_label" = "Začátek";
"endDate_label" = "Konec";
"due_label" = "Termín:";
"location_label" = "Místo:";
"location_label" = "Místo";
"summary_label" = "Název:";
"comment_label" = "Komentář:";
@@ -11,16 +11,16 @@ vtodo_class2 = "(Hemmelig opgave)";
"The event \"%{Summary}\" was created" = "Begivenheden \"%{Summary}\" blev oprettet";
"The event \"%{Summary}\" was deleted" = "Begivenheden \"%{Summary}\" blev slettet";
"The event \"%{Summary}\" was updated" = "Begivenheden \"%{Summary}\" blev opdateret";
"The following attendees(s) were notified:" = "Følgende deltager(e) blev underrettet:";
"The following attendees(s) were added:" = "Følgende deltager(e) blev tilføjet:";
"The following attendees(s) were removed:" = "Følgende deltager(e) blev fjernet:";
"The following attendees(s) were notified" = "Følgende deltager(e) blev underrettet";
"The following attendees(s) were added" = "Følgende deltager(e) blev tilføjet";
"The following attendees(s) were removed" = "Følgende deltager(e) blev fjernet";
/* IMIP messages */
"calendar_label" = "Kalender:";
"startDate_label" = "Start:";
"endDate_label" = "Slut:";
"calendar_label" = "Kalender";
"startDate_label" = "Start";
"endDate_label" = "Slut";
"due_label" = "Forfaldsdato:";
"location_label" = "Sted:";
"location_label" = "Sted";
"summary_label" = "Resumé:";
"comment_label" = "Kommentér:";
@@ -12,16 +12,16 @@ vtodo_class2 = "(Vertrouwelijke taak)";
"The event \"%{Summary}\" was created" = "De gebeurtenis \"%{Summary}\" is aangemaakt";
"The event \"%{Summary}\" was deleted" = "De gebeurtenis \"%{Summary}\" is verwijderd";
"The event \"%{Summary}\" was updated" = "De gebeurtenis \"%{Summary}\" is gewijzigd";
"The following attendees(s) were notified:" = "De volgende deelnemers zijn in kennis gesteld:";
"The following attendees(s) were added:" = "De volgende deelnemers zijn toegevoegd:";
"The following attendees(s) were removed:" = "De volgende deelnemers zijn verwijderd:";
"The following attendees(s) were notified" = "De volgende deelnemers zijn in kennis gesteld";
"The following attendees(s) were added" = "De volgende deelnemers zijn toegevoegd";
"The following attendees(s) were removed" = "De volgende deelnemers zijn verwijderd";
/* IMIP messages */
"calendar_label" = "Agenda:";
"startDate_label" = "Start:";
"endDate_label" = "Einde:";
"calendar_label" = "Agenda";
"startDate_label" = "Start";
"endDate_label" = "Einde";
"due_label" = "Vervaldag:";
"location_label" = "Locatie:";
"location_label" = "Locatie";
"summary_label" = "Samenvatting:";
"comment_label" = "Commentaar:";
@@ -12,16 +12,16 @@ vtodo_class2 = "(Confidential task)";
"The event \"%{Summary}\" was created" = "The event \"%{Summary}\" was created";
"The event \"%{Summary}\" was deleted" = "The event \"%{Summary}\" was deleted";
"The event \"%{Summary}\" was updated" = "The event \"%{Summary}\" was updated";
"The following attendees(s) were notified:" = "The following attendee(s) were notified:";
"The following attendees(s) were added:" = "The following attendee(s) were added:";
"The following attendees(s) were removed:" = "The following attendee(s) were removed:";
"The following attendees(s) were notified" = "The following attendee(s) were notified";
"The following attendees(s) were added" = "The following attendee(s) were added";
"The following attendees(s) were removed" = "The following attendee(s) were removed";
/* IMIP messages */
"calendar_label" = "Calendar:";
"startDate_label" = "Start:";
"endDate_label" = "End:";
"calendar_label" = "Calendar";
"startDate_label" = "Start";
"endDate_label" = "End";
"due_label" = "Due Date:";
"location_label" = "Location:";
"location_label" = "Location";
"summary_label" = "Summary:";
"comment_label" = "Comment:";
@@ -12,16 +12,16 @@ vtodo_class2 = "(Luottamuksellinen tehtävä)";
"The event \"%{Summary}\" was created" = "Tapahtuma \"%{Summary}\" luotiin";
"The event \"%{Summary}\" was deleted" = "Tapahtuma \"%{Summary}\" poistettiin";
"The event \"%{Summary}\" was updated" = "Tapahtuma \"%{Summary}\" päivitettiin";
"The following attendees(s) were notified:" = "Seuraava(t) osallistuja(t) on informoitu:";
"The following attendees(s) were added:" = "Seuraava(t) osalllistuja(t) on lisätty:";
"The following attendees(s) were removed:" = "Seuraava(t) osalllistuja(t) poistettiin:";
"The following attendees(s) were notified" = "Seuraava(t) osallistuja(t) on informoitu";
"The following attendees(s) were added" = "Seuraava(t) osalllistuja(t) on lisätty";
"The following attendees(s) were removed" = "Seuraava(t) osalllistuja(t) poistettiin";
/* IMIP messages */
"calendar_label" = "Kalenteri:";
"startDate_label" = "Alkaa:";
"endDate_label" = "Päättyy:";
"calendar_label" = "Kalenteri";
"startDate_label" = "Alkaa";
"endDate_label" = "Päättyy";
"due_label" = "Määräpäivä:";
"location_label" = "Sijainti:";
"location_label" = "Sijainti";
"summary_label" = "Yhteenveto:";
"comment_label" = "Kommentti:";
@@ -12,16 +12,16 @@ vtodo_class2 = "(Tâche confidentielle)";
"The event \"%{Summary}\" was created" = "L'événement «%{Summary}» a été créé";
"The event \"%{Summary}\" was deleted" = "L'événement «%{Summary}» a été effacé";
"The event \"%{Summary}\" was updated" = "L'événement «%{Summary}» a été modifié";
"The following attendees(s) were notified:" = "Les invités suivants ont été avisés :";
"The following attendees(s) were added:" = "Les invités suivants ont été ajoutés :";
"The following attendees(s) were removed:" = "Les invités suivants ont été supprimés :";
"The following attendees(s) were notified" = "Les invités suivants ont été avisés ";
"The following attendees(s) were added" = "Les invités suivants ont été ajoutés ";
"The following attendees(s) were removed" = "Les invités suivants ont été supprimés ";
/* IMIP messages */
"calendar_label" = "Agenda :";
"startDate_label" = "Début :";
"endDate_label" = "Fin :";
"calendar_label" = "Agenda ";
"startDate_label" = "Début ";
"endDate_label" = "Fin ";
"due_label" = "Fin prévue :";
"location_label" = "Lieu :";
"location_label" = "Lieu ";
"summary_label" = "Titre :";
"comment_label" = "Description :";
+1 -1
View File
@@ -54,7 +54,7 @@ Appointments_RESOURCE_FILES += \
MSExchangeFreeBusySOAPResponseMap.plist \
MSExchangeFreeBusySOAPRequest.wo
Appointments_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
Appointments_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
Appointments_LOCALIZED_RESOURCE_FILES = Localizable.strings
@@ -12,16 +12,16 @@ vtodo_class2 = "(Vertrauliche Aufgabe)";
"The event \"%{Summary}\" was created" = "Der Termin \"%{Summary}\" wurde angelegt";
"The event \"%{Summary}\" was deleted" = "Der Termin \"%{Summary}\" wurde gelöscht";
"The event \"%{Summary}\" was updated" = "Der Termin \"%{Summary}\" wurde geändert";
"The following attendees(s) were notified:" = "Die folgenden Teilnehmer wurden benachrichtigt:";
"The following attendees(s) were added:" = "Die folgenden Teilnehmer wurden hinzugefügt:";
"The following attendees(s) were removed:" = "Die folgenden Teilnehmer wurden entfernt:";
"The following attendees(s) were notified" = "Die folgenden Teilnehmer wurden benachrichtigt";
"The following attendees(s) were added" = "Die folgenden Teilnehmer wurden hinzugefügt";
"The following attendees(s) were removed" = "Die folgenden Teilnehmer wurden entfernt";
/* IMIP messages */
"calendar_label" = "Kalender:";
"startDate_label" = "Beginn:";
"endDate_label" = "Ende:";
"calendar_label" = "Kalender";
"startDate_label" = "Beginn";
"endDate_label" = "Ende";
"due_label" = "Fälligkeit:";
"location_label" = "Ort:";
"location_label" = "Ort";
"summary_label" = "Zusammenfassung:";
"comment_label" = "Kommentar:";
@@ -12,16 +12,16 @@ vtodo_class2 = "(Bizalmas feladat)";
"The event \"%{Summary}\" was created" = "A \"%{Summary}\" esemény létre lett hozva";
"The event \"%{Summary}\" was deleted" = "A \"%{Summary}\" esemény törölve lett";
"The event \"%{Summary}\" was updated" = "A \"%{Summary}\" esemény módosítva lett";
"The following attendees(s) were notified:" = "Az alábbi résztvevők lettek értesítve:";
"The following attendees(s) were added:" = "Az alábbi résztvevők lettek hozzáadva:";
"The following attendees(s) were removed:" = "Az alábbi résztvevők lettek eltávolítva:";
"The following attendees(s) were notified" = "Az alábbi résztvevők lettek értesítve";
"The following attendees(s) were added" = "Az alábbi résztvevők lettek hozzáadva";
"The following attendees(s) were removed" = "Az alábbi résztvevők lettek eltávolítva";
/* IMIP messages */
"calendar_label" = "Naptár:";
"startDate_label" = "Kezdete:";
"endDate_label" = "Vége:";
"calendar_label" = "Naptár";
"startDate_label" = "Kezdete";
"endDate_label" = "Vége";
"due_label" = "Lejárat napja:";
"location_label" = "Hely:";
"location_label" = "Hely";
"summary_label" = "Összegzés:";
"comment_label" = "Megjegyzés:";
@@ -25,10 +25,10 @@ vtodo_class2 = "(Verkefni er trúnaðarmál)";
"... from the following event:" = "... af eftirfarandi viðburði:";
/* IMIP messages */
"startDate_label" = "Byrjun:";
"endDate_label" = "Endir:";
"startDate_label" = "Byrjun";
"endDate_label" = "Endir";
"due_label" = "Lokadagur:";
"location_label" = "Staðsetning:";
"location_label" = "Staðsetning";
"summary_label" = "Samantekt:";
"comment_label" = "Athugasemd:";
@@ -25,10 +25,10 @@ vtodo_class2 = "(Attività confidenziale)";
"... from the following event:" = "... dal seguente evento:";
/* IMIP messages */
"startDate_label" = "Inizio:";
"endDate_label" = "Fine:";
"startDate_label" = "Inizio";
"endDate_label" = "Fine";
"due_label" = "Scadenza:";
"location_label" = "Luogo:";
"location_label" = "Luogo";
"summary_label" = "Summario:";
"comment_label" = "Commento:";
@@ -12,16 +12,16 @@ vtodo_class2 = "(Konfidensiell oppgave)";
"The event \"%{Summary}\" was created" = "Hendelsen \"%{Summary}\" ble opprettet";
"The event \"%{Summary}\" was deleted" = "Hendelsen \"%{Summary}\" ble slettet";
"The event \"%{Summary}\" was updated" = "Hendelsen \"%{Summary}\" ble oppdatert";
"The following attendees(s) were notified:" = "Følgende deltaker(e) ble varslet:";
"The following attendees(s) were added:" = "Følgende deltaker(e) ble lagt til:";
"The following attendees(s) were removed:" = "Følgende deltaker(e) ble fjernet:";
"The following attendees(s) were notified" = "Følgende deltaker(e) ble varslet";
"The following attendees(s) were added" = "Følgende deltaker(e) ble lagt til";
"The following attendees(s) were removed" = "Følgende deltaker(e) ble fjernet";
/* IMIP messages */
"calendar_label" = "Kalender:";
"startDate_label" = "Start:";
"endDate_label" = "Slutt:";
"calendar_label" = "Kalender";
"startDate_label" = "Start";
"endDate_label" = "Slutt";
"due_label" = "Forfallsdato:";
"location_label" = "Sted:";
"location_label" = "Sted";
"summary_label" = "Sammendrag:";
"comment_label" = "Kommentar:";
@@ -25,10 +25,10 @@ vtodo_class2 = "(Konfidensiell oppgave)";
"... from the following event:" = "... fra følgende hendelse:";
/* IMIP messages */
"startDate_label" = "Start:";
"endDate_label" = "End:";
"startDate_label" = "Start";
"endDate_label" = "End";
"due_label" = "Due Date:";
"location_label" = "Location:";
"location_label" = "Location";
"summary_label" = "Summary:";
"comment_label" = "Comment:";
@@ -12,16 +12,16 @@ vtodo_class2 = "(Zadanie poufne)";
"The event \"%{Summary}\" was created" = "Utworzono wydarzenie \"%{Summary}\"";
"The event \"%{Summary}\" was deleted" = "Skasowano wydarzenie \"%{Summary}\"";
"The event \"%{Summary}\" was updated" = "Zaktualizowano wydarzenie \"%{Summary}\"";
"The following attendees(s) were notified:" = "Następujący uczestnicy zostali powiadomieni:";
"The following attendees(s) were added:" = "Następujący uczestnicy zostali dodani:";
"The following attendees(s) were removed:" = "Następujący uczestnicy zostali usunięci:";
"The following attendees(s) were notified" = "Następujący uczestnicy zostali powiadomieni";
"The following attendees(s) were added" = "Następujący uczestnicy zostali dodani";
"The following attendees(s) were removed" = "Następujący uczestnicy zostali usunięci";
/* IMIP messages */
"calendar_label" = "Kalendarz:";
"startDate_label" = "Początek:";
"endDate_label" = "Koniec:";
"calendar_label" = "Kalendarz";
"startDate_label" = "Początek";
"endDate_label" = "Koniec";
"due_label" = "Termin:";
"location_label" = "Miejsce:";
"location_label" = "Miejsce";
"summary_label" = "Podsumowanie:";
"comment_label" = "Komentarz:";
@@ -12,16 +12,16 @@ vtodo_class2 = "(Конфиденциальная задача)";
"The event \"%{Summary}\" was created" = "Было создано мероприятие \"%{Summary}\"";
"The event \"%{Summary}\" was deleted" = "Мероприятие \"%{Summary}\" было удалено";
"The event \"%{Summary}\" was updated" = "Мероприятие \"%{Summary}\" было обновлено";
"The following attendees(s) were notified:" = "Следующие приглашенные были оповещены:";
"The following attendees(s) were added:" = "Следующие люди были добавлены в список приглашенных:";
"The following attendees(s) were removed:" = "Следующие люди были исключены из списка приглашенных:";
"The following attendees(s) were notified" = "Следующие приглашенные были оповещены";
"The following attendees(s) were added" = "Следующие люди были добавлены в список приглашенных";
"The following attendees(s) were removed" = "Следующие люди были исключены из списка приглашенных";
/* IMIP messages */
"calendar_label" = "Календарь:";
"startDate_label" = "Начало:";
"endDate_label" = "Конец:";
"calendar_label" = "Календарь";
"startDate_label" = "Начало";
"endDate_label" = "Конец";
"due_label" = "Должно быть готово к:";
"location_label" = "Местонахождение:";
"location_label" = "Местонахождение";
"summary_label" = "Краткое содержание:";
"comment_label" = "Комментарий:";
@@ -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;
+76 -63
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];
@@ -3132,6 +3140,11 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
return (![inactiveFolders containsObject: nameInContainer]);
}
- (BOOL) isWebCalendar
{
return ([self isKindOfClass: [SOGoWebAppointmentFolder class]]);
}
- (NSString *) importComponent: (iCalEntityObject *) event
timezone: (iCalTimeZone *) timezone
{
+27 -35
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2007-2014 Inverse inc.
Copyright (C) 2007-2015 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOGo
@@ -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>
@@ -317,7 +318,7 @@
enumerator = [attendees objectEnumerator];
while ((currentAttendee = [enumerator nextObject]))
{
currentUID = [currentAttendee uid];
currentUID = [currentAttendee uidInContext: context];
if (currentUID)
[self _removeEventFromUID: currentUID
owner: owner
@@ -398,7 +399,7 @@
enumerator = [updateAttendees objectEnumerator];
while ((currentAttendee = [enumerator nextObject]))
{
currentUID = [currentAttendee uid];
currentUID = [currentAttendee uidInContext: context];
if (currentUID)
[self _addOrUpdateEvent: newEvent
forUID: currentUID
@@ -438,7 +439,7 @@
while ((currentAttendee = [enumerator nextObject]))
{
currentUID = [currentAttendee uid];
currentUID = [currentAttendee uidInContext: context];
if (currentUID)
{
@@ -520,7 +521,7 @@
enumerator = [theAttendees objectEnumerator];
while ((currentAttendee = [enumerator nextObject]))
{
currentUID = [currentAttendee uid];
currentUID = [currentAttendee uidInContext: context];
if (currentUID)
{
[attendees addObject: currentUID];
@@ -635,7 +636,7 @@
for (i = 0; i < [theAttendees count]; i++)
{
currentAttendee = [theAttendees objectAtIndex: i];
if ([[currentAttendee uid] isEqualToString: currentUID])
if ([[currentAttendee uidInContext: context] isEqualToString: currentUID])
break;
else
currentAttendee = nil;
@@ -713,7 +714,7 @@
enumerator = [attendees objectEnumerator];
while ((currentAttendee = [enumerator nextObject]))
{
currentUID = [currentAttendee uid];
currentUID = [currentAttendee uidInContext: context];
if (currentUID)
[self _addOrUpdateEvent: newEvent
forUID: currentUID
@@ -838,7 +839,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
enumerator = [updatedAttendees objectEnumerator];
while ((currentAttendee = [enumerator nextObject]))
{
currentUID = [currentAttendee uid];
currentUID = [currentAttendee uidInContext: context];
if (currentUID)
[self _addOrUpdateEvent: newEvent
forUID: currentUID
@@ -1215,7 +1216,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
{
[delegates addObject: otherDelegate];
delegatedUID = [otherDelegate uid];
delegatedUID = [otherDelegate uidInContext: context];
if (delegatedUID)
// Delegate attendee is a local user; remove event from their calendar
[self _removeEventFromUID: delegatedUID
@@ -1244,7 +1245,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
if (addDelegate)
{
delegatedUID = [delegate uid];
delegatedUID = [delegate uidInContext: context];
delegates = [NSArray arrayWithObject: delegate];
[event addToAttendees: delegate];
@@ -1271,11 +1272,11 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
[self sendResponseToOrganizer: event
from: ownerUser];
organizerUID = [[event organizer] uid];
organizerUID = [[event organizer] uidInContext: context];
// Event is an exception to a recurring event; retrieve organizer from master event
if (!organizerUID)
organizerUID = [[(iCalEntityObject*)[[event parent] firstChildWithTag: [self componentTag]] organizer] uid];
organizerUID = [[(iCalEntityObject*)[[event parent] firstChildWithTag: [self componentTag]] organizer] uidInContext: context];
if (organizerUID)
// Update the attendee in organizer's calendar.
@@ -1302,7 +1303,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
for (i = 0; i < [attendees count]; i++)
{
att = [attendees objectAtIndex: i];
uid = [att uid];
uid = [att uidInContext: context];
if (uid && att != attendee && ![uid isEqualToString: delegatedUID])
[self _updateAttendee: attendee
withDelegate: delegate
@@ -1461,7 +1462,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
{
if (delegate && ![[delegate email] isEqualToString: [attendee delegatedTo]])
{
delegatedUid = [delegate uid];
delegatedUid = [delegate uidInContext: context];
if (delegatedUid)
delegatedUser = [SOGoUser userWithLogin: delegatedUid];
if (delegatedUser != nil && [event userIsOrganizer: delegatedUser])
@@ -1831,7 +1832,7 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
// We now make sure that the organizer, if managed by SOGo, is using
// its default email when creating events and inviting attendees.
uid = [[SOGoUserManager sharedUserManager] getUIDForEmail: [[event organizer] rfc822Email]];
uid = [[event organizer] uidInContext: context];
if (uid)
{
NSDictionary *defaultIdentity;
@@ -2131,32 +2132,23 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
//
if ([[newEvent attendees] count] || [[oldEvent attendees] count])
{
NSString *uid;
BOOL userIsOrganizer;
// newEvent might be nil here, if we're deleting a RECURRENCE-ID with attendees
// If that's the case, we use the oldEvent for now just to obtain the organizer
// and we'll swap it back to nil once we're done.
if (!newEvent)
newEvent = oldEvent;
// We fetch the organizer's uid. Sometimes, the recurrence-id will
// have it, sometimes not. If it doesn't, we fetch it from the master event.
uid = [[newEvent organizer] uid];
if (!uid && !recurrenceId)
uid = [[[[[newEvent parent] events] objectAtIndex: 0] organizer] uid];
// If that's the case, we use the oldEvent to obtain the organizer
if (newEvent)
userIsOrganizer = [newEvent userIsOrganizer: ownerUser];
else
userIsOrganizer = [oldEvent userIsOrganizer: ownerUser];
// With Thunderbird 10, if you create a recurring event with an exception
// occurence, and invite someone, the PUT will have the organizer in the
// recurrence-id and not in the master event. We must fix this, otherwise
// SOGo will break.
if (!recurrenceId && ![[[[[newEvent parent] events] objectAtIndex: 0] organizer] uid])
if (!recurrenceId && ![[[[[newEvent parent] events] objectAtIndex: 0] organizer] uidInContext: context])
[[[[newEvent parent] events] objectAtIndex: 0] setOrganizer: [newEvent organizer]];
if (newEvent == oldEvent)
newEvent = nil;
if (uid && [uid caseInsensitiveCompare: owner] == NSOrderedSame)
if (userIsOrganizer)
{
// A RECCURENCE-ID was removed
if (!newEvent && oldEvent)
@@ -11,16 +11,16 @@ vtodo_class2 = "(Dôverná úloha)";
"The event \"%{Summary}\" was created" = "Udalosť \"%{Summary}\" bola vytvorená";
"The event \"%{Summary}\" was deleted" = "Udalosť \"%{Summary}\" bola vymazaná";
"The event \"%{Summary}\" was updated" = "Udalosť \"%{Summary}\" bola aktualizovaná";
"The following attendees(s) were notified:" = "Nasledujúci účastník(ci) bol upozornený:";
"The following attendees(s) were added:" = "Nasledujúci účastník(ci) bol pridaný:";
"The following attendees(s) were removed:" = "Nasledujúci účastník(ci) bol odstránený:";
"The following attendees(s) were notified" = "Nasledujúci účastník(ci) bol upozornený";
"The following attendees(s) were added" = "Nasledujúci účastník(ci) bol pridaný";
"The following attendees(s) were removed" = "Nasledujúci účastník(ci) bol odstránený";
/* IMIP messages */
"calendar_label" = "Kalendár:";
"startDate_label" = "Začiatok:";
"endDate_label" = "Koniec:";
"calendar_label" = "Kalendár";
"startDate_label" = "Začiatok";
"endDate_label" = "Koniec";
"due_label" = "Platnosť:";
"location_label" = "Miesto:";
"location_label" = "Miesto";
"summary_label" = "Zhrnutie:";
"comment_label" = "Komentár:";
@@ -0,0 +1,67 @@
"Inviting the following persons is prohibited:" = "Povabilo za naslednje osebe je prepovedano:";
"Personal Calendar" = "Osebni koledar";
vevent_class0 = "(Javni dogodek)";
vevent_class1 = "(Osebni dogodek)";
vevent_class2 = "(Zaupni dogodek)";
vtodo_class0 = "(Javno opravilo)";
vtodo_class1 = "(Osebno opravilo)";
vtodo_class2 = "(Zaupno opravilo)";
/* Receipts */
"The event \"%{Summary}\" was created" = "Dogodek \"%{Summary}\" je bil ustvarjen";
"The event \"%{Summary}\" was deleted" = "Dogodek \"%{Summary}\" je bil izbrisan";
"The event \"%{Summary}\" was updated" = "Dogodek \"%{Summary}\" je bil posodobljen";
"The following attendees(s) were notified" = "Naslednji udeleženci so bili obveščeni";
"The following attendees(s) were added" = "Naslednji udeleženci so bili dodani";
"The following attendees(s) were removed" = "Naslednji udeleženci so bili odstranjeni";
/* IMIP messages */
"calendar_label" = "Koledar";
"startDate_label" = "Začetek";
"endDate_label" = "Konec";
"due_label" = "Datum zapadlosti:";
"location_label" = "Mesto";
"summary_label" = "Povzetek:";
"comment_label" = "Komentar:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Povabila na dogodek: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(posladno od %{SentBy})";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText} te je povabil na %{Summary}.\n\n\nZačetek: %{StartDate}\nKonec: %{EndDate}\nOpis: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} te je povabil na %{Summary}.\n\n\nZačetek: %{StartDate} at %{StartTime}\nKonec: %{EndDate} at %{EndTime}\nOpis: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Dogodek odpovedan: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} je odpovedal ta dogodek: %{Summary}.\n\n\nZačetek: %{StartDate}\nKonec: %{EndDate}\nOpis: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} je odpovedal ta dogodek: %{Summary}.\n\n\nZačetek: %{StartDate} at %{StartTime}\nKonec: %{EndDate} at %{EndTime}\nOpis: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "Sestanek \"%{Summary}\" za %{OldStartDate} se je spremenil";
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
= "Sestanek \"%{Summary}\" za %{OldStartDate} at %{OldStartTime} se je spremenil";
"The following parameters have changed in the \"%{Summary}\" meeting:"
= "Naslednji parametri so se spremenil v \"%{Summary}\" srečanju:";
"Please accept or decline those changes."
= "Prosim sprejmi ali zavrni te spremembe.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Sprejeto povabilo: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Zavrnjeno povabilo: \"%{Summary}\"";
"Delegated invitation: \"%{Summary}\"" = "Dodeljeno povabilo: \"%{Summary}\"";
"Not yet decided on invitation: \"%{Summary}\"" = "Še neodločeno glede povabila: \"%{Summary}\"";
"%{Attendee} %{SentByText}has accepted your event invitation."
= "%{Attendee} %{SentByText} je sprejel tvoje povabilo.";
"%{Attendee} %{SentByText}has declined your event invitation."
= "%{Attendee} %{SentByText} je zavrnil tvoje povabilo.";
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
= "%{Attendee} %{SentByText} je posredoval povabilo k %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText} se še ni odločil glede tvojega povabila.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Ne morem dostopati do vira: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Doseženo je največje število sočasnih rezervacij (%{NumberOfSimultaneousBookings}) za vir \"%{Cn} %{SystemEmail}\". Konfliktni dogodek je \"%{EventTitle}\", začel se je %{StartDate}.";
@@ -12,16 +12,16 @@ vtodo_class2 = "(Tarea confidencial)";
"The event \"%{Summary}\" was created" = "Se creó el evento \"%{Summary}\"";
"The event \"%{Summary}\" was deleted" = "Se borró el evento \"%{Summary}\"";
"The event \"%{Summary}\" was updated" = "Se actualizó la información del evento \"%{Summary}\"";
"The following attendees(s) were notified:" = "Se notificó a los siguientes participantes:";
"The following attendees(s) were added:" = "Se agregaron los siguientes participantes:";
"The following attendees(s) were removed:" = "Se removieron los siguientes participantes:";
"The following attendees(s) were notified" = "Se notificó a los siguientes participantes";
"The following attendees(s) were added" = "Se agregaron los siguientes participantes";
"The following attendees(s) were removed" = "Se removieron los siguientes participantes";
/* IMIP messages */
"calendar_label" = "Calendario:";
"startDate_label" = "Inicio:";
"endDate_label" = "Fin:";
"calendar_label" = "Calendario";
"startDate_label" = "Inicio";
"endDate_label" = "Fin";
"due_label" = "Fecha de finalización:";
"location_label" = "Lugar:";
"location_label" = "Lugar";
"summary_label" = "Resumen:";
"comment_label" = "Comentario:";
@@ -12,16 +12,16 @@ vtodo_class2 = "(Tarea confidencial)";
"The event \"%{Summary}\" was created" = "El evento \"%{Summary}\" ha sido creado";
"The event \"%{Summary}\" was deleted" = "El evento \"%{Summary}\" ha sido borrado";
"The event \"%{Summary}\" was updated" = "El evento \"%{Summary}\" ha sido actualizado";
"The following attendees(s) were notified:" = "Los invitados siguientes han sido notificado:";
"The following attendees(s) were added:" = "Los invitados siguientes han sido añadido:";
"The following attendees(s) were removed:" = "Los invitados siguientes han sido quitado:";
"The following attendees(s) were notified" = "Los invitados siguientes han sido notificado";
"The following attendees(s) were added" = "Los invitados siguientes han sido añadido";
"The following attendees(s) were removed" = "Los invitados siguientes han sido quitado";
/* IMIP messages */
"calendar_label" = "Calendario:";
"startDate_label" = "Inicio:";
"endDate_label" = "Fin:";
"calendar_label" = "Calendario";
"startDate_label" = "Inicio";
"endDate_label" = "Fin";
"due_label" = "Fecha Fin:";
"location_label" = "Localización:";
"location_label" = "Localización";
"summary_label" = "Resumen:";
"comment_label" = "Comentario:";
@@ -25,10 +25,10 @@ vtodo_class2 = "(Konfidentiell uppgift)";
"... from the following event:" = "... från följande händelse:";
/* IMIP messages */
"startDate_label" = "Startdatum:";
"endDate_label" = "Slutdatum:";
"startDate_label" = "Startdatum";
"endDate_label" = "Slutdatum";
"due_label" = "Datum:";
"location_label" = "Plats:";
"location_label" = "Plats";
"summary_label" = "Sammanfattning:";
"comment_label" = "Kommentar:";
@@ -25,10 +25,10 @@ vtodo_class2 = "(Конфіденційне завдання)";
"... from the following event:" = "... з участі в події:";
/* IMIP messages */
"startDate_label" = "Початок:";
"endDate_label" = "Кінець:";
"startDate_label" = "Початок";
"endDate_label" = "Кінець";
"due_label" = "Триватиме до:";
"location_label" = "Місце:";
"location_label" = "Місце";
"summary_label" = "Резюме:";
"comment_label" = "Коментар:";
@@ -25,10 +25,10 @@ vtodo_class2 = "(Tasg gyfrinachol)";
"... from the following event:" = "... from the following event:";
/* IMIP messages */
"startDate_label" = "Start:";
"endDate_label" = "End:";
"startDate_label" = "Start";
"endDate_label" = "End";
"due_label" = "Due Date:";
"location_label" = "Location:";
"location_label" = "Location";
"summary_label" = "Summary:";
"comment_label" = "Comment:";
+3 -3
View File
@@ -1,6 +1,6 @@
/* iCalAlarm+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2014 Inverse inc.
* Copyright (C) 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,11 +20,11 @@
#import <NGCards/iCalAlarm.h>
@class iCalRepeatableEntityObject;
@class iCalEntityObject;
@interface iCalAlarm (SOGoExtensions)
+ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity
+ (id) alarmForEvent: (iCalEntityObject *) theEntity
owner: (NSString *) theOwner
action: (NSString *) reminderAction
unit: (NSString *) reminderUnit
+3 -2
View File
@@ -1,6 +1,6 @@
/* iCalAlarm+SOGo.m - this file is part of SOGo
*
* Copyright (C) 2014 Inverse inc.
* Copyright (C) 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,6 +28,7 @@
#import <NGCards/iCalPerson.h>
#import <NGCards/iCalTrigger.h>
#import <NGCards/iCalEntityObject.h>
@implementation iCalAlarm (SOGoExtensions)
@@ -65,7 +66,7 @@
[alarm addChild: aAttendee];
}
+ (id) alarmForEvent: (iCalRepeatableEntityObject *) theEntity
+ (id) alarmForEvent: (iCalEntityObject *) theEntity
owner: (NSString *) theOwner
action: (NSString *) reminderAction
unit: (NSString *) reminderUnit
+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
+383 -23
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)
@@ -184,16 +523,17 @@ NSNumber *iCalDistantFutureNumber = nil;
NSArray *oldAttendees;
unsigned int count, max;
iCalPerson *currentAttendee;
NSString *userID;
NSString *userID, *domain;
userID = [user login];
domain = [user domain];
oldAttendees = [self attendees];
max = [oldAttendees count];
newAttendees = [NSMutableArray arrayWithCapacity: max];
for (count = 0; count < max; count++)
{
currentAttendee = [oldAttendees objectAtIndex: count];
if (![[currentAttendee uid] isEqualToString: userID])
if (![[currentAttendee uidInDomain: domain] isEqualToString: userID])
[newAttendees addObject: currentAttendee];
}
@@ -249,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;
;
}
//
@@ -348,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:
//
@@ -376,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
@@ -393,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)
{
@@ -408,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];
@@ -421,7 +781,7 @@ NSNumber *iCalDistantFutureNumber = nil;
if (!webstatus
|| ([webstatus caseInsensitiveCompare: @"TRIGGERED"]
!= NSOrderedSame))
v = delta;
nextAlarmDate = [NSDate dateWithTimeIntervalSince1970: [[[alarms objectAtIndex: i] objectForKey: @"c_nextalarm"] intValue]];
}
@@ -435,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
+3 -4
View File
@@ -1,9 +1,6 @@
/* iCalPerson+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2007-2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Ludovic Marcotte <lmarcotte@inverse.ca>
* Copyright (C) 2007-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -32,6 +29,8 @@
- (NSString *) mailAddress;
- (NSString *) uid;
- (NSString *) uidInDomain: (NSString *) domain;
- (NSString *) uidInContext: (WOContext *) context;
- (NSString *) contactIDInContext: (WOContext *) context;
- (BOOL) hasSentBy;
- (NSString *) sentBy;
+26 -4
View File
@@ -1,9 +1,6 @@
/* iCalPerson+SOGo.m - this file is part of SOGo
*
* Copyright (C) 2007-2012 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Ludovic Marcotte <lmarcotte@inverse.ca>
* Copyright (C) 2007-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -67,6 +64,31 @@ static SOGoUserManager *um = nil;
return [um getUIDForEmail: [self rfc822Email]];
}
- (NSString *) uidInContext: (WOContext *) context
{
NSString *domain;
domain = [[context activeUser] domain];
return [self uidInDomain: domain];
}
- (NSString *) uidInDomain: (NSString *) domain
{
NSDictionary *contact;
NSString *uid;
if (!um)
um = [SOGoUserManager sharedUserManager];
uid = nil;
contact = [um contactInfosForUserWithUIDorEmail: [self rfc822Email] inDomain: domain];
if (contact)
uid = [contact valueForKey: @"c_uid"];
return uid;
}
- (NSString *) contactIDInContext: (WOContext *) context
{
NSString *domain, *uid;
@@ -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: @","]
@@ -0,0 +1,2 @@
"Personal Address Book" = "個人通訊錄";
"Collected Address Book" = "公用通訊錄";
+1 -1
View File
@@ -27,7 +27,7 @@ Contacts_OBJC_FILES = \
Contacts_RESOURCE_FILES += \
product.plist \
Contacts_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
Contacts_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
Contacts_LOCALIZED_RESOURCE_FILES = Localizable.strings
+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)] &&
@@ -0,0 +1,2 @@
"Personal Address Book" = "Osebni adresar";
"Collected Address Book" = "Zbrani adresar";
@@ -0,0 +1,2 @@
"OtherUsersFolderName" = "其他使用者";
"SharedFoldersName" = "共用資料匣";
+5 -2
View File
@@ -50,6 +50,8 @@ Mailer_RESOURCE_FILES += \
SOGoMailBrazilianPortugueseReply.wo \
SOGoMailCatalanForward.wo \
SOGoMailCatalanReply.wo \
SOGoMailChineseTaiwanForward.wo \
SOGoMailChineseTaiwanReply.wo \
SOGoMailCzechForward.wo \
SOGoMailCzechReply.wo \
SOGoMailDanishForward.wo \
@@ -80,6 +82,8 @@ Mailer_RESOURCE_FILES += \
SOGoMailRussianReply.wo \
SOGoMailSlovakForward.wo \
SOGoMailSlovakReply.wo \
SOGoMailSlovenianForward.wo \
SOGoMailSlovenianReply.wo \
SOGoMailSpanishSpainForward.wo \
SOGoMailSpanishSpainReply.wo \
SOGoMailSpanishArgentinaForward.wo \
@@ -91,8 +95,7 @@ Mailer_RESOURCE_FILES += \
SOGoMailWelshForward.wo \
SOGoMailWelshReply.wo
Mailer_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovak SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
Mailer_LANGUAGES = Arabic Basque BrazilianPortuguese ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian NorwegianBokmal NorwegianNynorsk Polish Russian Slovenian Slovenian SpanishSpain SpanishArgentina Swedish Ukrainian Welsh
Mailer_LOCALIZED_RESOURCE_FILES = Localizable.strings
+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];
}
@@ -86,12 +86,3 @@ signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
signaturePlacementOnTop: WOConditional {
condition = signaturePlacementOnTop;
}
signaturePlacementOnBottom: WOConditional {
condition = signaturePlacementOnTop;
negate = YES;
}
Loaded 100 of 1236 files, more files were not shown because too many files have changed in this diff. Show more