Compare commits

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

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

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

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

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

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

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

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

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

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

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

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

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

	UI/Common/UIxAclEditor.m
	UI/Common/UIxUserRightsEditor.m
	UI/WebServerResources/js/Common/acl-model.js
2015-06-11 15:38:09 -04:00
Francis Lachapelle e4b1c0d5b9 Fix buttons in card editor 2015-06-11 15:27:09 -04:00
Francis Lachapelle 3364263247 Improve handling of contact lists 2015-06-11 15:27:09 -04:00
Francis Lachapelle 61c48d37e4 Fix creation of new contacts when editing a list 2015-06-11 15:27:09 -04:00
Alexandre Cloutier 5b83b0c7b6 Enable search by fullname and email 2015-06-11 15:27:09 -04:00
Alexandre Cloutier 7e43b13f21 GUI contact list creation 2015-06-11 15:27:08 -04:00
Francis Lachapelle 57791e384b Revert "Creation of aclUsers window"
This reverts commit ee5ef02f50.
2015-06-11 15:27:08 -04:00
Francis Lachapelle 63b20c4ba2 Revert "rebase modification"
This reverts commit 45c5bbd36a.
2015-06-11 15:27:08 -04:00
Francis Lachapelle e11eea7548 Revert "file lost through rebasing"
This reverts commit a952402c39.
2015-06-11 15:27:08 -04:00
Francis Lachapelle f7df4c1fa0 Allow reverting edition of addressbook name w/ESC 2015-06-11 15:27:07 -04:00
Francis Lachapelle 4457997276 AngularJS sgEscape directive for SOGo.UIDesktop 2015-06-11 15:27:07 -04:00
Francis Lachapelle 16628ab104 Fix installation of ionic JS in Gruntfile.js 2015-06-11 15:27:07 -04:00
Alexandre Cloutier 56b765c6a5 delete unused .png 2015-06-11 15:27:06 -04:00
Alexandre Cloutier 3bf1224c9b file lost through rebasing 2015-06-11 15:27:06 -04:00
Alexandre Cloutier edbd661104 rebase modification 2015-06-11 15:27:05 -04:00
Alexandre Cloutier 8ef222ab44 Creation of aclUsers window 2015-06-11 15:27:05 -04:00
Francis Lachapelle 654c8d26b7 Contact editor: fix cancel and reset buttons 2015-06-11 15:27:05 -04:00
Francis Lachapelle 31ea7a5ab5 Improve contact module models
- Fixed reset behavior
- Created an abstract state for the card viewer and editor
- Moved the card instance from the $rootScope.addressbook to the $scope
2015-06-11 15:27:05 -04:00
Francis Lachapelle 342441ecf8 Improve JavaScript coding style
To comitted code now passes the Airbnb pattern using jscs
2015-06-11 15:27:03 -04:00
Francis Lachapelle df37fd8b92 AddressBook JS model: rename $all to $findAll 2015-06-11 15:27:03 -04:00
Francis Lachapelle 32d732caf0 Update GNUmakefile for WebServerResources 2015-06-11 15:27:02 -04:00
Francis Lachapelle 3e2173db7a Improve JavaScript coding style
To comitted code now passes the Airbnb pattern using jscs
2015-06-11 15:27:02 -04:00
Francis Lachapelle 17ee62f4de Improve usage of ui.router with "resolve" 2015-06-11 15:27:02 -04:00
Francis Lachapelle f363257e51 Use angularModule variable in mobile templates 2015-06-11 15:27:02 -04:00
Francis Lachapelle 865bf096f5 Don't add an empty category to a contact 2015-06-11 15:27:01 -04:00
Francis Lachapelle c4802df024 Remove debugging output 2015-06-11 15:27:01 -04:00
Francis Lachapelle 130194a740 Update GNUmakefile for WebServerResources 2015-06-11 15:27:01 -04:00
Francis Lachapelle 21f63c15fc Cleanup of Web server resources 2015-06-11 15:27:00 -04:00
Francis Lachapelle bb9e8c0348 Remove trailing slash from template theme name 2015-06-11 15:26:38 -04:00
Francis Lachapelle 16cd231ce3 New .button.outline CSS style 2015-06-11 15:26:37 -04:00
Francis Lachapelle 20497e49e3 Add creation & deletion of addressbooks 2015-06-11 15:26:37 -04:00
Francis Lachapelle b97bf2b25f JSON'ed [UIxParentFolderActions createFolder] 2015-06-11 15:26:37 -04:00
Francis Lachapelle 4f7a5d6ef5 Rename ng module SOGo.RootPage to SOGo.MainUI 2015-06-11 15:26:37 -04:00
Francis Lachapelle f321abc63a Desktop list editor, mobile contact editor 2015-06-11 15:26:36 -04:00
Francis Lachapelle 4b0ef6086a Cleanup contact and list viewers 2015-06-11 15:26:36 -04:00
Francis Lachapelle b57a8163fe Add link to switch to mobile version 2015-06-11 15:26:36 -04:00
Francis Lachapelle 86a6f5c67b Add Angular module assignation in page frame 2015-06-11 15:26:35 -04:00
Francis Lachapelle e40b42c213 Move setAttributes from NGVCard to UIx*Editors 2015-06-11 15:26:35 -04:00
Francis Lachapelle 2f6bc1c4ff Update .gitignore 2015-06-11 15:25:31 -04:00
Francis Lachapelle 958f0b4958 Contacts: Add possibility to add/delete objects 2015-06-11 15:25:31 -04:00
Francis Lachapelle 0c720bb902 New ng-module for common desktop widgets 2015-06-11 15:25:30 -04:00
Francis Lachapelle 34b3f2ac0c Improve build scripts of Web frontend 2015-06-11 15:25:30 -04:00
Francis Lachapelle ef4de28ed4 ContactView: lowercase the card tag 2015-06-11 15:25:30 -04:00
Francis Lachapelle c98d580b29 Return contact UID when saving contact 2015-06-11 15:25:30 -04:00
Francis Lachapelle a2460f9368 Add "newguid" action accessible from any folder 2015-06-11 15:25:29 -04:00
Francis Lachapelle c0eac04437 Adapt SOGoContactGCSFolder contact representation
To match [UIxContactViewer dataAction].
2015-06-11 15:25:29 -04:00
Francis Lachapelle 7f9be26541 Improve localization func to support placeholders 2015-06-11 15:25:28 -04:00
Francis Lachapelle c8ea3da5bc Improve detection of theme-specific JavaScript 2015-06-11 15:25:28 -04:00
Francis Lachapelle a70287870b Improve build scripts of Web frontend 2015-06-11 15:25:28 -04:00
Francis Lachapelle e4ec5313db Improve grunt tasks to copy fonts files 2015-06-11 15:25:27 -04:00
Francis Lachapelle a07d918fbf Add category to CardElement
New method to return a JSON representation of the element.
2015-06-11 15:25:27 -04:00
Francis Lachapelle 8da98367db Fix path to ionic scss files 2015-06-11 15:25:26 -04:00
Francis Lachapelle af5489bf57 Fix redirection URLs in JavaScript 2015-06-11 15:25:26 -04:00
Francis Lachapelle 54103124e8 Restore/move missing images 2015-06-11 15:25:25 -04:00
Francis Lachapelle 459c040d8a Initial mobile theme using ionic 2015-06-11 15:25:25 -04:00
Francis Lachapelle f248f81583 Improve template of Contacts module 2015-06-11 15:25:25 -04:00
Francis Lachapelle a6d8dcc5b6 Common JavaScript utlity functions 2015-06-11 15:25:25 -04:00
Francis Lachapelle b4d6667f88 Remove duplicated method 2015-06-11 15:25:24 -04:00
Francis Lachapelle 77b80e27c6 Add support for themes 2015-06-11 15:25:24 -04:00
Francis Lachapelle 16d04e998f Improve Sass of SOGoRootPage & Contacts modules 2015-06-11 15:25:24 -04:00
Francis Lachapelle d52d7582ca Improve build scripts of Web frontend 2015-06-11 15:25:23 -04:00
Francis Lachapelle e817a3b3d0 Improve JavaScript of SOGoRootPage module 2015-06-11 15:25:23 -04:00
Francis Lachapelle 2d6f8e4ea6 Improve JavaScript for Contacts module 2015-06-11 15:25:23 -04:00
Francis Lachapelle fbe412db08 Allow js/img/css subfolders in WebServerResources 2015-06-11 15:25:22 -04:00
Francis Lachapelle 24f01e72c2 JSON-based operations for root page 2015-06-11 15:25:22 -04:00
Francis Lachapelle 6970acfaa4 AngularJS-based templates 2015-06-11 15:25:22 -04:00
Francis Lachapelle 0a49031b01 JSON-based operations on contacts 2015-06-11 15:23:27 -04:00
Francis Lachapelle 29e147ee86 Initial Sass and JavaScript files 2015-06-11 15:19:22 -04:00
Francis Lachapelle c21ec86365 Icons font 2015-06-11 15:19:21 -04:00
Francis Lachapelle 825c45f872 Initial JavaScript models for addressbooks module 2015-06-11 15:19:21 -04:00
Francis Lachapelle 3ad4352ffe Initial cleanup of Web server resources 2015-06-11 15:19:21 -04:00
Alexandre Cloutier a45dc96885 applied comments 2015-06-11 15:17:26 -04:00
Alexandre Cloutier b041ec6e65 activeTasks updates dynamically 2015-06-11 15:15:39 -04:00
Alexandre Cloutier 257d731e04 multiColumns print view + bugFixes 2015-06-11 15:15:39 -04:00
Alexandre Cloutier 720a946d39 The multiColumns view 2015-06-11 15:15:39 -04:00
Pablo Carranza 2d5f2d88fa Revert "Started Ubuntu w/PostgreSQL tutorial"
This reverts commit 51d2372976.
2013-11-05 22:32:35 -06:00
Pablo Carranza eb5ca2cba6 Revert "Update ubuntu_postgresql.md"
This reverts commit 92d68de332.
2013-11-05 22:31:54 -06:00
Pablo Carranza 54cd55ebef Update README.md 2013-10-19 13:15:13 -05:00
Pablo Carranza 5f4ab6eaaf Update README.md 2013-10-19 13:11:36 -05:00
Pablo Carranza 1d8e6b6295 Update README.md 2013-10-19 13:03:40 -05:00
Pablo Carranza aa791b2571 Create README.md 2013-10-19 12:42:31 -05:00
Pablo Carranza 92d68de332 Update ubuntu_postgresql.md 2013-10-19 12:25:19 -05:00
Pablo Carranza 51d2372976 Started Ubuntu w/PostgreSQL tutorial 2013-10-19 12:17:39 -05:00
1452 changed files with 41513 additions and 82276 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
@@ -18,12 +18,10 @@ trans.fr = UI/MailerUI/French.lproj/Localizable.strings
trans.hu = UI/MailerUI/Hungarian.lproj/Localizable.strings
trans.is = UI/MailerUI/Icelandic.lproj/Localizable.strings
trans.it = UI/MailerUI/Italian.lproj/Localizable.strings
trans.mk_MK = UI/MailerUI/Macedonian.lproj/Localizable.strings
trans.nb_NO = UI/MailerUI/NorwegianBokmal.lproj/Localizable.strings
trans.nl = UI/MailerUI/Dutch.lproj/Localizable.strings
trans.nn_NO = UI/MailerUI/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/MailerUI/Polish.lproj/Localizable.strings
trans.pt = UI/MailerUI/Portuguese.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
@@ -49,12 +47,10 @@ trans.fr = UI/PreferencesUI/French.lproj/Localizable.strings
trans.hu = UI/PreferencesUI/Hungarian.lproj/Localizable.strings
trans.is = UI/PreferencesUI/Icelandic.lproj/Localizable.strings
trans.it = UI/PreferencesUI/Italian.lproj/Localizable.strings
trans.mk_MK = UI/PreferencesUI/Macedonian.lproj/Localizable.strings
trans.nb_NO = UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings
trans.nl = UI/PreferencesUI/Dutch.lproj/Localizable.strings
trans.nn_NO = UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/PreferencesUI/Polish.lproj/Localizable.strings
trans.pt = UI/PreferencesUI/Portuguese.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
@@ -80,12 +76,10 @@ trans.fr = UI/Scheduler/French.lproj/Localizable.strings
trans.hu = UI/Scheduler/Hungarian.lproj/Localizable.strings
trans.is = UI/Scheduler/Icelandic.lproj/Localizable.strings
trans.it = UI/Scheduler/Italian.lproj/Localizable.strings
trans.mk_MK = UI/Scheduler/Macedonian.lproj/Localizable.strings
trans.nb_NO = UI/Scheduler/NorwegianBokmal.lproj/Localizable.strings
trans.nl = UI/Scheduler/Dutch.lproj/Localizable.strings
trans.nn_NO = UI/Scheduler/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/Scheduler/Polish.lproj/Localizable.strings
trans.pt = UI/Scheduler/Portuguese.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
@@ -111,12 +105,10 @@ trans.fr = UI/Contacts/French.lproj/Localizable.strings
trans.hu = UI/Contacts/Hungarian.lproj/Localizable.strings
trans.is = UI/Contacts/Icelandic.lproj/Localizable.strings
trans.it = UI/Contacts/Italian.lproj/Localizable.strings
trans.mk_MK = UI/Contacts/Macedonian.lproj/Localizable.strings
trans.nb_NO = UI/Contacts/NorwegianBokmal.lproj/Localizable.strings
trans.nl = UI/Contacts/Dutch.lproj/Localizable.strings
trans.nn_NO = UI/Contacts/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/Contacts/Polish.lproj/Localizable.strings
trans.pt = UI/Contacts/Portuguese.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
@@ -142,12 +134,10 @@ trans.fr = UI/MainUI/French.lproj/Localizable.strings
trans.hu = UI/MainUI/Hungarian.lproj/Localizable.strings
trans.is = UI/MainUI/Icelandic.lproj/Localizable.strings
trans.it = UI/MainUI/Italian.lproj/Localizable.strings
trans.mk_MK = UI/MainUI/Macedonian.lproj/Localizable.strings
trans.nb_NO = UI/MainUI/NorwegianBokmal.lproj/Localizable.strings
trans.nl = UI/MainUI/Dutch.lproj/Localizable.strings
trans.nn_NO = UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/MainUI/Polish.lproj/Localizable.strings
trans.pt = UI/MainUI/Portuguese.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
@@ -173,12 +163,10 @@ trans.fr = UI/Common/French.lproj/Localizable.strings
trans.hu = UI/Common/Hungarian.lproj/Localizable.strings
trans.is = UI/Common/Icelandic.lproj/Localizable.strings
trans.it = UI/Common/Italian.lproj/Localizable.strings
trans.mk_MK = UI/Common/Macedonian.lproj/Localizable.strings
trans.nb_NO = UI/Common/NorwegianBokmal.lproj/Localizable.strings
trans.nl = UI/Common/Dutch.lproj/Localizable.strings
trans.nn_NO = UI/Common/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/Common/Polish.lproj/Localizable.strings
trans.pt = UI/Common/Portuguese.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
@@ -204,12 +192,10 @@ trans.fr = UI/AdministrationUI/French.lproj/Localizable.strings
trans.hu = UI/AdministrationUI/Hungarian.lproj/Localizable.strings
trans.is = UI/AdministrationUI/Icelandic.lproj/Localizable.strings
trans.it = UI/AdministrationUI/Italian.lproj/Localizable.strings
trans.mk_MK = UI/AdministrationUI/Macedonian.lproj/Localizable.strings
trans.nb_NO = UI/AdministrationUI/NorwegianBokmal.lproj/Localizable.strings
trans.nl = UI/AdministrationUI/Dutch.lproj/Localizable.strings
trans.nn_NO = UI/AdministrationUI/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/AdministrationUI/Polish.lproj/Localizable.strings
trans.pt = UI/AdministrationUI/Portuguese.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
@@ -235,12 +221,10 @@ trans.fr = SoObjects/Appointments/French.lproj/Localizable.strings
trans.hu = SoObjects/Appointments/Hungarian.lproj/Localizable.strings
trans.is = SoObjects/Appointments/Icelandic.lproj/Localizable.strings
trans.it = SoObjects/Appointments/Italian.lproj/Localizable.strings
trans.mk_MK = SoObjects/Appointments/Macedonian.lproj/Localizable.strings
trans.nb_NO = SoObjects/Appointments/NorwegianBokmal.lproj/Localizable.strings
trans.nl = SoObjects/Appointments/Dutch.lproj/Localizable.strings
trans.nn_NO = SoObjects/Appointments/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = SoObjects/Appointments/Polish.lproj/Localizable.strings
trans.pt = SoObjects/Appointments/Portuguese.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
@@ -266,12 +250,10 @@ trans.fr = SoObjects/Contacts/French.lproj/Localizable.strings
trans.hu = SoObjects/Contacts/Hungarian.lproj/Localizable.strings
trans.is = SoObjects/Contacts/Icelandic.lproj/Localizable.strings
trans.it = SoObjects/Contacts/Italian.lproj/Localizable.strings
trans.mk_MK = SoObjects/Contacts/Macedonian.lproj/Localizable.strings
trans.nb_NO = SoObjects/Contacts/NorwegianBokmal.lproj/Localizable.strings
trans.nl = SoObjects/Contacts/Dutch.lproj/Localizable.strings
trans.nn_NO = SoObjects/Contacts/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = SoObjects/Contacts/Polish.lproj/Localizable.strings
trans.pt = SoObjects/Contacts/Portuguese.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
@@ -297,12 +279,10 @@ trans.fr = SoObjects/Mailer/French.lproj/Localizable.strings
trans.hu = SoObjects/Mailer/Hungarian.lproj/Localizable.strings
trans.is = SoObjects/Mailer/Icelandic.lproj/Localizable.strings
trans.it = SoObjects/Mailer/Italian.lproj/Localizable.strings
trans.mk_MK = SoObjects/Mailer/Macedonian.lproj/Localizable.strings
trans.nb_NO = SoObjects/Mailer/NorwegianBokmal.lproj/Localizable.strings
trans.nl = SoObjects/Mailer/Dutch.lproj/Localizable.strings
trans.nn_NO = SoObjects/Mailer/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = SoObjects/Mailer/Polish.lproj/Localizable.strings
trans.pt = SoObjects/Mailer/Portuguese.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
@@ -328,12 +308,10 @@ trans.fr = UI/MailPartViewers/French.lproj/Localizable.strings
trans.hu = UI/MailPartViewers/Hungarian.lproj/Localizable.strings
trans.is = UI/MailPartViewers/Icelandic.lproj/Localizable.strings
trans.it = UI/MailPartViewers/Italian.lproj/Localizable.strings
trans.mk_MK = UI/MailPartViewers/Macedonian.lproj/Localizable.strings
trans.nb_NO = UI/MailPartViewers/NorwegianBokmal.lproj/Localizable.strings
trans.nl = UI/MailPartViewers/Dutch.lproj/Localizable.strings
trans.nn_NO = UI/MailPartViewers/NorwegianNynorsk.lproj/Localizable.strings
trans.pl = UI/MailPartViewers/Polish.lproj/Localizable.strings
trans.pt = UI/MailPartViewers/Portuguese.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
+1 -4
View File
@@ -1,4 +1 @@
ifeq ($(HAS_LIBRARY_ssl),yes)
ADDITIONAL_CPPFLAGS += -DHAVE_OPENSSL=1
BUNDLE_LIBS += -lcrypto
endif
# compilation settings
+1 -1
View File
@@ -101,7 +101,7 @@ static NSArray *asElementArray = nil;
int i, count;
if (!asElementArray)
asElementArray = [[NSArray alloc] initWithObjects: @"Attendee", @"Category", @"Exception", nil];
asElementArray = [[NSArray alloc] initWithObjects: @"Attendee", @"Category", nil];
data = [NSMutableDictionary dictionary];
+49 -181
View File
@@ -48,42 +48,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@implementation NGVCard (ActiveSync)
//
// This function is called for each elements which can be ghosted according to specs.
// https://msdn.microsoft.com/en-us/library/gg650908%28v=exchg.80%29.aspx
//
- (BOOL) _isGhosted: (NSString *) element
inContext: (WOContext *) context
{
NSArray *supportedElements;
supportedElements = [context objectForKey: @"SupportedElements"];
// If the client does not include a Supported element in the initial Sync command request for
// a folder, then all of the elements that can be ghosted are considered not ghosted.
if (!supportedElements)
return NO;
// If the client includes an empty Supported element in the initial Sync command request for
// a folder, then all elements that can be ghosted are considered ghosted.
if (![supportedElements count])
return YES;
// If the client includes a Supported element that contains child elements in the initial
// Sync command request for a folder, then each child element of that Supported element is
// considered not ghosted. All elements that can be ghosted that are not included as child
// elements of the Supported element are considered ghosted.
if (!([supportedElements indexOfObject: element] == NSNotFound))
return YES;
return NO;
}
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
{
NSArray *emails, *addresses, *categories, *elements;
CardElement *n, *homeAdr, *workAdr;
NSMutableString *s, *a;
NSMutableString *s;
NSString *url;
id o;
@@ -94,7 +63,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ((o = [n flattenedValueAtIndex: 0 forKey: @""]))
[s appendFormat: @"<LastName xmlns=\"Contacts:\">%@</LastName>", [o activeSyncRepresentationInContext: context]];
if ((o = [n flattenedValueAtIndex: 1 forKey: @""]))
[s appendFormat: @"<FirstName xmlns=\"Contacts:\">%@</FirstName>", [o activeSyncRepresentationInContext: context]];
@@ -177,22 +146,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ([addresses count])
{
homeAdr = [addresses objectAtIndex: 0];
a = [NSMutableString string];
if ((o = [homeAdr flattenedValueAtIndex: 2 forKey: @""]))
[a appendString: o];
if ((o = [homeAdr flattenedValueAtIndex: 1 forKey: @""]) && [o length])
[a appendFormat: @"\n%@", o];
[s appendFormat: @"<HomeStreet xmlns=\"Contacts:\">%@</HomeStreet>", [o activeSyncRepresentationInContext: context]];
[s appendFormat: @"<HomeStreet xmlns=\"Contacts:\">%@</HomeStreet>", [a activeSyncRepresentationInContext: context]];
if ((o = [homeAdr flattenedValueAtIndex: 3 forKey: @""]))
[s appendFormat: @"<HomeCity xmlns=\"Contacts:\">%@</HomeCity>", [o activeSyncRepresentationInContext: context]];
if ((o = [homeAdr flattenedValueAtIndex: 4 forKey: @""]))
[s appendFormat: @"<HomeState xmlns=\"Contacts:\">%@</HomeState>", [o activeSyncRepresentationInContext: context]];
if ((o = [homeAdr flattenedValueAtIndex: 5 forKey: @""]))
[s appendFormat: @"<HomePostalCode xmlns=\"Contacts:\">%@</HomePostalCode>", [o activeSyncRepresentationInContext: context]];
@@ -208,15 +171,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ([addresses count])
{
workAdr = [addresses objectAtIndex: 0];
a = [NSMutableString string];
if ((o = [workAdr flattenedValueAtIndex: 2 forKey: @""]))
[a appendString: o];
if ((o = [workAdr flattenedValueAtIndex: 1 forKey: @""]) && [o length])
[a appendFormat: @"\n%@", o];
[s appendFormat: @"<BusinessStreet xmlns=\"Contacts:\">%@</BusinessStreet>", [a activeSyncRepresentationInContext: context]];
[s appendFormat: @"<BusinessStreet xmlns=\"Contacts:\">%@</BusinessStreet>", [o activeSyncRepresentationInContext: context]];
if ((o = [workAdr flattenedValueAtIndex: 3 forKey: @""]))
[s appendFormat: @"<BusinessCity xmlns=\"Contacts:\">%@</BusinessCity>", [o activeSyncRepresentationInContext: context]];
@@ -260,7 +217,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
inContext: (WOContext *) context
{
CardElement *element;
NSMutableArray *addressLines;
id o;
// Contact's note
@@ -270,8 +226,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Categories
if ((o = [theValues objectForKey: @"Categories"]) && [o length])
[self setCategories: o];
else
[[self children] removeObjectsInArray: [self childrenWithTag: @"Categories"]];
// Birthday
if ((o = [theValues objectForKey: @"Birthday"]))
@@ -279,11 +233,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
o = [o calendarDate];
[self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d" timeZone: nil locale: nil]];
}
else if (![self _isGhosted: @"Birthday" inContext: context])
{
[self setBday: @""];
}
//
// Business address information
@@ -295,48 +244,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// BusinessCountry
//
element = [self elementWithTag: @"adr" ofType: @"work"];
if ((o = [theValues objectForKey: @"BusinessStreet"]) || ![self _isGhosted: @"BusinessStreet" inContext: context])
{
addressLines = [NSMutableArray arrayWithArray: [o componentsSeparatedByString: @"\n"]];
[element setSingleValue: @""
atIndex: 1 forKey: @""];
[element setSingleValue: [addressLines count] ? [addressLines objectAtIndex: 0] : @""
atIndex: 2 forKey: @""];
// Extended address line. If there are more than 2 address lines we add them to the extended address line.
if ([addressLines count] > 1)
{
[addressLines removeObjectAtIndex: 0];
[element setSingleValue: [addressLines componentsJoinedByString: @" "]
atIndex: 1 forKey: @""];
}
}
if ((o = [theValues objectForKey: @"BusinessCity"]) || ![self _isGhosted: @"BusinessCity" inContext: context])
{
[element setSingleValue: [theValues objectForKey: @"BusinessCity"]
atIndex: 3 forKey: @""];
}
if ((o = [theValues objectForKey: @"BusinessState"]) || ![self _isGhosted: @"BusinessState" inContext: context])
{
[element setSingleValue: [theValues objectForKey: @"BusinessState"]
atIndex: 4 forKey: @""];
}
if ((o = [theValues objectForKey: @"BusinessPostalCode"]) || ![self _isGhosted: @"BusinessPostalCode" inContext: context])
{
[element setSingleValue: [theValues objectForKey: @"BusinessPostalCode"]
atIndex: 5 forKey: @""];
}
if ((o = [theValues objectForKey: @"BusinessCountry"]) || ![self _isGhosted: @"BusinessCountry" inContext: context])
{
[element setSingleValue: [theValues objectForKey: @"BusinessCountry"]
atIndex: 6 forKey: @""];
}
[element setSingleValue: @""
atIndex: 1 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"BusinessStreet"]
atIndex: 2 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"BusinessCity"]
atIndex: 3 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"BusinessState"]
atIndex: 4 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"BusinessPostalCode"]
atIndex: 5 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"BusinessCountry"]
atIndex: 6 forKey: @""];
//
// Home address information
@@ -348,69 +267,35 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// HomeCountry
//
element = [self elementWithTag: @"adr" ofType: @"home"];
if ((o = [theValues objectForKey: @"HomeStreet"]) || ![self _isGhosted: @"HomeStreet" inContext: context])
{
addressLines = [NSMutableArray arrayWithArray: [o componentsSeparatedByString: @"\n"]];
[element setSingleValue: @""
atIndex: 1 forKey: @""];
[element setSingleValue: [addressLines count] ? [addressLines objectAtIndex: 0] : @""
atIndex: 2 forKey: @""];
// Extended address line. If there are more then 2 address lines we add them to the extended address line.
if ([addressLines count] > 1)
{
[addressLines removeObjectAtIndex: 0];
[element setSingleValue: [addressLines componentsJoinedByString: @" "]
atIndex: 1 forKey: @""];
}
}
if ((o = [theValues objectForKey: @"HomeCity"]) || ![self _isGhosted: @"HomeCity" inContext: context])
{
[element setSingleValue: [theValues objectForKey: @"HomeCity"]
atIndex: 3 forKey: @""];
}
if ((o = [theValues objectForKey: @"HomeState"]) || ![self _isGhosted: @"HomeState" inContext: context])
{
[element setSingleValue: [theValues objectForKey: @"HomeState"]
atIndex: 4 forKey: @""];
}
if ((o = [theValues objectForKey: @"HomePostalCode"]) || ![self _isGhosted: @"HomePostalCode" inContext: context])
{
[element setSingleValue: [theValues objectForKey: @"HomePostalCode"]
atIndex: 5 forKey: @""];
}
if ((o = [theValues objectForKey: @"HomeCountry"]) || ![self _isGhosted: @"HomeCountry" inContext: context])
{
[element setSingleValue: [theValues objectForKey: @"HomeCountry"]
atIndex: 6 forKey: @""];
}
[element setSingleValue: @""
atIndex: 1 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"HomeStreet"]
atIndex: 2 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"HomeCity"]
atIndex: 3 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"HomeState"]
atIndex: 4 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"HomePostalCode"]
atIndex: 5 forKey: @""];
[element setSingleValue: [theValues objectForKey: @"HomeCountry"]
atIndex: 6 forKey: @""];
// Company's name
if ((o = [theValues objectForKey: @"CompanyName"]))
[self setOrg: o units: nil];
else if (![self _isGhosted: @"CompanyName" inContext: context])
[self setOrg: @"" units: nil];
// Department
if ((o = [theValues objectForKey: @"Department"]))
[self setOrg: nil units: [NSArray arrayWithObjects:o,nil]];
else if (![self _isGhosted: @"Department" inContext: context])
[self setOrg: nil units: [NSArray arrayWithObjects:@"",nil]];
// Email addresses
if ((o = [theValues objectForKey: @"Email1Address"]) || ![self _isGhosted: @"Email1Address" inContext: context])
if ((o = [theValues objectForKey: @"Email1Address"]))
{
element = [self elementWithTag: @"email" ofType: @"work"];
[element setSingleValue: [o pureEMailAddress] forKey: @""];
}
if ((o = [theValues objectForKey: @"Email2Address"]) || ![self _isGhosted: @"Email2Address" inContext: context])
if ((o = [theValues objectForKey: @"Email2Address"]))
{
element = [self elementWithTag: @"email" ofType: @"home"];
[element setSingleValue: [o pureEMailAddress] forKey: @""];
@@ -418,7 +303,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// SOGo currently only supports 2 email addresses ... but AS clients might send 3
// FIXME: revise this when the GUI revamp is done in SOGo
if ((o = [theValues objectForKey: @"Email3Address"]) || ![self _isGhosted: @"Email3Address" inContext: context])
if ((o = [theValues objectForKey: @"Email3Address"]))
{
element = [self elementWithTag: @"email" ofType: @"three"];
[element setSingleValue: [o pureEMailAddress] forKey: @""];
@@ -428,62 +313,45 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// MiddleName
// Suffix (II)
// Title (Mr.)
if ((o = [theValues objectForKey: @"FileAs"]) || ![self _isGhosted: @"FileAs" inContext: context])
[self setFn: [theValues objectForKey: @"FileAs"]];
[self setFn: [theValues objectForKey: @"FileAs"]];
[self setNWithFamily: [theValues objectForKey: @"LastName"]
given: [theValues objectForKey: @"FirstName"]
additional: nil prefixes: nil suffixes: nil];
// IM information
if ((o = [theValues objectForKey: @"IMAddress"]) || ![self _isGhosted: @"IMAddress" inContext: context])
[[self uniqueChildWithTag: @"x-aim"]
setSingleValue: [theValues objectForKey: @"IMAddress"]
forKey: @""];
[[self uniqueChildWithTag: @"x-aim"]
setSingleValue: [theValues objectForKey: @"IMAddress"]
forKey: @""];
//
// Phone numbrrs
//
if ((o = [theValues objectForKey: @"BusinessPhoneNumber"]) || ![self _isGhosted: @"BusinessPhoneNumber" inContext: context])
{
element = [self elementWithTag: @"tel" ofType: @"work"];
[element setSingleValue: [theValues objectForKey: @"BusinessPhoneNumber"] forKey: @""];
}
element = [self elementWithTag: @"tel" ofType: @"work"];
[element setSingleValue: [theValues objectForKey: @"BusinessPhoneNumber"] forKey: @""];
if ((o = [theValues objectForKey: @"HomePhoneNumber"]) || ![self _isGhosted: @"HomePhoneNumber" inContext: context])
{
element = [self elementWithTag: @"tel" ofType: @"home"];
[element setSingleValue: [theValues objectForKey: @"HomePhoneNumber"] forKey: @""];
}
element = [self elementWithTag: @"tel" ofType: @"home"];
[element setSingleValue: [theValues objectForKey: @"HomePhoneNumber"] forKey: @""];
if ((o = [theValues objectForKey: @"MobilePhoneNumber"]) || ![self _isGhosted: @"MobilePhoneNumber" inContext: context])
{
element = [self elementWithTag: @"tel" ofType: @"cell"];
[element setSingleValue: [theValues objectForKey: @"MobilePhoneNumber"] forKey: @""];
}
element = [self elementWithTag: @"tel" ofType: @"cell"];
[element setSingleValue: [theValues objectForKey: @"MobilePhoneNumber"] forKey: @""];
if ((o = [theValues objectForKey: @"BusinessFaxNumber"]) || ![self _isGhosted: @"BusinessFaxNumber" inContext: context])
{
element = [self elementWithTag: @"tel" ofType: @"fax"];
[element setSingleValue: [theValues objectForKey: @"BusinessFaxNumber"] forKey: @""];
}
element = [self elementWithTag: @"tel" ofType: @"fax"];
[element setSingleValue: [theValues objectForKey: @"BusinessFaxNumber"] forKey: @""];
if ((o = [theValues objectForKey: @"PagerNumber"]) || ![self _isGhosted: @"PagerNumber" inContext: context])
{
element = [self elementWithTag: @"tel" ofType: @"pager"];
[element setSingleValue: [theValues objectForKey: @"PagerNumber"] forKey: @""];
}
element = [self elementWithTag: @"tel" ofType: @"pager"];
[element setSingleValue: [theValues objectForKey: @"PagerNumber"] forKey: @""];
// Job's title
if ((o = [theValues objectForKey: @"JobTitle"]) || ![self _isGhosted: @"JobTitle" inContext: context])
if ((o = [theValues objectForKey: @"JobTitle"]))
[self setTitle: o];
// WebPage (work)
if ((o = [theValues objectForKey: @"WebPage"]) || ![self _isGhosted: @"WebPage" inContext: context])
if ((o = [theValues objectForKey: @"WebPage"]))
[[self elementWithTag: @"url" ofType: @"work"]
setSingleValue: o forKey: @""];
if ((o = [theValues objectForKey: @"NickName"]) || ![self _isGhosted: @"NickName" inContext: context])
if ((o = [theValues objectForKey: @"NickName"]))
[self setNickname: o];
if ((o = [theValues objectForKey: @"Picture"]))
+82 -457
View File
@@ -112,73 +112,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@implementation SOGoActiveSyncDispatcher (Sync)
- (void) _setOrUnsetSyncRequest: (BOOL) set
collections: (NSArray *) collections
{
SOGoCacheGCSObject *o;
NSNumber *processIdentifier;
NSString *key;
int i;
processIdentifier = [NSNumber numberWithInt: [[NSProcessInfo processInfo] processIdentifier]];
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil useCache: NO];
[o setObjectType: ActiveSyncGlobalCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
if (set)
{
RELEASE(syncRequest);
syncRequest = [NSNumber numberWithUnsignedInt: [[NSCalendarDate date] timeIntervalSince1970]];
RETAIN(syncRequest);
[[o properties] setObject: syncRequest forKey: @"SyncRequest"];
for (i = 0; i < [collections count]; i++)
{
key = [NSString stringWithFormat: @"SyncRequest+%@", [[[(id)[[collections objectAtIndex: i] getElementsByTagName: @"CollectionId"] lastObject] textValue] stringByUnescapingURL]];
[[o properties] setObject: processIdentifier forKey: key];
}
}
else
{
[[o properties] removeObjectForKey: @"SyncRequest"];
for (i = 0; i < [collections count]; i++)
{
key = [NSString stringWithFormat: @"SyncRequest+%@", [[[(id)[[collections objectAtIndex: i] getElementsByTagName: @"CollectionId"] lastObject] textValue] stringByUnescapingURL]];
[[o properties] removeObjectForKey: key];
}
}
[o save];
}
- (void) _setFolderMetadata: (NSDictionary *) theFolderMetadata
forKey: (NSString *) theFolderKey
{
NSNumber *processIdentifier, *processIdentifierInCache;
SOGoCacheGCSObject *o;
NSDictionary *values;
NSString *key;
if ([theFolderKey hasPrefix: @"folder"])
key = [NSString stringWithFormat: @"SyncRequest+mail/%@", [theFolderKey substringFromIndex: 6]];
else
key = [NSString stringWithFormat: @"SyncRequest+%@", theFolderKey];
processIdentifier = [NSNumber numberWithInt: [[NSProcessInfo processInfo] processIdentifier]];
processIdentifierInCache = [[self globalMetadataForDevice] objectForKey: key];
// Don't update the cache if another request is processing the same collection.
if (!([processIdentifierInCache isEqual: processIdentifier]))
{
if (debugOn)
[self logWithFormat: @"EAS - We lost our lock - discard folder cache update %@ %@ <> %@", key, processIdentifierInCache, processIdentifier];
return;
}
key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theFolderKey];
values = [theFolderMetadata copy];
@@ -192,9 +132,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[[o properties] removeObjectForKey: @"DateCache"];
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SupportedElements"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
[[o properties] addEntriesFromDictionary: values];
[o save];
@@ -221,7 +159,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSString *nameInCache;
if (theFolderType == ActiveSyncMailFolder)
nameInCache = [imapFolderGUIDS objectForKey: [theCollection nameInContainer]];
nameInCache= [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]];
else
{
NSString *component_name;
@@ -232,7 +170,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
component_name = @"vtodo";
nameInCache = [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]];
nameInCache= [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]];
}
return nameInCache;
@@ -465,8 +403,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[o takeActiveSyncValues: allChanges inContext: context];
[sogoObject saveComponent: o];
if ([syncCache objectForKey: serverId])
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
}
break;
case ActiveSyncEventFolder:
@@ -476,27 +414,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[o takeActiveSyncValues: allChanges inContext: context];
[sogoObject saveComponent: o];
if ([syncCache objectForKey: serverId])
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
[syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
}
break;
case ActiveSyncMailFolder:
default:
{
NSDictionary *result;
NSNumber *modseq;
NSString *modseq;
[sogoObject takeActiveSyncValues: allChanges inContext: context];
result = [sogoObject fetchParts: [NSArray arrayWithObject: @"MODSEQ"]];
modseq = [[[result objectForKey: @"RawResponse"] objectForKey: @"fetch"] objectForKey: @"modseq"];
if (modseq && [syncCache objectForKey: serverId])
[syncCache setObject: [modseq stringValue] forKey: serverId];
if (modseq)
[syncCache setObject: modseq forKey: serverId];
}
}
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
[theBuffer appendString: @"<Change>"];
@@ -639,44 +577,36 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
withFilterType: (NSCalendarDate *) theFilterType
inBuffer: (NSMutableString *) theBuffer
lastServerKey: (NSString **) theLastServerKey
defaultInterval: (unsigned int) theDefaultInterval
{
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
NSString *davCollectionTagToStore;
NSAutoreleasePool *pool;
NSMutableString *s;
BOOL cleanup_needed, more_available;
BOOL more_available;
int i, max;
s = [NSMutableString string];
cleanup_needed = more_available = NO;
more_available = NO;
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
// If this is a new sync operation, DateCache and SyncCache need to be deleted
// If this is a new sync operation, DateCache and SyncCache needs to be deleted
if ([theSyncKey isEqualToString: @"-1"])
{
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
}
else if ([folderMetadata objectForKey: @"SyncKey"] && !([theSyncKey isEqualToString: [folderMetadata objectForKey: @"SyncKey"]]))
{
// The syncKey received from the client doesn't match the syncKey we have in cache - client might have missed a response.
// We need to cleanup this mess.
[self logWithFormat: @"Cache cleanup needed for device %@ - user: %@ syncKey: %@ cache: %@", [context objectForKey: @"DeviceId"], [[context activeUser] login], theSyncKey, [folderMetadata objectForKey: @"SyncKey"]];
cleanup_needed = YES;
}
syncCache = [folderMetadata objectForKey: @"SyncCache"];
dateCache = [folderMetadata objectForKey: @"DateCache"];
if ((theFolderType == ActiveSyncMailFolder || theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder) &&
(cleanup_needed ||
( !([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize
!([folderMetadata objectForKey: @"InitialLoadSequence"]))) &&
theFilterType
)
!([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize
!([theSyncKey isEqualToString: @"-1"]) && // new sync operation
theFilterType)
{
NSArray *allKeys;
NSString *key;
@@ -692,36 +622,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ([[dateCache objectForKey:key] compare: theFilterType] == NSOrderedAscending)
{
if ([syncCache objectForKey:key])
{
if (debugOn)
[self logWithFormat: @"EAS - SoftDelete %@", key];
[s appendString: @"<SoftDelete xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", key];
[s appendString: @"</SoftDelete>"];
[s appendString: @"<SoftDelete xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", key];
[s appendString: @"</SoftDelete>"];
[syncCache removeObjectForKey: key];
//[dateCache removeObjectForKey: key];
[syncCache removeObjectForKey: key];
[dateCache removeObjectForKey: key];
softdelete_count++;
}
else if (cleanup_needed)
{
if (debugOn)
[self logWithFormat: @"EAS - SoftDelete cleanup %@", key];
// With this we make sure that a SoftDelete is set again on next sync.
[syncCache setObject: @"0" forKey: key];
}
else
{
if (debugOn)
[self logWithFormat: @"EAS - SoftDelete final delete %@", key];
// Now we are save to remove the dateCache entry.
[dateCache removeObjectForKey: key];
}
softdelete_count++;
}
if (softdelete_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
@@ -748,6 +656,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
if ([theSyncKey isEqualToString: [theCollection davCollectionTag]] && !([s length]))
return;
more_available = NO;
davCollectionTagToStore = [theCollection davCollectionTag];
@@ -763,7 +673,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSDictionary *component;
NSArray *allComponents;
BOOL updated, initialLoadInProgress;
BOOL updated;
int deleted, return_count;
if (theFolderType == ActiveSyncContactFolder)
@@ -773,76 +683,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
component_name = @"vtodo";
initialLoadInProgress = NO;
if ([theSyncKey isEqualToString: @"-1"])
[folderMetadata setObject: davCollectionTagToStore forKey: @"InitialLoadSequence"];
if ([folderMetadata objectForKey: @"InitialLoadSequence"])
{
if ([theSyncKey intValue] < [[folderMetadata objectForKey: @"InitialLoadSequence"] intValue])
initialLoadInProgress = YES;
else
[folderMetadata removeObjectForKey: @"InitialLoadSequence"];
}
allComponents = [theCollection syncTokenFieldsWithProperties: nil
matchingSyncToken: theSyncKey
fromDate: theFilterType
initialLoad: initialLoadInProgress];
allComponents = [allComponents sortedArrayUsingDescriptors: [NSArray arrayWithObject: [[[NSSortDescriptor alloc] initWithKey: @"c_lastmodified" ascending: YES] autorelease]]];
allComponents = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
allComponents = [allComponents sortedArrayUsingDescriptors: [NSArray arrayWithObjects: [[NSSortDescriptor alloc] initWithKey: @"c_lastmodified" ascending:YES], nil]];
// Check for the WindowSize
max = [allComponents count];
//
// Cleanup the mess
//
if (cleanup_needed)
{
for (i = 0; i < max; i++)
{
component = [allComponents objectAtIndex: i];
deleted = [[component objectForKey: @"c_deleted"] intValue];
if (!deleted && ![[component objectForKey: @"c_component"] isEqualToString: component_name])
continue;
uid = [[component objectForKey: @"c_name"] sanitizedServerIdWithType: theFolderType];
if (deleted)
{
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: DELETE %@", uid];
// For deletes we have to recreate a cache entry to make sure the delete is sent again.
[syncCache setObject: @"0" forKey: uid];
}
else
{
if ([syncCache objectForKey: uid] && [[component objectForKey: @"c_creationdate"] intValue] > [theSyncKey intValue])
{
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: ADD %@", uid];
// Cleanup the cache to make sure the add is sent again.
[syncCache removeObjectForKey: uid];
[dateCache removeObjectForKey: uid];
}
else
{
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: CHANGE %@", uid];
// Update cache entry to make sure the change is sent again.
[syncCache setObject: @"0" forKey: uid];
}
}
}
}
return_count = 0;
for (i = 0; i < max; i++)
@@ -1000,38 +847,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSMutableArray *allCacheObjects, *sortedBySequence;
SOGoMailObject *mailObject;
NSArray *allMessages, *a;
NSString *firstUIDAdded;
NSArray *allMessages;
int j, k, return_count, highestmodseq;
BOOL found_in_cache, initialLoadInProgress;
int j, k, return_count;
BOOL found_in_cache;
initialLoadInProgress = NO;
found_in_cache = NO;
firstUIDAdded = nil;
if ([theSyncKey isEqualToString: @"-1"])
{
highestmodseq = 0;
a = [[theCollection davCollectionTag] componentsSeparatedByString: @"-"];
[folderMetadata setObject: [a objectAtIndex: 1] forKey: @"InitialLoadSequence"];
}
else
{
a = [theSyncKey componentsSeparatedByString: @"-"];
highestmodseq = [[a objectAtIndex: 1] intValue];
}
if ([folderMetadata objectForKey: @"InitialLoadSequence"])
{
if (highestmodseq < [[folderMetadata objectForKey: @"InitialLoadSequence"] intValue])
initialLoadInProgress = YES;
else
[folderMetadata removeObjectForKey: @"InitialLoadSequence"];
}
allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType initialLoad: initialLoadInProgress];
allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
max = [allMessages count];
allCacheObjects = [NSMutableArray array];
@@ -1039,7 +860,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
for (i = 0; i < max; i++)
{
[allCacheObjects addObject: [SOGoSyncCacheObject syncCacheObjectWithUID: [[[allMessages objectAtIndex: i] allKeys] lastObject]
sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
}
sortedBySequence = [[NSMutableArray alloc] initWithDictionary: syncCache];
@@ -1048,82 +869,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[allCacheObjects sortUsingSelector: @selector(compareSequence:)];
if (debugOn)
{
[self logWithFormat: @"EAS - sortedBySequence (%d) - lastObject: %@", [sortedBySequence count], [sortedBySequence lastObject]];
[self logWithFormat: @"EAS - allCacheObjects (%d) - lastObject: %@", [allCacheObjects count], [allCacheObjects lastObject]];
}
//NSLog(@"sortedBySequence (%d) - lastObject: %@", [sortedBySequence count], [sortedBySequence lastObject]);
//NSLog(@"allCacheObjects (%d) - lastObject: %@", [allCacheObjects count], [allCacheObjects lastObject]);
lastCacheObject = [sortedBySequence lastObject];
//
// Cleanup the mess
//
if (cleanup_needed)
{
NSMutableArray *sortedByUID;
int uidnextFromCache;
sortedByUID = [[NSMutableArray alloc] initWithDictionary: syncCache];
[sortedByUID sortUsingSelector: @selector(compareUID:)];
// Get the uid from SyncKey in cache. The uid is the first uid added to cache by the last sync request.
a = [[folderMetadata objectForKey: @"SyncKey"] componentsSeparatedByString: @"-"];
uidnextFromCache = [[a objectAtIndex: 0] intValue];
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: from uid: %d to uid: %d", uidnextFromCache, [[[sortedByUID lastObject] uid] intValue]];
// Remove all entries from cache beginning with the first uid added by the last sync request.
for (j = uidnextFromCache; j <= [[[sortedByUID lastObject] uid] intValue]; j++)
{
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: ADD %d", j];
[syncCache removeObjectForKey: [NSString stringWithFormat:@"%d", j]];
[dateCache removeObjectForKey: [NSString stringWithFormat:@"%d", j]];
}
RELEASE(sortedByUID);
for (j = 0; j < [allCacheObjects count]; j++)
{
// Update the modseq in cache, sence othersie, it would be identical to the modseq from server
//and we would skip the cache when generating the response.
if ([syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]] && ![[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]])
{
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: CHANGE %@", [[allCacheObjects objectAtIndex: j] uid]];
[syncCache setObject: @"0" forKey:[[allCacheObjects objectAtIndex: j] uid]];
}
else if ([[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]])
{
if (debugOn)
[self logWithFormat: @"EAS - Cache cleanup: DELETE %@", [[allCacheObjects objectAtIndex: j] uid]];
// For deletes we have to recreate a cache entry to have the <Delete> included in the response.
[syncCache setObject: @"0" forKey:[[allCacheObjects objectAtIndex: j] uid]];
}
}
}
if (!cleanup_needed &&
[folderMetadata objectForKey: @"MoreAvailable"] &&
lastCacheObject &&
!([[lastCacheObject sequence] isEqual: @"0"])) // Sequence 0 is set during cache cleanup.
if ([folderMetadata objectForKey: @"MoreAvailable"] && lastCacheObject)
{
for (j = 0; j < [allCacheObjects count]; j++)
{
if (([[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]] && [syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]]) ||
(![[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]] && ![syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]]))
{
// We need to continue with adds or deletes from here.
found_in_cache = YES;
j--;
break;
}
if ([[lastCacheObject uid] isEqual: [[allCacheObjects objectAtIndex: j] uid]])
{
// Found out where we're at, let's continue from there...
@@ -1138,10 +892,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (found_in_cache)
k = j+1;
else
j = k = 0;
if (debugOn)
[self logWithFormat: @"EAS - found in cache: %d k = %d", found_in_cache, k];
{
k = 0;
j = 0;
}
//NSLog(@"found in cache: %d k = %d", found_in_cache, k);
return_count = 0;
@@ -1155,35 +911,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSString *lastSequence;
more_available = YES;
if (!firstUIDAdded)
{
a = [davCollectionTagToStore componentsSeparatedByString: @"-"];
firstUIDAdded = [a objectAtIndex: 0];
RETAIN(firstUIDAdded);
}
lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? [NSString stringWithFormat:@"%d", highestmodseq] : [aCacheObject sequence]);
*theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", firstUIDAdded, lastSequence];
if (debugOn)
[self logWithFormat: @"EAS - Reached windowSize - lastUID will be: %@", *theLastServerKey];
lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? @"1" : [aCacheObject sequence]);
*theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", [aCacheObject uid], lastSequence];
//NSLog(@"Reached windowSize - lastUID will be: %@", *theLastServerKey);
DESTROY(pool);
break;
}
aCacheObject = [allCacheObjects objectAtIndex: k];
if (debugOn)
[self logWithFormat: @"EAS - Dealing with cacheObject: %@", aCacheObject];
// If found in cache, it's either a Change or a Delete operation.
// If found in cache, it's either a Change or a Delete
if ([syncCache objectForKey: [aCacheObject uid]])
{
if ([[aCacheObject sequence] isEqual: [NSNull null]])
{
if (debugOn)
[self logWithFormat: @"EAS - DELETE!"];
// Deleted
[s appendString: @"<Delete xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
@@ -1201,12 +942,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
mailObject = [theCollection lookupName: [aCacheObject uid]
inContext: context
acquire: 0];
if (![[aCacheObject sequence] isEqual: [syncCache objectForKey: [aCacheObject uid]]])
{
if (debugOn)
[self logWithFormat: @"EAS - CHANGE!"];
[s appendString: @"<Change xmlns=\"AirSync:\">"];
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", [aCacheObject uid]];
[s appendString: @"<ApplicationData xmlns=\"AirSync:\">"];
@@ -1222,9 +960,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
else
{
if (debugOn)
[self logWithFormat: @"EAS - ADD!"];
// Added
if (![[aCacheObject sequence] isEqual: [NSNull null]])
{
@@ -1257,22 +992,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[syncCache setObject: [aCacheObject sequence] forKey: [aCacheObject uid]];
[dateCache setObject: [NSCalendarDate date] forKey: [aCacheObject uid]];
// Save the frist UID we add. We will use it for the synckey late.
if (!firstUIDAdded)
{
firstUIDAdded = [aCacheObject uid];
RETAIN(firstUIDAdded);
if (debugOn)
[self logWithFormat: @"EAS - first uid added %@", firstUIDAdded];
}
return_count++;
}
else
{
if (debugOn)
[self logWithFormat: @"EAS - skipping old deleted UID: %@", [aCacheObject uid]];
//NSLog(@"skipping old deleted UID: %@", [aCacheObject uid]);
}
}
@@ -1281,25 +1005,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (more_available)
{
[folderMetadata setObject: [NSNumber numberWithInt: YES] forKey: @"MoreAvailable"];
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
[folderMetadata setObject: *theLastServerKey forKey: @"SyncKey"];
}
else
{
[folderMetadata removeObjectForKey: @"MoreAvailable"];
if (firstUIDAdded)
{
a = [davCollectionTagToStore componentsSeparatedByString: @"-"];
[folderMetadata setObject: [[NSString alloc] initWithFormat: @"%@-%@", firstUIDAdded, [a objectAtIndex: 1]] forKey: @"SyncKey"];
RELEASE(firstUIDAdded);
}
else
[folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
[folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
}
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
RELEASE(*theLastServerKey);
} // default:
@@ -1398,16 +1113,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
changeDetected: (BOOL *) changeDetected
maxSyncResponseSize: (int) theMaxSyncResponseSize
{
NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *mimeSupport, *lastServerKey, *syncKeyInCache, *folderKey;
NSMutableDictionary *folderMetadata, *folderOptions;
NSMutableArray *supportedElements, *supportedElementNames;
NSMutableString *changeBuffer, *commandsBuffer;
id collection, value;
NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *mimeSupport, *lastServerKey, *syncKeyInCache;
SOGoMicrosoftActiveSyncFolderType folderType;
unsigned int windowSize, v, status, i;
id collection, value;
NSMutableString *changeBuffer, *commandsBuffer;
BOOL getChanges, first_sync;
unsigned int windowSize, v, status;
NSMutableDictionary *folderMetadata, *folderOptions;
changeBuffer = [NSMutableString string];
commandsBuffer = [NSMutableString string];
@@ -1430,15 +1144,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//[theBuffer appendString: @"</Collection>"];
return;
}
//
// First check if we have any concurrent Sync requests going on for this device.
// If we do and we are still within our maximumSyncInterval, we let our EAS
// device know to retry.
//
folderKey = [self _getNameInCache: collection withType: folderType];
folderMetadata = [self _folderMetadataForKey: folderKey];
// We check for a window size, default to 100 if not specfied or out of bounds
windowSize = [[[(id)[theDocumentElement getElementsByTagName: @"WindowSize"] lastObject] textValue] intValue];
@@ -1463,35 +1169,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
first_sync = NO;
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
if ([syncKey isEqualToString: @"0"])
{
davCollectionTag = @"-1";
first_sync = YES;
*changeDetected = YES;
supportedElementNames = [[[NSMutableArray alloc] init] autorelease];
value = [theDocumentElement getElementsByTagName: @"Supported"];
if ([value count])
{
supportedElements = (id)[[value lastObject] childNodes];
if ([supportedElements count])
{
for (i = 0; i < [supportedElements count]; i++)
{
if ([[supportedElements objectAtIndex: i] nodeType] == DOM_ELEMENT_NODE)
[supportedElementNames addObject: [[supportedElements objectAtIndex: i] tagName]];
}
}
[folderMetadata setObject: supportedElementNames forKey: @"SupportedElements"];
[self _setFolderMetadata: folderMetadata forKey: folderKey];
if (debugOn)
[self logWithFormat: @"EAS - %d %@: supportedElements saved: %@", [supportedElements count], [collection nameInContainer], supportedElementNames];
}
}
else if ((![syncKey isEqualToString: @"-1"]) && !([folderMetadata objectForKey: @"SyncCache"]))
{
@@ -1516,11 +1200,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// By default, send MIME mails. See #3146 for details.
if (!bodyPreferenceType)
bodyPreferenceType = @"4";
mimeSupport = [[folderMetadata objectForKey: @"FolderOptions"] objectForKey: @"MIMESupport"];
if (!mimeSupport)
mimeSupport = @"1";
}
else
{
@@ -1546,13 +1225,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
folderOptions = [[NSDictionary alloc] initWithObjectsAndKeys: mimeSupport, @"MIMESupport", bodyPreferenceType, @"BodyPreferenceType", nil];
[folderMetadata setObject: folderOptions forKey: @"FolderOptions"];
[self _setFolderMetadata: folderMetadata forKey: folderKey];
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: collection withType: folderType]];
}
}
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
[context setObject: mimeSupport forKey: @"MIMESupport"];
[context setObject: [folderMetadata objectForKey: @"SupportedElements"] forKey: @"SupportedElements"];
//
// We process the commands from the request
@@ -1591,11 +1268,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
withFolderType: folderType
withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]]
inBuffer: changeBuffer
lastServerKey: &lastServerKey
defaultInterval: [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncInterval]];
lastServerKey: &lastServerKey];
}
folderMetadata = [self _folderMetadataForKey: folderKey];
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
// If we got any changes or if we have applied any commands
// let's regenerate our SyncKey based on the collection tag.
@@ -1754,24 +1430,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
SOGoSystemDefaults *defaults;
id <DOMElement> aCollection;
NSMutableString *output, *s;
NSMutableDictionary *globalMetadata;
NSNumber *syncRequestInCache, *processIdentifier;
NSString *key;
NSArray *allCollections;
NSData *d;
int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize, total_sleep;
int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize;
BOOL changeDetected;
changeDetected = NO;
maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize];
// We initialize our output buffer
output = [[NSMutableString alloc] init];
defaults = [SOGoSystemDefaults sharedSystemDefaults];
defaultInterval = [defaults maximumSyncInterval];
processIdentifier = [NSNumber numberWithInt: [[NSProcessInfo processInfo] processIdentifier]];
allCollections = (id)[theDocumentElement getElementsByTagName: @"Collection"];
[output appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[output appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
[output appendString: @"<Sync xmlns=\"AirSync:\">"];
@@ -1786,16 +1457,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[output appendString: @"</Sync>"];
d = [[output dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
[theResponse setContent: d];
RELEASE(output);
return;
}
// Let other requests know about the collections we are dealing with.
[self _setOrUnsetSyncRequest: YES collections: allCollections];
changeDetected = NO;
maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize];
defaults = [SOGoSystemDefaults sharedSystemDefaults];
heartbeatInterval = [[[(id)[theDocumentElement getElementsByTagName: @"HeartbeatInterval"] lastObject] textValue] intValue];
defaultInterval = [defaults maximumSyncInterval];
internalInterval = [defaults internalSyncInterval];
// If the request doesn't contain "HeartbeatInterval" there is no reason to delay the response.
@@ -1805,20 +1472,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// We check to see if our heartbeat interval falls into the supported ranges.
if (heartbeatInterval > defaultInterval || heartbeatInterval < 1)
{
int limit;
// Interval is too long, inform the client.
heartbeatInterval = defaultInterval;
// When Status = 14, the Wait interval is specified in minutes while
// defaultInterval is specifed in seconds. Adjust accordinlgy.
limit = defaultInterval/60;
if (limit < 1) limit = 1;
if (limit > 59) limit = 59;
//[output appendFormat: @"<Limit>%d</Limit>", limit];
// Outlook doesn't like this...
//[output appendFormat: @"<Limit>%d</Limit>", defaultInterval];
//[output appendFormat: @"<Status>%d</Status>", 14];
}
[output appendString: @"<Collections>"];
allCollections = (id)[theDocumentElement getElementsByTagName: @"Collection"];
// We enter our loop detection change
for (i = 0; i < (heartbeatInterval/internalInterval); i++)
@@ -1828,62 +1492,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
for (j = 0; j < [allCollections count]; j++)
{
aCollection = [allCollections objectAtIndex: j];
[self processSyncCollection: aCollection
inBuffer: s
changeDetected: &changeDetected
maxSyncResponseSize: maxSyncResponseSize];
// Don't return a response if another Sync is waiting.
globalMetadata = [self globalMetadataForDevice];
key = [NSString stringWithFormat: @"SyncRequest+%@", [[[(id)[aCollection getElementsByTagName: @"CollectionId"] lastObject] textValue] stringByUnescapingURL]];
if (!([[globalMetadata objectForKey: key] isEqual: processIdentifier]))
{
if (debugOn)
[self logWithFormat: @"EAS - Discard response %@", [self globalMetadataForDevice]];
[theResponse setStatus: 503];
RELEASE(output);
return;
}
if ((maxSyncResponseSize > 0 && [s length] >= maxSyncResponseSize))
if (maxSyncResponseSize > 0 && [s length] >= maxSyncResponseSize)
break;
}
if (changeDetected)
{
[self logWithFormat: @"Change detected during Sync, we push the content."];
[self logWithFormat: @"Change detected, we push the content."];
break;
}
else if (heartbeatInterval > 1)
{
total_sleep = 0;
while (total_sleep < internalInterval)
{
// We check if we must break the current synchronization since an other Sync
// has just arrived.
syncRequestInCache = [[self globalMetadataForDevice] objectForKey: @"SyncRequest"];
if (!([syncRequest isEqualToNumber: syncRequestInCache]))
{
if (debugOn)
[self logWithFormat: @"EAS - Heartbeat stopped %@", [self globalMetadataForDevice]];
// Make sure we end the heardbeat-loop.
heartbeatInterval = internalInterval = 1;
break;
}
else
{
[self logWithFormat: @"Sleeping %d seconds while detecting changes in Sync...", internalInterval-total_sleep];
sleep(5);
total_sleep += 5;
}
}
[self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval];
sleep(internalInterval);
}
else
{
@@ -1891,10 +1518,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
}
//
// Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0,
// otherwise send an empty response.
//
// Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0 oterwise send an empty response.
if (changeDetected || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"] || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"12.0"])
{
// We always return the last generated response.
+1 -11
View File
@@ -1,6 +1,6 @@
/*
Copyright (c) 2014-2015, Inverse inc.
Copyright (c) 2014, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -31,25 +31,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "SOGoActiveSyncConstants.h"
@class NSCalendarDate;
@class NSException;
@class NSMutableDictionary;
@class NSURL;
@class NSNumber;
@interface SOGoActiveSyncDispatcher : NSObject
{
NSURL *folderTableURL;
NSDictionary *imapFolderGUIDS;
id context;
NSNumber *syncRequest;
BOOL debugOn;
}
- (NSMutableDictionary *) globalMetadataForDevice;
- (id) collectionFromId: (NSString *) theCollectionId
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
+73 -311
View File
@@ -30,12 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "SOGoActiveSyncDispatcher.h"
#import <Foundation/NSArray.h>
#import <Foundation/NSData.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSCalendarDate.h>
#if GNUSTEP_BASE_MINOR_VERSION >= 21
#import <Foundation/NSLocale.h>
#endif
#import <Foundation/NSProcessInfo.h>
#import <Foundation/NSTimeZone.h>
#import <Foundation/NSURL.h>
@@ -138,12 +134,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <unistd.h>
#ifdef HAVE_OPENSSL
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#endif
@interface SOGoActiveSyncDispatcher (Sync)
- (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey;
@@ -153,22 +143,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@implementation SOGoActiveSyncDispatcher
static BOOL debugOn = NO;
- (id) init
{
[super init];
debugOn = [[SOGoSystemDefaults sharedSystemDefaults] easDebugEnabled];
folderTableURL = nil;
imapFolderGUIDS = nil;
syncRequest = nil;
return self;
}
- (void) dealloc
{
RELEASE(folderTableURL);
RELEASE(imapFolderGUIDS);
RELEASE(syncRequest);
[super dealloc];
}
@@ -181,16 +169,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
[[o properties] setObject: theSyncKey
forKey: @"FolderSyncKey"];
[[o properties] removeAllObjects];
[[o properties] addEntriesFromDictionary: [NSDictionary dictionaryWithObject: theSyncKey forKey: @"FolderSyncKey"]];
[o save];
}
- (NSMutableDictionary *) globalMetadataForDevice
- (NSMutableDictionary *) _globalMetadataForDevice
{
SOGoCacheGCSObject *o;
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil useCache: NO];
o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil];
[o setObjectType: ActiveSyncGlobalCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
@@ -214,7 +202,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (theFilter)
{
o = [SOGoCacheGCSObject objectWithName: [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theCollectionId] inContainer: nil];
[o setObjectType: ActiveSyncFolderCacheObject];
[o setObjectType: ActiveSyncGlobalCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
@@ -241,27 +229,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
SOGoMailAccounts *accountsFolder;
SOGoMailAccount *accountFolder;
SOGoUserFolder *userFolder;
NSDictionary *imapGUIDs;
if (!imapFolderGUIDS)
{
userFolder = [[context activeUser] homeFolderInContext: context];
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
// Get the GUID of the IMAP folder
imapFolderGUIDS = [accountFolder imapFolderGUIDs];
[imapFolderGUIDS retain];
}
return [[[imapFolderGUIDS allKeysForObject: [NSString stringWithFormat: @"folder%@", theIdToTranslate]] objectAtIndex: 0] substringFromIndex: 6] ;
userFolder = [[context activeUser] homeFolderInContext: context];
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
// Get the GUID of the IMAP folder
imapGUIDs = [accountFolder imapFolderGUIDs];
//return [[imapGUIDs allKeysForObject: theIdToTranslate] objectAtIndex: 0];
return [[[imapGUIDs allKeysForObject: [NSString stringWithFormat: @"folder%@", theIdToTranslate]] objectAtIndex: 0] substringFromIndex: 6] ;
}
return theIdToTranslate;
}
//
//
//
@@ -707,25 +691,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (void) processFolderSync: (id <DOMElement>) theDocumentElement
inResponse: (WOResponse *) theResponse
{
NSString *key, *cKey, *nkey, *name, *serverId, *parentId, *nameInCache, *personalFolderName, *syncKey, *folderType, *operation;
NSMutableDictionary *cachedGUIDs, *metadata;
NSMutableArray *folders, *processedFolders;
NSString *key, *cKey, *nkey, *name, *serverId, *parentId, *nameInCache, *personalFolderName, *syncKey, *folderType;
NSDictionary *folderMetadata, *imapGUIDs;
NSArray *allFoldersMetadata, *allKeys;
NSMutableDictionary *cachedGUIDs, *metadata;
SOGoMailAccounts *accountsFolder;
SOGoMailAccount *accountFolder;
NSMutableString *s, *commands;
SOGoUserFolder *userFolder;
NSMutableArray *folders, *processedFolders;
SoSecurityManager *sm;
SOGoCacheGCSObject *o;
id currentFolder;
NSData *d;
int status, command_count, i, type, fi, count;
BOOL first_sync;
sm = [SoSecurityManager sharedSecurityManager];
metadata = [self globalMetadataForDevice];
metadata = [self _globalMetadataForDevice];
syncKey = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
s = [NSMutableString string];
@@ -839,38 +824,32 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
else
{
if ([cKey rangeOfString: @"vevent" options: NSCaseInsensitiveSearch].location != NSNotFound ||
[cKey rangeOfString: @"vtodo" options: NSCaseInsensitiveSearch].location != NSNotFound)
folderType = @"Calendar";
else
folderType = @"Contacts";
if ([cKey rangeOfString: @"vevent" options: NSCaseInsensitiveSearch].location != NSNotFound ||
[cKey rangeOfString: @"vtodo" options: NSCaseInsensitiveSearch].location != NSNotFound)
folderType = @"Calendar";
else
folderType = @"Contacts";
if ([ cKey rangeOfString: @"/"].location != NSNotFound)
currentFolder = [[[[context activeUser] homeFolderInContext: context] lookupName: folderType inContext: context acquire: NO]
if ([ cKey rangeOfString: @"/"].location != NSNotFound)
currentFolder = [[[[context activeUser] homeFolderInContext: context] lookupName: folderType inContext: context acquire: NO]
lookupName: [cKey substringFromIndex: [cKey rangeOfString: @"/"].location+1] inContext: context acquire: NO];
// We skip personal GCS folders - we always want to synchronize these
if ([currentFolder isKindOfClass: [SOGoGCSFolder class]] &&
[[currentFolder nameInContainer] isEqualToString: @"personal"])
continue;
// Remove the folder from device if it doesn't exist, we don't want to sync it, or it doesn't have the proper permissions
if (!currentFolder ||
![currentFolder synchronize] ||
[sm validatePermission: SoPerm_DeleteObjects
onObject: currentFolder
inContext: context] ||
[sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
onObject: currentFolder
inContext: context])
{
[commands appendFormat: @"<Delete><ServerId>%@</ServerId></Delete>", [cKey stringByEscapingURL] ];
command_count++;
[o destroy];
}
}
}
}
// remove the folder from device if it doesn't exists or it has not the proper permissions
if (!currentFolder ||
[sm validatePermission: SoPerm_DeleteObjects
onObject: currentFolder
inContext: context] ||
[sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
onObject: currentFolder
inContext: context])
{
[commands appendFormat: @"<Delete><ServerId>%@</ServerId></Delete>", [cKey stringByEscapingURL] ];
command_count++;
[o destroy];
}
}
}
}
// Handle addition and changes
for (i = 0; i < [allFoldersMetadata count]; i++)
@@ -958,9 +937,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[[o properties] removeObjectForKey: @"DateCache"];
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SupportedElements"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
[o save];
command_count++;
@@ -969,29 +946,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
personalFolderName = [[[context activeUser] personalCalendarFolderInContext: context] nameInContainer];
folders = [[[[[context activeUser] homeFolderInContext: context] lookupName: @"Calendar" inContext: context acquire: NO] subFolders] mutableCopy];
[folders autorelease];
[folders addObjectsFromArray: [[[[context activeUser] homeFolderInContext: context] lookupName: @"Contacts" inContext: context acquire: NO] subFolders]];
// We remove all the folders that aren't GCS-ones, that we don't want to synchronize and
// the ones without write/delete permissions.
// Inside this loop we remove all the folder without write/delete permissions
count = [folders count]-1;
for (; count >= 0; count--)
{
currentFolder = [folders objectAtIndex: count];
// We skip personal GCS folders - we always want to synchronize these
if ([currentFolder isKindOfClass: [SOGoGCSFolder class]] &&
[[currentFolder nameInContainer] isEqualToString: @"personal"])
continue;
if (![currentFolder isKindOfClass: [SOGoGCSFolder class]] ||
![currentFolder synchronize] ||
[sm validatePermission: SoPerm_DeleteObjects
onObject: currentFolder
if ([sm validatePermission: SoPerm_DeleteObjects
onObject: [folders objectAtIndex: count]
inContext: context] ||
[sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
onObject: currentFolder
onObject: [folders objectAtIndex: count]
inContext: context])
{
[folders removeObjectAtIndex: count];
@@ -999,6 +964,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
count = [folders count]-1;
NSString *operation;
for (fi = 0; fi <= count ; fi++)
{
@@ -1056,9 +1022,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[[o properties] removeObjectForKey: @"DateCache"];
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SupportedElements"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
}
[o save];
@@ -1081,9 +1045,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[[o properties] removeObjectForKey: @"DateCache"];
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SupportedElements"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
}
[o save];
@@ -1242,7 +1204,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
filter = [NSCalendarDate dateFromFilterType: [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"FilterType"] lastObject] textValue]];
syncKey = [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"SyncKey"] lastObject] textValue];
allMessages = [currentCollection syncTokenFieldsWithProperties: nil matchingSyncToken: syncKey fromDate: filter initialLoad: NO];
allMessages = [currentCollection syncTokenFieldsWithProperties: nil matchingSyncToken: syncKey fromDate: filter];
count = [allMessages count];
@@ -1292,7 +1254,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (void) processItemOperations: (id <DOMElement>) theDocumentElement
inResponse: (WOResponse *) theResponse
{
NSString *fileReference, *realCollectionId, *serverId, *bodyPreferenceType, *mimeSupport, *collectionId;
NSString *fileReference, *realCollectionId, *serverId, *bodyPreferenceType, *collectionId;
NSMutableString *s;
NSArray *fetchRequests;
id aFetch;
@@ -1405,8 +1367,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
serverId = [[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue];
bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue];
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
mimeSupport = [[(id)[theDocumentElement getElementsByTagName: @"MIMESupport"] lastObject] textValue];
[context setObject: mimeSupport forKey: @"MIMESupport"];
currentCollection = [self collectionFromId: realCollectionId type: folderType];
@@ -2064,13 +2024,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ([foldersWithChanges count])
{
[self logWithFormat: @"Change detected using Ping, we let the EAS client know to send a Sync."];
[self logWithFormat: @"Change detected, we push the content."];
status = 2;
break;
}
else
{
[self logWithFormat: @"Sleeping %d seconds while detecting changes in Ping...", internalInterval];
[self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval];
sleep(internalInterval);
}
}
@@ -2134,123 +2094,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[theResponse setContent: d];
}
//
//
//
#ifdef HAVE_OPENSSL
- (unsigned int) validateCert: (NSString *) theCert
{
NSData *d;
const unsigned char *data;
X509_STORE_CTX *ctx;
X509_LOOKUP *lookup;
X509_STORE *store;
X509 *cert;
BOOL success;
size_t len;
int rc;
success = NO;
d = [theCert dataByDecodingBase64];
data = (unsigned char *)[d bytes];
len = [d length];
cert = d2i_X509(NULL, &data, len);
if (!cert)
{
[self logWithFormat: @"EAS - validateCert failed for device %@: d2i_X509 failed", [context objectForKey: @"DeviceId"]];
return 17;
}
store = X509_STORE_new();
OpenSSL_add_all_algorithms();
if (store)
{
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
if (lookup)
{
X509_LOOKUP_load_file(lookup, NULL, X509_FILETYPE_DEFAULT);
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
if (lookup)
{
X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT);
ERR_clear_error();
success = YES;
}
}
}
if (!success)
{
if (store)
{
X509_STORE_free(store);
store = NULL;
}
}
ctx = X509_STORE_CTX_new();
if (!ctx)
{
[self logWithFormat: @"EAS - validateCert failed for device %@: X509_STORE_CTX_new failed", [context objectForKey: @"DeviceId"]];
return 17;
}
if (X509_STORE_CTX_init(ctx, store, cert, NULL) != 1)
{
[self logWithFormat: @"EAS - validateCert failed for device %@: X509_STORE_CTX_init failed", [context objectForKey: @"DeviceId"]];
X509_STORE_CTX_free(ctx);
return 17;
}
rc = X509_verify_cert(ctx);
X509_STORE_CTX_free(ctx);
X509_free(cert);
if (rc)
{
return 1;
}
else
{
[self logWithFormat: @"EAS - validateCert failed for device %@: err=%d", [context objectForKey: @"DeviceId"], X509_STORE_CTX_get_error(ctx)];
return 17;
}
}
#else
- (unsigned int) validateCert: (NSString *) theCert
{
return 17;
}
#endif
- (void) processValidateCert: (id <DOMElement>) theDocumentElement
inResponse: (WOResponse *) theResponse
{
NSMutableString *s;
NSString *cert;
NSData *d;
cert = [[(id)[theDocumentElement getElementsByTagName: @"Certificate"] lastObject] textValue];
s = [NSMutableString string];
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
[s appendString: @"<ValidateCert xmlns=\"ValidateCert:\">"];
[s appendString: @"<Status>1</Status><Certificate>"];
[s appendFormat: @"<Status>%d</Status>", [self validateCert: cert]];
[s appendString: @"</Certificate></ValidateCert>"];
d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
[theResponse setContent: d];
}
//
// <?xml version="1.0"?>
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
@@ -2567,100 +2410,39 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NGMimeMessageParser *parser;
NGMimeMessage *message;
NSException *error;
NSMutableData *data;
NSData *new_from_header;
NSData *data;
NGMutableHashMap *map;
NGMimeMessage *messageToSend;
NGMimeMessageGenerator *generator;
NSDictionary *identity;
NSString *fullName, *email;
const char *bytes;
int i, e, len;
BOOL found_header;
// We get the mail's data
data = [NSMutableData dataWithData: [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding]];
data = [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding];
// We extract the recipients
parser = [[NGMimeMessageParser alloc] init];
message = [parser parsePartFromData: data];
RELEASE(parser);
map = [NGHashMap hashMapWithDictionary: [message headers]];
identity = [[context activeUser] primaryIdentity];
fullName = [identity objectForKey: @"fullName"];
email = [identity objectForKey: @"email"];
if ([fullName length])
new_from_header = [[NSString stringWithFormat: @"From: %@ <%@>\r\n", [fullName asQPSubjectString: @"utf-8"], email] dataUsingEncoding:NSUTF8StringEncoding];
[map setObject: [NSString stringWithFormat: @"%@ <%@>", fullName, email] forKey: @"from"];
else
new_from_header = [[NSString stringWithFormat: @"From: %@\r\n", email] dataUsingEncoding:NSUTF8StringEncoding];
[map setObject: email forKey: @"from"];
bytes = [data bytes];
len = [data length];
i = 0;
found_header = NO;
messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
// Search for the from-header
while (i < len)
{
if (i == 0 &&
(*bytes == 'f' || *bytes == 'F') &&
(*(bytes+1) == 'r' || *(bytes+1) == 'R') &&
(*(bytes+2) == 'o' || *(bytes+2) == 'O') &&
(*(bytes+3) == 'm' || *(bytes+3) == 'M') &&
(*(bytes+4) == ':'))
{
found_header = YES;
break;
}
if (((*bytes == '\r') && (*(bytes+1) == '\n')) &&
(*(bytes+2) == 'f' || *(bytes+2) == 'F') &&
(*(bytes+3) == 'r' || *(bytes+3) == 'R') &&
(*(bytes+4) == 'o' || *(bytes+4) == 'O') &&
(*(bytes+5) == 'm' || *(bytes+5) == 'M') &&
(*(bytes+6) == ':'))
{
found_header = YES;
i = i + 2; // \r\n
bytes = bytes + 2;
break;
}
bytes++;
i++;
}
// We search for the first \r\n AFTER the From: header to get the length of the string to replace.
e = i;
while (e < len)
{
if ((*bytes == '\r') && (*(bytes+1) == '\n'))
{
e = e + 2;
break;
}
bytes++;
e++;
}
// Update/Add the From header in the MIMEBody of the SendMail request.
// Any other way to modify the mail body would break s/mime emails.
if (found_header)
{
// Change the From header
[data replaceBytesInRange: NSMakeRange(i, (NSUInteger)(e-i))
withBytes: [new_from_header bytes]
length: [new_from_header length]];
}
else
{
// Add a From header
[data replaceBytesInRange: NSMakeRange(0, 0)
withBytes: [new_from_header bytes]
length: [new_from_header length]];
}
[messageToSend setBody: [message body]];
generator = [[[NGMimeMessageGenerator alloc] init] autorelease];
data = [generator generateMimeFromPart: messageToSend];
error = [self _sendMail: data
recipients: [message allRecipients]
saveInSentItems: ([(id)[theDocumentElement getElementsByTagName: @"SaveInSentItems"] count] ? YES : NO)];
@@ -2837,11 +2619,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[map setObject: [mailObject messageId] forKey: @"in-reply-to"];
references = [[[[[mailObject mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] mutableCopy] autorelease];
// If there is no References: header, initialize it with In-Reply-To.
if ([mailObject inReplyTo] && ![references count])
references = [NSMutableArray arrayWithObject: [mailObject inReplyTo]];
if ([references count] > 0)
{
// If there are more than ten identifiers listed, we eliminate the second one.
@@ -3157,25 +2934,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
options: NSCaseInsensitiveSearch].location == NSNotFound)
{
NSString *value;
#if GNUSTEP_BASE_MINOR_VERSION < 21
value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z"
timeZone: [NSTimeZone timeZoneWithName: @"GMT"]
locale: nil];
#else
value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z"
timeZone: [NSTimeZone timeZoneWithName: @"GMT"]
locale: [NSDictionary dictionaryWithObjectsAndKeys:
[NSArray arrayWithObjects: @"Jan", @"Feb", @"Mar", @"Apr",
@"May", @"Jun", @"Jul", @"Aug",
@"Sep", @"Oct", @"Nov", @"Dec", nil],
@"NSShortMonthNameArray",
[NSArray arrayWithObjects: @"Sun", @"Mon", @"Tue", @"Wed", @"Thu",
@"Fri", @"Sat", nil],
@"NSShortWeekDayNameArray",
nil]];
#endif
s = [NSString stringWithFormat: @"Date: %@\r\n%@", value, [theRequest contentAsString]];
value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z" timeZone: [NSTimeZone timeZoneWithName: @"GMT"] locale: nil];
s = [NSString stringWithFormat: @"Date: %@\n%@", value, [theRequest contentAsString]];
}
else
{
@@ -3265,7 +3026,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
return nil;
urlString = [[user domainDefaults] folderInfoURL];
parts = [[urlString componentsSeparatedByString: @"/"] mutableCopy];
parts = [[urlString componentsSeparatedByString: @"/"]
mutableCopy];
[parts autorelease];
if ([parts count] == 5)
{
+21 -70
View File
@@ -472,46 +472,18 @@ struct GlobalObjectId {
return d;
}
//
//
//
- (BOOL) _sanitinizeNeeded: (NSArray *) theParts
{
NSString *encoding, *charset;
int i;
for (i = 0; i < [theParts count]; i++)
{
encoding = [[theParts objectAtIndex: i ] objectForKey: @"encoding"];
charset = [[[theParts objectAtIndex: i ] objectForKey: @"parameterList"] objectForKey: @"charset"];
if (encoding && [encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame &&
charset && ![charset caseInsensitiveCompare: @"utf-8"] == NSOrderedSame
&& ![charset caseInsensitiveCompare: @"us-ascii"] == NSOrderedSame)
{
return YES;
}
if ([self _sanitinizeNeeded: [[theParts objectAtIndex: i ] objectForKey: @"parts"]])
return YES;
}
return NO;
}
//
//
//
- (NSData *) _preferredBodyDataUsingType: (int) theType
mimeSupport: (int) theMimeSupport
nativeType: (int *) theNativeType
{
NSString *type, *subtype, *encoding;
NSData *d;
BOOL isSMIME, sanitinizeNeeded;
type = [[[self bodyStructure] valueForKey: @"type"] lowercaseString];
subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString];
isSMIME = NO;
d = nil;
// We determine the native type
@@ -522,14 +494,8 @@ struct GlobalObjectId {
else if ([type isEqualToString: @"multipart"])
*theNativeType = 4;
if (([subtype isEqualToString: @"signed"] || [subtype isEqualToString: @"pkcs7-mime"] ) && theMimeSupport > 0)
{
*theNativeType = 4;
isSMIME = YES;
}
// We get the right part based on the preference
if ((theType == 1 || theType == 2) && !isSMIME)
if (theType == 1 || theType == 2)
{
if ([type isEqualToString: @"text"] && ![subtype isEqualToString: @"calendar"])
{
@@ -570,12 +536,12 @@ struct GlobalObjectId {
d = [self _preferredBodyDataInMultipartUsingType: theType nativeTypeFound: theNativeType];
}
}
else if (theType == 4 || isSMIME)
else if (theType == 4)
{
// We sanitize the content if the content-transfer-encoding is 8bit and charset is not utf-8 or us-ascii.
sanitinizeNeeded = [self _sanitinizeNeeded: [NSArray arrayWithObject: [self bodyStructure]]];
if (sanitinizeNeeded && !isSMIME)
// We sanitize the content *ONLY* for Outlook clients and if the content-transfer-encoding is 8bit. Outlook has strange issues
// with quoted-printable/base64 encoded text parts. It just doesn't decode them.
encoding = [[self lookupInfoForBodyPart: @""] objectForKey: @"encoding"];
if ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"] || ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame))
d = [self _sanitizedMIMEMessage];
else
d = [self content];
@@ -690,18 +656,16 @@ struct GlobalObjectId {
{
NSData *d, *globalObjId;
NSArray *attachmentKeys;
iCalCalendar *calendar;
NSString *p, *subtype;
NSMutableString *s;
id value;
int preferredBodyType, mimeSupport, nativeBodyType;
uint32_t v;
NSString *p;
id value;
subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString];
preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue];
mimeSupport = [[context objectForKey: @"MIMESupport"] intValue];
iCalCalendar *calendar;
int preferredBodyType, nativeBodyType;
s = [NSMutableString string];
@@ -898,12 +862,7 @@ struct GlobalObjectId {
else
{
// MesssageClass and ContentClass
if ([subtype isEqualToString: @"signed"])
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note.SMIME.MultipartSigned"];
else if ([subtype isEqualToString: @"pkcs7-mime"])
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note.SMIME"];
else
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note"];
[s appendFormat: @"<MessageClass xmlns=\"Email:\">%@</MessageClass>", @"IPM.Note"];
[s appendFormat: @"<ContentClass xmlns=\"Email:\">%@</ContentClass>", @"urn:content-classes:message"];
}
@@ -917,8 +876,10 @@ struct GlobalObjectId {
[s appendFormat: @"<InternetCPID xmlns=\"Email:\">%@</InternetCPID>", @"65001"];
// Body - namespace 17
preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue];
nativeBodyType = 1;
d = [self _preferredBodyDataUsingType: preferredBodyType mimeSupport: mimeSupport nativeType: &nativeBodyType];
d = [self _preferredBodyDataUsingType: preferredBodyType nativeType: &nativeBodyType];
if (calendar && !d)
{
@@ -1020,12 +981,9 @@ struct GlobalObjectId {
{
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
// Set the correct type if client requested text/html but we got text/plain.
// For s/mime mails type is always 4 if mimeSupport is 1 or 2.
// Set the correct type if client requested text/html but we got text/plain
if (preferredBodyType == 2 && nativeBodyType == 1)
[s appendString: @"<Type>1</Type>"];
else if (([subtype isEqualToString: @"signed"] || [subtype isEqualToString: @"pkcs7-mime"] ) && mimeSupport > 0)
[s appendString: @"<Type>4</Type>"];
else
[s appendFormat: @"<Type>%d</Type>", preferredBodyType];
@@ -1043,8 +1001,7 @@ struct GlobalObjectId {
// Attachments -namespace 16
attachmentKeys = [self fetchFileAttachmentKeys];
if ([attachmentKeys count] && !([subtype isEqualToString: @"signed"]))
if ([attachmentKeys count])
{
int i;
@@ -1114,15 +1071,9 @@ struct GlobalObjectId {
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.0"] ||
[[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.1"])
{
id value;
NSString *reference;
value = [[self mailHeaders] objectForKey: @"references"];
if ([value isKindOfClass: [NSArray class]])
reference = [[[value objectAtIndex: 0] componentsSeparatedByString: @" "] objectAtIndex: 0];
else
reference = [[value componentsSeparatedByString: @" "] objectAtIndex: 0];
reference = [[[[self mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] objectAtIndex: 0];
if ([reference length] > 0)
[s appendFormat: @"<ConversationId xmlns=\"Email2:\">%@</ConversationId>", [[reference dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
-1
View File
@@ -37,7 +37,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@interface SOGoSyncCacheObject : NSObject
{
@public
id _uid;
id _sequence;
}
+13 -22
View File
@@ -32,17 +32,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <Foundation/NSNull.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
static Class NSNullK;
@implementation SOGoSyncCacheObject
+ (void) initialize
{
NSNullK = [NSNull class];
}
- (id) init
{
if ((self = [super init]))
@@ -54,14 +46,14 @@ static Class NSNullK;
return self;
}
+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence
+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence;
{
id o;
o = [[self alloc] init];
[o setUID: [NSNumber numberWithInt: [theUID intValue]]];
[o setSequence: ([theSequence isKindOfClass: NSNullK] ? theSequence : [NSNumber numberWithInt: [theSequence intValue]])];
[o setUID: theUID];
[o setSequence: theSequence];
return [o autorelease];
}
@@ -75,7 +67,7 @@ static Class NSNullK;
- (id) uid
{
return [_uid description];
return _uid;
}
- (void) setUID: (id) theUID
@@ -85,7 +77,7 @@ static Class NSNullK;
- (id) sequence
{
return ([_sequence isKindOfClass: NSNullK] ? _sequence : [_sequence description]);
return _sequence;
}
- (void) setSequence: (id) theSequence
@@ -96,7 +88,7 @@ static Class NSNullK;
- (NSComparisonResult) compareUID: (SOGoSyncCacheObject *) theSyncCacheObject
{
return [self->_uid compare: theSyncCacheObject->_uid];
return [[self uid] compare: [theSyncCacheObject uid]];
}
//
@@ -105,21 +97,21 @@ static Class NSNullK;
//
- (NSComparisonResult) compareSequence: (SOGoSyncCacheObject *) theSyncCacheObject
{
if ([self->_sequence isEqual: [NSNull null]] &&
[theSyncCacheObject->_sequence isEqual: [NSNull null]])
if ([[self sequence] isEqual: [NSNull null]] &&
[[theSyncCacheObject sequence] isEqual: [NSNull null]])
return [self compareUID: theSyncCacheObject];
if (![self->_sequence isEqual: [NSNull null]] && [theSyncCacheObject->_sequence isEqual: [NSNull null]])
if (![[self sequence] isEqual: [NSNull null]] && [[theSyncCacheObject sequence] isEqual: [NSNull null]])
return NSOrderedDescending;
if ([self->_sequence isEqual: [NSNull null]] && ![theSyncCacheObject->_sequence isEqual: [NSNull null]])
if ([[self sequence] isEqual: [NSNull null]] && ![[theSyncCacheObject sequence] isEqual: [NSNull null]])
return NSOrderedAscending;
// Must check this here, to avoid comparing NSNull objects
if ([self->_sequence compare: theSyncCacheObject->_sequence] == NSOrderedSame)
if ([[self sequence] compare: [theSyncCacheObject sequence]] == NSOrderedSame)
return [self compareUID: theSyncCacheObject];
return [self->_sequence compare: theSyncCacheObject->_sequence];
return [[self sequence] compare: [theSyncCacheObject sequence]];
}
- (NSString *) description
@@ -128,4 +120,3 @@ static Class NSNullK;
}
@end
+1 -1
View File
@@ -58,7 +58,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// don't send negative reminder - not supported
if (delta > 0)
[s appendFormat: @"<Reminder xmlns=\"Calendar:\">%d</Reminder>", (int)delta];
[s appendFormat: @"<Reminder xmlns=\"Calendar:\">%d</Reminder>", delta];
}
return s;
+13 -276
View File
@@ -38,8 +38,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <Foundation/NSTimeZone.h>
#import <NGExtensions/NSString+misc.h>
#import <NGExtensions/NSCalendarDate+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGObjWeb/WOContext.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/WORequest.h>
@@ -47,16 +45,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <NGCards/iCalCalendar.h>
#import <NGCards/iCalDateTime.h>
#import <NGCards/iCalPerson.h>
#import <NGCards/NSCalendarDate+NGCards.h>
#import <NGCards/NSString+NGCards.h>
#import <NGCards/iCalCalendar.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/WORequest+SOGo.h>
#import <Appointments/iCalEntityObject+SOGo.h>
#import <Appointments/iCalRepeatableEntityObject+SOGo.h>
#include "iCalAlarm+ActiveSync.h"
#include "iCalRecurrenceRule+ActiveSync.h"
@@ -105,13 +98,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else if ([self created])
[s appendFormat: @"<DTStamp xmlns=\"Calendar:\">%@</DTStamp>", [[self created] activeSyncRepresentationWithoutSeparatorsInContext: context]];
// Timezone
tz = [(iCalDateTime *)[self firstChildWithTag: @"dtstart"] timeZone];
// StartTime -- http://msdn.microsoft.com/en-us/library/ee157132(v=exchg.80).aspx
if ([self startDate])
{
if ([self isAllDay] && !tz)
if ([self isAllDay])
[s appendFormat: @"<StartTime xmlns=\"Calendar:\">%@</StartTime>",
[[[self startDate] dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
@@ -124,7 +114,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// EndTime -- http://msdn.microsoft.com/en-us/library/ee157945(v=exchg.80).aspx
if ([self endDate])
{
if ([self isAllDay] && !tz)
if ([self isAllDay])
[s appendFormat: @"<EndTime xmlns=\"Calendar:\">%@</EndTime>",
[[[self endDate] dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
@@ -134,6 +124,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[s appendFormat: @"<EndTime xmlns=\"Calendar:\">%@</EndTime>", [[self endDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
}
// Timezone
tz = [(iCalDateTime *)[self firstChildWithTag: @"dtstart"] timeZone];
if (!tz)
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
@@ -233,9 +226,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//[s appendFormat: @"<Importance xmlns=\"Calendar:\">%d</Importance>", v];
// UID -- http://msdn.microsoft.com/en-us/library/ee159919(v=exchg.80).aspx
if (![self recurrenceId] && [[self uid] length])
if ([[self uid] length])
[s appendFormat: @"<UID xmlns=\"Calendar:\">%@</UID>", [self uid]];
// Sensitivity
if ([[self accessClass] isEqualToString: @"PRIVATE"])
v = 2;
@@ -258,6 +251,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[s appendFormat: @"</Categories>"];
}
// Reminder -- http://msdn.microsoft.com/en-us/library/ee219691(v=exchg.80).aspx
// TODO: improve this to handle more alarm types
if ([self hasAlarms])
@@ -271,74 +265,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Recurrence rules
if ([self isRecurrent])
{
NSMutableArray *components, *exdates;
iCalEvent *current_component;
NSString *recurrence_id;
unsigned int count, max, i;
[s appendString: [[[self recurrenceRules] lastObject] activeSyncRepresentationInContext: context]];
components = [NSMutableArray arrayWithArray: [[self parent] events]];
max = [components count];
if (max > 1 || [self hasExceptionDates])
{
exdates = [NSMutableArray arrayWithArray: [self exceptionDates]];
[s appendString: @"<Exceptions xmlns=\"Calendar:\">"];
for (count = 1; count < max; count++)
{
current_component = [components objectAtIndex: count] ;
if ([self isAllDay])
{
recurrence_id = [NSString stringWithFormat: @"%@",
[[[current_component recurrenceId] dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: -[userTimeZone secondsFromGMTForDate:
[current_component recurrenceId]]]
activeSyncRepresentationWithoutSeparatorsInContext: context]];
}
else
{
recurrence_id = [NSString stringWithFormat: @"%@", [[current_component recurrenceId]
activeSyncRepresentationWithoutSeparatorsInContext: context]];
}
[s appendString: @"<Exception>"];
[s appendFormat: @"<Exception_StartTime xmlns=\"Calendar:\">%@</Exception_StartTime>", recurrence_id];
[s appendFormat: @"%@", [current_component activeSyncRepresentationInContext: context]];
[s appendString: @"</Exception>"];
}
for (i = 0; i < [exdates count]; i++)
{
[s appendString: @"<Exception>"];
[s appendString: @"<Exception_Deleted>1</Exception_Deleted>"];
if ([self isAllDay])
{
recurrence_id = [NSString stringWithFormat: @"%@",
[[[[exdates objectAtIndex: i] asCalendarDate] dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: -[userTimeZone secondsFromGMTForDate:
[[exdates objectAtIndex: i] asCalendarDate]]]
activeSyncRepresentationWithoutSeparatorsInContext: context]];
}
else
{
recurrence_id = [NSString stringWithFormat: @"%@", [[[exdates objectAtIndex: i] asCalendarDate]
activeSyncRepresentationWithoutSeparatorsInContext: context]];
}
[s appendFormat: @"<Exception_StartTime xmlns=\"Calendar:\">%@</Exception_StartTime>", recurrence_id];
[s appendString: @"</Exception>"];
}
[s appendString: @"</Exceptions>"];
}
}
// Comment
@@ -358,14 +285,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
[s appendFormat: @"<Type>%d</Type>", 1];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", (int)[o length]];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
[s appendFormat: @"<Data>%@</Data>", o];
[s appendString: @"</Body>"];
}
}
if (![self recurrenceId])
[s appendFormat: @"<NativeBodyType xmlns=\"AirSyncBase:\">%d</NativeBodyType>", 1];
[s appendFormat: @"<NativeBodyType xmlns=\"AirSyncBase:\">%d</NativeBodyType>", 1];
return s;
}
@@ -411,19 +337,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (void) takeActiveSyncValues: (NSDictionary *) theValues
inContext: (WOContext *) context
{
iCalDateTime *start, *end; //, *oldstart;
NSCalendarDate *oldstart;
iCalDateTime *start, *end;
NSTimeZone *userTimeZone;
iCalTimeZone *tz;
id o;
int deltasecs;
BOOL isAllDay;
NSMutableArray *occurences;
occurences = [NSMutableArray arrayWithArray: [[self parent] events]];
if ((o = [theValues objectForKey: @"UID"]))
[self setUid: o];
@@ -436,11 +356,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
isAllDay = YES;
}
else if ([occurences count] && !([theValues objectForKey: @"AllDayEvent"]))
{
// If the occurence has no AllDay tag use it from master.
isAllDay = [[occurences objectAtIndex: 0] isAllDay];
}
//
// 0- free, 1- tentative, 2- busy and 3- out of office
@@ -507,7 +422,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
o = [o calendarDate];
start = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
oldstart = [start dateTime];
[start setTimeZone: tz];
if (isAllDay)
@@ -519,12 +433,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
[start setDateTime: o];
}
// Calculate delta if start date has been changed.
if (oldstart)
deltasecs = [[start dateTime ] timeIntervalSinceDate: oldstart] * -1;
else
deltasecs = 0;
}
if ((o = [theValues objectForKey: @"EndTime"]))
@@ -583,177 +491,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
RELEASE(rule);
[rule takeActiveSyncValues: o inContext: context];
// Exceptions
if ((o = [theValues objectForKey: @"Exceptions"]) && [o isKindOfClass: [NSArray class]])
{
NSCalendarDate *recurrenceId, *adjustedRecurrenceId, *currentId;
NSMutableArray *exdates, *exceptionstouched;
iCalEvent *currentOccurence;
NSDictionary *exception;
unsigned int i, count, max;
[self removeAllExceptionDates];
exdates = [NSMutableArray array];
exceptionstouched = [NSMutableArray array];
for (i = 0; i < [o count]; i++)
{
exception = [o objectAtIndex: i];
if (![[exception objectForKey: @"Exception_Deleted"] intValue])
{
recurrenceId = [[exception objectForKey: @"Exception_StartTime"] asCalendarDate];
if (isAllDay)
recurrenceId = [recurrenceId dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
seconds: [userTimeZone secondsFromGMTForDate:recurrenceId]];
// When moving the calendar entry (i.e. changing the startDate) iOS and Android sometimes send a value for
// Exception_StartTime which is not what we expect.
// With this we make sure the recurrenceId is always correct.
//
// iOS problem - If the master is a no-allday-event but the exception is, an invalid Exception_StartTime is in the request.
// e.g. it sends 20150409T040000Z instead of 20150409T060000Z;
// This might be a special case since iPhone doesn't allow an allday-exception on a non-allday-event.
if (!([[start dateTime] compare: [[start dateTime] hour:[recurrenceId hourOfDay] minute:[recurrenceId minuteOfHour]]] == NSOrderedSame))
recurrenceId = [recurrenceId hour:[[start dateTime] hourOfDay] minute:[[start dateTime] minuteOfHour]];
// We need to store the recurrenceIds and exception dates adjusted by deltasecs.
// This ensures that the adjustment in SOGoCalendarComponent->updateComponent->_updateRecurrenceIDsWithEvent gives the correct dates.
adjustedRecurrenceId = [recurrenceId dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0 seconds: deltasecs];
// search for an existing occurence and update it
max = [occurences count];
currentOccurence = nil;
count = 1;
while (count < max)
{
currentOccurence = [occurences objectAtIndex: count] ;
currentId = [currentOccurence recurrenceId];
if ([currentId compare: adjustedRecurrenceId] == NSOrderedSame)
{
[exceptionstouched addObject: adjustedRecurrenceId];
[currentOccurence takeActiveSyncValues: exception inContext: context];
break;
}
count++;
currentOccurence = nil;
}
// Create a new occurence if we found none to update.
if (!currentOccurence)
{
iCalDateTime *recid;
currentOccurence = [self mutableCopy];
[currentOccurence removeAllRecurrenceRules];
[currentOccurence removeAllExceptionRules];
[currentOccurence removeAllExceptionDates];
[[self parent] addToEvents: currentOccurence];
[currentOccurence takeActiveSyncValues: exception inContext: context];
recid = (iCalDateTime *)[currentOccurence uniqueChildWithTag: @"recurrence-id"];
if (isAllDay)
[recid setDate: adjustedRecurrenceId];
else
[recid setDateTime: adjustedRecurrenceId];
[exceptionstouched addObject: [recid dateTime]];
}
}
else if ([[exception objectForKey: @"Exception_Deleted"] intValue])
{
recurrenceId = [[exception objectForKey: @"Exception_StartTime"] asCalendarDate];
if (isAllDay)
recurrenceId = [recurrenceId dateByAddingYears: 0 months: 0
days: 0 hours: 0 minutes: 0
seconds: [userTimeZone secondsFromGMTForDate:recurrenceId]];
// We add only valid exception dates.
if ([self doesOccurOnDate: recurrenceId] &&
([[start dateTime] compare: [[start dateTime] hour:[recurrenceId hourOfDay]
minute:[recurrenceId minuteOfHour]]] == NSOrderedSame))
{
// We need to store the recurrenceIds and exception dates adjusted by deltasecs.
// This ensures that the adjustment in SOGoCalendarComponent->updateComponent->_updateRecurrenceIDsWithEvent gives the correct dates.
[exdates addObject: [recurrenceId dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: deltasecs]];
}
}
}
// Update exception dates in master event.
max = [exdates count];
if (max > 0)
{
for (count = 0; count < max; count++)
{
if (([exceptionstouched indexOfObject: [exdates objectAtIndex: count]] == NSNotFound))
[self addToExceptionDates: [exdates objectAtIndex: count]];
}
}
// Remove all exceptions included in the request.
max = [occurences count];
count = 1; // skip the master event
while (count < max)
{
currentOccurence = [occurences objectAtIndex: count] ;
currentId = [currentOccurence recurrenceId];
// Delete all occurences which are not touched (modified/added).
if (([exceptionstouched indexOfObject: currentId] == NSNotFound))
[[[self parent] children] removeObject: currentOccurence];
count++;
}
}
else if ([self isRecurrent])
{
// We have no exceptions in request but there is a recurrence rule.
// Remove all excpetions and exception dates.
iCalEvent *currentOccurence;
unsigned int count, max;
[self removeAllExceptionDates];
max = [occurences count];
count = 1; // skip the master event
while (count < max)
{
currentOccurence = [occurences objectAtIndex: count];
[[[self parent] children] removeObject: currentOccurence];
count++;
}
}
}
else if ([self isRecurrent])
{
// We have no recurrence rule in request.
// Remove all exceptions, exception dates and recurrence rules.
iCalEvent *currentOccurence;
unsigned int count, max;
[self removeAllRecurrenceRules];
[self removeAllExceptionRules];
[self removeAllExceptionDates];
max = [occurences count];
count = 1; // skip the master event
while (count < max)
{
currentOccurence = [occurences objectAtIndex: count];
[[[self parent] children] removeObject: currentOccurence];
count++;
}
}
// Organizer - we don't touch the value unless we're the organizer
@@ -761,7 +498,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
([self userIsOrganizer: [context activeUser]] || [[context activeUser] hasEmail: o]))
{
iCalPerson *person;
person = [iCalPerson elementWithTag: @"organizer"];
[person setEmail: o];
[person setCn: [theValues objectForKey: @"Organizer_Name"]];
+1 -1
View File
@@ -140,7 +140,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Simple reccurrence rule of type "Monthly"
type = 2;
[s appendFormat: @"<Recurrence_DayOfMonth xmlns=\"Calendar:\">%d</Recurrence_DayOfMonth>",
(int)[[[self parent] startDate] dayOfMonth]];
[[[self parent] startDate] dayOfMonth]];
}
}
else if ([self frequency] == iCalRecurrenceFrequenceYearly)
+1 -1
View File
@@ -140,7 +140,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
[s appendString: @"<Body xmlns=\"AirSyncBase:\">"];
[s appendFormat: @"<Type>%d</Type>", 1];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", (int)[o length]];
[s appendFormat: @"<EstimatedDataSize>%d</EstimatedDataSize>", [o length]];
[s appendFormat: @"<Data>%@</Data>", o];
[s appendString: @"</Body>"];
}
+2 -3
View File
@@ -70,8 +70,8 @@ ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0
<Proxy http://127.0.0.1:20000/SOGo>
## adjust the following to your configuration
RequestHeader set "x-webobjects-server-port" "443"
# RequestHeader set "x-webobjects-server-name" "yourhostname"
# RequestHeader set "x-webobjects-server-url" "https://yourhostname"
RequestHeader set "x-webobjects-server-name" "yourhostname"
RequestHeader set "x-webobjects-server-url" "https://yourhostname"
## When using proxy-side autentication, you need to uncomment and
## adjust the following line:
@@ -90,6 +90,5 @@ ProxyPass /SOGo http://127.0.0.1:20000/SOGo retry=0
<IfModule rewrite_module>
RewriteEngine On
RewriteRule ^/.well-known/caldav/?$ /SOGo/dav [R=301]
RewriteRule ^/.well-known/carddav/?$ /SOGo/dav [R=301]
</IfModule>
-3001
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
+5 -7
View File
@@ -1,7 +1,5 @@
Installation and Configuration Guide
====================================
:toc: left
:icons: font
////
@@ -56,7 +54,7 @@ at http://www.sogo.nu/
Architecture and Compatibility
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
image::images/architecture.png[System Architecture, 400, 964]
image::images/architecture.png[System Architecture]
Standard protocols such as CalDAV, CardDAV, GroupDAV, HTTP, IMAP and
SMTP are used to communicate with the SOGo platform or its
@@ -172,7 +170,7 @@ supported by SOGo:
* Red Hat Enterprise Linux (RHEL) Server 5, 6 and 7
* Community ENTerprise Operating System (CentOS) 5, 6 and 7
* Debian GNU/Linux 6.0 (Squeeze) to 8.0 (Jessie)
* Ubuntu 12.04 (Precise) to 14.04 (Trusty)
* Ubuntu 10.04 (Lucid) to 14.04 (Trusty)
Make sure the required components are started automatically at boot time
and that they are running before proceeding with the SOGo configuration.
@@ -181,7 +179,7 @@ standard distribution. For example, if you are using Red Hat Enterprise
Linux 5, you have to be subscribed to the Red Hat Network before
continuing with the SOGo software installation.
NOTE: This document covers the installation of SOGo under RHEL 6.
This document covers the installation of SOGo under RHEL 6.
For installation instructions on Debian and Ubuntu, please refer
directly to the SOGo website at http://www.sogo.nu/.
@@ -296,7 +294,7 @@ associated to a domain is limited to access only the users data from the
same domain. Consequently, the configuration parameters of SOGo are
defined on three levels:
image::images/preferences-hierarchy.png[Preferences Hierarchy, 400, 400]
image::images/preferences-hierarchy.png[Preferences Hierarchy]
Each level inherits the preferences of the parent level. Therefore,
domain preferences define the defaults values of the user preferences,
@@ -1127,7 +1125,7 @@ fax number, one could map it to the _facsimiletelephonenumber_ attribute
like this:
----
mapping = {
mapping = \{
  facsimiletelephonenumber = ("fax", "facsimiletelephonenumber");
};
----
@@ -233,7 +233,6 @@ Installation
This section will guide you through the installation of the native
Microsoft Outlook compatibility layer SOGo offers.
////
Red Hat Enterprise Linux v6 x86_64
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -317,7 +316,7 @@ from this guide.
On Ubuntu 12.04, the Samba init scripts need to be modified to
disable the upstart check. For more details, refer to:
https://wiki.samba.org/index.php/Samba4/InitScript
////
Debian 8 (Jessie) and Ubuntu 14.04 (Trusty Tahr)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -388,7 +387,7 @@ Run the following commands as root: 
----
samba-tool domain provision --realm=example.com \
--domain=EXAMPLE \
--domain=OPENCHANGE \
--adminpass='%1OpenChange' \
--server-role='domain controller'
@@ -396,7 +395,7 @@ samba-tool user setexpiry administrator --noexpiry
----
You might consider changing the realm and domain used, to suit your
environment.
enviroment.
You might also have to
remove `/etc/samba/smb.conf` prior running this command.
@@ -442,7 +441,7 @@ Your Samba 4 configuration file should look like this:
OpenChange Configuration
~~~~~~~~~~~~~~~~~~~~~~~~
Since v2.2, OpenChange stores its metadata in MySQL so you need to have it installed.
OpenChange 2.2 stores its metadata in MySQL so you need to have it installed.
First, create the OpenChange MySQL user:
@@ -513,11 +512,9 @@ mapistore:indexing_backend = mysql://openchange-user:openchange$123@localhost/op
mapiproxy:openchangedb = mysql://openchange-user:openchange$123@localhost/openchange
----
////
On RHEL, make sure SELinux is disabled:
setenforce 0
////
Next, you can start Samba using the usual command:
@@ -548,11 +545,9 @@ This service runs as a WSGI application under apache (mod_wsgi).
While HTTPS is not required to access this service, it is strongly
recommended.
////
On RHEL-based distributions, the apache configuration required by these
services can be found in `/etc/httpd/conf.d/ocsmanager.conf` and
`/etc/httpd/conf.d/rpcproxy.conf`.
////
For Debian-based distributions, these files can be found
in `/etc/apache2/conf.d/` or `/etc/apache2/conf-available`.
@@ -568,10 +563,8 @@ The OCS Manager and RPC Proxy configuration module can be enabled using:
a2enconf ocsmanager
a2enconf rpcproxy
////
On RHEL-based distributions, make sure the `LoadModule` directive is
uncommented in `/etc/httpd/conf.d/wsgi.conf`.
////
The _reqtimeout_ apache module is known to cause problems when using the
default configuration shipped with Debian-based systems. On such
@@ -587,12 +580,10 @@ To avoid this problem, use a much higher timeout or disable the module:
a2dismod reqtimeout
You should now restart the Apache service and make sure it will start on
boot.
////
On RHEL-based distributions, do:
boot. On RHEL-based distributions, do:
chkconfig httpd on && /etc/init.d/httpd restart
////
On Debian-based distributions, do:
update-rc.d apache2 defaults && /etc/init.d/apache2 restart
@@ -818,10 +809,11 @@ can be ignored for now. This feature is currently not supported.
remove any data associated with the user from the SOGo server and
recreate a Microsoft Outlook profile.
To remove any data associated to a user, use
the `openchange_user_cleanup` script distributed with OpenChange. The script
can be found in `/usr/share/openchange/`.
the `openchange_user_cleanup` script distributed with SOGo. The script
can be found in `/usr/share/doc/sogo/` (`/usr/share/sogo-VERSION/` on
RHEL).
To reset a user, run the script as root:
`openchange_user_cleanup username`. See the usage output for additional options.
`python openchange_user_cleanup username`. See the usage output for additional options.
* The "Out of Office Assistant" will not currently work. This feature
has not been implemented.
* Creating folders below INBOX (when not normally permitted by the IMAP
+3 -3
View File
@@ -1,7 +1,7 @@
<!-- TODO have the build system take care of this -->
<releaseinfo>Version 2.3.4 - December 2015</releaseinfo>
<subtitle>for version 2.3.4</subtitle>
<date>2015-12-15</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.4
:release_version: 2.3.1
// vim: set syntax=asciidoc tabstop=2 shiftwidth=2 expandtab:
+1 -1
View File
@@ -267,7 +267,7 @@
NSObject <DOMNodeList> *list;
NSObject <DOMNode> *valueNode;
NSArray *elements;
NSString *property=nil, *match=nil;
NSString *property, *match;
list = [searchElement getElementsByTagName: @"prop"];
if ([list length])
+5 -81
View File
@@ -1,87 +1,12 @@
2.3.4 (2015-12-15)
2.3.x (2015-MM-DD)
------------------
New features
- initial support for EAS calendar exceptions
Enhancements
- limit the maximum width of toolbar buttons (#3381)
Bug fixes
- JavaScript exception when printing events from calendars with no assigned color (#3203)
- EAS fix for wrong charset being used (#3392)
- EAS fix on qp-encoded subjects (#3390)
- correctly handle all-day event exceptions when the master event changes
- prevent characters in calendar component UID causing issues during import process
- avoid duplicating attendees when accepting event using a different identity over CalDAV
2.3.3a (2015-11-18)
-------------------
Bug fixes
- expanded mail folders list is not saved (#3386)
- cleanup translations
2.3.3 (2015-11-11)
------------------
New features
- initial S/MIME support for EAS (#3327)
- now possible to choose which folders to sync over EAS
Enhancements
- we no longer always entirely rewrite messages for Outlook 2013 when using EAS
- support for ghosted elements on contacts over EAS
- added Macedonian (mk_MK) translation - thanks to Miroslav Jovanovic
- added Portuguese (pt) translation - thanks to Eduardo Crispim
Bug fixes
- numerous EAS fixes when connections are dropped before the EAS client receives the response (#3058, #2849)
- correctly handle the References header over EAS (#3365)
- make sure English is always used when generating Date headers using EAS (#3356)
- don't escape quoted strings during versit generation
- we now return all cards when we receive an empty addressbook-query REPORT
- avoid crash when replying to a mail with no recipients (#3359)
- inline images sent from SOGo webmail are not displayed in Mozilla Thunderbird (#3271)
- prevent postal address showing on single line over EAS (#2614)
- display missing events when printing working hours only
- fix corner case making server crash when syncing hard deleted messages when clear offline items was set up (Zentyal)
- avoid infinite Outlook client loops trying to set read flag when it is already set (Zentyal)
- avoid crashing when calendar metadata is missing in the cache (Zentyal)
- fix recurrence pattern event corner case created by Mozilla Thunderbird which made server crash (Zentyal)
- fix corner case that removes attachments on sending messages from Outlook (Zentyal)
- freebusy on web interface works again in multidomain environments (Zentyal)
- fix double creation of folders in Outlook when the folder name starts with a digit (Zentyal)
- avoid crashing Outlook after setting a custom view in a calendar folder (Zentyal)
- handle emails having an attachment as their content
- fixed JavaScript syntax error in attendees editor
- fixed wrong comparison of meta vs. META tag in HTML mails
- fixed popup menu position when moved to the left (#3381)
- fixed dialog position when at the bottom of the window (#2646, #3378)
2.3.2 (2015-09-16)
------------------
Enhancements
- improved EAS speed and memory usage, avoiding many IMAP LIST commands (#3294)
- improved EAS speed during initial syncing of large mailboxes (#3293)
- updated CKEditor to version 4.5.3
Bug fixes
- fixed display of whitelisted attendees in Preferences window on Firefox (#3285)
- non-latin subfolder names are displayed correctly on Outlook (Zentyal)
- fixed several sync issues on environments with multiple users (Zentyal)
- folders from other users will no longer appear on your Outlook (Zentyal)
- use right auth in multidomain environments in contacts and calendar from Outlook (Zentyal)
- session fix when SOGoEnableDomainBasedUID is enabled but logins are domain-less
- less sync issues when setting read flag (Zentyal)
- attachments with non-latin filenames sent by Outlook are now received (Zentyal)
- support attachments from more mail clients (Zentyal)
- avoid conflicting message on saving a draft mail (Zentyal)
- less conflicting messages in Outlook while moving messages between folders (Zentyal)
- start/end shifting by 1 hour due to timezone change on last Sunday of October 2015 (#3344)
- fixed localization of calendar categories with empty profile (#3295)
- fixed options availability in contextual menu of Contacts module (#3342)
- Non-latin subfolder names are displayed correctly on Outlook (Zentyal)
- Fixed several sync issues on environments with multiple users (Zentyal)
- Folders from other users will no longer appear on your Outlook (Zentyal)
- Use right auth in multidomain environments in contacts and calendar from Outlook (Zentyal)
2.3.1 (2015-07-23)
------------------
@@ -94,7 +19,6 @@ Enhancements
- added create-folder subcommand to sogo-tool to create contact and calendar folders
- group mail addresses can be used as recipient in Outlook
- added 'ActiveSync' module constraints
- updated CKEditor to version 4.5.1
- added Slovenian translation - thanks to Jens Riecken
- added Chinese (Taiwan) translation
-3
View File
@@ -46,9 +46,6 @@
forURL: (NSURL *) server
forceRenew: (BOOL) renew;
- (NSString *) passwordInContext: (WOContext *) context;
@end
#endif /* MAPISTOREAUTHENTICATOR_H */
-4
View File
@@ -72,8 +72,4 @@
return imapPassword;
}
- (NSString *) passwordInContext: (WOContext *) context
{
return password;
}
@end
+1 -1
View File
@@ -671,7 +671,7 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
return newAttachment;
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
- (int) setReadFlag: (uint8_t) flag
{
return MAPISTORE_SUCCESS;
}
+7 -162
View File
@@ -22,7 +22,6 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSData.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
@@ -35,7 +34,6 @@
#import "MAPIStoreDBFolder.h"
#import "MAPIStoreDBMessage.h"
#import "MAPIStoreTypes.h"
#import "NSData+MAPIStore.h"
#import "NSObject+MAPIStore.h"
#import "NSString+MAPIStore.h"
@@ -45,28 +43,16 @@
@implementation MAPIStoreDBMessage
+ (enum mapistore_error) getAvailableProperties: (struct SPropTagArray **) propertiesP
inMemCtx: (TALLOC_CTX *) memCtx
+ (int) getAvailableProperties: (struct SPropTagArray **) propertiesP
inMemCtx: (TALLOC_CTX *) memCtx
{
struct SPropTagArray *properties;
NSUInteger count;
enum MAPITAGS faiProperties[] = {
0x68330048, /* PR_VIEW_CLSID */
0x68350102, /* PR_VIEW_STATE */
0x683c0102,
0x683d0040,
0x683e0102,
0x683f0102, /* PR_VIEW_VIEWTYPE_KEY */
0x68410003,
0x68420102,
0x68450102,
0x68460003,
0x7006001F, /* PR_VD_NAME_W */
0x70030003, /* PR_VD_FLAGS */
0x70070003 /* PR_VD_VERSION */
};
enum MAPITAGS faiProperties[] = { 0x68350102, 0x683c0102, 0x683e0102,
0x683f0102, 0x68410003, 0x68420102,
0x68450102, 0x68460003,
// PR_VD_NAME_W, PR_VD_FLAGS, PR_VD_VERSION, PR_VIEW_CLSID
0x7006001F, 0x70030003, 0x70070003, 0x68330048 };
size_t faiSize = sizeof(faiProperties) / sizeof(enum MAPITAGS);
properties = talloc_zero (memCtx, struct SPropTagArray);
@@ -88,13 +74,6 @@
return MAPISTORE_SUCCESS;
}
- (enum mapistore_error) getAvailableProperties: (struct SPropTagArray **) propertiesP
inMemCtx: (TALLOC_CTX *) memCtx
{
return [[self class] getAvailableProperties: propertiesP
inMemCtx: memCtx];
}
- (id) initWithSOGoObject: (id) newSOGoObject
inContainer: (MAPIStoreObject *) newContainer
{
@@ -125,105 +104,6 @@
return objectVersion;
}
- (void) _updatePredecessorChangeList
{
BOOL updated;
enum mapistore_error rc;
NSData *currentChangeList, *changeKey;
NSMutableArray *changeKeys;
NSMutableData *newChangeList;
NSUInteger count, len;
struct SizedXid *changes;
struct SPropValue property;
struct SRow aRow;
struct XID *currentChangeKey;
TALLOC_CTX *localMemCtx;
uint32_t nChanges;
localMemCtx = talloc_new (NULL);
if (!localMemCtx)
{
[self errorWithFormat: @"No more memory"];
return;
}
changeKey = [self getReplicaKeyFromGlobCnt: [self objectVersion]];
currentChangeList = [properties objectForKey: MAPIPropertyKey (PidTagPredecessorChangeList)];
if (!currentChangeList)
{
/* Create a new PredecessorChangeList */
len = [changeKey length];
newChangeList = [NSMutableData dataWithCapacity: len + 1];
[newChangeList appendUInt8: len];
[newChangeList appendData: changeKey];
}
else
{
/* Update current predecessor change list with new change key */
changes = [currentChangeList asSizedXidArrayInMemCtx: localMemCtx
with: &nChanges];
updated = NO;
currentChangeKey = [changeKey asXIDInMemCtx: localMemCtx];
for (count = 0; count < nChanges && !updated; count++)
{
if (GUID_equal(&changes[count].XID.NameSpaceGuid, &currentChangeKey->NameSpaceGuid))
{
NSData *globCnt, *oldGlobCnt;
oldGlobCnt = [NSData dataWithBytes: changes[count].XID.LocalId.data length: changes[count].XID.LocalId.length];
globCnt = [NSData dataWithBytes: currentChangeKey->LocalId.data length: currentChangeKey->LocalId.length];
if ([globCnt compare: oldGlobCnt] == NSOrderedDescending)
{
if ([globCnt length] != [oldGlobCnt length])
{
[self errorWithFormat: @"Cannot compare globcnt with different length: %@ and %@", globCnt, oldGlobCnt];
abort();
}
memcpy (changes[count].XID.LocalId.data, currentChangeKey->LocalId.data, currentChangeKey->LocalId.length);
updated = YES;
}
}
}
/* Serialise it */
changeKeys = [NSMutableArray array];
if (!updated)
[changeKeys addObject: changeKey];
for (count = 0; count < nChanges; count++)
{
changeKey = [NSData dataWithXID: &changes[count].XID];
[changeKeys addObject: changeKey];
}
[changeKeys sortUsingFunction: MAPIChangeKeyGUIDCompare context: localMemCtx];
newChangeList = [NSMutableData data];
len = [changeKeys count];
for (count = 0; count < len; count++)
{
changeKey = [changeKeys objectAtIndex: count];
[newChangeList appendUInt8: [changeKey length]];
[newChangeList appendData: changeKey];
}
}
if ([newChangeList length] > 0)
{
property.ulPropTag = PidTagPredecessorChangeList;
property.value.bin = *[newChangeList asBinaryInMemCtx: localMemCtx];
aRow.cValues = 1;
aRow.lpProps = &property;
rc = [self addPropertiesFromRow: &aRow];
if (rc != MAPISTORE_SUCCESS)
[self errorWithFormat: @"Impossible to add a new predecessor change list: %d", rc];
}
talloc_free (localMemCtx);
}
//
// FIXME: how this can happen?
//
@@ -286,9 +166,6 @@
[properties setObject: [NSNumber numberWithUnsignedLongLong: newVersion]
forKey: @"version"];
/* Update PredecessorChangeList accordingly */
[self _updatePredecessorChangeList];
[self logWithFormat: @"%d props in dict", [properties count]];
[sogoObject save];
@@ -332,36 +209,4 @@
return [sogoObject lastModified];
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
{
/* Modify PidTagMessageFlags from SetMessageReadFlag and
SyncImportReadStateChanges ROPs */
NSNumber *flags;
uint32_t newFlag;
flags = [properties objectForKey: MAPIPropertyKey (PR_MESSAGE_FLAGS)];
if (flags)
{
newFlag = [flags unsignedLongValue];
if (flag & SUPPRESS_RECEIPT)
newFlag |= MSGFLAG_READ;
if (flag & CLEAR_RN_PENDING)
newFlag &= ~MSGFLAG_RN_PENDING;
if (flag & CLEAR_READ_FLAG)
newFlag &= ~MSGFLAG_READ;
if (flag & CLEAR_NRN_PENDING)
newFlag &= ~MSGFLAG_NRN_PENDING;
}
else
{
newFlag = MSGFLAG_READ;
if (flag & CLEAR_READ_FLAG)
newFlag = 0x0;
}
[properties setObject: [NSNumber numberWithUnsignedLong: newFlag]
forKey: MAPIPropertyKey (PR_MESSAGE_FLAGS)];
return MAPISTORE_SUCCESS;
}
@end
-1
View File
@@ -121,7 +121,6 @@
fromFolder: (MAPIStoreFolder *) sourceFolder
withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
wantCopy: (uint8_t) want_copy
inMemCtx: (TALLOC_CTX *) memCtx;
+8 -20
View File
@@ -642,7 +642,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
fromFolder: (MAPIStoreFolder *) sourceFolder
withMID: (uint64_t) targetMid
andChangeKey: (struct Binary_r *) targetChangeKey
andPredecessorChangeList: (struct Binary_r *) targetPredecessorChangeList
wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
{
@@ -670,18 +669,15 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
[sourceMsg copyToMessage: destMsg inMemCtx: memCtx];
if (targetPredecessorChangeList)
if (targetChangeKey)
{
property.ulPropTag = PidTagPredecessorChangeList;
property.value.bin = *targetPredecessorChangeList;
property.ulPropTag = PidTagChangeKey;
property.value.bin = *targetChangeKey;
aRow.cValues = 1;
aRow.lpProps = &property;
rc = [destMsg addPropertiesFromRow: &aRow];
if (rc != MAPISTORE_SUCCESS)
{
[self errorWithFormat: @"Cannot add PredecessorChangeList on move"];
goto end;
}
goto end;
}
[destMsg save: memCtx];
if (!wantCopy)
@@ -700,7 +696,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
fromFolder: (MAPIStoreFolder *) sourceFolder
withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
{
@@ -710,7 +705,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
NSString *oldMessageURL;
MAPIStoreMapping *mapping;
SOGoUser *ownerUser;
struct Binary_r *targetChangeKey, *targetPredecessorChangeList;
struct Binary_r *targetChangeKey;
//TALLOC_CTX *memCtx;
//memCtx = talloc_zero (NULL, TALLOC_CTX);
@@ -731,21 +726,14 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
if (oldMessageURL)
{
[oldMessageURLs addObject: oldMessageURL];
if (targetChangeKeys && targetPredecessorChangeList)
{
targetChangeKey = targetChangeKeys[count];
targetPredecessorChangeList = targetPredecessorChangeLists[count];
}
if (targetChangeKeys)
targetChangeKey = targetChangeKeys[count];
else
{
targetChangeKey = NULL;
targetPredecessorChangeList = NULL;
}
targetChangeKey = NULL;
rc = [self _moveCopyMessageWithMID: srcMids[count]
fromFolder: sourceFolder
withMID: targetMids[count]
andChangeKey: targetChangeKey
andPredecessorChangeList: targetPredecessorChangeList
wantCopy: wantCopy
inMemCtx: memCtx];
}
+1 -3
View File
@@ -41,10 +41,8 @@
/* synchronisation */
- (BOOL) synchroniseCache;
- (BOOL) synchroniseCacheFor: (NSString *) nameInContainer;
- (void) updateVersionsForMessageWithKey: (NSString *) messageKey
withChangeKey: (NSData *) oldChangeKey
andPredecessorChangeList: (NSData *) pcl;
withChangeKey: (NSData *) newChangeKey;
- (NSNumber *) lastModifiedFromMessageChangeNumber: (NSString *) changeNumber;
- (NSString *) changeNumberForMessageWithKey: (NSString *) messageKey;
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey;
+18 -194
View File
@@ -75,7 +75,6 @@ static Class NSNumberK;
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
inContainer: dbFolder]);
[versionsMessage setObjectType: MAPIInternalCacheObject];
[versionsMessage reloadIfNeeded];
}
- (void) dealloc
@@ -262,6 +261,7 @@ static Class NSNumberK;
*/
- (void) _setChangeKey: (NSData *) changeKey
forMessageEntry: (NSMutableDictionary *) messageEntry
inChangeListOnly: (BOOL) inChangeListOnly
{
struct XID *xid;
NSString *guid;
@@ -270,15 +270,19 @@ static Class NSNumberK;
NSMutableDictionary *changeList;
xid = [changeKey asXIDInMemCtx: NULL];
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
guid = [NSString stringWithGUID: &xid->GUID];
globCnt = [NSData dataWithBytes: xid->Data length: xid->Size];
talloc_free (xid);
/* 1. set change key association */
changeKeyDict = [NSDictionary dictionaryWithObjectsAndKeys: guid, @"GUID",
globCnt, @"LocalId",
nil];
[messageEntry setObject: changeKeyDict forKey: @"ChangeKey"];
if (!inChangeListOnly)
{
/* 1. set change key association */
changeKeyDict = [NSDictionary dictionaryWithObjectsAndKeys:
guid, @"GUID",
globCnt, @"LocalId",
nil];
[messageEntry setObject: changeKeyDict forKey: @"ChangeKey"];
}
/* 2. append/update predecessor change list */
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
@@ -292,77 +296,6 @@ static Class NSNumberK;
[changeList setObject: globCnt forKey: guid];
}
- (void) _updatePredecessorChangeList: (NSData *) predecessorChangeList
forMessageEntry: (NSMutableDictionary *) messageEntry
withOldChangeKey: (NSData *) oldChangeKey
{
NSData *globCnt, *oldGlobCnt;
NSDictionary *changeKeyDict;
NSString *guid;
NSMutableDictionary *changeList;
struct SizedXid *sizedXIDList;
struct XID xid, *givenChangeKey;
TALLOC_CTX *localMemCtx;
uint32_t i, length;
localMemCtx = talloc_new (NULL);
if (!localMemCtx)
{
[self errorWithFormat: @"No more memory"];
return;
}
if (predecessorChangeList)
{
sizedXIDList = [predecessorChangeList asSizedXidArrayInMemCtx: localMemCtx with: &length];
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
if (!changeList)
{
changeList = [NSMutableDictionary new];
[messageEntry setObject: changeList
forKey: @"PredecessorChangeList"];
[changeList release];
}
if (sizedXIDList) {
for (i = 0; i < length; i++)
{
xid = sizedXIDList[i].XID;
guid = [NSString stringWithGUID: &xid.NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid.LocalId.data length: xid.LocalId.length];
oldGlobCnt = [changeList objectForKey: guid];
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
[changeList setObject: globCnt forKey: guid];
}
}
}
if (oldChangeKey)
{
givenChangeKey = [oldChangeKey asXIDInMemCtx: localMemCtx];
if (givenChangeKey) {
guid = [NSString stringWithGUID: &givenChangeKey->NameSpaceGuid];
globCnt = [NSData dataWithBytes: givenChangeKey->LocalId.data length: givenChangeKey->LocalId.length];
changeKeyDict = [messageEntry objectForKey: @"ChangeKey"];
if (!changeKeyDict ||
([guid isEqualToString: [changeKeyDict objectForKey: @"GUID"]]
&& ([globCnt compare: [changeKeyDict objectForKey: @"LocalId"]] == NSOrderedDescending)))
{
/* The given change key is greater than current one stored in
metadata or it does not exist */
[messageEntry setObject: [NSDictionary dictionaryWithObjectsAndKeys: guid, @"GUID",
globCnt, @"LocalId",
nil]
forKey: @"ChangeKey"];
}
}
}
talloc_free (localMemCtx);
}
- (EOQualifier *) componentQualifier
{
if (!componentQualifier)
@@ -532,7 +465,8 @@ static Class NSNumberK;
// A GLOBCNT structure is a 6-byte global namespace counter,
// we strip the first 2 bytes. The first two bytes is the ReplicaId
changeKey = [self getReplicaKeyFromGlobCnt: newChangeNum >> 16];
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
[self _setChangeKey: changeKey forMessageEntry: messageEntry
inChangeListOnly: NO];
}
now = [NSCalendarDate date];
@@ -548,123 +482,13 @@ static Class NSNumberK;
return rc;
}
- (BOOL) synchroniseCacheFor: (NSString *) nameInContainer
{
/* Try to synchronise old messages in versions.plist cache using an
specific c_name. It returns a boolean indicating if the
synchronisation was carried out succesfully.
It should be used as last resort, keeping synchroniseCache as the
main sync entry point. */
uint64_t changeNumber;
NSString *changeNumberStr;
NSData *changeKey;
NSNumber *cLastModified, *cDeleted, *cVersion;
EOFetchSpecification *fs;
EOQualifier *searchQualifier, *fetchQualifier;
NSArray *fetchResults;
NSDictionary *result;
NSMutableDictionary *currentProperties, *messages, *mapping, *messageEntry;
GCSFolder *ocsFolder;
static NSArray *fields;
[versionsMessage reloadIfNeeded];
currentProperties = [versionsMessage properties];
messages = [currentProperties objectForKey: @"Messages"];
if (!messages)
{
messages = [NSMutableDictionary new];
[currentProperties setObject: messages forKey: @"Messages"];
[messages release];
}
messageEntry = [messages objectForKey: nameInContainer];
if (!messageEntry)
{
/* Fetch the message by its name */
if (!fields)
fields = [[NSArray alloc]
initWithObjects: @"c_name", @"c_version", @"c_lastmodified",
@"c_deleted", nil];
searchQualifier = [[EOKeyValueQualifier alloc] initWithKey: @"c_name"
operatorSelector: EOQualifierOperatorEqual
value: nameInContainer];
fetchQualifier = [[EOAndQualifier alloc]
initWithQualifiers: searchQualifier,
[self contentComponentQualifier],
nil];
[fetchQualifier autorelease];
[searchQualifier release];
ocsFolder = [sogoObject ocsFolder];
fs = [EOFetchSpecification
fetchSpecificationWithEntityName: [ocsFolder folderName]
qualifier: fetchQualifier
sortOrderings: nil];
fetchResults = [ocsFolder fetchFields: fields
fetchSpecification: fs
ignoreDeleted: NO];
if ([fetchResults count] == 1)
{
result = [fetchResults objectAtIndex: 0];
cLastModified = [result objectForKey: @"c_lastmodified"];
cDeleted = [result objectForKey: @"c_deleted"];
if ([cDeleted isKindOfClass: NSNumberK] && [cDeleted intValue])
cVersion = [NSNumber numberWithInt: -1];
else
cVersion = [result objectForKey: @"c_version"];
changeNumber = [[self context] getNewChangeNumber];
changeNumberStr = [NSString stringWithUnsignedLongLong: changeNumber];
/* Create new message entry in Messages dict */
messageEntry = [NSMutableDictionary new];
[messages setObject: messageEntry forKey: nameInContainer];
[messageEntry release];
/* Store cLastModified, cVersion and the change number */
[messageEntry setObject: cLastModified forKey: @"c_lastmodified"];
[messageEntry setObject: cVersion forKey: @"c_version"];
[messageEntry setObject: changeNumberStr forKey: @"version"];
/* Store the change key */
changeKey = [self getReplicaKeyFromGlobCnt: changeNumber >> 16];
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
/* Store the changeNumber -> cLastModified mapping */
mapping = [currentProperties objectForKey: @"VersionMapping"];
if (!mapping)
{
mapping = [NSMutableDictionary new];
[currentProperties setObject: mapping forKey: @"VersionMapping"];
[mapping release];
}
[mapping setObject: cLastModified forKey: changeNumberStr];
/* Save the message */
[versionsMessage save];
return YES;
}
else
return NO;
}
/* If message entry exists, then synchroniseCache did its job */
return YES;
}
- (void) updateVersionsForMessageWithKey: (NSString *) messageKey
withChangeKey: (NSData *) oldChangeKey
andPredecessorChangeList: (NSData *) pcl
withChangeKey: (NSData *) newChangeKey
{
NSMutableDictionary *messages, *messageEntry;
[self synchroniseCache];
if (oldChangeKey || pcl)
if (newChangeKey)
{
messages = [[versionsMessage properties] objectForKey: @"Messages"];
messageEntry = [messages objectForKey: messageKey];
@@ -672,8 +496,8 @@ static Class NSNumberK;
[NSException raise: @"MAPIStoreIOException"
format: @"no version record found for message '%@'",
messageKey];
[self _updatePredecessorChangeList: pcl forMessageEntry: messageEntry
withOldChangeKey: oldChangeKey];
[self _setChangeKey: newChangeKey forMessageEntry: messageEntry
inChangeListOnly: YES];
[versionsMessage save];
}
}
+13 -29
View File
@@ -180,8 +180,7 @@
{
uint64_t version = ULLONG_MAX;
NSString *changeNumber;
BOOL synced;
if (!isNew)
{
changeNumber = [(MAPIStoreGCSFolder *) container
@@ -190,28 +189,16 @@
{
[self warnWithFormat: @"attempting to get change number"
@" by synchronising folder..."];
synced = [(MAPIStoreGCSFolder *) container synchroniseCache];
if (synced)
[(MAPIStoreGCSFolder *) container synchroniseCache];
changeNumber = [(MAPIStoreGCSFolder *) container
changeNumberForMessageWithKey: [self nameInContainer]];
if (changeNumber)
[self logWithFormat: @"got one"];
else
{
changeNumber = [(MAPIStoreGCSFolder *) container
changeNumberForMessageWithKey: [self nameInContainer]];
}
if (!changeNumber)
{
[self warnWithFormat: @"attempting to get change number"
@" by synchronising this specific message..."];
synced = [(MAPIStoreGCSFolder *) container
synchroniseCacheFor: [self nameInContainer]];
if (synced)
{
changeNumber = [(MAPIStoreGCSFolder *) container
changeNumberForMessageWithKey: [self nameInContainer]];
}
if (!changeNumber)
{
[self errorWithFormat: @"still nothing. We crash!"];
abort();
}
[self errorWithFormat: @"still nothing. We crash!"];
abort();
}
}
version = [changeNumber unsignedLongLongValue] >> 16;
@@ -222,16 +209,13 @@
- (void) updateVersions
{
/* Update ChangeKey and PredecessorChangeList on message's save */
NSData *newChangeKey, *predecessorChangeList;
NSData *newChangeKey;
newChangeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
predecessorChangeList = [properties objectForKey: MAPIPropertyKey (PR_PREDECESSOR_CHANGE_LIST)];
[(MAPIStoreGCSFolder *) container
updateVersionsForMessageWithKey: [self nameInContainer]
withChangeKey: newChangeKey
andPredecessorChangeList: predecessorChangeList];
updateVersionsForMessageWithKey: [self nameInContainer]
withChangeKey: newChangeKey];
}
@end
+15 -4
View File
@@ -30,7 +30,6 @@
#import <SOGo/NSArray+Utilities.h>
#import <Mailer/SOGoMailBodyPart.h>
#import <Mailer/SOGoMailObject.h>
#import <Mailer/NSDictionary+Mail.h>
#import "MAPIStoreTypes.h"
#import "MAPIStoreMailMessage.h"
@@ -109,7 +108,7 @@
static char recordBytes[] = {0xd9, 0xd8, 0x11, 0xa3, 0xe2, 0x90, 0x18, 0x41,
0x9e, 0x04, 0x58, 0x46, 0x9d, 0x6d, 0x1b,
0x68};
*data = [[NSData dataWithBytes: recordBytes length: 16]
asBinaryInMemCtx: memCtx];
@@ -118,7 +117,19 @@
- (NSString *) _fileName
{
return [bodyInfo filename];
NSString *fileName;
NSDictionary *parameters;
fileName = [[bodyInfo objectForKey: @"parameterList"]
objectForKey: @"name"];
if (!fileName)
{
parameters = [[bodyInfo objectForKey: @"disposition"]
objectForKey: @"parameterList"];
fileName = [parameters objectForKey: @"filename"];
}
return fileName;
}
- (int) getPidTagAttachLongFilename: (void **) data
@@ -167,7 +178,7 @@
- (int) getPidTagAttachContentId: (void **) data
inMemCtx: (TALLOC_CTX *) memCtx
{
{
*data = [[bodyInfo objectForKey: @"bodyId"]
asUnicodeInMemCtx: memCtx];
+11 -13
View File
@@ -160,25 +160,23 @@ MakeDisplayFolderName (NSString *folderName)
for (count = 0; count < max; count++)
{
context = talloc_zero (memCtx, struct mapistore_contexts_list);
// secondaryFolders has the names (1) Imap4Encoded ,(2) asCSSIdentifier and (3) "folder"-prefixed
// e.g.: folderProbl&AOg-mes_SP_de_SP_synchronisation
// secondaryFolders has the names (1) Imap4Encoded and (2) asCSSIdentifier
// e.g.: Probl&AOg-mes_SP_de_SP_synchronisation
currentName = [secondaryFolders objectAtIndex: count];
// To get the real name we have to revert that (applying the decode functions
// in reverse order)
// To get the real name we have to revert that (applying the decode functions)
// in reverse order
// e.g.: Problèmes de synchronisation
realName = [[[currentName substringFromIndex: 6]
fromCSSIdentifier]
stringByDecodingImap4FolderName];
realName = [[currentName fromCSSIdentifier]
stringByDecodingImap4FolderName];
// And finally to represent that as URI we have to (1) asCSSIdentifier,
// (2) Imap4Encode (3) AddPercentEscapes and (4) add the "folder" prefix
// e.g.: folderProbl&AOg-mes_SP_de_SP_synchronisation
// (2) Imap4Encode and (3) AddPercentEscapes
// e.g.: Probl&AOg-mes_SP_de_SP_synchronisation
// In the example there are no percent escapes added because is already ok
stringData = [[[realName asCSSIdentifier]
stringByEncodingImap4FolderName]
stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
stringData = [NSString stringWithFormat: @"folder%@", stringData];
context->url = [[NSString stringWithFormat: @"%@%@", urlBase, stringData] asUnicodeInMemCtx: context];
context->name = [realName asUnicodeInMemCtx: context];
context->name = [[realName substringFromIndex: 6] asUnicodeInMemCtx: context];
context->main_folder = false;
context->role = MAPISTORE_MAIL_ROLE;
context->tag = "tag";
@@ -202,7 +200,7 @@ MakeDisplayFolderName (NSString *folderName)
andTDBIndexing: NULL];
accountFolder = [[userContext rootFolders] objectForKey: @"mail"];
folderName = [NSString stringWithFormat: @"folder%@",
[[newFolderName stringByEncodingImap4FolderName] asCSSIdentifier]];
[newFolderName asCSSIdentifier]];
newFolder = [SOGoMailFolder objectWithName: folderName
inContainer: accountFolder];
if ([newFolder create])
@@ -211,7 +209,7 @@ MakeDisplayFolderName (NSString *folderName)
withString: @"%40"],
[userName stringByReplacingOccurrencesOfString: @"@"
withString: @"%40"],
[folderName stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
[[folderName stringByEncodingImap4FolderName] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
else
mapistoreURI = nil;
-2
View File
@@ -52,8 +52,6 @@
- (NSString *) changeNumberForMessageUID: (NSString *) messageUid;
- (void) setChangeKey: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey;
- (BOOL) updatePredecessorChangeListWith: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey;
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey;
- (NSData *) predecessorChangeListForMessageWithKey: (NSString *) messageKey;
+39 -122
View File
@@ -68,8 +68,6 @@
static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
#include <gen_ndr/exchange.h>
#undef DEBUG
#include <util/attr.h>
#include <libmapi/libmapi.h>
@@ -111,7 +109,6 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
inContainer: dbFolder]);
[versionsMessage setObjectType: MAPIInternalCacheObject];
[versionsMessage reloadIfNeeded];
}
- (BOOL) ensureFolderExists
@@ -519,44 +516,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
return [modseq1 compare: modseq2];
}
- (void) _updatePredecessorChangeListWith: (NSData *) predecessorChangeList
forMessageEntry: (NSMutableDictionary *) messageEntry
{
NSData *globCnt, *oldGlobCnt;
NSMutableDictionary *changeList;
NSString *guid;
struct SizedXid *sizedXIDList;
struct XID xid;
uint32_t i, length;
sizedXIDList = [predecessorChangeList asSizedXidArrayInMemCtx: NULL with: &length];
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
if (!changeList)
{
changeList = [NSMutableDictionary new];
[messageEntry setObject: changeList
forKey: @"PredecessorChangeList"];
[changeList release];
}
if (sizedXIDList) {
for (i = 0; i < length; i++)
{
xid = sizedXIDList[i].XID;
guid = [NSString stringWithGUID: &xid.NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid.LocalId.data length: xid.LocalId.length];
oldGlobCnt = [changeList objectForKey: guid];
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
[changeList setObject: globCnt forKey: guid];
}
talloc_free (sizedXIDList);
}
[versionsMessage save];
}
- (void) _setChangeKey: (NSData *) changeKey
forMessageEntry: (NSMutableDictionary *) messageEntry
{
@@ -567,8 +526,8 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
NSMutableDictionary *changeList;
xid = [changeKey asXIDInMemCtx: NULL];
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
guid = [NSString stringWithGUID: &xid->GUID];
globCnt = [NSData dataWithBytes: xid->Data length: xid->Size];
talloc_free (xid);
/* 1. set change key association */
@@ -594,7 +553,8 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
{
BOOL rc = YES;
uint64_t newChangeNum;
NSNumber *ti, *modseq, *lastModseq, *nextModseq;
NSNumber *ti, *modseq, *initialLastModseq, *lastModseq,
*nextModseq;
NSString *changeNumber, *uid, *messageKey;
uint64_t lastModseqNbr;
EOQualifier *searchQualifier;
@@ -633,6 +593,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
}
lastModseq = [currentProperties objectForKey: @"SyncLastModseq"];
initialLastModseq = lastModseq;
if (lastModseq)
{
lastModseqNbr = [lastModseq unsignedLongLongValue];
@@ -716,37 +677,40 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
}
/* 2. we synchronise expunged UIDs */
fetchResults = [(SOGoMailFolder *) sogoObject
fetchUIDsOfVanishedItems: lastModseqNbr];
max = [fetchResults count];
changeNumber = nil;
for (count = 0; count < max; count++)
if (initialLastModseq)
{
uid = [[fetchResults objectAtIndex: count] stringValue];
if ([messages objectForKey: uid])
fetchResults = [(SOGoMailFolder *) sogoObject
fetchUIDsOfVanishedItems: lastModseqNbr];
max = [fetchResults count];
changeNumber = nil;
for (count = 0; count < max; count++)
{
if (!changeNumber)
uid = [[fetchResults objectAtIndex: count] stringValue];
if ([messages objectForKey: uid])
{
newChangeNum = [[self context] getNewChangeNumber];
changeNumber = [NSString stringWithUnsignedLongLong: newChangeNum];
if (!changeNumber)
{
newChangeNum = [[self context] getNewChangeNumber];
changeNumber = [NSString stringWithUnsignedLongLong: newChangeNum];
}
[messages removeObjectForKey: uid];
[self logWithFormat: @"Removed message entry for UID %@", uid];
}
else
{
[self logWithFormat:@"Message entry not found for UID %@", uid];
}
[messages removeObjectForKey: uid];
[self logWithFormat: @"Removed message entry for UID %@", uid];
}
else
if (changeNumber)
{
[self logWithFormat:@"Message entry not found for UID %@", uid];
[currentProperties setObject: changeNumber
forKey: @"SyncLastDeleteChangeNumber"];
[mapping setObject: lastModseq forKey: changeNumber];
foundChange = YES;
}
}
if (changeNumber)
{
[currentProperties setObject: changeNumber
forKey: @"SyncLastDeleteChangeNumber"];
[mapping setObject: lastModseq forKey: changeNumber];
foundChange = YES;
}
if (foundChange)
{
@@ -960,7 +924,8 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
return changeNumber;
}
- (NSMutableDictionary *) _messageEntryFromMessageKey: (NSString *) messageKey
- (void) setChangeKey: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey
{
NSMutableDictionary *messages, *messageEntry;
NSString *messageUid;
@@ -971,7 +936,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
messageEntry = [messages objectForKey: messageUid];
if (!messageEntry)
{
[self warnWithFormat: @"attempting to synchronise to get the message entry for "
[self warnWithFormat: @"attempting to synchronise to set the change key for "
@"this message %@", messageKey];
synced = [self synchroniseCacheForUID: messageUid];
if (synced)
@@ -982,57 +947,11 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
abort ();
}
}
return messageEntry;
}
- (void) setChangeKey: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey
{
[self _setChangeKey: changeKey
forMessageEntry: [self _messageEntryFromMessageKey: messageKey]];
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
[versionsMessage save];
}
- (BOOL) updatePredecessorChangeListWith: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey
{
/* Update predecessor change list property given the change key. It
returns if the change key has been added to the list or not */
BOOL added = NO;
NSData *globCnt, *oldGlobCnt;
NSDictionary *messageEntry;
NSMutableDictionary *changeList;
NSString *guid;
struct XID *xid;
xid = [changeKey asXIDInMemCtx: NULL];
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
talloc_free (xid);
messageEntry = [self _messageEntryFromMessageKey: messageKey];
if (messageEntry)
{
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
if (changeList)
{
oldGlobCnt = [changeList objectForKey: guid];
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
{
[changeList setObject: globCnt forKey: guid];
[versionsMessage save];
added = YES;
}
}
else
[self errorWithFormat: @"Missing predecessor change list to update"];
}
return added;
}
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey
{
NSDictionary *messages, *changeKeyDict;
@@ -1298,7 +1217,6 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
fromFolder: (MAPIStoreFolder *) sourceFolder
withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
@@ -1313,13 +1231,12 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
NSDictionary *result;
NSUInteger count;
NSArray *a;
NSData *changeList;
NSData *changeKey;
if (![sourceFolder isKindOfClass: [MAPIStoreMailFolder class]])
return [super moveCopyMessagesWithMIDs: srcMids andCount: midCount
fromFolder: sourceFolder withMIDs: targetMids
andChangeKeys: targetChangeKeys
andPredecessorChangeLists: targetPredecessorChangeLists
wantCopy: wantCopy
inMemCtx: memCtx];
@@ -1408,11 +1325,11 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
[self synchroniseCache];
for (count = 0; count < midCount; count++)
{
changeList = [NSData dataWithBinary: targetPredecessorChangeLists[count]];
changeKey = [NSData dataWithBinary: targetChangeKeys[count]];
messageKey = [NSString stringWithFormat: @"%@.eml",
[destUIDs objectAtIndex: count]];
[self _updatePredecessorChangeListWith: changeList
forMessageEntry: [self _messageEntryFromMessageKey: messageKey]];
[self setChangeKey: changeKey
forMessageWithKey: messageKey];
}
}
+28 -54
View File
@@ -40,7 +40,6 @@
#import <Mailer/NSData+Mail.h>
#import <Mailer/SOGoMailBodyPart.h>
#import <Mailer/SOGoMailObject.h>
#import <Mailer/NSDictionary+Mail.h>
#import "Codepages.h"
#import "NSData+MAPIStore.h"
@@ -197,7 +196,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
count1 = [keys indexOfObject: data1];
data2 = [entry2 objectForKey: @"mimeType"];
count2 = [keys indexOfObject: data2];
if (count1 == count2)
{
data1 = [entry1 objectForKey: @"key"];
@@ -530,7 +529,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
else
stringValue = @"";
*data = [stringValue asUnicodeInMemCtx: memCtx];
return MAPISTORE_SUCCESS;
}
@@ -625,7 +624,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
NSDictionary *coreInfos;
NSArray *flags;
unsigned int v = 0;
coreInfos = [sogoObject fetchCoreInfos];
flags = [coreInfos objectForKey: @"flags"];
@@ -637,7 +636,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
if ([[self attachmentKeys]
count] > 0)
v |= MSGFLAG_HASATTACH;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -657,7 +656,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
v = 2;
else
v = 0;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -669,15 +668,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
NSDictionary *coreInfos;
NSArray *flags;
unsigned int v;
coreInfos = [sogoObject fetchCoreInfos];
flags = [coreInfos objectForKey: @"flags"];
if ([flags containsObject: @"flagged"])
v = 6;
else
v = 0;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -756,7 +755,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
if ([ngAddress isKindOfClass: [NGMailAddress class]])
{
cn = [ngAddress displayName];
// If we don't have a displayName, we use the email address instead. This
// avoid bug #2119 - where Outlook won't display anything in the "From" field,
// nor in the recipient field if we reply to the email.
@@ -810,7 +809,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
entryId = MAPIStoreExternalEntryId (cn, email);
*data = [entryId asBinaryInMemCtx: memCtx];
rc = MAPISTORE_SUCCESS;
}
else
@@ -967,15 +966,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
{
uint32_t v;
NSString *s;
s = [[sogoObject mailHeaders] objectForKey: @"x-priority"];
v = 0x1;
if ([s hasPrefix: @"1"]) v = 0x2;
else if ([s hasPrefix: @"2"]) v = 0x2;
else if ([s hasPrefix: @"4"]) v = 0x0;
else if ([s hasPrefix: @"5"]) v = 0x0;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -1164,14 +1163,14 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
if (!headerSetup)
[self _fetchHeaderData];
if ([headerMimeType isEqualToString: @"text/plain"])
format = EDITOR_FORMAT_PLAINTEXT;
else if ([headerMimeType isEqualToString: @"text/html"])
format = EDITOR_FORMAT_HTML;
else
format = 0; /* EDITOR_FORMAT_DONTKNOW */
*data = MAPILongValue (memCtx, format);
return MAPISTORE_SUCCESS;
@@ -1518,7 +1517,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
p = 0;
recipient->data = talloc_array (msgData, void *, msgData->columns->cValues);
memset (recipient->data, 0, msgData->columns->cValues * sizeof (void *));
// PR_OBJECT_TYPE = MAPI_MAILUSER (see MAPI_OBJTYPE)
recipient->data[p] = MAPILongValue (msgData, MAPI_MAILUSER);
p++;
@@ -1526,7 +1525,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
// PR_DISPLAY_TYPE = DT_MAILUSER (see MS-NSPI)
recipient->data[p] = MAPILongValue (msgData, 0);
p++;
// PR_7BIT_DISPLAY_NAME_UNICODE
recipient->data[p] = [cn asUnicodeInMemCtx: msgData];
p++;
@@ -1534,7 +1533,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
// PR_SMTP_ADDRESS_UNICODE
recipient->data[p] = [email asUnicodeInMemCtx: msgData];
p++;
// PR_SEND_INTERNET_ENCODING = 0x00060000 (plain text, see OXCMAIL)
recipient->data[p] = MAPILongValue (msgData, 0x00060000);
p++;
@@ -1567,9 +1566,12 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
withPrefix: (NSString *) keyPrefix
{
NSArray *parts;
NSDictionary *parameters;
NSUInteger count, max;
if ([[bodyInfo filename] length] > 0)
parameters = [[bodyInfo objectForKey: @"disposition"]
objectForKey: @"parameterList"];
if ([[parameters objectForKey: @"filename"] length] > 0)
{
if ([keyPrefix length] == 0)
keyPrefix = @"0";
@@ -1643,13 +1645,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
return attachment;
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
- (int) setReadFlag: (uint8_t) flag
{
NSString *imapFlag = @"\\Seen";
/* TODO: notifications should probably be emitted from here */
if (flag & CLEAR_READ_FLAG)
[properties setObject: [NSNumber numberWithBool: NO] forKey: @"read_flag_set"];
[sogoObject removeFlags: imapFlag];
else
[properties setObject: [NSNumber numberWithBool: YES] forKey: @"read_flag_set"];
[sogoObject addFlags: imapFlag];
return MAPISTORE_SUCCESS;
}
@@ -1690,12 +1694,9 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
return nil;
}
- (void) save: (TALLOC_CTX *) memCtx
- (void) save: (TALLOC_CTX *) memCtx
{
BOOL modified = NO;
BOOL seen, storedSeenFlag;
NSNumber *value;
NSString *imapFlag = @"\\Seen";
value = [properties objectForKey: MAPIPropertyKey (PR_FLAG_STATUS)];
if (value)
@@ -1705,35 +1706,8 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
[sogoObject addFlags: @"\\Flagged"];
else /* 0: unflagged, 1: follow up complete */
[sogoObject removeFlags: @"\\Flagged"];
modified = YES;
}
/* Manage seen flag on save */
value = [properties objectForKey: @"read_flag_set"];
if (value)
{
seen = [value boolValue];
storedSeenFlag = [[[sogoObject fetchCoreInfos] objectForKey: @"flags"] containsObject: @"seen"];
/* We modify the flags anyway to generate a new change number */
if (seen)
{
if (storedSeenFlag)
[sogoObject removeFlags: imapFlag];
[sogoObject addFlags: imapFlag];
}
else
{
if (!storedSeenFlag)
[sogoObject addFlags: imapFlag];
[sogoObject removeFlags: imapFlag];
}
modified = YES;
}
if (modified)
[(MAPIStoreMailFolder *)[self container] synchroniseCache];
if (mailIsSharingObject)
[[self _sharingObject] saveWithMessage: self
andSOGoObject: sogoObject];
+7 -22
View File
@@ -161,30 +161,15 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK;
//[self logWithFormat: @"change number from oxcfxics: %.16lx", [value unsignedLongLongValue]];
//[self logWithFormat: @" modseq: %.16lx", [modseq unsignedLongLongValue]];
if (modseq)
{
if (res->relop == RELOP_GT)
modseq = [NSNumber numberWithUnsignedLongLong:
[modseq unsignedLongLongValue] + 1];
}
modseq = [NSNumber numberWithUnsignedLongLong:
[modseq unsignedLongLongValue] + 1];
else
modseq = [NSNumber numberWithUnsignedLongLong: 0];
if (res->relop == RELOP_GT || res->relop == RELOP_GE)
{
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"MODSEQ"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
value: modseq];
[*qualifier autorelease];
rc = MAPIRestrictionStateNeedsEval;
}
else
{
/* Ignore other operations as IMAP only support MODSEQ >= X */
[self warnWithFormat: @"Ignoring %@ as only supported operators are > and >=",
[self operatorFromRestrictionOperator: res->relop]];
rc = MAPIRestrictionStateAlwaysTrue;
}
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"MODSEQ"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
value: modseq];
[*qualifier autorelease];
rc = MAPIRestrictionStateNeedsEval;
}
break;
+15 -29
View File
@@ -34,7 +34,6 @@
#import <NGExtensions/NGBase64Coding.h>
#import <NGExtensions/NGHashMap.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSObject+Values.h>
#import <NGExtensions/NSString+Encoding.h>
#import <NGMime/NGMimeBodyPart.h>
#import <NGMime/NGMimeMultipartBody.h>
@@ -167,9 +166,9 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" };
- (BOOL) hasContentId
{
NSString *contentId = [properties
objectForKey: MAPIPropertyKey (PR_ATTACH_CONTENT_ID_UNICODE)];
return contentId && [contentId length] > 0;
return ([properties
objectForKey: MAPIPropertyKey (PR_ATTACH_CONTENT_ID_UNICODE)]
!= nil);
}
- (NGMimeBodyPart *) asMIMEBodyPart
@@ -233,7 +232,7 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" };
[map addObject: contentDisposition forKey: @"content-disposition"];
contentId = [properties
objectForKey: MAPIPropertyKey (PR_ATTACH_CONTENT_ID_UNICODE)];
if (contentId && [contentId length] > 0)
if (contentId)
[map setObject: [NSString stringWithFormat: @"<%@>", contentId]
forKey: @"content-id"];
bodyPart = [NGMimeBodyPart bodyPartWithHeader: map];
@@ -286,7 +285,7 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" };
version = [properties objectForKey: @"version"];
return (version
? [version unsignedLongLongValue]
? exchange_globcnt ([version unsignedLongLongValue])
: ULLONG_MAX);
}
@@ -1111,8 +1110,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
- (void) save: (TALLOC_CTX *) memCtx
{
BOOL updatedMetadata;
NSString *folderName, *flag, *newIdString, *messageKey, *changeNumber;
NSString *folderName, *flag, *newIdString, *messageKey;
NSData *changeKey, *messageData;
NGImap4Connection *connection;
NGImap4Client *client;
@@ -1148,33 +1146,21 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
[sogoObject setNameInContainer: messageKey];
[mapping registerURL: [self url] withID: mid];
/* synchronise the cache and update the predecessor change list
with the change key provided by the client. Before doing
this, lets issue a unselect/select combo because of timing
issues with Dovecot in obtaining the latest modseq.
Sometimes, Dovecot doesn't return the newly appended UID if
we do a "UID SORT (DATE) UTF-8 (MODSEQ XYZ) (NOT DELETED)"
command (where XYZ is the HIGHESTMODSEQ+1) immediately after
IMAP APPEND */
/* synchronise the cache and update the change key with the one provided
by the client. Before doing this, lets issue a unselect/select combo
because of timing issues with Dovecot in obtaining the latest modseq.
Sometimes, Dovecot doesn't return the newly appended UID if we do
a "UID SORT (DATE) UTF-8 (MODSEQ XYZ) (NOT DELETED)" command (where
XYZ is the HIGHESTMODSEQ+1) immediately after IMAP APPEND */
[client unselect];
[client select: folderName];
[(MAPIStoreMailFolder *) container synchroniseCache];
changeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
if (changeKey)
{
updatedMetadata = [(MAPIStoreMailFolder *) container updatePredecessorChangeListWith: changeKey
forMessageWithKey: messageKey];
if (!updatedMetadata)
[self warnWithFormat: @"Predecessor change list not updated with client data"];
}
/* Update version property (PR_CHANGE_KEY indeed) as it is
requested once it is saved */
changeNumber = [(MAPIStoreMailFolder *) container changeNumberForMessageUID: newIdString];
if (changeNumber)
[properties setObject: [NSNumber numberWithUnsignedLongLong: [changeNumber unsignedLongLongValue] >> 16]
forKey: @"version"];
[(MAPIStoreMailFolder *) container
setChangeKey: changeKey
forMessageWithKey: messageKey];
}
}
+1 -1
View File
@@ -63,7 +63,7 @@
withAID: (uint32_t) aid;
- (int) getAttachmentTable: (MAPIStoreAttachmentTable **) tablePtr
andRowCount: (uint32_t *) countPtr;
- (enum mapistore_error) setReadFlag: (uint8_t) flag;
- (int) setReadFlag: (uint8_t) flag;
- (enum mapistore_error) saveMessage: (TALLOC_CTX *) memCtx;
- (NSArray *) activeContainerMessageTables;
+2 -3
View File
@@ -549,12 +549,11 @@ rtf2html (NSData *compressedRTF)
}
[self save: memCtx];
/* We make sure that any change-related properties are removed from the
/* We make sure that any change-related properties are removes from the
properties dictionary, to make sure that related methods will be
invoked the next time they are requested. */
[properties removeObjectForKey: MAPIPropertyKey (PidTagChangeKey)];
[properties removeObjectForKey: MAPIPropertyKey (PidTagChangeNumber)];
[properties removeObjectForKey: MAPIPropertyKey (PidTagPredecessorChangeList)];
if ([container isKindOfClass: MAPIStoreFolderK])
{
@@ -919,7 +918,7 @@ rtf2html (NSData *compressedRTF)
return [self getNo: data inMemCtx: memCtx];;
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
- (int) setReadFlag: (uint8_t) flag
{
// [self subclassResponsibility: _cmd];
+1 -3
View File
@@ -674,7 +674,6 @@ sogo_folder_move_copy_messages(void *folder_object,
uint32_t mid_count,
uint64_t *src_mids, uint64_t *t_mids,
struct Binary_r **target_change_keys,
struct Binary_r **target_predecessor_change_lists,
uint8_t want_copy)
{
MAPIStoreFolder *sourceFolder, *targetFolder;
@@ -699,7 +698,6 @@ sogo_folder_move_copy_messages(void *folder_object,
fromFolder: sourceFolder
withMIDs: t_mids
andChangeKeys: target_change_keys
andPredecessorChangeLists: target_predecessor_change_lists
wantCopy: want_copy
inMemCtx: mem_ctx];
TRYCATCH_END(pool)
@@ -1120,7 +1118,7 @@ sogo_message_set_read_flag (void *message_object, uint8_t flag)
struct MAPIStoreTallocWrapper *wrapper;
NSAutoreleasePool *pool;
MAPIStoreMessage *message;
enum mapistore_error rc;
int rc;
if (message_object)
{
+1 -1
View File
@@ -531,7 +531,7 @@
}
[vToDo setTimeStampAsDate: now];
[sogoObject saveCalendar: vCalendar];
[sogoObject saveComponent: vCalendar];
[self updateVersions];
}
-2
View File
@@ -41,8 +41,6 @@
+ (id) dataWithXID: (const struct XID *) xid;
- (struct XID *) asXIDInMemCtx: (void *) memCtx;
- (struct SizedXid *) asSizedXidArrayInMemCtx: (void *) memCtx
with: (uint32_t *) length;
+ (id) dataWithChangeKeyGUID: (NSString *) guidString
andCnt: (NSData *) globCnt;
+6 -40
View File
@@ -22,7 +22,6 @@
#import <NGExtensions/NSObject+Logs.h>
#import "MAPIStoreTypes.h"
#import "NSObject+MAPIStore.h"
#import "NSString+MAPIStore.h"
@@ -30,7 +29,6 @@
#undef DEBUG
#include <stdbool.h>
#include <libmapi/libmapi.h>
#include <talloc.h>
#include <util/time.h>
#include <gen_ndr/exchange.h>
@@ -138,11 +136,11 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
NSMutableData *xidData;
struct FlatUID_r flatUID;
_fillFlatUIDWithGUID (&flatUID, &xid->NameSpaceGuid);
_fillFlatUIDWithGUID (&flatUID, &xid->GUID);
xidData = [NSMutableData dataWithCapacity: 16 + xid->LocalId.length];
xidData = [NSMutableData dataWithCapacity: 16 + xid->Size];
[xidData appendBytes: flatUID.ab length: 16];
[xidData appendBytes: xid->LocalId.data length: xid->LocalId.length];
[xidData appendBytes: xid->Data length: xid->Size];
return xidData;
}
@@ -158,12 +156,12 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
{
xid = talloc_zero (memCtx, struct XID);
[self _extractGUID: &xid->NameSpaceGuid];
[self _extractGUID: &xid->GUID];
xid->LocalId.length = max - 16;
xid->Size = max - 16;
bytes = (uint8_t *) [self bytes];
xid->LocalId.data = talloc_memdup (xid, (bytes+16), xid->LocalId.length);
xid->Data = talloc_memdup (xid, (bytes+16), xid->Size);
}
else
{
@@ -174,38 +172,6 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
return xid;
}
- (struct SizedXid *) asSizedXidArrayInMemCtx: (void *) memCtx
with: (uint32_t *) length
{
struct Binary_r bin;
struct SizedXid *sizedXIDArray;
bin.cb = [self length];
bin.lpb = (uint8_t *)[self bytes];
sizedXIDArray = get_SizedXidArray(memCtx, &bin, length);
if (!sizedXIDArray)
{
NSLog (@"Impossible to parse SizedXID array");
return NULL;
}
return sizedXIDArray;
}
- (NSComparisonResult) compare: (NSData *) otherGlobCnt
{
uint64_t globCnt = 0, oGlobCnt = 0;
if ([self length] > 0)
globCnt = *(uint64_t *) [self bytes];
if ([otherGlobCnt length] > 0)
oGlobCnt = *(uint64_t *) [otherGlobCnt bytes];
return MAPICNCompare (globCnt, oGlobCnt, NULL);
}
+ (id) dataWithChangeKeyGUID: (NSString *) guidString
andCnt: (NSData *) globCnt;
{
+27
View File
@@ -0,0 +1,27 @@
[![SOGo Logo](http://www.sogo.nu/fileadmin/sogo/logos/sogo.logo.png)](http://www.sogo.nu/)
Share your calendars, address books and mails in your community with a completely free and open source solution.
Let your Mozilla Thunderbird/Lightning, Microsoft Outlook, Apple iCal/iPhone, Android and BlackBerry users collaborate using a modern platform.
## What is SOGo?
SOGo is [fully supported](http://www.sogo.nu/support/commercial_support.html) and [trusted](http://www.sogo.nu/tour/testimonials.html) groupware server with a focus on scalability and open standards. SOGo is released under the GNU GPL/LGPL v2 and above.
SOGo provides a rich AJAX-based Web interface and supports multiple native clients through the use of standard protocols such as CalDAV, CardDAV and GroupDAV.
SOGo is the missing component of your infrastructure; it sits in the middle of your servers to offer your users an uniform and complete interface to access their information. It has been deployed in production environments where thousands of users are involved.
[![Sogo diagram](http://www.sogo.nu/uploads/pics/sogo.png)](http://www.sogo.nu/)
[Learn more...](http://www.sogo.nu/about/overview.html)
## Microsoft Outlook Compatible
SOGo offers native Microsoft Outlook compatibility using the [OpenChange](http://www.openchange.org/) backend developed by [Inverse](http://www.inverse.ca/). This means Microsoft Outlook 2003, 2007, 2010 and 2013 can talk directly to SOGo &ndash; just like if it was a Microsoft Exchange server. **No plugins are required in Outlook to make this work**.
The following screencast shows Outlook 2010 connected to SOGo as if it was an Exchange server, along the Web interface of SOGo.
[![SOGo Screencast](http://img.youtube.com/vi/1hcBSB4Kxww/0.jpg)](http://www.youtube.com/watch?v=1hcBSB4Kxww)
[![SOGo on GitHub](http://www.sogo.nu/fileadmin/images/icon.github.png)](https://github.com/inverse-inc/sogo/) [![SOGo Ohloh](http://www.sogo.nu/fileadmin/images/icon.ohloh.png)](https://www.ohloh.net/p/sogo) [![SOGo on Twitter](http://www.sogo.nu/fileadmin/images/icon.twitter.png)](http://twitter.com/scalableogo) [![SOGo on SourceForge](http://www.sogo.nu/fileadmin/images/icon.sourceforge.png)](http://sourceforge.net/projects/sogo-zeg) [![SOGo on free code](http://www.sogo.nu/fileadmin/images/icon.freecode.png)](https://freecode.com/projects/sogo-inverse) [![SOGo on LinkedIn](http://www.sogo.nu/fileadmin/images/icon.linkedin.png)](http://www.linkedin.com/groups/SOGo-Groupware-4164805)
+1 -1
View File
@@ -500,7 +500,7 @@ static NSTimeInterval ChannelCollectionTimer = 5 * 60;
ms = [NSMutableString stringWithCapacity: 256];
[ms appendFormat: @"<0x%p[%@]: ", self, NSStringFromClass ([self class])];
[ms appendFormat: @" #adaptors=%d", (int)[urlToAdaptor count]];
[ms appendFormat: @" #adaptors=%d", [urlToAdaptor count]];
[ms appendString: @">"];
return ms;
+1 -1
View File
@@ -414,7 +414,7 @@
if (group)
[str appendFormat: @"%@ (group: %@)\n", tag, group];
else
[str appendFormat: @"%@\n", tag];
[str appendFormat: @"%@\n", tag, group];
[str appendString: [self versitString]];
+1 -1
View File
@@ -409,7 +409,7 @@ static NGCardsSaxHandler *sax = nil;
max = [children count];
if (max > 0)
{
[str appendFormat: @"\n %d children: {\n", (int)[children count]];
[str appendFormat: @"\n %d children: {\n", [children count]];
for (count = 0; count < max; count++)
[str appendFormat: @" %@\n",
[[children objectAtIndex: count] description]];
+5 -5
View File
@@ -32,16 +32,16 @@
- (NSString *) iCalFormattedDateTimeString
{
return [NSString stringWithFormat: @"%.4d%.2d%.2dT%.2d%.2d%.2d",
(int)[self yearOfCommonEra], (int)[self monthOfYear],
(int)[self dayOfMonth], (int)[self hourOfDay],
(int)[self minuteOfHour], (int)[self secondOfMinute]];
[self yearOfCommonEra], [self monthOfYear],
[self dayOfMonth], [self hourOfDay],
[self minuteOfHour], [self secondOfMinute]];
}
- (NSString *) iCalFormattedDateString
{
return [NSString stringWithFormat: @"%.4d%.2d%.2d",
(int)[self yearOfCommonEra], (int)[self monthOfYear],
(int)[self dayOfMonth]];
[self yearOfCommonEra], [self monthOfYear],
[self dayOfMonth]];
}
@end
-15
View File
@@ -158,31 +158,16 @@
NSMutableString *string;
unsigned int len, i;
unichar c;
BOOL isQuoted;
len = [self length];
string = [NSMutableString stringWithCapacity: len * 1.5];
isQuoted = NO;
for (i = 0; i < len; i++)
{
c = [self characterAtIndex: i];
if (isQuoted)
{
if (c == '"')
isQuoted = NO;
[string appendFormat: @"%C", c];
continue;
}
switch (c)
{
case '"':
isQuoted = YES;
[string appendFormat: @"%C", c];
break;
case '\\':
[string appendString: @"\\\\"];
break;
+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;
+33 -20
View File
@@ -27,7 +27,6 @@
#import "NSCalendarDate+NGCards.h"
#import "iCalAlarm.h"
#import "iCalCalendar.h"
#import "iCalDateTime.h"
#import "iCalEntityObject.h"
#import "iCalEvent.h"
@@ -269,12 +268,9 @@
- (void) setRecurrenceId: (NSCalendarDate *) newRecId
{
iCalDateTime* recurrenceId;
BOOL isMasterAllDay;
isMasterAllDay = [[[[self parent] events] objectAtIndex: 0] isAllDay];
recurrenceId = (iCalDateTime *) [self uniqueChildWithTag: @"recurrence-id"];
if ([self isKindOfClass: [iCalEvent class]] && isMasterAllDay)
if ([self isKindOfClass: [iCalEvent class]] && [(iCalEvent *)self isAllDay])
[recurrenceId setDate: newRecId];
else
[recurrenceId setDateTime: newRecId];
@@ -404,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
@@ -348,7 +348,7 @@ static inline unsigned iCalDoWForNSDoW (int dow)
if ([byDayMask occursOnDay: currentWeekDay])
{
if ([bySetPos containsObject:
[NSString stringWithFormat: @"%d", (int)currentPos]])
[NSString stringWithFormat: @"%d", currentPos]])
monthDays[monthDay+1] = YES;
currentPos++;
}
@@ -362,7 +362,7 @@ static inline unsigned iCalDoWForNSDoW (int dow)
if ([byDayMask occursOnDay: currentWeekDay])
{
if ([bySetPos containsObject:
[NSString stringWithFormat: @"%d", (int)currentPos]])
[NSString stringWithFormat: @"%d", currentPos]])
monthDays[monthDay] = YES;
currentPos--;
}
+2
View File
@@ -44,6 +44,8 @@ typedef enum {
@interface iCalPerson : CardElement
+ (NSString *) descriptionForParticipationStatus: (iCalPersonPartStat) _status;
/* accessors */
- (void)setCn:(NSString *)_s;
+46 -35
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];
@@ -256,7 +267,7 @@
- (BOOL)isEqual:(id)_other {
if(_other == nil)
return NO;
if([_other class] != object_getClass(self))
if([_other class] != self->isa)
return NO;
if([_other hash] != [self hash])
return NO;
+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;
+2 -2
View File
@@ -309,10 +309,10 @@ NSString *iCalWeekDayString[] = { @"SU", @"MO", @"TU", @"WE", @"TH", @"FR",
else if ([frequency isEqualToString:@"SECONDLY"])
freq = iCalRecurrenceFrequenceSecondly;
else
freq = (iCalRecurrenceFrequency)NSNotFound;
freq = NSNotFound;
}
else
freq = (iCalRecurrenceFrequency)NSNotFound;
freq = NSNotFound;
return freq;
}
+3 -2
View File
@@ -1,6 +1,6 @@
/* iCalTimeZone.m - this file is part of SOPE
*
* Copyright (C) 2006-2014 Inverse inc.
* Copyright (C) 2006-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -147,7 +147,8 @@ static NSArray *knownTimeZones;
if (ext.location != NSNotFound)
{
zone = [zone substringToIndex: ext.location];
[timeZoneNames addObject: zone];
if (![timeZoneNames containsObject: zone])
[timeZoneNames addObject: zone];
}
}
}
+4 -36
View File
@@ -161,11 +161,8 @@
minute: [tzStart minuteOfHour] second: 0
timeZone: [NSTimeZone timeZoneWithName: @"GMT"]];
tmpDate = [tmpDate addYear: 0
month: ((pos > 0) ? 0 : 1)
day: 0
hour: 0
minute: 0
tmpDate = [tmpDate addYear: 0 month: ((pos > 0) ? 0 : 1)
day: 0 hour: 0 minute: 0
second: 0];
/* If the day of the time change is "-XSU", we need to determine whether the
@@ -200,41 +197,12 @@
END:STANDARD
END:VTIMEZONE
The time changes occur on a Sunday, but in March, the 1st is a Sunday itself and in November
The time changes occure on a Sunday, but in March, the 1st is a Sunday itself and in November
the 1st is also a Sunday. If we don't decrement "pos" by one, tmpDate (which is set to March or November 1st
because of "day: 1" will have 14 more days added for March and 7 more days added for November - which will
effectively shift the time change by a whole week.
In Europe/Berlin, we have a different use-case for November. In 2015, November 1st is a Sunday.
The time change in November must occur on October 25th but since tmpDate will be November 1st,
so a Sunday, dateDayOfWeek will be 0 and dayOfWeek will also be 0 we would decrement tmpDate by 14 days,
which is incorrect because it would shift the timezone change one week earlier. We take care about this
one with check if pos is greater or equal than 0 and if so, we don't decrement it.
BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
*/
if (dayOfWeek == dateDayOfWeek && pos >= 0)
if (dayOfWeek == dateDayOfWeek)
pos--;
offset = (dayOfWeek - dateDayOfWeek) + (pos * 7);
+1
View File
@@ -39,6 +39,7 @@
- (NSString *) percentComplete;
- (void) setDue: (NSCalendarDate *) _date;
- (void) setAllDayDue: (NSCalendarDate *) _date;
- (NSCalendarDate *) due;
- (void) setCompleted: (NSCalendarDate *) _date;
+6
View File
@@ -65,6 +65,12 @@
setDateTime: newDueDate];
}
- (void) setAllDayDue: (NSCalendarDate *) newDueDate
{
[(iCalDateTime *) [self uniqueChildWithTag: @"due"]
setDate: newDueDate];
}
- (NSCalendarDate *) due
{
return [(iCalDateTime *) [self uniqueChildWithTag: @"due"]
+2
View File
@@ -34,6 +34,8 @@
- (NSCalendarDate *) nextAlarmDate;
- (NSDictionary *) asDictionary;
@end
#endif /* __NGCards_iCalTrigger_H__ */
+74
View File
@@ -20,7 +20,9 @@
*/
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
#import "iCalEvent.h"
#import "iCalTrigger.h"
@@ -52,6 +54,78 @@
return [self value: 0 ofAttribute: @"related"];
}
- (NSDictionary *) asDictionary
{
NSDictionary *data;
NSString *duration, *relation, *reference, *quantity, *unit;
NSUInteger i;
unichar c;
data = nil;
if (![[self valueType] length] ||
[[self valueType] caseInsensitiveCompare: @"DURATION"] == NSOrderedSame)
{
relation = [[self relationType] uppercaseString];
duration = [self flattenedValuesForKey: @""];
i = 0;
c = [duration characterAtIndex: i];
if (c == '-')
{
reference = @"BEFORE";
i++;
}
else
{
reference = @"AFTER";
}
c = [duration characterAtIndex: i];
if (c == 'P')
{
quantity = @"";
unit = @"";
// Parse duration -- ignore first character (P)
for (i++; i < [duration length]; i++)
{
c = [duration characterAtIndex: i];
if (c == 't' || c == 'T')
// time -- ignore character
continue;
else if (isdigit (c))
quantity = [quantity stringByAppendingFormat: @"%c", c];
else
{
switch (c)
{
case 'D': /* day */
unit = @"DAYS";
break;
case 'H': /* hour */
unit = @"HOURS";
break;
case 'M': /* min */
unit = @"MINUTES";
break;
case 'W': /* week */
unit = @"WEEKS";
break;
default:
NSLog(@"Cannot process duration unit: '%c'", c);
break;
}
}
}
}
data = [NSDictionary dictionaryWithObjectsAndKeys:
relation, @"relation",
reference, @"reference",
[NSNumber numberWithInt: [quantity intValue]], @"quantity",
unit, @"unit",
nil];
}
return data;
}
- (NSCalendarDate *) nextAlarmDate
{
NSCalendarDate *relationDate, *nextAlarmDate;
@@ -1045,7 +1045,7 @@ static NSCharacterSet *whitespaceCharSet = nil;
if (debugOn)
{
NSLog(@"%s: trying to decode data (0x%p,len=%d) ...",
__PRETTY_FUNCTION__, _data, (int)[_data length]);
__PRETTY_FUNCTION__, _data, [_data length]);
}
if ((len = [_data length]) == 0)
@@ -1164,7 +1164,7 @@ static NSCharacterSet *whitespaceCharSet = nil;
if (debugOn)
{
NSLog(@"%s: trying to parse string (0x%p,len=%d) ...",
__PRETTY_FUNCTION__, _source, (int)[_source length]);
__PRETTY_FUNCTION__, _source, [_source length]);
}
if (!_sysId) _sysId = @"<string>";
[self _parseString: _source];
+309
View File
@@ -0,0 +1,309 @@
#!/usr/bin/env python
import getopt
import imaplib
import ldb
import plistlib
import os
import re
import shutil
import subprocess
import sys
from samba.param import LoadParm
imaphost = '127.0.0.1'
imapport = 143
samba_lp = LoadParm()
sambaprivate = samba_lp.get("private dir")
mapistorefolder = samba_lp.private_path("mapistore")
sogoSysDefaultsFile = "/etc/sogo/sogo.conf"
sogoUserDefaultsFile = os.path.expanduser("~sogo/GNUstep/Defaults/.GNUstepDefaults")
# - takes a username and optionally its password
# - removes the entry in samba's ldap tree via ldbedit (NOTYET)
# - remove the user's directory under mapistore/ and mapistore/SOGo
# - cleanup Junk Folders and Sync Issues imap folders
# - Delete the sogo_cache_folder_ table for the username.
def usage():
print """
%s [-i imaphost] ] [-p imapport] [-s sambaprivate] username [password]
-i imaphost IMAP host to connect to [%s]
-p imappost IMAP port to use [%d]
-s sambaprivate samba private directory [%s]
""" % (os.path.basename(sys.argv[0]), imaphost, imapport, sambaprivate)
def main():
global sambaprivate
global mapistorefolder
global imaphost
global imapport
try:
opts, args = getopt.getopt(sys.argv[1:], "i:p:s:")
except getopt.GetoptError, err:
print str(err)
usage()
sys.exit(2)
for o, a in opts:
if o == "-i":
imaphost = a
elif o == "-p":
imapport = a
elif o == "-s":
sambaprivate = a
mapistorefolder = "%s/mapistore" % (sambaprivate)
else:
assert False, "unhandled option"
argslen = len(args)
if (argslen == 2):
username = args[0]
userpass = args[1]
elif (argslen == 1):
username = args[0]
userpass = username
print "Using username as password"
else:
usage()
print "Specify a user (and optionally the password)"
sys.exit(2)
# cleanup starts here
try:
imapCleanup(imaphost, imapport, username, userpass)
except Exception as e:
print "Error during imapCleanup, continuing: %s" % str(e)
try:
mapistoreCleanup(mapistorefolder, username)
except (shutil.Error, OSError) as e:
print "Error during mapistoreCleanup, continuing: %s" % str(e)
try:
ldbCleanup(sambaprivate, username)
except ldb.LdbError as e:
print "Error during ldbCleanup, continuing: %s" % str(e)
try:
sqlCleanup(username)
except Exception as e:
print "Error during sqlCleanup, continuing: %s" % str(e)
def getsep(client):
seq = None
(code, data) = client.list("", "")
if code == "OK" and data is not None:
# yes this is ugly but it works on cyrus and dovecot.
# (\\Noinferiors \\HasNoChildren) "/" INBOX
m = re.search(".*\s+[\"\']?(.)[\"\']?\s+[\"\']?.*[\"\']?$", data[0])
sep = m.group(1)
return sep
def extractmb(si):
inparen = False
inquote = False
part = []
parts = []
for char in si:
if inparen:
if char == ")":
inparen = False
parts.append("".join(part))
else:
part.append(char)
elif inquote:
if char == "\"":
inquote = False
parts.append("".join(part))
else:
part.append(char)
else:
if char == "(":
inparen = True
elif char == "\"":
inquote = True
elif char == " ":
part = []
else:
part.append(char)
return parts[-1]
def cleanupmb(mb, sep, client):
(code, data) = client.list("%s%s" % (mb, sep), "%")
if code == "OK":
for si in data:
if si is not None:
submb = extractmb(si)
cleanupmb(submb, sep, client)
else:
raise Exception, "Failure while cleaning up '%s'" % mb
client.unsubscribe(mb)
(code, data) = client.delete(mb)
if code == "OK":
print "mailbox '%s' deleted" % mb
else:
print "mailbox '%s' coult NOT be deleted (code = '%s')" % (mb, code)
def imapCleanup(imaphost, imapport, username, userpass):
print "Starting IMAP cleanup"
client = imaplib.IMAP4(imaphost, imapport)
(code, data) = client.login(username, userpass)
if code != "OK":
raise Exception, "Login failure"
print "Logged in as '%s'" % username
sep = getsep(client)
if not sep:
client.logout()
return
for foldername in ("Sync Issues", "Junk E-mail",
"INBOX%sSync Issues" % sep, "INBOX%sJunk E-mail" % sep,
"Probl&AOg-mes de synchronisation"):
(code, data) = client.list(foldername, "%")
if code == "OK":
for si in data:
if si is not None:
mb = extractmb(si)
cleanupmb(mb, sep, client)
client.logout()
def mapistoreCleanup(mapistorefolder, username):
print "Starting MAPIstore cleanup"
# delete the user's folder under the mapistore and under mapistore/SOGo
mapistoreUserDir = "%s/%s" % (mapistorefolder, username)
for dirpath, dirnames, filenames in os.walk(mapistoreUserDir):
for f in filenames:
if f != "password":
os.unlink("%s/%s" % (dirpath,f))
break #one level only
shutil.rmtree("%s/SOGo/%s" % (mapistorefolder, username), ignore_errors=True)
def ldbCleanup(sambaprivate, username):
conn = ldb.Ldb("%s/openchange.ldb" % (sambaprivate))
# find the DN of the user
entries = conn.search(None, expression="(cn=%s)" % (username), scope=ldb.SCOPE_SUBTREE)
if not entries:
print "cn = %s not found in openchange.ldb" %(username)
return
for entry in entries:
# search again, but with the user's DN as a base
subentries = conn.search(entry.dn.extended_str(), expression="(distinguishedName=*)", scope=ldb.SCOPE_SUBTREE)
for subentry in subentries:
print "Deleting %s" % (subentry.dn)
conn.delete(subentry.dn)
def mysqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username):
try:
import MySQLdb
except ImportError:
msg ="""The python 'MySQLdb' module is not available
On Debian based distro, install it using 'apt-get install python-mysqlbd'
On RHEL, install it using 'yum install MySQL-python'"""
raise Exception(msg)
conn = MySQLdb.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, db=dbname)
c=conn.cursor()
tablename="sogo_cache_folder_%s" % (username)
c.execute("TRUNCATE TABLE %s" % tablename)
print "Table %s emptied" % tablename
def postgresqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username):
try:
import pg
except ImportError:
msg ="""The python 'pg' module is not available
On Debian based distro, install it using 'apt-get install python-pygresql'
On RHEL, install it using 'yum install python-pgsql'"""
raise Exception(msg)
conn = pg.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, dbname=dbname)
tablename = "sogo_cache_folder_%s" % username
conn.query("DELETE FROM %s" % tablename)
print "Table '%s' emptied" % tablename
def getOCSFolderInfoURL():
global sogoSysDefaultsFile, sogoUserDefaultsFile
OCSFolderInfoURL = ""
# read defaults from defaults files
# order is important, user defaults must have precedence
for f in [sogoSysDefaultsFile, sogoUserDefaultsFile]:
if os.path.exists(f):
# FIXME: this is ugly, we should have a python plist parser
# plistlib only supports XML plists.
# the following magic replaces this shell pipeline:
# sogo-tool dump-defaults -f %s | awk -F\\" '/ OCSFolderInfoURL =/ {print $2}'
p1 = subprocess.Popen(["sogo-tool", "dump-defaults", "-f", f], stdout=subprocess.PIPE)
p2 = subprocess.Popen(["awk", "-F\"", "/ OCSFolderInfoURL =/ {print $2}"], stdin=p1.stdout, stdout=subprocess.PIPE)
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
tmp = p2.communicate()[0]
if tmp: OCSFolderInfoURL = tmp
return OCSFolderInfoURL
def asCSSIdentifier(inputString):
cssEscapingCharMap = {"_" : "_U_",
"." : "_D_",
"#" : "_H_",
"@" : "_A_",
"*" : "_S_",
":" : "_C_",
"," : "_CO_",
" " : "_SP_",
"'" : "_SQ_",
"&" : "_AM_",
"+" : "_P_"}
newChars = []
for c in inputString:
if c in cssEscapingCharMap:
newChars.append(cssEscapingCharMap[c])
else:
newChars.append(c)
return "".join(newChars)
def sqlCleanup(username):
print "Starting SQL cleanup"
OCSFolderInfoURL = getOCSFolderInfoURL()
if OCSFolderInfoURL is None:
raise Exception("Couldn't fetch OCSFolderInfoURL or it is not set. the sogo_cache_folder_%s table should be truncated manually" % (username))
# postgresql://sogo:sogo@127.0.0.1:5432/sogo/sogo_folder_info
m = re.search("(.+)://(.+):(.+)@(.+):(\d+)/(.+)/(.+)", OCSFolderInfoURL)
if not m:
raise Exception("Unable to parse OCSFolderInfoURL: %s" % OCSFolderInfoURL)
proto = m.group(1)
dbuser = m.group(2)
dbpass = m.group(3)
dbhost = m.group(4)
dbport = m.group(5)
dbname = m.group(6)
# 7 is folderinfo table
encodedUserName = asCSSIdentifier(username)
if (proto == "postgresql"):
postgresqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, encodedUserName)
elif (proto == "mysql"):
mysqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, encodedUserName)
else:
raise Exception("Unknown sql proto: %s" % (proto))
if __name__ == "__main__":
main()
View File
File mode changed.
View File
File mode changed.
View File
File mode changed.
View File
File mode changed.
Executable → Regular
View File
File mode changed.
@@ -6,6 +6,7 @@ vevent_class2 = "(حدث سري)";
vtodo_class0 = "(مهمة عامة)";
vtodo_class1 = "(مهمة خاصة)";
vtodo_class2 = "(مهمة سرية)";
/* Receipts */
"The event \"%{Summary}\" was created" = "أُنشِئ الحدث \"%{Summary}\"";
"The event \"%{Summary}\" was deleted" = "حُذِفَ الحدث \"%{Summary}\"";
@@ -13,6 +14,7 @@ vtodo_class2 = "(مهمة سرية)";
"The following attendees(s) were notified" = "تم ابلاغ المدعو (المدعويين) الأتي أسماءهم";
"The following attendees(s) were added" = "تم أضافة المدعو (المدعويين) الأتي أسماءهم";
"The following attendees(s) were removed" = "تم مسح المدعو (المدعويين) الأتي أسماءهم";
/* IMIP messages */
"calendar_label" = "التقويم";
"startDate_label" = "البداية";
@@ -21,17 +23,20 @@ vtodo_class2 = "(مهمة سرية)";
"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}\n";
"%{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}\n";
/* 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"
= "موعد \"%{Summary}\" بتاريخ %{OldStartDate} قد تغير";
@@ -41,6 +46,7 @@ vtodo_class2 = "(مهمة سرية)";
= " لقد تغيرت المحددات التالية في الاجتماع \"%{Summary}\":";
"Please accept or decline those changes."
= "يرجى قبول أو رفض تلك التغييرات.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "دعوةٌ مقبولة: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "دعوةٌ مرفوضة: \"%{Summary}\"";
@@ -54,6 +60,7 @@ vtodo_class2 = "(مهمة سرية)";
= "وقد فوض%{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}." = "الحد الأقصى لعدد الحجوزات في وقت واحد (%{NumberOfSimultaneousBookings}) وصلت للموارد \"%{Cn} %{SystemEmail}\" . الحدث المتضارب في الموعد هو \"٪ {EventTitle}\"، ويبدأ فى٪ {StartDate}.";
@@ -7,6 +7,7 @@ vevent_class2 = "(Isilpeko ekitaldia)";
vtodo_class0 = "(Zeregin publikoa)";
vtodo_class1 = "(Zeregin pribatua)";
vtodo_class2 = "(Isilpeko egitekoa)";
/* Receipts */
"The event \"%{Summary}\" was created" = "\"%{Summary}\" ekitaldia sortu da";
"The event \"%{Summary}\" was deleted" = "\"%{Summary}\" ekitaldia ezabatu da";
@@ -14,6 +15,7 @@ vtodo_class2 = "(Isilpeko egitekoa)";
"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";
@@ -22,17 +24,20 @@ vtodo_class2 = "(Isilpeko egitekoa)";
"location_label" = "Kokapena";
"summary_label" = "Laburpena:";
"comment_label" = "Iruzkina:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Ekitaldirako gonbidapena: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(%{SentBy}-k bidalia) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}-k gonbidatu zaitu %{Summary}.-ra\n\nHasiera: %{StartDate}\nAmaiera: %{EndDate}\nDeskribapena: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}-k gonbidatu zaitu %{Summary}-ra \n\nHasiera: %{StartDate}-an, %{StartTime}-tan\nEnd: %{EndDate}-an, %{EndTime}-tan\nDeskribapena: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Ekitaldia bertan behera utzi da: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} ekitaldi hau bertan behera utzi du: %{Summary}.\n\nHasiera: %{StartDate}\nAmaiera: %{EndDate}\nDEskribapena: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}-k honako ekitaldia bertan behera utzi du: %{Summary}.\n\nHasiera: %{StartDate}-an, %{StartTime}-tan\nAmaiera: %{EndDate}-an %{EndTime}-tan\nDeskribapena: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "%{OldStartDate} eguneko \"%{Summary}\" hitzordua aldatu da";
@@ -42,6 +47,7 @@ vtodo_class2 = "(Isilpeko egitekoa)";
= " \"%{Summary}\" bilerako ondorengo parametroak aldatu dira:";
"Please accept or decline those changes."
= "Mesedez, onartu edo ezetsi ondorengo aldaketak.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Gonbidapena onartua: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Ezetzitako gonbidapena: \"%{Summary}\"";
@@ -55,6 +61,7 @@ vtodo_class2 = "(Isilpeko egitekoa)";
= "%{Attendee} %{SentByText}-k zure gonbidapena %{Delegate}-ri delegatu dio.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}-k ez du oraindik zure gonbidapenari buruz erabakirik hartu.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Ezin da honako baliabidea atzitu: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "\"%{Cn} %{SystemEmail}\" baliabidearentzako gehienezko erreserba kopurura (%{NumberOfSimultaneousBookings}) iritsi gara. Gatazka sortu duen ekitaldia \"%{EventTitle}\" da, eta %{StartDate}-n hasten da.";
@@ -7,6 +7,7 @@ vevent_class2 = "(Evento Confidencial)";
vtodo_class0 = "(Tarefa Pública)";
vtodo_class1 = "(Tarefa Privada)";
vtodo_class2 = "(Tarefa Confidencial)";
/* Receipts */
"The event \"%{Summary}\" was created" = "O evento \"%{Summary}\" foi criado";
"The event \"%{Summary}\" was deleted" = "O evento \"%{Summary}\" foi removido";
@@ -14,25 +15,29 @@ vtodo_class2 = "(Tarefa Confidencial)";
"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";
"due_label" = "Data de Vencimento:";
"location_label" = "Localização";
"location_label" = "Local";
"summary_label" = "Resumo:";
"comment_label" = "Comentário:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Convite do Evento: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(enviado por %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}convidou você para %{Summary}.\n\nInicio: %{StartDate}\nFim: %{EndDate}\nDescrição: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} convidou você para %{Summary}.\n\nInício: %{StartDate} as %{StartTime}\nFim: %{EndDate} as %{EndTime}\nDescrição: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Evento Cancelado: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}cancelou este evento: %{Summary}.\n\nInicio: %{StartDate}\nFim: %{EndDate}\nDescrição: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} cancelou este evento: %{Summary}.\n\nInício: %{StartDate} as %{StartTime}\nFim: %{EndDate} as %{EndTime}\nDescrição: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "O compromisso \"%{Summary}\" de %{OldStartDate} mudou";
@@ -42,6 +47,7 @@ vtodo_class2 = "(Tarefa Confidencial)";
= "Os seguintes parâmetros mudaram na reunião \"%{Summary}\" :\n\n";
"Please accept or decline those changes."
= "Por favor, aceitar ou recusar as alterações.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Convite aceito: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Convite recusado: \"%{Summary}\"";
@@ -55,6 +61,7 @@ vtodo_class2 = "(Tarefa Confidencial)";
= "%{Attendee} %{SentByText} delegou o convite para %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}foi ainda não decidiu seu convite ao evento.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Não foi possível acessar o recurso: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "O número máximo de reservas simultaneas (%{NumberOfSimultaneousBookings}) acabou para o recurso \"%{Cn} %{SystemEmail}\". O evento conflitante é \"%{EventTitle}\", e inicia em %{StartDate}.";
@@ -7,32 +7,37 @@ vevent_class2 = "(Esdeveniment confidencial)";
vtodo_class0 = "(Tasca pública)";
vtodo_class1 = "(Tasca privada)";
vtodo_class2 = "(Tasca confidencial)";
/* Receipts */
"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" = "S'han afegit els 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";
"due_label" = "Data de venciment";
"due_label" = "Data límit:";
"location_label" = "Lloc";
"summary_label" = "Resum:";
"comment_label" = "Comentari:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Invitació a l'esdeveniment: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(enviada per %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText} us ha invitat a %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} us ha invitat a %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Esdeveniment suspès: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}ha suspès aquest esdeveniment: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}ha suspès aquest esdeveniment: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "La cita \"%{Summary}\" per al dia %{OldStartDate} ha canviat";
@@ -42,6 +47,7 @@ vtodo_class2 = "(Tasca confidencial)";
= "Els paràmetres següents han canviat en la reunió \"%{Summary}\":";
"Please accept or decline those changes."
= "Si us plau, accepteu o rebutgeu aquests canvis.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Invitació acceptada: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Invitació rebutjada; \"%{Summary}\"";
@@ -55,6 +61,7 @@ vtodo_class2 = "(Tasca confidencial)";
= "%{Attendee} %{SentByText}ha delegat la invitació en %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}encara no s'ha decidit sobre la invitació a l'esdeveniment.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "No es pot accedir al recurs: \"%{Cn} %{SystemEmail}\" ";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Nombre màxim de reserves simultànies (%{NumberOfSimultaneousBookings}) assolit per al recurs \"%{Cn} %{SystemEmail}\". L'esdeveniment que entra en conflicte és \"%{EventTitle}\", i comença el %{StartDate}.";
@@ -7,6 +7,7 @@ vevent_class2 = "(機密事件)";
vtodo_class0 = "(公開任務)";
vtodo_class1 = "(私人任務)";
vtodo_class2 = "(機密任務)";
/* Receipts */
"The event \"%{Summary}\" was created" = "事件 \"%{Summary}\" 已建立";
"The event \"%{Summary}\" was deleted" = "事件\"%{Summary}\" 已刪除";
@@ -14,6 +15,7 @@ vtodo_class2 = "(機密任務)";
"The following attendees(s) were notified" = "已通知下列出席者";
"The following attendees(s) were added" = "已增加下列出席者";
"The following attendees(s) were removed" = "已移除下列出席者";
/* IMIP messages */
"calendar_label" = "行事曆";
"startDate_label" = "開始";
@@ -22,17 +24,20 @@ vtodo_class2 = "(機密任務)";
"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}\" 已變更";
@@ -42,6 +47,7 @@ vtodo_class2 = "(機密任務)";
= "已變更會議 \"%{Summary}\" 下列參數:";
"Please accept or decline those changes."
= "請接受或拒絶這些變更。";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "接受邀請: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "拒絶邀請: \"%{Summary}\"";
@@ -55,6 +61,7 @@ vtodo_class2 = "(機密任務)";
= "\"%{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}。";
@@ -7,6 +7,7 @@ vevent_class2 = "(Skrytá událost)";
vtodo_class0 = "(Veřejný úkol)";
vtodo_class1 = "(Soukromý úkol)";
vtodo_class2 = "(Skrytý úkol)";
/* Receipts */
"The event \"%{Summary}\" was created" = "Událost \"%{Summary}\" byla vytvořena";
"The event \"%{Summary}\" was deleted" = "Událost \"%{Summary}\" byla smazána";
@@ -14,6 +15,7 @@ vtodo_class2 = "(Skrytý úkol)";
"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";
@@ -22,17 +24,20 @@ vtodo_class2 = "(Skrytý úkol)";
"location_label" = "Místo";
"summary_label" = "Název:";
"comment_label" = "Komentář:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Pozvání na událost: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(poslal/a %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}Vás pozval/a na %{Summary}.\n\nZačátek: %{StartDate}\nKonec: %{EndDate}\nPopis: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}Vás pozval/a na %{Summary}.\n\nZačátek: %{StartDate} v %{StartTime}\nKonec: %{EndDate} v %{EndTime}\nPopis: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Zrušení události: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}zrušil/a událost: %{Summary}.\n\nZačátek: %{StartDate}\nKonec: %{EndDate}\nPopis: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}zrušil/a událost: %{Summary}.\n\nZačátek: %{StartDate} v %{StartTime}\nKonec: %{EndDate} v %{EndTime}\nPopis: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "Schůzka \"%{Summary}\" na %{OldStartDate} byla změněna";
@@ -42,6 +47,7 @@ vtodo_class2 = "(Skrytý úkol)";
= "Následující parametry byly ve schůzce \"%{Summary}\" změněny:";
"Please accept or decline those changes."
= "Prosím přijměte nebo odmítněte tyto změny.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Přijaté pozvání: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Odmítnuté pozvání: \"%{Summary}\"";
@@ -55,6 +61,7 @@ vtodo_class2 = "(Skrytý úkol)";
= "%{Attendee} %{SentByText}delegoval/a Vaše pozvání na %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}dosud o Vašem pozvání k události nerozhodl/a.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Nedostupný zdroj: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Byl dosažen maximální počet současných rezervací\n(%{NumberOfSimultaneousBookings}) pro zdroj \"%{Cn} %{SystemEmail}\". Konfliktní událost je \"%{EventTitle}\" a začíná %{StartDate}.";
@@ -6,6 +6,7 @@ vevent_class2 = "(Hemmelig begivenhed)";
vtodo_class0 = "(Offentlig opgave)";
vtodo_class1 = "(Privat opgave)";
vtodo_class2 = "(Hemmelig opgave)";
/* Receipts */
"The event \"%{Summary}\" was created" = "Begivenheden \"%{Summary}\" blev oprettet";
"The event \"%{Summary}\" was deleted" = "Begivenheden \"%{Summary}\" blev slettet";
@@ -13,6 +14,7 @@ vtodo_class2 = "(Hemmelig opgave)";
"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";
@@ -21,17 +23,20 @@ vtodo_class2 = "(Hemmelig opgave)";
"location_label" = "Sted";
"summary_label" = "Resumé:";
"comment_label" = "Kommentér:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Begivenhed Invitation: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(Sendt af%{SentBy})";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}har inviteret dig til %{Summary}.\n\nStart: %{StartDate}\nSlut: %{EndDate}\nBeskrivelse: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer}%{SentByText} har inviteret dig til%{Summary}.\n\nStart:%{StartDate}ved%{StartTime}\nEnd:%{EndDate}ved%{endTime}\nDescription:%{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Begivenhed Annulleret: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}har aflyst denne begivenhed: %{Summary}.\n\nStart: %{StartDate}\nSlut: %{EndDate}\nBeskrivelse: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}har annulleret denne begivenhed: %{Summary}.\n\nStart: %{StartDate} %{StartTime}\nSlut: %{EndDate} %{EndTime}\nBeskrivelse: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "Aftalen \"%{Summary}\" for %{OldStartDate} er ændret";
@@ -41,6 +46,7 @@ vtodo_class2 = "(Hemmelig opgave)";
= "Følgende parametre har ændret sig i \"%{Summary}\" møde:";
"Please accept or decline those changes."
= "Venligst acceptér eller afvis disse ændringer.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Accepterede invitationen: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Afslåede invitationen: \"%{Summary}\"";
@@ -54,6 +60,7 @@ vtodo_class2 = "(Hemmelig opgave)";
= "%{Attendee} %{SentByText} har delegeret en invitation til %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText} har endnu ikke taget stilling til din begivenhedsinvitation.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Kan ikke tilgå ressourcen: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Maksimalt antal tilladte indkaldelser (%{NumberOfSimultaneousBookings}) er nået for ressource \"%{Cn} %{SystemEmail}\". Konflikter med begivenheden \"%{EventTitle}\", som starter %{StartDate}.";
@@ -7,6 +7,7 @@ vevent_class2 = "(Vertrouwelijke afspraak)";
vtodo_class0 = "(Publieke taak)";
vtodo_class1 = "(Privétaak)";
vtodo_class2 = "(Vertrouwelijke taak)";
/* Receipts */
"The event \"%{Summary}\" was created" = "De gebeurtenis \"%{Summary}\" is aangemaakt";
"The event \"%{Summary}\" was deleted" = "De gebeurtenis \"%{Summary}\" is verwijderd";
@@ -14,6 +15,7 @@ vtodo_class2 = "(Vertrouwelijke taak)";
"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";
@@ -22,17 +24,20 @@ vtodo_class2 = "(Vertrouwelijke taak)";
"location_label" = "Locatie";
"summary_label" = "Samenvatting:";
"comment_label" = "Commentaar:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Uitnodiging voor gebeurtenis: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(Verzonden door %{SentBy})";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}heeft u uitgenodigd voor: %{Summary}.\n\nBegin: %{StartDate}\nEinde: %{EndDate}\nBeschrijving: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}heeft u uitgenodigd voor %{Summary}.\n\nStart: %{StartDate} om %{StartTime}\nEinde: %{EndDate} om %{EndTime}\nOmschrijving: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Gebeurtenis geannuleerd: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}heeft deze gebeurtenis afgezegd: %{Summary}.\n\nBegin: %{StartDate}\nEinde: %{EndDate}\nBeschrijving: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}heeft de gebeurtenis: %{Summary} geannuleerd.\n\nStart: %{StartDate} om %{StartTime}\nEinde: %{EndDate} om %{EndTime}\nOmschrijving: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "De gebeurtenis \"%{Summary}\" op %{OldStartDate} is veranderd.";
@@ -42,6 +47,7 @@ vtodo_class2 = "(Vertrouwelijke taak)";
= "De volgende parameters zijn veranderd in de \"%{Summary}\" vergadering:";
"Please accept or decline those changes."
= "Accepteer deze wijzigingen of wijs ze af.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Uitnodiging aanvaard: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Uitnodiging afgewezen: \"%{Summary}\"";
@@ -55,6 +61,7 @@ vtodo_class2 = "(Vertrouwelijke taak)";
= "%{Attendee} %{SentByText}heeft de uitnodiging gedelegeerd aan %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}heeft nog niet gereageerd op uw uitnodiging.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Kan bron \"%{Cn} %{SystemEmail}\" niet benaderen";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Het maximale aantal gelijktijdige reserveringen (%{NumberOfSimultaneousBookings}) voor \"%{Cn} %{SystemEmail}\" is bereikt. De conflicterende gebeurtenis is \"%{EventTitle}\" en begint op %{StartDate}.";
@@ -7,6 +7,7 @@ vevent_class2 = "(Confidential event)";
vtodo_class0 = "(Public task)";
vtodo_class1 = "(Private task)";
vtodo_class2 = "(Confidential task)";
/* Receipts */
"The event \"%{Summary}\" was created" = "The event \"%{Summary}\" was created";
"The event \"%{Summary}\" was deleted" = "The event \"%{Summary}\" was deleted";
@@ -14,25 +15,29 @@ vtodo_class2 = "(Confidential task)";
"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";
"due_label" = "Due Date";
"due_label" = "Due Date:";
"location_label" = "Location";
"summary_label" = "Summary";
"comment_label" = "Comment";
"summary_label" = "Summary:";
"comment_label" = "Comment:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Event Invitation: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(sent by %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Event Cancelled: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "The appointment \"%{Summary}\" for the %{OldStartDate} has changed";
@@ -42,6 +47,7 @@ vtodo_class2 = "(Confidential task)";
= "The following parameters have changed in the \"%{Summary}\" meeting:";
"Please accept or decline those changes."
= "Please accept or decline those changes.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Accepted invitation: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Declined invitation: \"%{Summary}\"";
@@ -55,6 +61,7 @@ vtodo_class2 = "(Confidential task)";
= "%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}has not yet decided upon your event invitation.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Cannot access resource: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}.";
@@ -7,6 +7,7 @@ vevent_class2 = "(Luottamuksellinen tapahtuma)";
vtodo_class0 = "(Julkinen tehtävä)";
vtodo_class1 = "(Yksityinen tehtävä)";
vtodo_class2 = "(Luottamuksellinen tehtävä)";
/* Receipts */
"The event \"%{Summary}\" was created" = "Tapahtuma \"%{Summary}\" luotiin";
"The event \"%{Summary}\" was deleted" = "Tapahtuma \"%{Summary}\" poistettiin";
@@ -14,6 +15,7 @@ vtodo_class2 = "(Luottamuksellinen tehtävä)";
"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";
@@ -22,17 +24,20 @@ vtodo_class2 = "(Luottamuksellinen tehtävä)";
"location_label" = "Sijainti";
"summary_label" = "Yhteenveto:";
"comment_label" = "Kommentti:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Tapahtumakutsu: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(lähettäjä %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}on kutsunut sinut %{Summary}n.⏎ ⏎ Alkaa: %{StartDate}⏎ Loppuu: %{EndDate}⏎ Kuvaus: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}on kutsunut sinut %{Summary}n.⏎ ⏎ Alkaa: %{StartDate} klo %{StartTime}⏎ Loppuu: %{EndDate} klo %{EndTime}⏎ Kuvaus: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Tapahtuma peruttu: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}on perunut tapahtuman: %{Summary}.⏎ ⏎ Alkaa: %{StartDate}⏎ Loppuu: %{EndDate}⏎ Kuvaus: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}on peruuttanut tapahtuman: %{Summary}.⏎ ⏎ Alkaa: %{StartDate}klot %{StartTime}⏎ Loppuu: %{EndDate} klo %{EndTime}⏎ Kuvaus: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "Tapaaminen \"%{Summary}\" %{OldStartDate} on muuttunut";
@@ -42,6 +47,7 @@ vtodo_class2 = "(Luottamuksellinen tehtävä)";
= "Seuraavat tiedot ovat muuttuneet tapaamisessa \"%{Summary}\":";
"Please accept or decline those changes."
= "Ole hyvä ja hyväksy tai hylkää muutokset.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Hyväksytty kutsu: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Hylätty kutsu: \"%{Summary}\"";
@@ -55,6 +61,7 @@ vtodo_class2 = "(Luottamuksellinen tehtävä)";
= "%{Attendee} %{SentByText} on lähettänyt kutsusi edelleen osoitteeseen %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText} ei vielä ole tehnyt päätöstä tapahtumakutsustasi.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Ei pääsyä resurssiin: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Maksimimäärä yhtäaikaisia varauksia\n(%{NumberOfSimultaneousBookings}) saavutettu resurssille \"%{Cn} %{SystemEmail}\". Ristiriitainen tapahtuma: \"%{EventTitle}\" alkaa %{StartDate}.";
@@ -7,6 +7,7 @@ vevent_class2 = "(Événement confidentiel)";
vtodo_class0 = "(Tâche publique)";
vtodo_class1 = "(Tâche privée)";
vtodo_class2 = "(Tâche confidentielle)";
/* Receipts */
"The event \"%{Summary}\" was created" = "L'événement «%{Summary}» a été créé";
"The event \"%{Summary}\" was deleted" = "L'événement «%{Summary}» a été effacé";
@@ -14,6 +15,7 @@ vtodo_class2 = "(Tâche confidentielle)";
"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 ";
@@ -22,17 +24,20 @@ vtodo_class2 = "(Tâche confidentielle)";
"location_label" = "Lieu ";
"summary_label" = "Titre :";
"comment_label" = "Description :";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Invitation à la réunion : «%{Summary}»";
"(sent by %{SentBy}) " = "(envoyé par %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}vous a invité à la réunion « %{Summary} ».\n\nDébut: %{StartDate}\nFin: %{EndDate}\nDescription: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}vous a invité à la réunion « %{Summary} ».\n\nDébut: %{StartDate} à %{StartTime}\nFin: %{EndDate} à %{EndTime}\nDescription: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Réunion annulée : « %{Summary} »";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}a annulé cette réunion : « %{Summary} ».\n\nDébut: %{StartDate}\nFin: %{EndDate}\nDescription: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}a annulé cette réunion : « %{Summary} ».\n\nDébut: %{StartDate} à %{StartTime}\nFin: %{EndDate} à %{EndTime}\nDescription: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "La réunion « %{Summary} » de %{OldStartDate} a été modifiée";
@@ -42,6 +47,7 @@ vtodo_class2 = "(Tâche confidentielle)";
= "Les paramètres suivants ont été modifiés pour la réunion « %{Summary} » :";
"Please accept or decline those changes."
= "Veuillez reconfirmer ou annuler votre présence.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Invitation acceptée : «%{Summary}»";
"Declined invitation: \"%{Summary}\"" = "Invitation refusée : «%{Summary}»";
@@ -55,6 +61,7 @@ vtodo_class2 = "(Tâche confidentielle)";
= "%{Attendee} %{SentByText}a délégué votre invitation à %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}choisit de reporter sa décision par rapport à votre invitation.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Impossible d'accéder à la ressource suivante: %{Cn} %{SystemEmail}";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Le nombre maximal de réservations simultanées (%{NumberOfSimultaneousBookings}) est atteint pour la ressource «%{Cn} %{SystemEmail}». L'événement en conflit est «%{EventTitle}» et débute le %{StartDate}.";
+1 -1
View File
@@ -54,7 +54,7 @@ Appointments_RESOURCE_FILES += \
MSExchangeFreeBusySOAPResponseMap.plist \
MSExchangeFreeBusySOAPRequest.wo
Appointments_LANGUAGES = Arabic Basque BrazilianPortuguese Catalan ChineseTaiwan Czech Danish Dutch English Finnish French German Hungarian Icelandic Italian Macedonian NorwegianBokmal NorwegianNynorsk Polish Portuguese Russian Slovak Slovenian 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
@@ -7,6 +7,7 @@ vevent_class2 = "(Vertraulicher Termin)";
vtodo_class0 = "(Öffentliche Aufgabe)";
vtodo_class1 = "(Private Aufgabe)";
vtodo_class2 = "(Vertrauliche Aufgabe)";
/* Receipts */
"The event \"%{Summary}\" was created" = "Der Termin \"%{Summary}\" wurde angelegt";
"The event \"%{Summary}\" was deleted" = "Der Termin \"%{Summary}\" wurde gelöscht";
@@ -14,6 +15,7 @@ vtodo_class2 = "(Vertrauliche Aufgabe)";
"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";
@@ -22,17 +24,20 @@ vtodo_class2 = "(Vertrauliche Aufgabe)";
"location_label" = "Ort";
"summary_label" = "Zusammenfassung:";
"comment_label" = "Kommentar:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Termineinladung: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(gesendet von %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText} Hat Sie eingeladen zu \"%{Summary}\".\n\nBeginn: %{StartDate}\nEnde: %{EndDate}\nBeschreibung: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} hat Sie eingeladen zu \"%{Summary}\".\n\nBeginn: %{StartDate} um %{StartTime}\nEnde: %{EndDate} um %{EndTime}\nBeschreibung: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Termin abgesagt: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} hat den folgenden Termin abgesagt: \"%{Summary}\".\n\nBeginn: %{StartDate}\nEnde: %{EndDate}\nBeschreibung: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} hat diesen Termin abgesagt: \"%{Summary}\".\n\nBeginn: %{StartDate} um %{StartTime}\nEnde: %{EndDate} um %{EndTime}\nBeschreibung: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "Die Verabredung \"%{Summary}\" am %{OldStartDate} wurde geändert";
@@ -42,6 +47,7 @@ vtodo_class2 = "(Vertrauliche Aufgabe)";
= "Folgendes wurde am Termin \"%{Summary}\" geändert:";
"Please accept or decline those changes."
= "Bitte akzeptieren Sie diese Änderung oder lehnen Sie diese ab.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Einladung zugestimmt: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Einladung abgelehnt: \"%{Summary}\"";
@@ -55,6 +61,7 @@ vtodo_class2 = "(Vertrauliche Aufgabe)";
= "%{Attendee} %{SentByText} hat die Einladung delegiert an %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText} hat noch nicht über Ihre Termineinladung entschieden.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Kann auf folgende Ressource nicht zugreifen: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Die maximale Anzahl von gleichzeitigen Buchungen (%{NumberOfSimultaneousBookings}) für die Ressource \"%{Cn} %{SystemEmail}\" ist erreicht. Der kollidierende Termin ist \"%{EventTitle}\", und beginnt am %{StartDate}.";
@@ -7,6 +7,7 @@ vevent_class2 = "(Bizalmas esemény)";
vtodo_class0 = "(Nyilvános feladat)";
vtodo_class1 = "(Magán feladat)";
vtodo_class2 = "(Bizalmas feladat)";
/* Receipts */
"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";
@@ -14,6 +15,7 @@ vtodo_class2 = "(Bizalmas feladat)";
"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";
@@ -22,17 +24,20 @@ vtodo_class2 = "(Bizalmas feladat)";
"location_label" = "Hely";
"summary_label" = "Összegzés:";
"comment_label" = "Megjegyzés:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Meghívás eseményre: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(%{SentBy} által elküldve) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText} meghívta Önt a(z) %{Summary} tárgyú eseményre.\n\nKezdete: %{StartDate}: \nVége: %{EndDate}\nLeírása: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} meghívta Önt erre az eseményre: %{Summary}.\n\nKezdete: %{StartDate} at %{StartTime}\nVége: %{EndDate} at %{EndTime}\nLeírás: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Az esemény törölve lett: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} törlte az alábbi eseményt: %{Summary}.\n\nKzdete: %{StartDate}\nVége: %{EndDate}\nLeírás: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} törölte az alábbi eseményt: %{Summary}.\n\nKezdete: %{StartDate} at %{StartTime}\nVége: %{EndDate} at %{EndTime}\nLeírás: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "A(z) \"%{Summary}\" tárgyú, %{OldStartDate} időpontra tervezett esemény módosult.";
@@ -42,6 +47,7 @@ vtodo_class2 = "(Bizalmas feladat)";
= "A \"%{Summary}\" tárgyú találkozó következő adatai módosultak:";
"Please accept or decline those changes."
= "Kérem fogadja el vagy utasítsa vissza a változásokat.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Elfogadott meghivás: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Elutasított meghívás: \"%{Summary}\"";
@@ -55,6 +61,7 @@ vtodo_class2 = "(Bizalmas feladat)";
= "%{Attendee} %{SentByText} átruházta a meghívást az alábbi személynek %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}még meggondolja a meghívását.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Az alábbi elem nem elérhető: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "A maximális egyidejű foglalás számát (%{NumberOfSimultaneousBookings}) elérte a(z) \"%{Cn} %{SystemEmail}\" elem esetében. Az ütköző esemény a(z) \"%{EventTitle}\", a kezdeti időpontja %{StartDate}.";
@@ -6,19 +6,24 @@ vevent_class2 = "(Viðburður er trúnaðarmál)";
vtodo_class0 = "(Almennt verkefni)";
vtodo_class1 = "(Einkaverkefni)";
vtodo_class2 = "(Verkefni er trúnaðarmál)";
/* Receipts */
"Title:" = "Titill:";
"Start:" = "Byrjun:";
"End:" = "Endir:";
"Receipt: users invited to a meeting" = "Móttakandi: notendur boðnir á fund";
"You have invited the following attendees(s):" = "Eftirfarandi þáttakanda/þáttakendum hefur verið boðið:";
"... to attend the following event:" = "... að vera viðstaddir eftirfarandi viðburð:";
"Receipt: invitation updated" = "Móttakandi: uppfærður boðsmiði";
"The following attendees(s):" = "Eftirfarandi þáttakandi/Þáttakendur:";
"... have been notified of the changes to the following event:" = "... hafa fengið upplýsingar um breytingarnar á eftirfarandi viðburði:";
"Receipt: attendees removed from an event" = "Móttakandi: þáttakendur fjarlægðir af viðburði";
"You have removed the following attendees(s):" = "Eftirfarandi þáttakandi/þáttakendur hafa verið fjarlægðir:";
"... from the following event:" = "... af eftirfarandi viðburði:";
/* IMIP messages */
"startDate_label" = "Byrjun";
"endDate_label" = "Endir";
@@ -26,14 +31,17 @@ vtodo_class2 = "(Verkefni er trúnaðarmál)";
"location_label" = "Staðsetning";
"summary_label" = "Samantekt:";
"comment_label" = "Athugasemd:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Boð á viðburð: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(%{SentBy} sendi þetta) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}hefur boðið þér á þennan viðburð: %{Summary}.\n\nByrjun: %{StartDate} at %{StartTime}\nEndir: %{EndDate} at %{EndTime}\nLýsing: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Viðburði aflýst: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}hefur aflýst þessum viðburði: %{Summary}.\n\nByrjun: %{StartDate} kl. %{StartTime}\nEndir: %{EndDate} kl. %{EndTime}\nLýsing: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
= "Tímapöntunin \"%{Summary}\" sem var þann %{OldStartDate} kl. %{OldStartTime} hefur breyst.";
@@ -41,6 +49,7 @@ vtodo_class2 = "(Verkefni er trúnaðarmál)";
= "Eftirfarandi atriði hafa breyst varðandi fundinn: \"%{Summary}\"";
"Please accept or decline those changes."
= "Þessar breytingar þarf að samþykkja eða hafna.";
/* Reply */
"%{Attendee} %{SentByText}has accepted your event invitation."
= "%{Attendee} %{SentByText}hefur samþykkt boð þitt á viðburð.";
@@ -50,5 +59,6 @@ vtodo_class2 = "(Verkefni er trúnaðarmál)";
= "%{Attendee} %{SentByText}hefur skipað sem fulltrúa sinn %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}hefur ekki enn tekið ákvörðun varðandi boð þitt.";
/* Resources */
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Hámarksfjöldi samtímabókana (%{NumberOfSimultaneousBookings}) hefur verið náð varðandi aðfangið \"%{Cn} %{SystemEmail}\".";
@@ -6,19 +6,24 @@ vevent_class2 = "(Evento confidenziale)";
vtodo_class0 = "(Attività pubblica)";
vtodo_class1 = "(Attività privata)";
vtodo_class2 = "(Attività confidenziale)";
/* Receipts */
"Title:" = "Titolo: ";
"Start:" = "Inizio: ";
"End:" = "Fine: ";
"Receipt: users invited to a meeting" = "Notifica: utenti invitati al meeting";
"You have invited the following attendees(s):" = "Hai invitato i seguenti partecipanti:";
"... to attend the following event:" = "... a partecipare al seguente evento:";
"Receipt: invitation updated" = "Notifica: invito aggiornato";
"The following attendees(s):" = "I seguenti partecipanti:";
"... have been notified of the changes to the following event:" = "... hanno ricevuto una notifica di cambiamento relativa al seguente evento:";
"Receipt: attendees removed from an event" = "Notifica: partecipanti rimossi dall'evento";
"You have removed the following attendees(s):" = "Hai rimosso i seguenti partecipanti:";
"... from the following event:" = "... dal seguente evento:";
/* IMIP messages */
"startDate_label" = "Inizio";
"endDate_label" = "Fine";
@@ -26,14 +31,17 @@ vtodo_class2 = "(Attività confidenziale)";
"location_label" = "Luogo";
"summary_label" = "Summario:";
"comment_label" = "Commento:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Invito Evento: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(inviato da %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}ti ha invitato al %{Summary}.\n\nInizio: %{StartDate} alle %{StartTime}\nFine: %{EndDate} alle %{EndTime}\nDescrizione: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Evento cancellato: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}ha cancellato questo evento: %{Summary}.\n\nInizio: %{StartDate} alle %{StartTime}\nFine: %{EndDate} alle %{EndTime}\nDescrizione: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
= "L'appuntamento \"%{Summary}\" per la data %{OldStartDate} all'ora %{OldStartTime} è cambiato";
@@ -41,6 +49,7 @@ vtodo_class2 = "(Attività confidenziale)";
= "I seguenti dati sono cambiati nell'appuntamento \"%{Summary}\" :";
"Please accept or decline those changes."
= "Prego accettare o rifiutare questi cambiamenti";
/* Reply */
"%{Attendee} %{SentByText}has accepted your event invitation."
= "%{Attendee} %{SentByText}ha accettato il tuo invito al evento.";
@@ -50,5 +59,6 @@ vtodo_class2 = "(Attività confidenziale)";
= "%{Attendee} %{SentByText}ha delegato l'invito a %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}non ha ancora deciso riguardo il tuo invito all'evento.";
/* Resources */
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Numero massimo di prenotazioni simultanee (%{NumberOfSimultaneousBookings}) raggiunto per la risorsa \"%{Cn} %{SystemEmail}\".";
+1 -1
View File
@@ -344,7 +344,7 @@ size_t curl_body_function_freebusy(void *ptr, size_t size, size_t nmemb, void *i
NSMutableString *s;
s = [NSMutableString stringWithCapacity: 64];
[s appendFormat:@"<0x%08X[%@]:", (unsigned int)self, NSStringFromClass([self class])];
[s appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
if (freeBusyViewType)
[s appendFormat:@" freeBusyViewType='%@'", freeBusyViewType];
if (mergedFreeBusy)
@@ -1,60 +0,0 @@
"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\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{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"
= "Состанокот \"%{Summary}\" за %{OldStartDate} е променет";
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
= "Состанокот \"%{Summary}\" за %{OldStartDate} на %{OldStartTime} се промени";
"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}." = "Максималниот број на едновремени закажувања (%{NumberOfSimultaneousBookings}) е постигнат за ресурсот \"%{Cn} %{SystemEmail}\". Конфликтниот настан е \"%{EventTitle}\", и почнува на %{StartDate}.";
@@ -7,6 +7,7 @@ vevent_class2 = "(Konfidensiell hendelse)";
vtodo_class0 = "(Offentlig oppgave)";
vtodo_class1 = "(Privat oppgave)";
vtodo_class2 = "(Konfidensiell oppgave)";
/* Receipts */
"The event \"%{Summary}\" was created" = "Hendelsen \"%{Summary}\" ble opprettet";
"The event \"%{Summary}\" was deleted" = "Hendelsen \"%{Summary}\" ble slettet";
@@ -14,6 +15,7 @@ vtodo_class2 = "(Konfidensiell oppgave)";
"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";
@@ -22,17 +24,20 @@ vtodo_class2 = "(Konfidensiell oppgave)";
"location_label" = "Sted";
"summary_label" = "Sammendrag:";
"comment_label" = "Kommentar:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Hendelseinvitasjon: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(sendt av %{SentBy})";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText} har invitert deg til %{Summary}.\n\nStart: %{StartDate}\nSlutt: %{EndDate}\nBeskrivelse: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} har invitert deg til %{Summary}.\n\nStart: %{StartDate} klokken %{StartTime}\nSlutt: %{EndDate} klokken %{EndTime}\nBeskrivelse: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Hendelse avlyst: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} har avlyst hendelsen: %{Summary}.\n\nStart: %{StartDate}\nSlutt: %{EndDate}\nBeskrivelse: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} har avlyst : %{Summary}.\n\nStart: %{StartDate} klokken %{StartTime}\nEnd: %{EndDate} klokken %{EndTime}\nBeskrivelse: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "Avtalen \"%{Summary}\", %{OldStartDate} har endret seg";
@@ -42,6 +47,7 @@ vtodo_class2 = "(Konfidensiell oppgave)";
= "Følgende parametre har endret seg for \"%{Summary}\" møtet:";
"Please accept or decline those changes."
= "Vennligst godta eller avvis endringene.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Aksepterte invitasjonen: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Avviste invitasjonen: \"%{Summary}\"";
@@ -55,6 +61,7 @@ vtodo_class2 = "(Konfidensiell oppgave)";
= "%{Attendee} %{SentByText} har delegert invitasjonen til %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText} har enda ikke svart på invitasjonen din.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Har ikke tilgang til ressurs: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Maksimum antall samtidige reservasjoner (%{NumberOfSimultaneousBookings}) er nådd for ressursen \"%{Cn} %{SystemEmail}\". Den kolliderende hendelsen er \"%{EventTitle}\", og starter %{StartDate}.";
@@ -6,19 +6,24 @@ vevent_class2 = "(Konfidensiell hendelse)";
vtodo_class0 = "(Offentlig oppgave)";
vtodo_class1 = "(Privat oppgave)";
vtodo_class2 = "(Konfidensiell oppgave)";
/* Receipts */
"Title:" = "Tittel:";
"Start:" = "Start:";
"End:" = "Slutt:";
"Receipt: users invited to a meeting" = "Kvittering: brukere invitert til et møte";
"You have invited the following attendees(s):" = "Du har invitert følende deltagere:";
"... to attend the following event:" = "... til å delta i følgende hendelse:";
"Receipt: invitation updated" = "Kvittering: invitasjon oppdatert";
"The following attendees(s):" = "Følgende deltager(e):";
"... have been notified of the changes to the following event:" = "... har blitt informert om endringene i følgende hendelse:";
"Receipt: attendees removed from an event" = "Kvittering: deltagere fjernet fra en hendelse";
"You have removed the following attendees(s):" = "Du har tatt bort følgende deltagere:";
"... from the following event:" = "... fra følgende hendelse:";
/* IMIP messages */
"startDate_label" = "Start";
"endDate_label" = "End";
@@ -26,14 +31,17 @@ vtodo_class2 = "(Konfidensiell oppgave)";
"location_label" = "Location";
"summary_label" = "Summary:";
"comment_label" = "Comment:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Event Invitation: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(sent by %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Event Cancelled: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
= "The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed";
@@ -41,6 +49,7 @@ vtodo_class2 = "(Konfidensiell oppgave)";
= "The following parameters have changed in the \"%{Summary}\" meeting:";
"Please accept or decline those changes."
= "Please accept or decline those changes.";
/* Reply */
"%{Attendee} %{SentByText}has accepted your event invitation."
= "%{Attendee} %{SentByText}has accepted your event invitation.";
@@ -50,5 +59,6 @@ vtodo_class2 = "(Konfidensiell oppgave)";
= "%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}has not yet decided upon your event invitation.";
/* Resources */
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\"." = "Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\".";
@@ -7,6 +7,7 @@ vevent_class2 = "(Wydarzenie poufne)";
vtodo_class0 = "(Zadanie publiczne)";
vtodo_class1 = "(Zadanie prywatne)";
vtodo_class2 = "(Zadanie poufne)";
/* Receipts */
"The event \"%{Summary}\" was created" = "Utworzono wydarzenie \"%{Summary}\"";
"The event \"%{Summary}\" was deleted" = "Skasowano wydarzenie \"%{Summary}\"";
@@ -14,25 +15,29 @@ vtodo_class2 = "(Zadanie poufne)";
"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";
"due_label" = "Termin";
"due_label" = "Termin:";
"location_label" = "Miejsce";
"summary_label" = "Podsumowanie";
"comment_label" = "Komentarz";
"summary_label" = "Podsumowanie:";
"comment_label" = "Komentarz:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Zaproszenie na wydarzenie: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(wysłane przez %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}zaprosił Cię na %{Summary}.⏎ ⏎ Początek: %{StartDate}⏎ Koniec: %{EndDate}⏎ Opis: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText}zaprosił cię na %{Summary}.\n\nPoczątek: %{StartDate} o %{StartTime}\nKoniec: %{EndDate} o %{EndTime}\nOpis: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Wydarzenie anulowane: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} anulował(a) to wydarzenie: %{Summary}.⏎⏎ Początek: %{StartDate}⏎ Koniec: %{EndDate}⏎ Opis: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}anulował(a) to wydarzenie: %{Summary}.\n\nPoczątek: %{StartDate} o %{StartTime}\nKoniec: %{EndDate} o %{EndTime}\nOpis: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "Wydarzenie \"%{Summary}\" z dnia %{OldStartDate} zmieniło się";
@@ -42,6 +47,7 @@ vtodo_class2 = "(Zadanie poufne)";
= "Zmienione zostały poniższe parametry spotkania \"%{Summary}\":";
"Please accept or decline those changes."
= "Zaakceptuj lub odrzuć te zmiany.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Zaakceptowano zaproszenie: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Odrzucono zaproszenie: \"%{Summary}\"";
@@ -55,6 +61,7 @@ vtodo_class2 = "(Zadanie poufne)";
= "%{Attendee} %{SentByText}oddelegował(a) %{Delegate} na twoje wydarzenie.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText}jeszcze nie zdecydował(a) o obecności na twoim wydarzeniu.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Nie ma dostępu do: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "Osiągnięto maksymalną liczbę równoległych rezerwacji (%{NumberOfSimultaneousBookings}) dla \"%{Cn} %{SystemEmail}\". Problem wywołuje wydarzenie \"%{EventTitle}\" zaczynające się w %{StartDate}.";
@@ -1,60 +0,0 @@
"Inviting the following persons is prohibited:" = "Convidando as seguintes pessoas é proibido:";
"Personal Calendar" = "Calendário Pessoal";
vevent_class0 = "(Evento Público)";
vevent_class1 = "(Evento Privado)";
vevent_class2 = "(Evento Confidencial)";
vtodo_class0 = "(Tarefa Pública)";
vtodo_class1 = "(Tarefa Privada)";
vtodo_class2 = "(Tarefa Confidencial)";
/* Receipts */
"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:" = "Os seguintes participantes foram notificados:";
"The following attendees(s) were added:" = "Os seguintes participantes foram adicionados:";
"The following attendees(s) were removed:" = "Os seguintes participantes foram removidos:";
/* IMIP messages */
"calendar_label" = "Calendário:";
"startDate_label" = "Início:";
"endDate_label" = "Fim:";
"due_label" = "Data de Vencimento:";
"location_label" = "Local:";
"summary_label" = "Resumo:";
"comment_label" = "Comentário:";
/* Invitation */
"Event Invitation: \"%{Summary}\"" = "Convite do Evento: \"%{Summary}\"";
"(sent by %{SentBy}) " = "(enviado por %{SentBy}) ";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}" = "%{Organizer} %{SentByText}convidou-o para %{Summary}.\n\nInicio: %{StartDate}\nFim: %{EndDate}\nDescrição: %{Description}";
"%{Organizer} %{SentByText}has invited you to %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}" = "%{Organizer} %{SentByText} convidou-o para %{Summary}.\n\nInício: %{StartDate} as %{StartTime}\nFim: %{EndDate} as %{EndTime}\nDescrição: %{Description}";
/* Deletion */
"Event Cancelled: \"%{Summary}\"" = "Evento Cancelado: \"%{Summary}\"";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate}\nEnd: %{EndDate}\nDescription: %{Description}"
= "%{Organizer} %{SentByText}cancelou este evento: %{Summary}.\n\nInicio: %{StartDate}\nFim: %{EndDate}\nDescrição: %{Description}";
"%{Organizer} %{SentByText}has cancelled this event: %{Summary}.\n\nStart: %{StartDate} at %{StartTime}\nEnd: %{EndDate} at %{EndTime}\nDescription: %{Description}"
= "%{Organizer} %{SentByText} cancelou este evento: %{Summary}.\n\nInício: %{StartDate} às %{StartTime}\nFim: %{EndDate} as %{EndTime}\nDescrição: %{Description}";
/* Update */
"The appointment \"%{Summary}\" for the %{OldStartDate} has changed"
= "O compromisso \"%{Summary}\" de %{OldStartDate} mudou";
"The appointment \"%{Summary}\" for the %{OldStartDate} at %{OldStartTime} has changed"
= "O Compromisso \"%{Summary}\" de %{OldStartDate} as %{OldStartTime} mudou";
"The following parameters have changed in the \"%{Summary}\" meeting:"
= "Os seguintes parâmetros mudaram na reunião \"%{Summary}\" :\n\n";
"Please accept or decline those changes."
= "Por favor, aceitar ou recusar as alterações.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Convite aceite: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Convite recusado: \"%{Summary}\"";
"Delegated invitation: \"%{Summary}\"" = "Convite delegado: \"%{Summary}\"";
"Not yet decided on invitation: \"%{Summary}\"" = "Convite ainda não decidido: \"%{Summary}\"";
"%{Attendee} %{SentByText}has accepted your event invitation."
= "%{Attendee} %{SentByText} aceitou seu convite ao evento.";
"%{Attendee} %{SentByText}has declined your event invitation."
= "%{Attendee} %{SentByText} rejeitou seu convite ao evento.";
"%{Attendee} %{SentByText}has delegated the invitation to %{Delegate}."
= "%{Attendee} %{SentByText} delegou o convite para %{Delegate}.";
"%{Attendee} %{SentByText}has not yet decided upon your event invitation."
= "%{Attendee} %{SentByText} ainda não decidiu seu convite ao evento.";
/* Resources */
"Cannot access resource: \"%{Cn} %{SystemEmail}\"" = "Não foi possível aceder ao recurso: \"%{Cn} %{SystemEmail}\"";
"Maximum number of simultaneous bookings (%{NumberOfSimultaneousBookings}) reached for resource \"%{Cn} %{SystemEmail}\". The conflicting event is \"%{EventTitle}\", and starts on %{StartDate}." = "O número máximo de reservas simultâneas (%{NumberOfSimultaneousBookings}) acabou para o recurso \"%{Cn} %{SystemEmail}\". O evento em conflito é \"%{EventTitle}\", e inicia em %{StartDate}.";
@@ -7,6 +7,7 @@ vevent_class2 = "(Конфиденциальное событие)";
vtodo_class0 = "(Публичная задача)";
vtodo_class1 = "(Личная задача)";
vtodo_class2 = "(Конфиденциальная задача)";
/* Receipts */
"The event \"%{Summary}\" was created" = "Было создано мероприятие \"%{Summary}\"";
"The event \"%{Summary}\" was deleted" = "Мероприятие \"%{Summary}\" было удалено";
@@ -14,6 +15,7 @@ vtodo_class2 = "(Конфиденциальная задача)";
"The following attendees(s) were notified" = "Следующие приглашенные были оповещены";
"The following attendees(s) were added" = "Следующие люди были добавлены в список приглашенных";
"The following attendees(s) were removed" = "Следующие люди были исключены из списка приглашенных";
/* IMIP messages */
"calendar_label" = "Календарь";
"startDate_label" = "Начало";
@@ -22,17 +24,20 @@ vtodo_class2 = "(Конфиденциальная задача)";
"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\Начало: %{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"
= "Встреча \"%{Summary}\" назначенная на %{OldStartDate} была изменена";
@@ -42,6 +47,7 @@ vtodo_class2 = "(Конфиденциальная задача)";
= "в мероприятии \"%{Summary}\" изменены следующие параметры:";
"Please accept or decline those changes."
= "Пожалуйста подтвердите или отмените эти изменения.";
/* Reply */
"Accepted invitation: \"%{Summary}\"" = "Принятое приглашение: \"%{Summary}\"";
"Declined invitation: \"%{Summary}\"" = "Отклонённое приглашение: \"%{Summary}\"";
@@ -55,6 +61,7 @@ vtodo_class2 = "(Конфиденциальная задача)";
= "%{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}." = "Достигнуто предельное число заявок (%{NumberOfSimultaneousBookings}) на ресурс \"%{Cn} %{SystemEmail}\". Конкурирующее событие называется \"%{EventTitle}\", оно начинается %{StartDate}.";
@@ -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
Loaded 100 of 1452 files, more files were not shown because too many files have changed in this diff. Show more