Compare commits

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

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

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

No files matched your search

+13104 -402
View File
File diff suppressed because it is too large. Load diff
+1
View File
@@ -7,6 +7,7 @@ Bug fixes
- 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
2.3.1 (2015-07-23)
------------------
@@ -1493,8 +1493,9 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
// the database. We do this ONLY when using SOGo from the
// Web interface or over ActiveSync.
// Over DAV, it'll be handled directly in PUTAction:
if (![context request] || [[context request] handledByDefaultHandler]
|| [[[context request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"])
if (![context request] ||
[[context request] handledByDefaultHandler] ||
[[[context request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"])
{
// If an alarm was specified, let's use it. This would happen if an attendee accepts/declines/etc. an
// event invitation and also sets an alarm along the way. This would happen ONLY from the web interface.
@@ -84,17 +84,30 @@
*/
- (NSDictionary *) attributesInContext: (WOContext *) context
{
NSArray *rules;
NSArray *allEvents, *rules;
NSCalendarDate *untilDate;
NSMutableDictionary *data, *repeat;
NSString *frequency;
NSTimeZone *timeZone;
SOGoUserDefaults *ud;
iCalEvent *masterComponent;
iCalRecurrenceRule *rule;
data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]];
rules = [self recurrenceRules];
if ([self recurrenceId])
{
// If the component is an occurrence of a recurrent component,
// consider the recurrence rules of the master component.
allEvents = [[self parent] events];
masterComponent = [allEvents objectAtIndex: 0];
rules = [masterComponent recurrenceRules];
}
else
{
rules = [self recurrenceRules];
}
if ([rules count] > 0)
{
rule = [rules objectAtIndex: 0];
+5 -1
View File
@@ -1,6 +1,6 @@
/* NGVCard+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2009-2014 Inverse inc.
* Copyright (C) 2009-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -31,6 +31,10 @@
- (CardElement *) elementWithTag: (NSString *) elementTag
ofType: (NSString *) type;
- (void) addElementWithTag: (NSString *) elementTag
ofType: (NSString *) type
withValue: (id) value;
- (void) updateFromLDIFRecord: (NSDictionary *) ldifRecord;
- (NSMutableDictionary *) asLDIFRecord;
+2 -2
View File
@@ -188,8 +188,8 @@ convention:
}
- (void) addElementWithTag: (NSString *) elementTag
ofType: (NSString *) type
withValue: (id) value
ofType: (NSString *) type
withValue: (id) value
{
NSArray *allValues;
NSEnumerator *list;
+6 -3
View File
@@ -1,6 +1,5 @@
/*
Copyright (C) 2006-2014 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
Copyright (C) 2006-2015 Inverse inc.
This file is part of SOGo.
@@ -32,9 +31,13 @@
@class NSString;
@interface SOGoContactGCSFolder : SOGoGCSFolder <SOGoContactFolder>
{
NSString *baseCardDAVURL, *basePublicCardDAVURL;
}
- (void) fixupContactRecord: (NSMutableDictionary *) contactRecord;
- (NSDictionary *) lookupContactWithName: (NSString *) aName;
- (NSString *) cardDavURL;
- (NSString *) publicCardDavURL;
@end
+63 -2
View File
@@ -1,6 +1,5 @@
/*
Copyright (C) 2006-2013 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
Copyright (C) 2006-2013-2015 Inverse inc.
This file is part of SOGo.
@@ -67,6 +66,24 @@ static NSArray *folderListingFields = nil;
@"c_component", nil];
}
- (id) init
{
if ((self = [super init]))
{
baseCardDAVURL = nil;
basePublicCardDAVURL = nil;
}
return self;
}
- (void) dealloc
{
[baseCardDAVURL release];
[basePublicCardDAVURL release];
[super dealloc];
}
- (Class) objectClassForContent: (NSString *) content
{
CardGroup *cardEntry;
@@ -436,4 +453,48 @@ static NSArray *folderListingFields = nil;
return [NSString stringWithFormat: @"%@:%@", nsRep, nodeName];
}
- (NSString *) _baseCardDAVURL
{
NSString *davURL;
if (!baseCardDAVURL)
{
davURL = [[self realDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
baseCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
baseCardDAVURL = davURL;
[baseCardDAVURL retain];
}
return baseCardDAVURL;
}
- (NSString *) cardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _baseCardDAVURL]];
}
- (NSString *) _basePublicCardDAVURL
{
NSString *davURL;
if (!basePublicCardDAVURL)
{
davURL = [[self publicDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
basePublicCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
basePublicCardDAVURL = davURL;
[basePublicCardDAVURL retain];
}
return basePublicCardDAVURL;
}
- (NSString *) publicCardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _basePublicCardDAVURL]];
}
@end /* SOGoContactGCSFolder */
+1 -3
View File
@@ -1,8 +1,6 @@
/* NSArray+Utilities.h - this file is part of SOGo
*
* Copyright (C) 2006 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
+1 -3
View File
@@ -1,8 +1,6 @@
/* NSArray+Utilities.m - this file is part of SOGo
*
* Copyright (C) 2006-2011 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
+1 -3
View File
@@ -1,8 +1,6 @@
/* NSObject+Utilities.h - this file is part of SOGo
*
* Copyright (C) 2007 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2007-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
+1 -3
View File
@@ -1,8 +1,6 @@
/* NSObject+Utilities.m - this file is part of SOGo
*
* Copyright (C) 2007 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2007-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
+4 -1
View File
@@ -778,7 +778,10 @@ static Class NSNullK;
NSRange r;
r = [theUID rangeOfString: [NSString stringWithFormat: @"@%@", theDomain]];
theUID = [theUID substringToIndex: r.location];
// We check if the range is ok here since we could be using DomainLessLogin
if (r.location != NSNotFound)
theUID = [theUID substringToIndex: r.location];
}
sogoSources = [[self authenticationSourceIDsInDomain: theDomain] objectEnumerator];
+5 -1
View File
@@ -70,7 +70,11 @@ class Carddav:
if not self.cards:
url = "/SOGo/so/%s/Contacts/personal/view" % (self.login)
content = self._get(url)
self.cards = content['cards']
#print "\nCONTENT:", content
if 'cards' in content:
self.cards = content['cards']
else:
self.cards = []
return self.cards
def get_cards(self, pattern):
+4 -4
View File
@@ -89,9 +89,9 @@ class preferences:
get = HTTPPreferencesGET(url)
get.cookie = self.cookie
self.client.execute(get)
if DEBUG: print "LUC (url):", url
if DEBUG: print "LUC (status):", get.response["status"]
if DEBUG: print "LUC (body):", get.response['body']
if DEBUG: print "DEBUG (url):", url
if DEBUG: print "DEBUG (status):", get.response["status"]
if DEBUG: print "DEBUG (body):", get.response['body']
content = simplejson.loads(get.response['body'])
result = None
try:
@@ -117,7 +117,7 @@ class preferences:
# Get the right sub-dict and change the key/value
subdict = self.find_key(self.preferences, preference)
if not subdict:
print "LUC(nosubdict):", preference, self.preferences
raise AttributeError("ERROR(nosubdict): looking for %s in: %s" %(preference, str(self.preferences)))
subdict[preference] = value
def set(self, preference, value=None):
+24
View File
@@ -85,6 +85,13 @@ class JsonDavPhoneTests(unittest.TestCase):
self.allphones = list(self.newphone)
self.allphones.extend(self.newphones_difftype)
self.allphones.extend(self.newphones_sametype)
#- In case there are no cards for this user
try:
self._get_card()
except IndexError:
path = 'Contacts/personal'
(card, path, gid) = self._create_new_card(path)
self._save_card(card)
def tearDown(self):
self._connect_as_user()
@@ -102,6 +109,23 @@ class JsonDavPhoneTests(unittest.TestCase):
def _connect_as_user(self, newuser=username, newpassword=password):
self.dv = carddav.Carddav(newuser, newpassword)
def _create_new_card(self, path):
gid = self.dv.newguid(path)
card = {'c_categories': None,
'c_cn': 'John Doe',
'c_component': 'vcard',
'c_givenname': 'John Doe',
'c_mail': 'johndoe@nothere.com',
'c_name': gid,
'c_o': '',
'c_screenname': '',
'c_sn': '',
'c_telephonenumber': '123.456.7890',
'emails': [{'type': 'pref', 'value': 'johndoe@nothere.com'}],
'phones': [{'type': 'home', 'value': '111.222.3333'}],
'id': gid}
return (card, path, gid)
def _get_card(self, name="John Doe"):
tmp_card = self.dv.get_cards(name)[0]
self.card = self.dv.get_card(tmp_card['c_name'])
@@ -28,12 +28,14 @@ class preventInvitationsTest(unittest.TestCase):
self.caldav = CalDAVSchedulingTest(self)
cal = self.prefs.get("Calendar")
if "PreventInvitationsWhitelist" not in cal:
#print "LUC (cal):", cal
cal["PreventInvitationsWhitelist"] = None
self.prefs.set("PreventInvitationsWhitelist", None)
if "PreventInvitations" not in cal:
cal["PreventInvitations"] = 0
self.prefs.set("PreventInvitations", 0)
def tearDown(self):
self.prefs.set("autoReplyText", "")
#self.prefs.set("autoReplyText", "")
self.prefs.set('PreventInvitations', 0)
self.prefs.set("PreventInvitationsWhitelist", None)
#- Manual Cleanup, not called because classs is not derived from unittest
+4 -6
View File
@@ -21,8 +21,8 @@ class UIPostsTests(unittest.TestCase):
self.cookie = sogoLogin.getAuthCookie(hostname, port, username, password)
def _urlPostData(self, client, url, data, exp_status=200):
post = webdavlib.HTTPPOST(url, data)
post.content_type = "application/x-www-form-urlencoded"
post = webdavlib.HTTPPOST(url, simplejson.dumps(data))
post.content_type = "application/json"
post.cookie = self.cookie
client.execute(post)
@@ -43,16 +43,14 @@ class UIPostsTests(unittest.TestCase):
""" Add Web Calendar """
ret=True
data = "url=%s" % webCalendarURL
data = {"url":"%s" % webCalendarURL}
calendarBaseURL="/SOGo/so/%s/Calendar" % username
addWebCalendarURL = "%s/addWebCalendar" % calendarBaseURL
response = self._urlPostData(self.client, addWebCalendarURL, data)
respJSON = simplejson.loads(response['body'])
folderID = respJSON['folderID']
calID = respJSON['id']
#sogo1:Calendar/C07-5006F300-1-370E2480
(_, calID) = folderID.split('/', 1)
self.assertNotEqual(calID, None)
# reload the cal
+10 -8
View File
@@ -408,16 +408,17 @@
- (id <WOActionResults>) copyAction
{
WORequest *request;
id <WOActionResults> response;
NSString *destinationFolderId;
NSArray *contactsId;
NSDictionary *data;
NSException *ex;
request = [context request];
data = [[[context request] contentAsString] objectFromJSONString];
contactsId = [data objectForKey: @"uids"];
destinationFolderId = [data objectForKey: @"folder"];
if ((destinationFolderId = [request formValueForKey: @"folder"]) &&
(contactsId = [request formValuesForKey: @"uid"]))
if (destinationFolderId && [contactsId count])
ex = [self _moveContacts: contactsId
toFolder: destinationFolderId
andKeepCopy: YES];
@@ -436,16 +437,17 @@
- (id <WOActionResults>) moveAction
{
WORequest *request;
id <WOActionResults> response;
NSString *destinationFolderId;
NSDictionary *data;
NSArray *contactsId;
NSException *ex;
request = [context request];
data = [[[context request] contentAsString] objectFromJSONString];
contactsId = [data objectForKey: @"uids"];
destinationFolderId = [data objectForKey: @"folder"];
if ((destinationFolderId = [request formValueForKey: @"folder"])
&& (contactsId = [request formValuesForKey: @"uid"]))
if (destinationFolderId && [contactsId count])
ex = [self _moveContacts: contactsId
toFolder: destinationFolderId
andKeepCopy: NO];
+6 -34
View File
@@ -440,40 +440,6 @@
return ([[self productCSSURL] length] > 0);
}
- (BOOL) _moduleIs: (NSString *) moduleName
{
NSString *frameworkName;
frameworkName = [[context page] frameworkName];
return [frameworkName isEqualToString: moduleName];
}
- (BOOL) isCalendar
{
return [self _moduleIs: @"SchedulerUI"];
}
- (BOOL) isContacts
{
return [self _moduleIs: @"ContactsUI"];
}
- (BOOL) isMail
{
return [self _moduleIs: @"MailerUI"];
}
- (BOOL) isPreferences
{
return [self _moduleIs: @"PreferencesUI"];
}
- (BOOL) isAdministration
{
return [self _moduleIs: @"AdministrationUI"];
}
- (void) setToolbar: (NSString *) newToolbar
{
ASSIGN (toolbar, newToolbar);
@@ -509,6 +475,12 @@
return [[context activeUser] cn];
}
- (NSString *) userEmail
{
return [[context activeUser] systemEmail];
}
- (BOOL) canLogoff
{
BOOL canLogoff;
@@ -43,6 +43,12 @@
"Copy To" = "Copy To";
"Add to:" = "Add to:";
/* Number of selected contacts in list */
"selected" = "selected";
/* Empty right pane */
"No contact selected" = "No contact selected";
/* Tooltips */
"Create a new address book card" = "Create a new address book card";
+11 -6
View File
@@ -367,6 +367,8 @@ static Class SOGoContactGCSEntryK = Nil;
day = [date dayOfMonth];
[card setBday: [NSString stringWithFormat: @"%.4d%.2d%.2d", year, month, day]];
}
else
[card setBday: nil];
if ([[attributes objectForKey: @"addresses"] isKindOfClass: [NSArray class]])
{
@@ -430,8 +432,9 @@ static Class SOGoContactGCSEntryK = Nil;
{
if ([attrs isKindOfClass: [NSDictionary class]])
{
element = [card elementWithTag: @"tel" ofType: [attrs objectForKey: @"type"]];
[element setSingleValue: [attrs objectForKey: @"value"] forKey: @""];
[card addElementWithTag: @"tel"
ofType: [attrs objectForKey: @"type"]
withValue: [attrs objectForKey: @"value"]];
}
}
}
@@ -448,8 +451,9 @@ static Class SOGoContactGCSEntryK = Nil;
{
if ([o isKindOfClass: [NSDictionary class]])
{
element = [card elementWithTag: @"email" ofType: [o objectForKey: @"type"]];
[element setSingleValue: [o objectForKey: @"value"] forKey: @""];
[card addElementWithTag: @"email"
ofType: [o objectForKey: @"type"]
withValue: [o objectForKey: @"value"]];
}
}
}
@@ -466,8 +470,9 @@ static Class SOGoContactGCSEntryK = Nil;
{
if ([attrs isKindOfClass: [NSDictionary class]])
{
element = [card elementWithTag: @"url" ofType: [attrs objectForKey: @"type"]];
[element setSingleValue: [attrs objectForKey: @"value"] forKey: @""];
[card addElementWithTag: @"url"
ofType: [attrs objectForKey: @"type"]
withValue: [attrs objectForKey: @"value"]];
}
}
}
+7 -13
View File
@@ -112,15 +112,11 @@
request = [context request];
rc = [NSMutableDictionary dictionary];
data = [request formValueForKey: @"contactsFile"];
if ([data respondsToSelector: @selector(isEqualToString:)])
fileContent = (NSString *) data;
else
{
fileContent = [[NSString alloc] initWithData: (NSData *) data
encoding: NSUTF8StringEncoding];
[fileContent autorelease];
}
data = [[[[[request httpRequest] body] parts] lastObject] body];
fileContent = [[NSString alloc] initWithData: (NSData *) data
encoding: NSUTF8StringEncoding];
[fileContent autorelease];
if (fileContent && [fileContent length])
{
@@ -132,12 +128,10 @@
imported = 0;
}
[rc setObject: [NSNumber numberWithInt: imported]
forKey: @"imported"];
[rc setObject: [NSNumber numberWithInt: imported] forKey: @"imported"];
response = [self responseWithStatus: 200];
[response setHeader: @"text/html"
forKey: @"content-type"];
[response setHeader: @"text/html" forKey: @"content-type"];
[(WOResponse*)response appendContentString: [rc jsonRepresentation]];
return response;
@@ -20,14 +20,6 @@
#import <SOGoUI/UIxComponent.h>
@class NSString;
@class SOGoContactGCSFolder;
@interface UIxContactFolderLinksTemplate : UIxComponent
{
SOGoContactGCSFolder *addressBook;
NSString *baseCardDAVURL, *basePublicCardDAVURL;
}
@end
@@ -24,69 +24,6 @@
@implementation UIxContactFolderLinksTemplate
- (id) init
{
if ((self = [super init]))
{
addressBook = [self clientObject];
baseCardDAVURL = nil;
basePublicCardDAVURL = nil;
}
return self;
}
- (void) dealloc
{
[baseCardDAVURL release];
[basePublicCardDAVURL release];
[super dealloc];
}
- (NSString *) _baseCardDAVURL
{
NSString *davURL;
if (!baseCardDAVURL)
{
davURL = [[addressBook realDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
baseCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
baseCardDAVURL = davURL;
[baseCardDAVURL retain];
}
return baseCardDAVURL;
}
- (NSString *) cardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _baseCardDAVURL]];
}
- (NSString *) _basePublicCardDAVURL
{
NSString *davURL;
if (!basePublicCardDAVURL)
{
davURL = [[addressBook publicDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
basePublicCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
basePublicCardDAVURL = davURL;
[basePublicCardDAVURL retain];
}
return basePublicCardDAVURL;
}
- (NSString *) publicCardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _basePublicCardDAVURL]];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
+43 -11
View File
@@ -275,32 +275,70 @@ Class SOGoContactSourceFolderK, SOGoGCSFolderK;
return [[self queryParameterForKey: @"popup"] boolValue];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
return [[SOGoSystemDefaults sharedSystemDefaults] enablePublicAccess];
}
- (NSString *) contactFolders
{
SOGoContactFolders *folderContainer;
NSArray *folders;
NSMutableDictionary *urls, *acls;
NSMutableArray *foldersAttrs;
NSString *userLogin, *owner;
NSArray *folders, *allACLs;
NSDictionary *folderAttrs;
id currentFolder;
BOOL objectCreator, objectEditor, objectEraser;
int max, i;
userLogin = [[context activeUser] login];
folderContainer = [self clientObject];
// return [folderContainer subFolders];
folders = [folderContainer subFolders];
max = [folders count];
foldersAttrs = [NSMutableArray arrayWithCapacity: max];
urls = nil;
for (i = 0; i < max; i++)
{
currentFolder = [folders objectAtIndex: i];
owner = [currentFolder ownerInContext: context];
// We extract URLs for this address book
if ([currentFolder respondsToSelector: @selector(cardDavURL)])
{
urls = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[currentFolder cardDavURL], @"cardDavURL",
nil];
if ([self isPublicAccessEnabled])
{
[urls setObject: [currentFolder publicCardDavURL] forKey: @"publicCardDavURL"];
}
}
// We extract ACLs for this address book
allACLs = ([owner isEqualToString: userLogin] ? nil : [currentFolder aclsForUser: userLogin]);
objectCreator = ([owner isEqualToString: userLogin] || [allACLs containsObject: SOGoRole_ObjectCreator]);
objectEditor = ([owner isEqualToString: userLogin] || [allACLs containsObject: SOGoRole_ObjectEditor]);
objectEraser = ([owner isEqualToString: userLogin] || [allACLs containsObject: SOGoRole_ObjectEraser]);
acls = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool: objectCreator], @"objectCreator",
[NSNumber numberWithBool: objectEditor], @"objectEditor",
[NSNumber numberWithBool: objectEraser], @"objectEraser", nil];
// NOTE: keep urls as the last key/value here, to avoid chopping the dictionary
// if it is not a GCS folder
folderAttrs = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat: @"%@", [currentFolder nameInContainer]], @"id",
[currentFolder displayName], @"name",
[currentFolder ownerInContext: context], @"owner",
owner, @"owner",
[NSNumber numberWithBool: [currentFolder isKindOfClass: SOGoGCSFolderK]], @"isEditable",
[NSNumber numberWithBool: [currentFolder isKindOfClass: SOGoContactSourceFolderK]
&& ![currentFolder isPersonalSource]], @"isRemote",
acls, @"acls",
urls, @"urls",
nil];
[foldersAttrs addObject: folderAttrs];
}
@@ -419,12 +457,6 @@ Class SOGoContactSourceFolderK, SOGoGCSFolderK;
return [super defaultAction];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
return [[SOGoSystemDefaults sharedSystemDefaults] enablePublicAccess];
}
@end
@interface UIxContactViewTemplate : UIxComponent
+3 -4
View File
@@ -838,11 +838,10 @@
o = [card birthday];
if (o)
{
NSNumber *time = [NSNumber numberWithInt: [o timeIntervalSince1970]];
[data setObject: time forKey: @"birthday"];
[data setObject: [NSNumber numberWithInt: [o timeIntervalSince1970]]
forKey: @"birthday"];
}
// o = [card source];
// if (o) [data setObject: o forKey: @"source"];
o = [card tz];
if (o) [data setObject: o forKey: @"tz"];
+31 -1
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2006-2014 Inverse inc.
Copyright (C) 2006-2015 Inverse inc.
This file is part of SOGo.
@@ -39,6 +39,9 @@
#import <Contacts/SOGoContactFolder.h>
#import <Contacts/SOGoContactFolders.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserSettings.h>
#import <NGCards/NGVCard.h>
#import <NGCards/NGVList.h>
#import <SoObjects/Contacts/SOGoContactGCSEntry.h>
@@ -85,6 +88,31 @@
return s;
}
- (void) saveSortValue
{
NSMutableDictionary *contactSettings;
NSString *ascending, *sort;
SOGoUserSettings *us;
sort = [[context request] formValueForKey: @"sort"];
ascending = [[context request] formValueForKey: @"asc"];
if ([sort length])
{
us = [[context activeUser] userSettings];
contactSettings = [us objectForKey: @"Contact"];
// Must create if it doesn't exist
if (!contactSettings)
{
contactSettings = [NSMutableDictionary dictionary];
[us setObject: contactSettings forKey: @"Contact"];
}
[contactSettings setObject: [NSArray arrayWithObjects: [sort lowercaseString], [NSString stringWithFormat: @"%d", [ascending intValue]], nil]
forKey: @"SortingState"];
[us synchronize];
}
}
- (NSArray *) contactInfos
{
id <SOGoContactFolder> folder;
@@ -97,6 +125,8 @@
WORequest *rq;
unsigned int i;
[self saveSortValue];
if (!contactInfos)
{
folder = [self clientObject];
+1 -3
View File
@@ -1,8 +1,6 @@
/* UIxMailPartTextViewer.h - this file is part of $PROJECT_NAME_HERE$
*
* Copyright (C) 2006 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Copyright (C) 2006-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
+19 -20
View File
@@ -1,23 +1,22 @@
/*
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
OGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
/* UIxMailPartTextViewer.m - this file is part of $PROJECT_NAME_HERE$
*
* Copyright (C) 2006-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
UIxMailPartTextViewer
@@ -65,6 +65,9 @@
"Account: " = "Account: ";
"Shared Account: " = "Shared Account: ";
/* Empty right pane */
"No message selected" = "No message selected";
/* acls */
"Access rights to" = "Access rights to";
"For user" = "For user";
+7 -7
View File
@@ -92,7 +92,7 @@
}
else
{
errorFormat = [self labelForKey: @"The folder with name \"%@\" could not be created."];
errorFormat = [self labelForKey: @"The folder with name \"%@\" could not be created." inContext: context];
jsonResponse = [NSDictionary dictionaryWithObject: [NSString stringWithFormat: errorFormat, folderName]
forKey: @"error"];
response = [self responseWithStatus: 500
@@ -101,7 +101,7 @@
}
else
{
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing 'name' parameter."]
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing 'name' parameter." inContext: context]
forKey: @"error"];
response = [self responseWithStatus: 500
andJSONRepresentation: jsonResponse];
@@ -149,7 +149,7 @@
if (!newFolderName || [newFolderName length] == 0)
{
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing name parameter"]
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing name parameter" inContext: context]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [message jsonRepresentation]];
@@ -160,7 +160,7 @@
error = [co renameTo: newFolderName];
if (error)
{
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to rename folder."]
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to rename folder." inContext: context]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [message jsonRepresentation]];
@@ -251,7 +251,7 @@
error = [connection moveMailboxAtURL: srcURL toURL: destURL];
if (error)
{
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder."]
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder." inContext: context]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [jsonResponse jsonRepresentation]];
@@ -283,7 +283,7 @@
}
else
{
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder."]
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder." inContext: context]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [jsonResponse jsonRepresentation]];
@@ -374,7 +374,7 @@
{
uids = [value componentsSeparatedByString: @","];
response = [co archiveUIDs: uids
inArchiveNamed: [self labelForKey: @"Saved Messages.zip"]
inArchiveNamed: [self labelForKey: @"Saved Messages.zip" inContext: context]
inContext: context];
if (!response)
response = [self responseWith204];
+18 -13
View File
@@ -55,6 +55,7 @@
#import <Mailer/SOGoSentFolder.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSObject+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoDateFormatter.h>
#import <SOGo/SOGoUser.h>
@@ -131,7 +132,7 @@
else if ([now dayOfCommonEra] - [messageDate dayOfCommonEra] == 1)
{
// Yesterday
return [self labelForKey: @"Yesterday"];
return [self labelForKey: @"Yesterday" inContext: context];
}
else if ([now dayOfCommonEra] - [messageDate dayOfCommonEra] < 7)
{
@@ -190,10 +191,10 @@
s = [[s substringFromIndex: r.location+1]
stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([s hasPrefix: @"1"]) result = [self labelForKey: @"highest"];
else if ([s hasPrefix: @"2"]) result = [self labelForKey: @"high"];
else if ([s hasPrefix: @"4"]) result = [self labelForKey: @"low"];
else if ([s hasPrefix: @"5"]) result = [self labelForKey: @"lowest"];
if ([s hasPrefix: @"1"]) result = [self labelForKey: @"highest" inContext: context];
else if ([s hasPrefix: @"2"]) result = [self labelForKey: @"high" inContext: context];
else if ([s hasPrefix: @"4"]) result = [self labelForKey: @"low" inContext: context];
else if ([s hasPrefix: @"5"]) result = [self labelForKey: @"lowest" inContext: context];
}
}
}
@@ -242,7 +243,7 @@
{
NSString *s;
s = [self labelForKey:@"View Mail Folder"];
s = [self labelForKey:@"View Mail Folder" inContext: context];
s = [s stringByAppendingString:@": "];
s = [s stringByAppendingString:[self objectTitle]];
return s;
@@ -622,11 +623,12 @@
- (NSDictionary *) getUIDsInFolder: (SOGoMailFolder *) folder
withHeaders: (BOOL) includeHeaders
{
NSMutableDictionary *data;
NSArray *uids, *threadedUids, *headers;
NSRange r;
NSMutableDictionary *data;
SOGoMailAccount *account;
id quota;
NSRange r;
int count;
data = [NSMutableDictionary dictionary];
@@ -644,10 +646,14 @@
if (includeHeaders)
{
// Also retrieve the first headers, up to 'headersPrefetchMaxSize'
count = [[uids flattenedArray] count];
if (count > headersPrefetchMaxSize) count = headersPrefetchMaxSize;
NSArray *a;
a = [uids flattenedArray];
count = [a count];
if (count > headersPrefetchMaxSize)
count = headersPrefetchMaxSize;
r = NSMakeRange(0, count);
headers = [self getHeadersForUIDs: [[uids flattenedArray] subarrayWithRange: r]
headers = [self getHeadersForUIDs: [a subarrayWithRange: r]
inFolder: folder];
[data setObject: headers forKey: @"headers"];
@@ -744,7 +750,6 @@
noHeaders = [[[requestContent objectForKey: @"sortingAttributes"] objectForKey:@"noHeaders"] boolValue];
data = [self getUIDsInFolder: folder
withHeaders: !noHeaders];
[response appendContentString: [data jsonRepresentation]];
return response;
@@ -878,7 +883,7 @@
data = [NSDictionary dictionaryWithObjectsAndKeys:
@"No UID specified", @"error", nil];
return [self responseWithStatus: 404 /* Not Found */
andString: [data jsonRepresentation]];
andString: [data jsonRepresentation]];
}
uids = [data objectForKey: @"uids"];
+12
View File
@@ -118,6 +118,18 @@
return [accounts jsonRepresentation];
}
- (WOResponse *) mailAccountsAction
{
WOResponse *response;
NSString *s;
s = [self mailAccounts];
response = [self responseWithStatus: 200
andString: s];
return response;
}
- (NSString *) userNames
{
NSArray *accounts, *userNames;
+5
View File
@@ -342,6 +342,11 @@
actionClass = "UIxMailMainFrame";
actionName = "unseenCount";
};
mailAccounts = {
protectedBy = "View";
actionClass = "UIxMailMainFrame";
actionName = "mailAccounts";
};
// viewerTemplate = {
// protectedBy = "View";
// pageName = "UIxMailViewTemplate";
@@ -9,6 +9,7 @@
"Connect" = "Connect";
"Authentication Failed" = "Authentication Failed";
"Wrong username or password." = "Wrong username or password.";
"cookiesNotEnabled" = "You cannot login because your browser's cookies are disabled. Please enable cookies in your browser's settings and try again.";
+7 -6
View File
@@ -183,7 +183,6 @@
SOGoWebAuthenticator *auth;
SOGoAppointmentFolders *calendars;
SOGoUserDefaults *ud;
SOGoSystemDefaults *sd;
SOGoUser *loggedInUser;
NSDictionary *params;
NSString *username, *password, *language, *domain, *remoteHost;
@@ -226,13 +225,15 @@
response = [self responseWithStatus: 200
andJSONRepresentation: json];
// We get the proper username for cookie creation. If we are using a multidomain
// environment with SOGoEnableDomainBasedUID, we could have to append the domain
// to the username. Also when SOGoEnableDomainBasedUID is enabled, we could be in
// the DomainLessLogin situation, so we would NOT add the domain. -getUIDForEmail
// has all the logic for this, so lets use it.
if ([domain isNotNull])
{
sd = [SOGoSystemDefaults sharedSystemDefaults];
if ([sd enableDomainBasedUID] &&
[username rangeOfString: @"@"].location == NSNotFound)
username = [NSString stringWithFormat: @"%@@%@", username, domain];
username = [[SOGoUserManager sharedUserManager] getUIDForEmail: username];
}
authCookie = [auth cookieWithUsername: username
@@ -1,6 +1,7 @@
/* toolbar */
"Save and Close" = "Save and Close";
"Close" = "Close";
"Preferences saved" = "Preferences saved";
/* tabs */
"General" = "General";
+35 -20
View File
@@ -61,7 +61,8 @@ static SoProduct *preferencesProduct = nil;
SOGoUserDefaults *defaults;
NSMutableArray *accounts;
NSArray *categoryLabels;
NSDictionary *locale;
if (!preferencesProduct)
{
preferencesProduct = [[SoProduct alloc] initWithBundle:
@@ -78,7 +79,7 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoTimeZone"])
[[defaults source] setObject: [defaults timeZoneName] forKey: @"SOGoTimeZone"];
if (![[defaults source] objectForKey: @"SOGoShortDateFormat"])
[[defaults source] setObject: [defaults shortDateFormat] forKey: @"SOGoShortDateFormat"];
@@ -90,7 +91,7 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoLoginModule"])
[[defaults source] setObject: [defaults loginModule] forKey: @"SOGoLoginModule"];
if (![[defaults source] objectForKey: @"SOGoRefreshViewCheck"])
[[defaults source] setObject: [defaults refreshViewCheck] forKey: @"SOGoRefreshViewCheck"];
@@ -99,13 +100,13 @@ static SoProduct *preferencesProduct = nil;
//
if (![[defaults source] objectForKey: @"SOGoFirstDayOfWeek"])
[[defaults source] setObject: [NSNumber numberWithInt: [defaults firstDayOfWeek]] forKey: @"SOGoFirstDayOfWeek"];
if (![[defaults source] objectForKey: @"SOGoDayStartTime"])
[[defaults source] setObject: [NSString stringWithFormat: @"%02d:00", [defaults dayStartHour]] forKey: @"SOGoDayStartTime"];
if (![[defaults source] objectForKey: @"SOGoDayEndTime"])
[[defaults source] setObject: [NSString stringWithFormat: @"%02d:00", [defaults dayEndHour]] forKey: @"SOGoDayEndTime"];
if (![[defaults source] objectForKey: @"SOGoFirstWeekOfYear"])
[[defaults source] setObject: [defaults firstWeekOfYear] forKey: @"SOGoFirstWeekOfYear"];
@@ -114,20 +115,20 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoCalendarEventsDefaultClassification"])
[[defaults source] setObject: [defaults calendarEventsDefaultClassification] forKey: @"SOGoCalendarEventsDefaultClassification"];
if (![[defaults source] objectForKey: @"SOGoCalendarTasksDefaultClassification"])
[[defaults source] setObject: [defaults calendarTasksDefaultClassification] forKey: @"SOGoCalendarTasksDefaultClassification"];
if (![[defaults source] objectForKey: @"SOGoCalendarDefaultReminder"])
[[defaults source] setObject: [defaults calendarDefaultReminder] forKey: @"SOGoCalendarDefaultReminder"];
// Populate default calendar categories, based on the user's preferred language
if (![defaults calendarCategories])
{
categoryLabels = [[[self labelForKey: @"calendar_category_labels" withResourceManager: [preferencesProduct resourceManager]]
componentsSeparatedByString: @","]
sortedArrayUsingSelector: @selector (localizedCaseInsensitiveCompare:)];
[defaults setCalendarCategories: categoryLabels];
}
if (![defaults calendarCategoriesColors])
@@ -143,7 +144,7 @@ static SoProduct *preferencesProduct = nil;
[defaults setCalendarCategoriesColors: colors];
}
//
// Default Contacts preferences
//
@@ -154,10 +155,10 @@ static SoProduct *preferencesProduct = nil;
categoryLabels = [[[self labelForKey: @"contacts_category_labels" withResourceManager: [preferencesProduct resourceManager]]
componentsSeparatedByString: @","]
sortedArrayUsingSelector: @selector (localizedCaseInsensitiveCompare:)];
if (!categoryLabels)
categoryLabels = [NSArray array];
[defaults setContactsCategories: categoryLabels];
}
@@ -172,27 +173,27 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoMailReplyPlacement"])
[[defaults source] setObject: [defaults mailReplyPlacement] forKey: @"SOGoMailReplyPlacement"];
if (![[defaults source] objectForKey: @"SOGoMailSignaturePlacement"])
[[defaults source] setObject: [defaults mailSignaturePlacement] forKey: @"SOGoMailSignaturePlacement"];
if (![[defaults source] objectForKey: @"SOGoMailComposeMessageType"])
[[defaults source] setObject: [defaults mailComposeMessageType] forKey: @"SOGoMailComposeMessageType"];
if (![[defaults source] objectForKey: @"SOGoMailDisplayRemoteInlineImages"])
[[defaults source] setObject: [defaults mailDisplayRemoteInlineImages] forKey: @"SOGoMailDisplayRemoteInlineImages"];
// Populate default mail lablels, based on the user's preferred language
if (![[defaults source] objectForKey: @"SOGoMailLabelsColors"])
{
SOGoMailLabel *label;
NSArray *labels;
id v;
int i;
v = [defaults mailLabelsColors];
labels = [SOGoMailLabel labelsFromDefaults: v component: self];
v = [NSMutableDictionary dictionary];
for (i = 0; i < [labels count]; i++)
@@ -201,7 +202,7 @@ static SoProduct *preferencesProduct = nil;
[v setObject: [NSArray arrayWithObjects: [label label], [label color], nil]
forKey: [label name]];
}
[defaults setMailLabelsColors: v];
}
@@ -214,6 +215,10 @@ static SoProduct *preferencesProduct = nil;
//
values = [[[[defaults source] values] mutableCopy] autorelease];
// Add locale code (used by CK Editor)
locale = [[preferencesProduct resourceManager] localeForLanguageNamed: [defaults language]];
[values setObject: [locale objectForKey: @"NSLocaleCode"] forKey: @"LocaleCode"];
accounts = [NSMutableArray arrayWithArray: [values objectForKey: @"AuxiliaryMailAccounts"]];
account = [[[context activeUser] mailAccounts] objectAtIndex: 0];
if (![account objectForKey: @"receipts"])
@@ -250,6 +255,16 @@ static SoProduct *preferencesProduct = nil;
forKey: @"PreventInvitationsWhitelist"];
}
// Initialize some default values
if (![settings objectForKey: @"Calendar"])
[settings setObject: [NSMutableDictionary dictionary] forKey: @"Calendar"];
if (![settings objectForKey: @"Contact"])
[settings setObject: [NSMutableDictionary dictionary] forKey: @"Contact"];
if (![settings objectForKey: @"Mail"])
[settings setObject: [NSMutableDictionary dictionary] forKey: @"Mail"];
return [self _makeResponse: [[settings source] values]];
}
+1 -1
View File
@@ -1064,7 +1064,7 @@ static NSArray *reminderValues = nil;
if (!capabilities)
{
if ([self _sieveClient])
if ([self isSieveScriptsEnabled] && [self _sieveClient])
capabilities = [[self _sieveClient] capabilities];
else
capabilities = [NSArray array];
+49 -20
View File
@@ -94,7 +94,7 @@ static NSArray *tasksFields = nil;
@"c_classification", @"c_category",
@"c_partmails", @"c_partstates", @"c_owner",
@"c_iscycle", @"c_nextalarm",
@"c_recurrence_id", @"isException", @"editable",
@"c_recurrence_id", @"isException", @"viewable", @"editable",
@"erasable", @"ownerIsOrganizer", @"c_description", nil];
[eventsFields retain];
}
@@ -104,7 +104,7 @@ static NSArray *tasksFields = nil;
@"calendarName",
@"c_status", @"c_title", @"c_enddate",
@"c_classification", @"c_location", @"c_category",
@"editable", @"erasable",
@"viewable", @"editable", @"erasable",
@"c_priority", @"c_owner",
@"c_iscycle", @"c_nextalarm",
@"c_recurrence_id", @"isException", @"c_description", nil];
@@ -258,7 +258,7 @@ static NSArray *tasksFields = nil;
labelKey = [NSString stringWithFormat: @"%@_class%@",
type, [component objectForKey: @"c_classification"]];
[component setObject: [self labelForKey: labelKey]
[component setObject: [self labelForKey: labelKey inContext: context]
forKey: @"c_title"];
}
@@ -441,21 +441,35 @@ static NSArray *tasksFields = nil;
}
else
{
id foo;
foo = [currentFolder fetchCoreInfosFrom: startDate
currentInfos = [[currentFolder fetchCoreInfosFrom: startDate
to: endDate
title: value
component: component];
currentInfos = [foo objectEnumerator];
component: component]
objectEnumerator];
}
owner = [currentFolder ownerInContext: context];
ownerUser = [SOGoUser userWithLogin: owner];
isErasable = ([owner isEqualToString: userLogin] || [[currentFolder aclsForUser: userLogin] containsObject: SOGoRole_ObjectEraser]);
while ((newInfo = [currentInfos nextObject]))
{
if ([fields containsObject: @"viewable"])
{
if ([owner isEqualToString: userLogin])
[newInfo setObject: [NSNumber numberWithInt: 1] forKey: @"viewable"];
else
{
role = [currentFolder roleForComponentsWithAccessClass: [[newInfo objectForKey: @"c_classification"] intValue]
forUser : userLogin];
if ([role isEqualToString: @"ComponentViewer"])
[newInfo setObject: [NSNumber numberWithInt: 1] forKey: @"viewable"];
else
[newInfo setObject: [NSNumber numberWithInt: 0] forKey: @"viewable"];
}
}
if ([fields containsObject: @"editable"])
{
if (folderIsRemote)
@@ -548,19 +562,19 @@ static NSArray *tasksFields = nil;
{
// Same day
if (forAllDay)
return [self labelForKey: @"Today"];
return [self labelForKey: @"Today" inContext: context];
else
return [dateFormatter formattedTime: date];
}
else if ([now dayOfCommonEra] - [date dayOfCommonEra] == 1)
{
// Yesterday
return [self labelForKey: @"Yesterday"];
return [self labelForKey: @"Yesterday" inContext: context];
}
else if ([date dayOfCommonEra] - [now dayOfCommonEra] == 1)
{
// Tomorrow
return [self labelForKey: @"Tomorrow"];
return [self labelForKey: @"Tomorrow" inContext: context];
}
else if (abs(delta = [date dayOfCommonEra] - [now dayOfCommonEra]) < 7)
{
@@ -573,7 +587,7 @@ static NSArray *tasksFields = nil;
return dayOfWeek;
else
// With the past 7 days
return [NSString stringWithFormat: [self labelForKey: @"last %@"], dayOfWeek];
return [NSString stringWithFormat: [self labelForKey: @"last %@" inContext: context], dayOfWeek];
}
else
{
@@ -652,10 +666,11 @@ static NSArray *tasksFields = nil;
return [self _responseWithData: data];
}
- (void) saveFilterValue
- (void) saveFilterValue: (NSString *) submodule
{
NSString *filter;
SOGoUserSettings *us;
NSMutableDictionary *calendarSettings;
filter = [[context request] formValueForKey: @"filterpopup"];
if ([filter length]
@@ -663,12 +678,20 @@ static NSArray *tasksFields = nil;
&& ![filter isEqualToString: @"view_future"])
{
us = [[context activeUser] userSettings];
[us setObject: filter forKey: @"CalendarDefaultFilter"];
calendarSettings = [us objectForKey: @"Calendar"];
// Must create if it doesn't exist
if (!calendarSettings)
{
calendarSettings = [NSMutableDictionary dictionary];
[us setObject: calendarSettings forKey: @"Calendar"];
}
//[us setObject: filter forKey: submodule];
[calendarSettings setObject: filter forKey: submodule];
[us synchronize];
}
}
- (void) saveSortValue
- (void) saveSortValue: (NSString *) submodule
{
NSString *sort, *ascending;
SOGoUserSettings *us;
@@ -686,7 +709,8 @@ static NSArray *tasksFields = nil;
calendarSettings = [NSMutableDictionary dictionary];
[us setObject: calendarSettings forKey: @"Calendar"];
}
[calendarSettings setObject: [NSArray arrayWithObjects: [sort lowercaseString], [NSString stringWithFormat: @"%d", (ascending?1:0)], nil] forKey: @"SortingState"];
[calendarSettings setObject: [NSArray arrayWithObjects: [sort lowercaseString], [NSString stringWithFormat: @"%d", [ascending intValue]], nil]
forKey: submodule];
[us synchronize];
}
}
@@ -722,10 +746,11 @@ static NSArray *tasksFields = nil;
* @apiSuccess (Success 200) {String[]} events.c_partmails Participants email addresses
* @apiSuccess (Success 200) {String[]} events.c_partstates Participants states
* @apiSuccess (Success 200) {String} events.c_owner Event's owner
* @apiSuccess (Success 200) {Number} events.c_iscycle 1 if the event is cyclic
* @apiSuccess (Success 200) {Number} events.c_iscycle 1 if the event is cyclic/recurring
* @apiSuccess (Success 200) {Number} events.c_nextalarm Epoch time of next alarm
* @apiSuccess (Success 200) {String} [events.c_recurrence_id] Recurrence ID if event is cyclic
* @apiSuccess (Success 200) {Number} events.isException 1 if recurrence is an exception
* @apiSuccess (Success 200) {Number} events.viewable 1 if active user can view the event
* @apiSuccess (Success 200) {Number} events.editable 1 if active user can edit the event
* @apiSuccess (Success 200) {Number} events.erasable 1 if active user can erase the event
* @apiSuccess (Success 200) {Number} events.ownerIsOrganizer 1 if owner is also the organizer
@@ -743,8 +768,8 @@ static NSArray *tasksFields = nil;
NSString *sort, *ascending;
[self _setupContext];
[self saveFilterValue];
[self saveSortValue];
[self saveFilterValue: @"EventsFilterState"];
[self saveSortValue: @"EventsSortingState"];
newEvents = [NSMutableArray array];
events = [[self _fetchFields: eventsFields
@@ -1244,6 +1269,7 @@ _computeBlocksPosition (NSArray *blocks)
* @apiSuccess (Success 200) {Number} events.c_nextalarm Epoch time of next alarm
* @apiSuccess (Success 200) {String} [events.c_recurrence_id] Recurrence ID if event is cyclic
* @apiSuccess (Success 200) {Number} events.isException 1 if recurrence is an exception
* @apiSuccess (Success 200) {Number} events.viewable 1 if active user can view the event
* @apiSuccess (Success 200) {Number} events.editable 1 if active user can edit the event
* @apiSuccess (Success 200) {Number} events.erasable 1 if active user can erase the event
* @apiSuccess (Success 200) {Number} events.ownerIsOrganizer 1 if owner is also the organizer
@@ -1408,6 +1434,7 @@ _computeBlocksPosition (NSArray *blocks)
* @apiSuccess (Success 200) {Number} tasks.c_classification 0: Public, 1: Private, 2: Confidential
* @apiSuccess (Success 200) {String} tasks.c_location Location
* @apiSuccess (Success 200) {String} tasks.c_category Category
* @apiSuccess (Success 200) {Number} tasks.viewable 1 if task is viewable by the active user
* @apiSuccess (Success 200) {Number} tasks.editable 1 if task is editable by the active user
* @apiSuccess (Success 200) {Number} tasks.erasable 1 if task is erasable by the active user
* @apiSuccess (Success 200) {String} tasks.c_priority Priority (0-9)
@@ -1436,6 +1463,8 @@ _computeBlocksPosition (NSArray *blocks)
filteredTasks = [NSMutableArray array];
[self _setupContext];
[self saveFilterValue: @"TasksFilterState"];
[self saveSortValue: @"TasksSortingState"];
startSecs = (unsigned int) [startDate timeIntervalSince1970];
endsSecs = (unsigned int) [endDate timeIntervalSince1970];
+4 -1
View File
@@ -33,6 +33,7 @@
#import <NGCards/iCalPerson.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
@@ -261,12 +262,14 @@
- (WOResponse *) saveSelectedListAction
{
WORequest *request;
NSDictionary *params;
NSString *selectedList;
[self _setupContext];
request = [context request];
params = [[request contentAsString] objectFromJSONString];
selectedList = [request formValueForKey: @"list"];
selectedList = [params objectForKey: @"list"];
[moduleSettings setObject: selectedList
forKey: @"SelectedList"];
[us synchronize];
@@ -20,14 +20,6 @@
#import <SOGoUI/UIxComponent.h>
@class NSString;
@class SOGoAppointmentFolder;
@interface UIxCalendarFolderLinksTemplate : UIxComponent
{
SOGoAppointmentFolder *calendar;
NSString *baseCalDAVURL, *basePublicCalDAVURL;
}
@end
@@ -28,89 +28,6 @@
@implementation UIxCalendarFolderLinksTemplate
- (id) init
{
if ((self = [super init]))
{
calendar = [self clientObject];
baseCalDAVURL = nil;
basePublicCalDAVURL = nil;
}
return self;
}
- (void) dealloc
{
[baseCalDAVURL release];
[basePublicCalDAVURL release];
[super dealloc];
}
- (NSString *) _baseCalDAVURL
{
NSString *davURL;
if (!baseCalDAVURL)
{
davURL = [[calendar realDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
baseCalDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
baseCalDAVURL = davURL;
[baseCalDAVURL retain];
}
return baseCalDAVURL;
}
- (NSString *) calDavURL
{
return [NSString stringWithFormat: @"%@/", [self _baseCalDAVURL]];
}
- (NSString *) webDavICSURL
{
return [calendar webDavICSURL];
}
- (NSString *) webDavXMLURL
{
return [calendar webDavXMLURL];
}
- (NSString *) _basePublicCalDAVURL
{
NSString *davURL;
if (!basePublicCalDAVURL)
{
davURL = [[calendar publicDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
basePublicCalDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
basePublicCalDAVURL = davURL;
[basePublicCalDAVURL retain];
}
return basePublicCalDAVURL;
}
- (NSString *) publicCalDavURL
{
return [NSString stringWithFormat: @"%@/", [self _basePublicCalDAVURL]];
}
- (NSString *) publicWebDavICSURL
{
return [calendar publicWebDavICSURL];
}
- (NSString *) publicWebDavXMLURL
{
return [calendar publicWebDavXMLURL];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
@@ -118,14 +35,4 @@
enablePublicAccess];
}
- (BOOL) isWebCalendar
{
return ([calendar isKindOfClass: [SOGoWebAppointmentFolder class]]);
}
- (NSString *) webCalendarURL
{
return [calendar folderPropertyValueInCategory: @"WebCalendars"];
}
@end
+1 -1
View File
@@ -842,7 +842,7 @@ static NSArray *reminderValues = nil;
clientObject = [self clientObject];
ownerUser = [SOGoUser userWithLogin: [clientObject ownerInContext: context]];
if ([clientObject isKindOfClass: [SOGoWebAppointmentFolder class]])
if ([[clientObject container] isKindOfClass: [SOGoWebAppointmentFolder class]])
rc = componentReadableOnly;
else
{
+1 -1
View File
@@ -193,7 +193,7 @@
actionName = "saveUserRights";
};
links = {
protectedBy = "View";
protectedBy = "Access Contents Information";
pageName = "UIxCalendarFolderLinksTemplate";
};
};
@@ -5,8 +5,8 @@
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label">
<md-content md-scroll-y="true" class="md-padding md-whiteframe-z1 bg-sogoPaper-50">
<div class="editor md-padding">
<md-content md-scroll-y="md-scroll-y" class="viewer md-padding md-whiteframe-z1 bg-sogoPaper-50">
<!-- <div class="editor md-padding">-->
<hgroup class="header">
<h1 class="sg-md-display-2--light" ng-bind-html="editor.card.$fullname()"><!-- fullname --></h1>
<h6 class="sg-md-display-2-subheader">{{editor.card.$description()}}</h6>
@@ -115,7 +115,6 @@
<md-icon>remove_circle</md-icon>
</md-button>
</div>
</div>
<div class="md-layout-margin" layout="row" layout-align="start center">
<md-button class="sg-icon-button" type="button" ng-click="editor.addEmail()">
@@ -128,12 +127,25 @@
</div>
<!-- birthday -->
<md-input-container>
<label>
<var:string label:value="Birthday"/>
</label>
<input type="date" ng-model="editor.card.birthday"/>
</md-input-container>
<div class="section">
<div layout="row" layout-align="space-between end" ng-show="editor.card.birthday">
<label>
<var:string label:value="Birthday"/>
</label>
<md-datepicker ng-model="editor.card.birthday"><!-- birthday --></md-datepicker>
<md-button class="sg-icon-button" type="button" ng-click="editor.card.$delete('birthday', -1)">
<md-icon>remove_circle</md-icon>
</md-button>
</div>
<div class="md-layout-margin" layout="row" layout-align="start center" ng-hide="editor.card.birthday">
<md-button class="sg-icon-button" type="button" ng-click="editor.addBirthday()">
<md-icon>add_circle</md-icon>
</md-button>
<label class="button-label">
<var:string label:value="Add Birthday"/>
</label>
</div>
</div>
<!-- phones -->
<div class="section">
@@ -342,7 +354,7 @@
</var:if>
</div>
</form>
</div>
<!-- </div> -->
</md-content>
</container>
@@ -6,14 +6,15 @@
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label"
>
<md-dialog flex="40">
<md-dialog flex="60" flex-sm="100">
<md-toolbar class="sg-padded">
<md-icon class="material-icons sg-icon-toolbar-bg">link</md-icon>
<div class="md-toolbar-tools">
<div class="pseudo-input-container md-flex">
<label class="pseudo-input-label"><var:string label:value="Links to this Address Book"/></label>
<div class="pseudo-input-field sg-md-title"><var:string value="addressBook.displayName"/></div>
<div class="pseudo-input-field sg-md-title">{{links.addressbook.name}}</div>
</div>
<md-button class="md-icon-button" ng-click="close()">
<md-button class="md-icon-button" ng-click="links.close()">
<md-icon aria-label="Close dialog">close</md-icon>
</md-button>
</div>
@@ -24,7 +25,7 @@
<div class="sg-md-title"><var:string label:value="Authenticated User Access"/></div>
<md-input-container>
<label><var:string label:value="CardDAV URL"/></label>
<input var:value="cardDavURL" ng-readonly="true"/>
<input type="text" ng-model="links.addressbook.urls.cardDavURL" ng-readonly="true"/>
</md-input-container>
</div>
<var:if condition="isPublicAccessEnabled">
@@ -32,7 +33,7 @@
<div class="sg-md-title"><var:string label:value="Public Access"/></div>
<md-input-container>
<label><var:string label:value="CardDAV URL"/></label>
<input var:value="publicCardDavURL" ng-readonly="true"/>
<input type="text" ng-model="links.addressbook.urls.publicCardDavURL" ng-readonly="true"/>
</md-input-container>
</div>
</var:if>
+93 -131
View File
@@ -9,94 +9,16 @@
className="UIxPageFrame"
title="title"
const:userDefaultsKeys="SOGoContactsCategories"
const:jsFiles="Common.js, Preferences.services.js, Contacts.js, Contacts.services.js">
const:jsFiles="vendor/ckeditor/ckeditor.js, vendor/ckeditor/ck.js, Common.js, Preferences.services.js, Mailer.services.js, Contacts.js, Contacts.services.js, vendor/angular-file-upload.min.js">
<script type="text/javascript">
var contactFolders = <var:string value="contactFolders" const:escapeHTML="NO" />;
</script>
<!--
<div id="uploadDialog" style="display: none" class="dialog left">
<div>
<h3><var:string label:value="Import Cards"/></h3>
<p class="prompt"><var:string label:value="Select a vCard or LDIF file."/></p>
<form const:href="" name="uploadForm" id="uploadForm" method="post"
enctype="multipart/form-data"
onsubmit="return AIM.submit(this, {'onStart' : validateUploadForm,
'onComplete' : uploadCompleted})">
<p class="prompt"><input type="file" name="contactsFile" id="contactsFile" class="textField" required="required" />
<a href="#" class="button actionButton" id="uploadSubmit"
onclick="if($('uploadForm').onsubmit()) $('uploadForm').submit()">
<span><var:string label:value="Upload" /></span></a>
<a href="#" class="button" const:id="uploadCancel">
<span><var:string label:value="Cancel" /></span></a></p>
</form>
<hr/>
</div>
</div>
<div id="uploadResults" dropdown-content="dropdown-content" class="f-dropdown">
<div>
<h3><var:string label:value="Import Cards"/></h3>
<p id="uploadResultsContent">!- empty -</p>
<p><a href="#" class="button" const:id="uploadOK">
<span><var:string label:value="Done" /></span></a></p>
<hr/>
</div>
</div>
<ul class="f-dropdown" dropdown-content="dropdown-content" id="contactMenu">
<li><var:string label:value="Properties" /></li>
<li><var:string label:value="Categories" /></li>
<li>!- separator -</li>
<li><var:string label:value="Write" /></li>
<li><var:string label:value="Instant Message" /></li>
<li>!- separator -</li>
<li><var:string label:value="Delete" /></li>
<li>!- separator -</li>
<li><var:string label:value="Move To" /></li>
<li><var:string label:value="Copy To" /></li>
<li><var:string label:value="Export" /></li>
<li><var:string label:value="View Raw Source" /></li>
</ul>
-->
<!--
MAIN CONTENT ROW
Content of the application view injected injected in the element bellow
MUST be the first html element after body
SHOULD be a main tag (with role="main")
Templates script tag wrappers goes next
-->
<main class="view md-layout-fill" ui-view="addressbooks" layout="row" ng-controller="navController"><!-- addressbooks list --></main>
<main class="view layout-fill" ui-view="addressbooks" layout="row" ng-controller="navController"><!-- addressbooks list --></main>
<!-- TEMPLATE SCRIPT WRAPPER -->
<script type="text/ng-template" id="UIxContactFoldersView">
<!-- dropdown menu for addressbook options button -->
<div id="addressbookProperties" class="f-dropdown icons-dropdown" ng-hide="true">
<ul class="button-group">
<li label:tooltip="Links to this Address Book"
tooltip-popup-delay="500">
<span class="button"
sg-dropdown-content-toggle="#addressbookLinks">
<i class="icon-hyperlink"><!-- links --></i>
</span>
</li>
<li label:tooltip="Rename"
tooltip-popup-delay="500">
<span class="button" ng-click="edit()">
<i class="icon-pencil"><!-- rename --></i>
</span>
</li>
<li label:tooltip="Import Cards"
tooltip-popup-delay="500">
<span class="button" ng-click="importCards()">
<i class="icon-file"><!-- import --></i>
</span>
</li>
</ul>
</div>
<!-- Sidenav -->
<md-sidenav id="left-sidenav" class="md-sidenav-left md-whiteframe-z1" md-component-id="left" md-is-locked-open="isGtMedium" layout="column">
<var:component className="UIxSidenavToolbarTemplate" />
@@ -106,7 +28,7 @@
<section>
<md-subheader class="sg-md-subheader">
<div layout="row" layout-align="space-between center">
<span><var:string label:value="AddressBooks"/></span>
<span><var:string label:value="Address Books"/></span>
<md-button class="sg-icon-button"
label:aria-label="New Addressbook..."
ng-click="app.newAddressbook()">
@@ -140,11 +62,22 @@
ng-click="$mdOpenMenu()"
md-menu-origin="md-menu-origin">more_vert</md-icon>
<md-menu-content width="2">
<md-menu-item>
<md-button type="button" ng-click="app.edit(folder)">
<var:string label:value="Rename"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button type="button" ng-click="app.confirmDelete()">
<var:string label:value="Delete"/>
</md-button>
</md-menu-item>
<md-menu-divider><!-- divider --></md-menu-divider>
<md-menu-item>
<md-button type="button" ng-click="app.importCards($event, folder)">
<var:string label:value="Import"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button type="button" ng-click="app.exportCards()">
<var:string label:value="Export"/>
@@ -205,6 +138,11 @@
ng-click="$mdOpenMenu()"
md-menu-origin="md-menu-origin">more_vert</md-icon>
<md-menu-content width="2">
<md-menu-item>
<md-button type="button" ng-click="app.edit(folder)">
<var:string label:value="Rename"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button type="button" ng-click="app.confirmDelete(folder)">
<var:string label:value="Delete"/>
@@ -234,17 +172,16 @@
<!-- </div> -->
</md-sidenav>
<section layout="column" class="sg-app-content md-layout-fill" ui-view="addressbook"><!-- contacts list --></section>
<section layout="column" class="sg-app-content layout-fill" ui-view="addressbook"><!-- contacts list --></section>
</script>
<script type="text/ng-template" id="addressbook">
<md-toolbar layout="column" layout-align="space-between start" class="md-tall toolbar-main">
<div class="md-toolbar-tools md-toolbar-tools-top sg-padded" layout="row" layout-align="space-between start">
<div class="md-toolbar-tools md-toolbar-tools-top" layout="row" layout-align="space-between start">
<var:component className="UIxTopnavToolbarTemplate" />
</div>
<div class="md-toolbar-tools" layout="row" layout-align="space-between center"
ng-show="addressbook.selectedFolder.$selectedCount() == 0">
<div class="view-list" layout="row" layout-align="space-between center"
ng-hide="addressbook.mode.search">
<div class="sg-toolbar-group">
@@ -295,9 +232,10 @@
</md-menu-item>
<md-menu-divider> <!-- divider --></md-menu-divider>
<md-menu-item >
<md-button ng-click="addressbook.sort()">
<md-button ng-href="#">
<md-checkbox
ng-model="addressbook.selectedFolder.$query.asc"
ng-change="addressbook.sort(addressbook.selectedFolder.constructor.$query.sort)"
ng-model="addressbook.selectedFolder.constructor.$query.asc"
ng-true-value="0"
ng-false-value="1"><var:string label:value="Descending Order"/></md-checkbox>
</md-button>
@@ -330,26 +268,51 @@
</md-input-container>
</div>
</div>
<div layout="row" layout-align="start center" ng-show="addressbook.selectedFolder.$selectedCount() > 0">
<md-button class="sg-icon-button" ng-click="addressbook.unselectCards()">
<md-icon>arrow_back</md-icon>
</md-button>
<label>{{addressbook.selectedFolder.$selectedCount()}} selected</label>
<!-- multiple selection mode -->
<div class="md-toolbar-tools" layout="row" layout-align="space-between center"
ng-show="addressbook.selectedFolder.$selectedCount() > 0">
<div class="view-list" layout="row" layout-align="space-between center">
<div class="sg-toolbar-group">
<md-button class="sg-icon-button" ng-click="addressbook.unselectCards()">
<md-icon>arrow_back</md-icon>
</md-button>
<label>{{addressbook.selectedFolder.$selectedCount()}} <var:string label:value="selected"/></label>
</div>
<div class="sg-toolbar-group-last">
<md-button class="sg-icon-button" ng-click="addressbook.selectAll()">
<md-tooltip md-direction="left"><var:string label:value="Select All"/></md-tooltip>
<md-icon>select_all</md-icon>
</md-button>
<md-button class="sg-icon-button" ng-click="addressbook.confirmDeleteSelectedCards()">
<md-button class="sg-icon-button"
ng-show="addressbook.selectedFolder.acls.objectEraser"
ng-click="addressbook.confirmDeleteSelectedCards()">
<md-icon>delete</md-icon>
</md-button>
<md-menu>
<md-button class="sg-icon-button" label:aria-label="Copy contacts" ng-click="$mdOpenMenu()">
<md-tooltip md-direction="left"><var:string label:value="Copy To"/></md-tooltip>
<md-icon>content_copy</md-icon>
</md-button>
<md-menu-content width="4">
<md-menu-item>
<md-button class="md-primary" ng-disabled="true"><var:string label:value="Address Books"/></md-button>
</md-menu-item>
<md-menu-divider><!-- divider --></md-menu-divider>
<md-menu-item ng-repeat="folder in app.service.$addressbooks track by folder.id"
ng-hide="addressbook.selectedFolder.id == folder.id">
<md-button ng-click="addressbook.copySelectedCards(folder.id)">
<span ng-class="'sg-child-level-' + folder.level">{{folder.name}}</span>
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>
<md-menu>
<md-button class="sg-icon-button" label:aria-label="More messages options" ng-click="$mdOpenMenu()">
<md-icon>more_vert</md-icon>
</md-button>
<md-menu-content width="2">
<md-menu-item>
<md-button>
<md-button ng-click="addressbook.newMessageWithSelectedCards($event)">
<var:string label:value="Write"/>
</md-button>
</md-menu-item>
@@ -360,9 +323,11 @@
</md-menu-item>
</md-menu-content>
</md-menu>
</div>
</div>
</div>
</md-toolbar>
<div layout="row" class="md-flex" layout-align="space-between">
<div layout="row" class="md-flex">
<div class="view-list" layout="column">
<md-content id="contactsList" layout="column" class="md-flex">
<header class="sg-md-subheader sg-md-subheader--fixed">
@@ -371,62 +336,59 @@
<span ng-show="addressbook.selectedFolder.cards.length == 0"><var:string label:value="No contact"/></span>
</h2>
</header>
<!-- Search field & special results
<input type="text" placeholder="Search" ng-model="search.filter" ng-keyup="doSearch($event)" />
<div ng-switch="search.status">
<div ng-switch-when="min-char" class="alert-bg">
<i class="icon-warning"></i><var:string label:value="Please enter at least three characters"/>
</div>
<div ng-switch-when="no-result" class="alert-bg">
<i class="icon-ion-search"></i><var:string label:value="No matching card"/>
</div>
<div ng-switch-when="remote-addressbook" ng-show="addressbook.cards.length == 0" class="alert-bg">
<i class="icon-ion-search"></i><var:string label:value="Initiate a search"/>
</div>
</div>
-->
<md-virtual-repeat-container class="md-flex">
<md-list>
<div md-virtual-repeat="currentCard in addressbook.selectedFolder.cards">
<md-list class="sg-section-list">
<div md-virtual-repeat="currentCard in addressbook.selectedFolder.cards" md-item-size="72">
<md-list-item
ng-class="{'sg-active': currentCard.id == addressbook.selectedFolder.selectedCard}"
ng-click="addressbook.selectCard(currentCard)"
ui-sref="app.addressbook.card.view({addressbookId: addressbook.selectedFolder.id, cardId: currentCard.id})">
<!-- ui-sref-active="sg-active" -->
<div class="sg-selected-avatar" ng-show="currentCard.selected"
ng-click="currentCard.selected = !currentCard.selected">
<!-- selected avatar -->
</div>
<sg-avatar-image class="md-tile-left"
ng-show="addressbook.notSelectedComponent(currentCard, 'vcard')"
ng-click="currentCard.selected = !currentCard.selected"
sg-email="currentCard.$preferredEmail(addressbook.selectedFolder.$query)"
size="48">
<!-- contact avatar -->
</sg-avatar-image>
<div class="sg-list-avatar"
ng-show="addressbook.notSelectedComponent(currentCard, 'vlist')"
ng-click="currentCard.selected = !currentCard.selected">
<!-- list avatar -->
</div>
<div class="sg-selected-avatar" ng-show="currentCard.selected"
ng-click="currentCard.selected = !currentCard.selected">
<!-- selected avatar -->
</div>
<sg-avatar-image class="md-tile-left"
ng-show="addressbook.notSelectedComponent(currentCard, 'vcard')"
ng-click="currentCard.selected = !currentCard.selected"
sg-email="currentCard.$preferredEmail(addressbook.selectedFolder.constructor.$query)"
size="40">
<!-- contact avatar -->
</sg-avatar-image>
<div class="sg-list-avatar"
ng-show="addressbook.notSelectedComponent(currentCard, 'vlist')"
ng-click="currentCard.selected = !currentCard.selected">
<!-- list avatar -->
</div>
<div class="sg-tile-content">
<div class="sg-md-subhead-multi" ng-bind-html="currentCard.$fullname()"><!-- cn --></div>
<div class="sg-md-body-multi">{{currentCard.$preferredEmail(addressbook.selectedFolder.$query)}}</div>
<div class="sg-md-body-multi">{{currentCard.$preferredEmail(addressbook.selectedFolder.constructor.$query)}}</div>
</div>
</md-list-item>
</div>
</md-list>
</md-virtual-repeat-container>
<div class="sg-progress-circular-floating"
ng-show="addressbook.selectedFolder.$isLoading">
<md-progress-circular class="md-accent"
md-mode="indeterminate"
md-diameter="32"><!-- progress --></md-progress-circular>
</div>
<md-button class="md-fab md-fab-bottom-right md-accent"
label:aria-label="New Contact"
ng-show="addressbook.selectedFolder.acls.objectCreator"
ng-click="addressbook.newComponent($event)">
<md-icon>add</md-icon>
</md-button>
</md-content>
</div>
<div id="detailView" class="view-detail ng-cloak" layout="column" ui-view="card"
ng-show="addressbook.selectedFolder.selectedCard"><!-- card view --></div>
<md-content id="detailView" class="view-detail md-hue-1" layout="column" layout-align="center center"
ng-class="{ 'sg-close': !addressbook.selectedFolder.selectedCard }"
ui-view="card">
<md-input-container class="hide-sm md-hue-1" style="flex-grow: 0">
<label class="sg-md-title"><var:string label:value="No contact selected"/></label>
</md-input-container>
</md-content>
</div>
</script>
@@ -5,7 +5,7 @@
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label"
>
<md-card class="viewer md-flex">
<md-card class="viewer flex-sm">
<md-card-content>
<header class="msg-header">
<div ng-show="editor.card.tag == 'vcard'">
@@ -30,18 +30,19 @@
<div class="sg-icon-bar--vertical">
<md-button class="sg-icon-button show-sm"
label:aria-label="Close"
ng-click="toggleDetailView()">
ng-click="editor.close()">
<md-icon>close</md-icon>
</md-button>
<md-button class="sg-icon-button"
label:aria-label="Edit"
ng-show="editor.currentFolder.isEditable"
ng-show="editor.currentFolder.acls.objectEditor"
ui-sref="app.addressbook.card.editor({addressbookId: editor.currentFolder.id, cardId: editor.card.id})">
<md-tooltip md-direction="left"><var:string label:value="Edit"/></md-tooltip>
<md-icon>mode_edit</md-icon>
</md-button>
<md-button class="sg-icon-button"
label:aria-label="Delete"
ng-show="editor.currentFolder.acls.objectEraser"
ng-click="editor.confirmDelete(editor.card)">
<md-tooltip md-direction="left"><var:string label:value="Delete"/></md-tooltip>
<md-icon>delete</md-icon>
@@ -53,7 +54,13 @@
</md-button>
<md-menu-content>
<md-menu-item>
<md-button aria-label="View Raw Source"
<md-button label:aria-label="Export"
ng-click="editor.exportCard()">
<var:string label:value="Export"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button label:aria-label="View Raw Source"
ng-click="editor.viewRawSource($event)">
<var:string label:value="View Raw Source"/>
</md-button>
@@ -65,14 +72,14 @@
</header>
<section class="msg-body">
<div class="pseudo-input-container" ng-show="editor.card.birthday">
<div class="pseudo-input-container" ng-show="editor.card.$birthday().length">
<div class="key">
<label class="pseudo-input-label">
<var:string label:value="Birthday"/>
</label>
</div>
<div class="pseudo-input-field">
<span>{{editor.card.$birthday() | date}}</span>
<span>{{editor.card.$birthday()}}</span>
</div>
</div>
@@ -111,7 +118,8 @@
</div>
<div class="pseudo-input-field">
<a href="mailto:{{email.value}}">{{email.value}}</a>
<a href="#" ng-bind="email.value"
ng-click="addressbook.newMessageWithRecipient($event, email.value, editor.card.$fullname())"><!-- recipient --></a>
</div>
</div>
@@ -165,5 +173,4 @@
</section>
</md-card-content>
</md-card>
</container>
@@ -7,6 +7,7 @@
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label">
<var:string value="cssContent" const:escapeHTML="NO" />
<div class="SOGoHTMLMail-CSS-Delimiter mailer_htmlcontent"
><var:string value="flatContentAsString" const:escapeHTML="NO" /></div>
<div class="SOGoHTMLMail-CSS-Delimiter mailer_htmlcontent">
<var:string value="flatContentAsString" const:escapeHTML="NO" />
</div>
</container>
@@ -250,7 +250,7 @@
<label class="pseudo-input-label"><var:string label:value="Attendees"/></label>
<!-- md-contact-chips don't support "readonly", so we build them using md-chips
in readonly mode and a template similar to the one of md-contact-chips -->
<md-chips class="md-contact-chips"
<md-chips class="md-contact-chips sg-readonly"
ng-model="part.participants"
readonly="true">
<md-chip-template>
@@ -11,13 +11,14 @@
var:title="filenameForDisplay"
class="md-card-image"></img>
<md-card-content>
<p>
<p class="md-caption">
<var:string value="filenameForDisplay" /><br/>
<var:string value="bodyInfo.size" formatter="sizeFormatter"/>
</p>
</md-card-content>
<div class="md-actions" layout="row" layout-align="end center">
<md-button class="sg-icon-button" var:href="pathForDownload">
<md-tooltip md-direction="left"><var:string label:value="Save Attachment"/></md-tooltip>
<md-icon>save</md-icon>
</md-button>
</div>
@@ -4,24 +4,16 @@
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label">
<md-whiteframe class="md-whiteframe-z1" layout="row" layout-align="space-between center">
<div layout="row">
<div>
<md-card>
<md-card-content>
<img var:src="mimeImageURL"/>
</div>
<div layout="column" layout-align="start start" layout-padding="layout-padding">
<div>
<var:string value="filenameForDisplay"/>
</div>
<div class="md-caption">
<p class="md-caption">
<var:string value="filenameForDisplay"/><br/>
<var:string value="bodyInfo.size" formatter="sizeFormatter"/>
</div>
</div>
</div>
<div layout="row" layout-align="end end">
<md-button class="sg-icon-button" var:href="pathToAttachment">
</p>
</md-card-content>
<div class="md-actions" layout="row" layout-align="end center">
<md-button class="sg-icon-button" var:href="pathToAttachment" target="_blank">
<md-tooltip md-direction="left"><var:string label:value="View Attachment"/></md-tooltip>
<md-icon>open_in_browser</md-icon>
</md-button>
@@ -50,5 +42,5 @@
PartPath: <var:string value="partPath" /><br />
-->
</md-whiteframe>
</md-card>
</div>
+170 -112
View File
@@ -6,180 +6,238 @@
xmlns:label="OGo:label">
<md-dialog flex="80" flex-sm="100">
<md-toolbar class="md-padding">
<!-- TOOLBAR TO SHOW CC/BCC/ATTACHMENT FIELDS -->
<md-toolbar class="sg-padded--right">
<div class="md-toolbar-tools">
<md-icon class="material-icons sg-icon-toolbar-bg">edit</md-icon>
<md-button ng-show="editor.hideCc" ng-click="editor.hideCc = false">Cc</md-button>
<md-button ng-show="editor.hideBcc" ng-click="editor.hideBcc = false">Bcc</md-button>
<md-button>
<label for="file-input">
Add files
</label>
<!-- from -->
<sg-avatar-image class="md-tile-left" hide-sm="hide-sm"
sg-email="editor.message.editable.from"
size="32"><!-- avatar --></sg-avatar-image>
<md-input-container flex="flex">
<label><var:string label:value="From"/></label>
<md-select name="from"
ng-model="editor.message.editable.from">
<md-option ng-value="identity" ng-repeat="identity in editor.identities">{{identity}}</md-option>
</md-select>
</md-input-container>
<div flex-gt-sm="flex-gt-sm"><!-- spacer --></div>
<md-button class="sg-icon-button" ng-click="editor.send()">
<md-icon>send</md-icon>
</md-button>
<md-button class="sg-icon-button" ng-click="editor.message.$save()">
<md-icon>save</md-icon>
</md-button>
<md-button class="sg-icon-button" ng-click="editor.cancel()">
<md-icon>close</md-icon>
</md-button>
<input id="file-input" type="file"
nv-file-select="nv-file-select"
multiple="multiple"
uploader="editor.uploader"
ng-show="false"/>
<md-menu>
<md-button label:aria-label="More mail options" class="iconBtton" ng-click="$mdOpenMenu($event)">
<md-icon>more_vert</md-icon>
</md-button>
<md-menu-content width="4">
<md-menu-item>
<md-button>
<md-checkbox ng-model="editor.message.editable.receipt">
<var:string label:value="Receipt"/>
</md-checkbox>
</md-button>
</md-menu-item>
<md-menu-item>
<div layout="row" layout-align="center center">
<span>
<var:string label:value="Priority"/>
</span>
<md-slider flex="flex"
md-discrete="md-discrete"
ng-model="editor.message.editable.priority"
step="1"
min="1"
max="5"
label:aria-label="Priority">
</md-slider>
</div>
</md-menu-item>
</md-menu-content>
</md-menu>
</div>
</md-toolbar>
<md-dialog-content>
<form>
<header>
<!-- FROM / TO / CC/ BCC / SUBJECT / ATTACHMENTS -->
<div class="msg-header-content">
<md-input-container>
<label> <var:string label:value="From"/></label>
<md-select name="from"
ng-model="editor.message.editable.from">
<md-option ng-value="identity" ng-repeat="identity in editor.identities">{{identity}}</md-option>
</md-select>
</md-input-container>
<!-- to -->
<div class="pseudo-input-container">
<label class="pseudo-input-label">
<var:string label:value="To"/>
</label>
<md-chips ng-model="editor.message.editable.to">
<md-chips ng-model="editor.message.editable.to"
md-on-append="editor.addRecipient($chip)">
<md-autocomplete
md-selected-item="editor.autocomplete.to.selected"
class="sg-chips-autocomplete"
md-search-text="editor.autocomplete.to.searchText"
md-selected-item="editor.autocomplete.to.selected"
md-items="user in editor.contactFilter(editor.autocomplete.to.searchText)"
md-min-length="3"
md-delay="300"
md-no-cache="true"
label:placeholder="Add a recipient">
<span md-highlight-text="editor.autocomplete.to.searchText"
md-highlight-flags="^i">{{user}}</span>
<md-item-template>
<span class="md-contact-suggestion">
<span class="md-contact-name"
md-highlight-text="editor.autocomplete.to.searchText"
md-highlight-flags="^i">{{user.$$fullname}}</span>
<span class="md-contact-email"
md-highlight-text="editor.autocomplete.to.searchText"
md-highlight-flags="^i">{{user.$$email}}</span>
</span>
</md-item-template>
</md-autocomplete>
<md-chip-template>{{$chip}}</md-chip-template>
</md-chips>
</div>
<!-- cc -->
<div class="pseudo-input-container" ng-hide="editor.hideCc">
<label class="pseudo-input-label">
<var:string label:value="Cc"/>
</label>
<md-chips ng-model="editor.message.editable.cc">
<md-chips ng-model="editor.message.editable.cc"
md-on-append="editor.addRecipient($chip)">
<md-autocomplete
md-selected-item="editor.autocomplete.cc.selected"
class="sg-chips-autocomplete"
md-search-text="editor.autocomplete.cc.searchText"
md-selected-item="editor.autocomplete.cc.selected"
md-items="user in editor.contactFilter(editor.autocomplete.cc.searchText)"
label:placeholder="Add a recipient">
<span md-highlight-text="editor.autocomplete.cc.searchText"
md-highlight-flags="^i">{{user}}</span>
<md-item-template>
<span class="md-contact-suggestion">
<span class="md-contact-name"
md-highlight-text="editor.autocomplete.to.searchText"
md-highlight-flags="^i">{{user.$$fullname}}</span>
<span class="md-contact-email"
md-highlight-text="editor.autocomplete.to.searchText"
md-highlight-flags="^i">{{user.$$email}}</span>
</span>
</md-item-template>
</md-autocomplete>
<md-chip-template>{{$chip}}</md-chip-template>
</md-chips>
</div>
<!-- bcc -->
<div class="pseudo-input-container" ng-hide="editor.hideBcc">
<label class="pseudo-input-label">
<var:string label:value="Bcc"/>
</label>
<md-chips ng-model="editor.message.editable.bcc">
<md-chips ng-model="editor.message.editable.bcc"
md-on-append="editor.addRecipient($chip)">
<md-autocomplete
class="sg-chips-autocomplete"
md-selected-item="editor.autocomplete.bcc.selected"
md-search-text="editor.autocomplete.bcc.searchText"
md-items="user in editor.contactFilter(editor.autocomplete.bcc.searchText)"
label:placeholder="Add a recipient">
<span md-highlight-text="editor.autocomplete.bcc.searchText"
md-highlight-flags="^i">{{user}}</span>
<md-item-template>
<span class="md-contact-suggestion">
<span class="md-contact-name"
md-highlight-text="editor.autocomplete.to.searchText"
md-highlight-flags="^i">{{user.$$fullname}}</span>
<span class="md-contact-email"
md-highlight-text="editor.autocomplete.to.searchText"
md-highlight-flags="^i">{{user.$$email}}</span>
</span>
</md-item-template>
</md-autocomplete>
<md-chip-template>{{$chip}}</md-chip-template>
</md-chips>
</div>
<!-- subject -->
<md-input-container>
<label>
<var:string label:value="Subject"/>
</label>
<input type="text" name="subject" ng-model="editor.message.editable.subject"/>
</md-input-container>
<md-list>
<!-- FILES ALREADY UPLOADED, FOR EXAMPLE WHEN WE FORWARD A
MAIL WITH ATTACHMENTS -->
<md-list-item ng-repeat="item in editor.message.editable.attachmentAttrs">
<div layout="row" layout-align="space-between center">
<span ng-bind="item.filename"><!-- filename --></span>
<md-button class="sg-icon-button"
ng-click="editor.message.$deleteAttachment(item.filename);">
<md-icon>remove_circle_outline</md-icon>
</md-button>
</div>
</md-list-item>
<!-- FILE BEING ATTACHED TO A MAIL -->
<md-list-item ng-repeat="item in editor.uploader.queue">
<div layout="row" layout-align="space-between center">
<span ng-bind="item.file.name"><!-- filename --></span>
<md-progress-linear md-mode="determinate"
value="{{item.progress}}"
ng-show="item.isUploading" ><!-- progress--></md-progress-linear>
<md-button class="sg-icon-button"
ng-click="editor.uploader.cancelItem(item)"
ng-show="item.isUploading">
<md-icon>cancel</md-icon>
</md-button>
<md-button class="sg-icon-button"
ng-click="editor.message.$deleteAttachment(item.file.name); item.remove();"
ng-show="!item.isUploading">
<md-icon>remove_circle_outline</md-icon>
</md-button>
</div>
</md-list-item>
</md-list>
</div>
<!-- toolbar to show cc/bcc/attachment fields -->
<div layout="column" layout-align="start end">
<md-button class="sg-icon-button " ng-show="editor.hideCc" ng-click="editor.hideCc = false">Cc</md-button>
<md-button class="sg-icon-button " ng-show="editor.hideBcc" ng-click="editor.hideBcc = false">Bcc</md-button>
<md-menu>
<md-button label:aria-label="More mail options" class="sg-icon-button" ng-click="$mdOpenMenu($event)">
<md-icon>more_vert</md-icon>
</md-button>
<md-menu-content width="4">
<md-menu-item>
<md-button ng-href="#">
<md-checkbox ng-model="editor.message.editable.receipt">
<var:string label:value="Return Receipt"/>
</md-checkbox>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button class="md-primary" ng-disabled="true"><var:string label:value="Priority"/></md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="editor.message.editable.priority = 1">
<md-icon ng-class="{ 'icon-check': editor.message.editable.priority == 1}"><!-- highest --></md-icon>
<var:string label:value="highest"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="editor.message.editable.priority = 2">
<md-icon ng-class="{ 'icon-check': editor.message.editable.priority == 2}"><!-- high --></md-icon>
<var:string label:value="high"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="editor.message.editable.priority = 3">
<md-icon ng-class="{ 'icon-check':
!editor.message.editable.priority
|| editor.message.editable.priority == 3}"><!-- normal --></md-icon>
<var:string label:value="normal"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="editor.message.editable.priority = 4">
<md-icon ng-class="{ 'icon-check': editor.message.editable.priority == 4}"><!-- low --></md-icon>
<var:string label:value="low"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="editor.message.editable.priority = 5">
<md-icon ng-class="{ 'icon-check': editor.message.editable.priority == 5}"><!-- lowest --></md-icon>
<var:string label:value="lowest"/>
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>
</div>
</header>
<!-- MESSAGE CONTENT -->
<textarea name="content" var:class="editorClass"
ck-locale="editor.localeCode"
ng-model="editor.message.editable.text"/>
<!-- TOOLBAR BUTTONS -->
<div class="buttonsToolbar">
<div layout="row" layout-align="end center">
<md-button ng-click="editor.cancel()"><var:string label:value="Cancel"/></md-button>
<md-button ng-click="editor.message.$save()"><var:string label:value="Save"/></md-button>
<md-button class="fg-sogoBlue-700 md-primary md-hue-3"
ng-click="editor.send()"><var:string label:value="Send"/></md-button>
</div>
</div>
</form>
</md-dialog-content>
</md-dialog>
<!-- TOOLBAR BUTTONS -->
<div class="md-actions">
<div layout="row" layout-align="space-between center" layout-fill="layout-fill">
<div>
<!-- md-icon does not play well with Firefox - makes the
file input never appear. This should eventually be
fixed in Angular Material -->
<label class="md-button" for="file-input">
<span><var:string label:value="Attach"/></span>
</label>
<input id="file-input" type="file"
nv-file-select="nv-file-select"
multiple="multiple"
uploader="editor.uploader"
ng-show="false"/>
</div>
<div>
<!-- FILES ALREADY UPLOADED, FOR EXAMPLE WHEN WE FORWARD A
MAIL WITH ATTACHMENTS -->
<md-chips ng-model="editor.message.editable.attachmentAttrs"
class="sg-readonly" readonly="true">
<md-chip-template>
<span>{{$chip.filename}}</span>
<md-icon ng-click="editor.message.$deleteAttachment($chip.filename);">close</md-icon>
</md-chip-template>
</md-chips>
<!-- FILE BEING ATTACHED TO A MAIL -->
<md-chips ng-model="editor.uploader.queue"
class="sg-readonly" readonly="true">
<md-chip-template>
<span>{{$chip.file.name}}</span>
<md-icon ng-show="!$chip.isUploading"
ng-click="editor.message.$deleteAttachment($chip.file.name);
$chip.remove();">close</md-icon>
<md-icon ng-show="$chip.isUploading"
ng-click="editor.uploader.cancelItem($chip)"
ng-style="{ 'color': '#F00' }">cancel</md-icon>
<span ng-show="$chip.isUploading">{{$chip.progress}} %</span>
</md-chip-template>
</md-chips>
</div>
</div>
</div>
</md-dialog>
</container>
+43 -34
View File
@@ -5,7 +5,6 @@
xmlns:label="OGo:label">
<div class="view-list" layout="column">
<md-content id="messagesList" layout="column" class="md-flex">
<md-progress-linear ng-show="mailbox.selectedFolder.$isLoading" md-mode="indeterminate"><!-- progress --></md-progress-linear>
<header class="sg-md-subheader sg-md-subheader--fixed">
<h2 class="md-default-theme sg-md-subhead-solo fg-sogoBlue-700">
<span ng-show="mailbox.selectedFolder.$messages.length > 0">{{mailbox.selectedFolder.$messages.length}} <var:string label:value="messages"/></span>
@@ -13,39 +12,44 @@
</h2>
</header>
<md-virtual-repeat-container class="md-flex">
<md-list>
<div md-virtual-repeat="currentMessage in mailbox.selectedFolder.$messages">
<md-list-item
ng-class="{'sg-active': currentMessage.uid == mailbox.selectedFolder.selectedMessage, unread: !currentMessage.isread}"
ng-click="mailbox.selectMessage(currentMessage)"
ui-sref="mail.account.mailbox.message({accountId: mailbox.account.id, mailboxId: (mailbox.selectedFolder.path | encodeUri), messageId: currentMessage.uid})">
<!-- ui-sref-active="sg-active"> -->
<div class="sg-selected-avatar"
ng-show="currentMessage.selected"
ng-click="currentMessage.selected = !currentMessage.selected"><!-- selected avatar --></div>
<sg-avatar-image class="md-tile-left"
ng-show="!currentMessage.selected"
ng-click="currentMessage.selected = !currentMessage.selected"
sg-email="currentMessage['from'][0].email"
size="48"><!-- avatar --></sg-avatar-image>
<div class="sg-tile-content">
<span class="msg-date"
ng-bind-html="currentMessage.relativedate"><!-- date --></span>
<div class="sg-md-subhead-multi">{{currentMessage.$shortAddress('from')}}</div>
<div class="sg-md-body-multi">{{currentMessage.subject}}</div>
<i class="icon-ion-refresh"
ng-hide="mailbox.selectedFolder.$loadMessage(currentMessage.uid)"><!-- loading --></i>
</div>
<div class="sg-tile-icons">
<md-icon ng-show="currentMessage.isflagged">star</md-icon>
<md-icon ng-show="currentMessage.isanswered">reply</md-icon>
<md-icon ng-show="currentMessage.isforwarded">forward</md-icon>
<md-icon ng-show="currentMessage.hasattachment">attach_file</md-icon>
</div>
</md-list-item>
</div>
<md-list class="sg-section-list">
<md-list-item
md-virtual-repeat="currentMessage in mailbox.selectedFolder"
md-on-demand="md-on-demand"
ng-class="{'sg-active': currentMessage.uid == mailbox.selectedFolder.selectedMessage, unread: !currentMessage.isread}"
ng-click="mailbox.selectMessage(currentMessage)"
ui-sref="mail.account.mailbox.message({accountId: mailbox.account.id, mailboxId: (mailbox.selectedFolder.path | encodeUri), messageId: currentMessage.uid})">
<!-- ui-sref-active="sg-active"> -->
<div class="sg-selected-avatar"
ng-show="currentMessage.selected"
ng-click="currentMessage.selected = !currentMessage.selected"><!-- selected avatar --></div>
<sg-avatar-image class="md-tile-left"
ng-show="!currentMessage.selected"
ng-click="currentMessage.selected = !currentMessage.selected"
sg-email="::currentMessage.from[0].email"
size="48"><!-- avatar --></sg-avatar-image>
<div class="sg-tile-content">
<span class="msg-date"
ng-bind-html="currentMessage.relativedate"><!-- date --></span>
<div class="sg-md-subhead-multi">{{currentMessage.$shortAddress('from')}}</div>
<div class="sg-md-body-multi">{{currentMessage.subject}}</div>
</div>
<div class="sg-tile-icons">
<md-icon ng-show="currentMessage.priority == 'highest' || currentMessage.priority == 'high'">warning</md-icon>
<md-icon ng-show="currentMessage.isflagged">star</md-icon>
<md-icon ng-show="currentMessage.isanswered">reply</md-icon>
<md-icon ng-show="currentMessage.isforwarded">forward</md-icon>
<md-icon ng-show="currentMessage.hasattachment">attach_file</md-icon>
</div>
</md-list-item>
</md-list>
</md-virtual-repeat-container>
<div class="sg-progress-circular-floating"
ng-show="mailbox.selectedFolder.$isLoading">
<md-progress-circular class="md-accent"
md-mode="indeterminate"
md-diameter="32"><!-- progress --></md-progress-circular>
</div>
<md-button class="md-fab md-fab-bottom-right md-accent"
label:aria-label="Write a new message"
ng-click="mailbox.newMessage($event)">
@@ -54,7 +58,12 @@
</md-content>
</div>
<div id="detailView" class="view-detail ng-cloak" layout="column" ui-view="message"
ng-show="mailbox.selectedFolder.selectedMessage"><!-- message view --></div>
<md-content id="detailView" class="view-detail md-hue-1" layout="column" layout-align="center center"
ng-class="{ 'sg-close': !mailbox.selectedFolder.selectedMessage }"
ui-view="message">
<md-input-container class="hide-sm md-hue-1" style="flex-grow: 0">
<label class="sg-md-title"><var:string label:value="No message selected"/></label>
</md-input-container>
</md-content>
</container>
+28 -26
View File
@@ -221,13 +221,13 @@
Templates script tag wrappers go next
-->
<main class="view md-layout-fill" ui-view="mailboxes" layout="row" ng-controller="navController"><!-- mailboxes list --></main>
<main class="view layout-fill" ui-view="mailboxes" layout="row" ng-controller="navController"><!-- mailboxes list --></main>
<!-- TEMPLATE SCRIPT WRAPPER -->
<script type="text/ng-template" id="UIxMailMainFrame">
<!-- Sidenav -->
<md-sidenav id="left-sidenav" class="md-sidenav-left md-whiteframe-z1 md-layout-fill" md-component-id="left" md-is-locked-open="isGtMedium" layout="column">
<md-sidenav id="left-sidenav" class="md-sidenav-left md-whiteframe-z1 layout-fill" md-component-id="left" md-is-locked-open="isGtMedium" layout="column">
<var:component className="UIxSidenavToolbarTemplate" />
<md-content md-scroll-y="md-scroll-y" class="md-flex">
<section ng-repeat="account in app.accounts track by account.id">
@@ -235,17 +235,17 @@
<div layout="row" layout-align="space-between center">
<span>{{account.name}}</span>
<div>
<md-button class="sg-icon-button"
label:aria-label="New Folder..."
ng-click="app.newFolder(account)">
<md-icon>add_circle_outline</md-icon>
</md-button>
<md-button class="sg-icon-button"
ng-show="account.id == 0"
label:aria-label="Delegation..."
ng-click="app.delegate(account)">
<md-icon>people</md-icon>
</md-button>
<md-button class="sg-icon-button"
label:aria-label="New Folder..."
ng-click="app.newFolder(account)">
<md-icon>add_circle_outline</md-icon>
</md-button>
<md-button class="sg-icon-button"
ng-show="account.id == 0"
label:aria-label="Delegation..."
ng-click="app.delegate(account)">
<md-icon>people</md-icon>
</md-button>
</div>
</div>
</md-subheader>
@@ -287,12 +287,17 @@
<var:string label:value="New Subfolder..."/>
</md-button>
</md-menu-item>
<md-menu-item ng-show="folder.$isEditable">
<md-button type="button" ng-click="app.editFolder(folder)">
<var:string label:value="Rename"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button type="button" ng-click="app.compactFolder(folder)">
<var:string label:value="Compact"/>
</md-button>
</md-menu-item>
<md-menu-item ng-show="folder.$isEditable">
<md-menu-item ng-show="folder.$isEditable">
<md-button type="button" ng-click="app.confirmDelete(folder)">
<var:string label:value="Delete"/>
</md-button>
@@ -337,18 +342,14 @@
</md-content>
</md-sidenav>
<section layout="column" class="sg-app-content md-layout-fill" ui-view="mailbox"><!-- messages list --></section>
<section layout="column" class="sg-app-content layout-fill" ui-view="mailbox"><!-- messages list --></section>
</script>
<script type="text/ng-template" id="UIxMailFolderTemplate">
<md-toolbar layout="column" layout-align="space-between start" class="md-tall toolbar-main">
<div class="md-toolbar-tools md-toolbar-tools-top sg-padded" layout="row" layout-align="space-between start">
<md-toolbar layout="column" layout-align="space-between start" class="md-tall toolbar-main" >
<div class="md-toolbar-tools md-toolbar-tools-top"
layout="row" layout-align="space-between start">
<var:component className="UIxTopnavToolbarTemplate" />
<div class="sg-toolbar-group-last">
<md-button class="sg-icon-button" label:aria-label="More">
<md-icon>more_vert</md-icon>
</md-button>
</div>
</div>
<div class="md-toolbar-tools" layout="row" layout-align="space-between center"
ng-show="mailbox.selectedFolder.$selectedCount() == 0">
@@ -393,9 +394,10 @@
</md-button>
</md-menu-item>
<md-menu-divider> <!-- divider --></md-menu-divider>
<md-menu-item >
<md-button ng-click="mailbox.selectedFolder.$filter()">
<md-menu-item>
<md-button ng-href="#">
<md-checkbox
ng-change="mailbox.selectedFolder.$filter()"
ng-model="mailbox.service.$query.asc"
ng-true-value="0"
ng-false-value="1"><var:string label:value="Descending Order"/></md-checkbox>
@@ -472,12 +474,12 @@
</md-button>
<md-menu-content width="2">
<md-menu-item>
<md-button>
<md-button ng-click="mailbox.markSelectedMessagesAsFlagged()">
<var:string label:value="Flag"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button>
<md-button ng-click="mailbox.markSelectedMessagesAsUnread()">
<var:string label:value="Mark as Unread"/>
</md-button>
</md-menu-item>
+43 -7
View File
@@ -20,8 +20,18 @@
<label class="pseudo-input-label">
<var:string label:value="From"/>
</label>
<div class="pseudo-input-field">
<a ng-href="mailto:{{viewer.message.from[0].email}}" ng-bind="viewer.message.from[0].full"><!-- from --></a>
<div layout="row" layout-align="start center">
<sg-avatar-image class="md-tile-left"
sg-email="viewer.message.from[0].email"
size="48">
<!-- contact avatar -->
</sg-avatar-image>
<div class="md-list-item-text">
<span>{{ viewer.message.from[0].name }}</span><br/>
<a class="md-caption" href="#"
ng-bind="viewer.message.from[0].email"
ng-click="viewer.newMessage($event, viewer.message.from[0])"><!-- from --></a>
</div>
</div>
</div>
@@ -29,8 +39,26 @@
<label class="pseudo-input-label">
<var:string label:value="To"/>
</label>
<div class="pseudo-input-field" ng-hide="viewer.message.$showDetailedRecipients">
<a href="#" ng-click="viewer.message.showDetailedRecipients()">{{viewer.message.$shortRecipients()}}</a>
</div>
<div class="pseudo-input-field" ng-show="viewer.message.$showDetailedRecipients">
<span ng-repeat="recipient in viewer.message.to">
<a href="#" ng-bind="recipient.full"
ng-click="viewer.newMessage($event, recipient)"><!-- recipient --></a>
</span>
</div>
</div>
<div class="pseudo-input-container--compact" ng-show="viewer.message.$showDetailedRecipients">
<label class="pseudo-input-label" ng-show="viewer.message.cc.length > 0">
<var:string label:value="Cc"/>
</label>
<div class="pseudo-input-field">
<a ng-href="mailto:{{viewer.message.to[0].email}}" ng-bind="viewer.message.to[0].full"><!-- to --></a>
<span ng-repeat="recipient in viewer.message.cc">
<a href="#" ng-bind="recipient.full"
ng-click="viewer.newMessage($event, recipient)"><!-- recipient --></a>
</span>
</div>
</div>
@@ -57,7 +85,7 @@
<div class="sg-icon-bar--vertical">
<md-button class="sg-icon-button show-sm"
label:aria-label="Close"
ng-click="toggleDetailView()">
ng-click="viewer.close()">
<md-icon>close</md-icon>
</md-button>
<!-- todo: Replace md-tooltip values by localizable string variable -->
@@ -102,6 +130,12 @@
<var:string label:value="Load Images"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button label:aria-label="Save As..."
ng-click="viewer.saveMessage()">
<var:string label:value="Save As..."/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button label:aria-label="View Message Source"
ng-click="viewer.viewRawSource($event)">
@@ -136,9 +170,11 @@
<div class="msg-date sg-md-body-multi">
<time datetime="viewer.message.date" ng-bind="viewer.message.date"><!-- date --></time>
</div>
<div class="mailer_mailcontent" ng-repeat="part in viewer.message.$content()">
<div ng-if="part.html" ng-bind-html="part.content"><!-- msg --></div>
<div ng-if="part.compile" sg-compile="part.content"><!-- msg --></div>
<div layout="row" layout-wrap="layout-wrap">
<div ng-class="part.msgclass" layout="row" layout-wrap="layout-wrap" class="mailer_mailcontent" ng-repeat="part in viewer.message.$content()">
<div ng-if="part.html" ng-bind-html="part.content"><!-- msg --></div>
<div ng-if="part.compile" sg-compile="part.content"><!-- msg --></div>
</div>
</div>
</div>
</md-content>
+29 -60
View File
@@ -7,7 +7,7 @@
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"
const:jsFiles="Main.app.js, Common.js"
const:jsFiles="Main.js, Common.js"
>
<!--
MAIN CONTENT ROW
@@ -15,32 +15,13 @@
MUST be the first html element after body
SHOULD be a main tag (with role="main")
-->
<main class="view md-layout-fill md-layout-padding" ui-view="login" layout="row" layout-align="center start">
<main class="view layout-fill layout-padding" ui-view="login" layout="row" layout-align="center start">
<md-content class="md-padding" md-scroll-y="true">
<div ng-controller="loginController">
<div class="sg-login" ng-controller="LoginController as app">
<script type="text/javascript">
var cookieUsername = '<var:string var:value="cookieUsername" const:escapeHTML="NO"/>';
</script>
<!-- <div id="aboutBox" style="display:none;">
<div>
<p class="logo"><img const:alt="SOGo" rsrc:src="img/sogo-logo.png"/></p>
<p>Version <var:string value="version"/> <span class="buildDate">(<var:string value="buildDate" />)</span></p>
<p class="scroll"><var:string label:value="AboutBox" const:escapeHTML="NO"/></p>
<img const:alt="Inverse" rsrc:src="img/inverse.png"/>
<p class="links"><a href="http://sogo.nu/" target="_new">sogo.nu</a> /
<a href="http://inverse.ca/" target="_new">inverse.ca</a></p>
<p><a id="aboutClose" href="#" class="button">
<span><var:string label:value="OK" /></span></a></p>
</div>
</div> -->
<!-- <nav class="top-bar">
<section class="top-bar-section">
<ul class="right">
<li><a href="#"><var:string label:value="About" /></a></li>
</ul>
</section>
</nav> -->
<div id="login">
<div id="logo">
<img const:alt="*" id="splash" rsrc:src="img/sogo-full.svg"/>
@@ -48,28 +29,22 @@
<form name="loginForm" layout="column"
ng-cloak="ng-cloak"
ng-controller="loginController"
ng-submit="login(creds)">
ng-submit="app.login()">
<var:if condition="hasLoginSuffix">
<input type="hidden" ng-model="creds.loginSuffix" var:value="loginSuffix"/>
<input type="hidden" ng-model="app.creds.loginSuffix" var:value="loginSuffix"/>
</var:if>
<div ng-show="warning" ng-cloak="ng-cloak" alert="alert" class="alert-box radius warning">{{warning}}</div>
<md-input-container>
<label>
<var:string label:value="Username"/>
</label>
<input autocapitalize="off" type="text" ng-model="creds.username" required="required" />
<md-icon>person</md-icon>
<input autocapitalize="off" type="text" ng-model="app.creds.username" ng-required="true" label:placeholder="Username"/>
</md-input-container>
<md-input-container>
<label>
<var:string label:value="Password"/>
</label>
<input type="password" ng-model="creds.password" required="required" />
<md-input-container md-no-float="md-no-float">
<md-icon>email</md-icon>
<input type="password" ng-model="app.creds.password" ng-required="true" label:placeholder="Password"/>
</md-input-container>
<!-- CONNECT BUTTON -->
<div layout="row" layout-align="end center">
<md-button class="md-raised" type="submit" ng-disabled='!loginForm.$valid'>
<md-button class="md-raised" type="submit" ng-disabled='app.loginForm.$invalid'>
<span>
<var:string label:value="Connect"/>
</span>
@@ -78,38 +53,32 @@
<!-- LANGUAGES SELECT -->
<div layout="row" layout-align="start end">
<md-input-container>
<label class="login-lang">
<var:string label:value="Language"/>
</label>
<md-select flex="true" ng-model="creds.language">
<var:foreach list="languages" item="item">
<md-option var:value="item">
<var:string value="languageText"/>
</md-option>
</var:foreach>
<md-icon>language</md-icon>
<md-select flex="true" ng-model="app.creds.language" label:placeholder="choose">
<var:foreach list="languages" item="item">
<md-option var:value="item">
<var:string value="languageText"/>
</md-option>
</var:foreach>
</md-select>
</md-input-container>
</div>
<!-- DOMAINS SELECT -->
<var:if condition="hasLoginDomains">
<div layout="row" layout-align="start end">
<label>
<var:string label:value="Domain"/>
</label>
<md-select flex="true" ng-model="creds.domain" label:placeholder="choose">
<var:foreach list="loginDomains" item="item">
<md-option var:value="item">
<var:string value="item"/>
</md-option>
</var:foreach>
</md-select>
<md-icon>domain</md-icon>
<md-select class="md-flex" ng-model="app.creds.domain" label:placeholder="choose">
<var:foreach list="loginDomains" item="item">
<md-option var:value="item">
<var:string value="item"/>
</md-option>
</var:foreach>
</md-select>
</div>
</var:if>
<div layout="row" layout-align="center center">
<md-switch ng-model="creds.rememberLogin" label:arial-label="Remember username">
<md-switch ng-model="app.creds.rememberLogin" label:arial-label="Remember username">
<var:string label:value="Remember username"/>
</md-switch>
</div>
@@ -117,7 +86,7 @@
<div layout="row" layout-align="end end" ng-cloak="ng-cloak">
<md-button class="sg-icon-button fg-sogoPaper-500"
label:aria-label="About"
ng-click="showAbout()">
ng-click="app.showAbout()">
<md-icon class="material-icons fg-sogoPaper-500">info</md-icon>
</md-button>
</div>
@@ -136,7 +105,7 @@
<!--<img class="full-image" const:alt="Inverse" rsrc:src="img/inverse.png"/>-->
</md-dialog-content>
<div class="md-actions">
<md-button ng-click="closeDialog()"><var:string label:value="Close"/></md-button>
<md-button ng-click="about.closeDialog()"><var:string label:value="Close"/></md-button>
</div>
</md-dialog>
</script>
+57 -35
View File
@@ -9,74 +9,98 @@
xmlns:label="OGo:label"
className="UIxPageFrame"
>
<md-dialog>
<md-dialog flex="50" flex-sm="100">
<md-toolbar class="md-padding">
<div class="md-toolbar-tools">
<md-icon class="material-icons sg-icon-toolbar-bg">account_box</md-icon>
<md-input-container>
<label><var:string label:value="Account Name"/></label>
<input type="text" required="required"
ng-readonly="$AccountDialogController.accountId == 0"
ng-model="$AccountDialogController.account.name"/>
</md-input-container>
<md-button type="button" class="sg-icon-button " ng-click="$AccountDialogController.cancel()">
<md-icon>close</md-icon>
</md-button>
</div>
</md-toolbar>
<md-dialog-content>
<form id="mainForm" name="accountForm" var:href="ownPath">
<md-input-container>
<label><var:string label:value="Account Name"/></label>
<input type="text" required="required"
ng-readonly="accountId == 0" ng-model="account.name"/>
</md-input-container>
<md-input-container>
<label><var:string label:value="Server Name"/></label>
<input type="text" required="required"
ng-readonly="accountId == 0" ng-model="account.serverName"/>
ng-readonly="$AccountDialogController.accountId == 0"
ng-model="$AccountDialogController.account.serverName"/>
</md-input-container>
<md-input-container>
<label><var:string label:value="Port"/></label>
<input type="number" required="required" min="1" max="65535"
ng-readonly="accountId == 0" ng-model="account.port"/>
ng-readonly="$AccountDialogController.accountId == 0"
ng-model="$AccountDialogController.account.port"/>
</md-input-container>
<label><var:string label:value="Encryption"/></label>
<md-radio-group ng-model="account.encryption">
<md-radio-button ng-disabled="accountId == 0" value="none" class="md-primary"><var:string label:value="None"/></md-radio-button>
<md-radio-button ng-disabled="accountId == 0" value="ssl"><var:string label:value="SSL"/></md-radio-button>
<md-radio-button ng-disabled="accountId == 0" value="tls"><var:string label:value="TLS"/></md-radio-button>
<md-radio-group ng-model="$AccountDialogController.account.encryption">
<md-radio-button ng-disabled="$AccountDialogController.accountId == 0" value="none" class="md-primary"><var:string label:value="None"/></md-radio-button>
<md-radio-button ng-disabled="$AccountDialogController.accountId == 0" value="ssl"><var:string label:value="SSL"/></md-radio-button>
<md-radio-button ng-disabled="$AccountDialogController.accountId == 0" value="tls"><var:string label:value="TLS"/></md-radio-button>
</md-radio-group>
<md-input-container>
<label><var:string label:value="User Name"/></label>
<input type="text" required="required"
ng-readonly="accountId == 0" ng-model="account.userName"/>
ng-readonly="$AccountDialogController.accountId == 0"
ng-model="$AccountDialogController.account.userName"/>
</md-input-container>
<md-input-container>
<label><var:string label:value="Password"/></label>
<input type="password" ng-readonly="accountId == 0"
ng-model="account.password"/>
<input type="password"
ng-readonly="$AccountDialogController.accountId == 0"
ng-model="$AccountDialogController.account.password"/>
</md-input-container>
<md-input-container>
<label><var:string label:value="Full Name"/></label>
<input type="text" required="required"
ng-readonly="customFromIsReadonly()"
ng-model="account.identities[0].fullName"/>
ng-readonly="$AccountDialogController.customFromIsReadonly()"
ng-model="$AccountDialogController.account.identities[0].fullName"/>
</md-input-container>
<md-input-container>
<label><var:string label:value="Email"/></label>
<input type="email" required="required"
ng-readonly="customFromIsReadonly()"
ng-model="account.identities[0].email"/>
ng-readonly="$AccountDialogController.customFromIsReadonly()"
ng-model="$AccountDialogController.account.identities[0].email"/>
</md-input-container>
<md-input-container>
<label><var:string label:value="Reply To Email"/></label>
<input type="email" ng-model="account.identities[0].replyTo"/>
<input type="email" ng-model="$AccountDialogController.account.identities[0].replyTo"/>
</md-input-container>
<!-- FIXME -->
<md-input-container>
<label><var:string label:value="Signature"/></label>
<input type="text" ng-model="account.identities[0].signature" />
</md-input-container>
<!-- To switch between a simple text editor and the CK/HTML editor, we use a ng-if and not
a ng-class as it doesn't get initialized by the ckEditor class directive -->
<md-input-container ng-if="$AccountDialogController.defaults.SOGoMailComposeMessageType == 'text'">
<label><var:string label:value="Signature"/> {{$AccountDialogController.defaults.SOGoMailComposeMessageType}}</label>
<textarea ng-model="$AccountDialogController.account.identities[0].signature"><!-- signature --></textarea>
</md-input-container>
<div class="pseudo-input-container"
ng-if="$AccountDialogController.defaults.SOGoMailComposeMessageType == 'html'">
<label class="pseudo-input-label"><var:string label:value="Signature"/> {{$AccountDialogController.defaults.LocaleCode}}</label>
<textarea class="ck-editor"
ck-locale="$AccountDialogController.defaults.LocaleCode"
ck-options="{ 'height': '70px',
'toolbar': [['Bold', 'Italic', '-', 'Link',
'Font','FontSize','-','TextColor',
'BGColor']] }"
ng-model="$AccountDialogController.account.identities[0].signature"><!-- signature --></textarea>
</div>
<md-radio-group ng-model="account.receipts.receiptAction">
<md-radio-group ng-model="$AccountDialogController.account.receipts.receiptAction">
<label><var:string label:value="When I receive a request for a return receipt"/></label>
<md-radio-button value="ignore"><var:string label:value="Never send a return receipt"/></md-radio-button>
<md-radio-button value="allow"><var:string label:value="Allow return receipts for some messages"/></md-radio-button>
@@ -84,7 +108,7 @@
<label>
<var:string label:value="If I'm not in the To or Cc of the message"/>
<md-select ng-model="account.receipts.receiptNonRecipientAction">
<md-select ng-model="$AccountDialogController.account.receipts.receiptNonRecipientAction">
<md-option const:value="ignore"><var:string label:value="Never send"/></md-option>
<md-option const:value="send"><var:string label:value="Always send"/></md-option>
<md-option const:value="ask"><var:string label:value="Ask me"/></md-option>
@@ -93,7 +117,7 @@
<label>
<var:string label:value="If the sender is outside my domain"/>
<md-select ng-model="account.receipts.receiptOutsideDomainAction">
<md-select ng-model="$AccountDialogController.account.receipts.receiptOutsideDomainAction">
<md-option const:value="ignore"><var:string label:value="Never send"/></md-option>
<md-option const:value="send"><var:string label:value="Always send"/></md-option>
<md-option const:value="ask"><var:string label:value="Ask me"/></md-option>
@@ -102,20 +126,18 @@
<label>
<var:string label:value="In all other cases"/>
<md-select ng-model="account.receipts.receiptAnyAction">
<md-select ng-model="$AccountDialogController.account.receipts.receiptAnyAction">
<md-option const:value="ignore"><var:string label:value="Never send"/></md-option>
<md-option const:value="send"><var:string label:value="Always send"/></md-option>
<md-option const:value="ask"><var:string label:value="Ask me"/></md-option>
</md-select>
</label>
<div class="md-actions" layout="row">
<md-button type="button" ng-click="cancel()">Cancel</md-button>
<md-button type="button" class="md-primary"
ng-disabled="accountForm.$invalid" ng-click="save()">Save</md-button>
</div>
</form>
</md-dialog-content>
<div class="md-actions">
<md-button ng-disabled="$AccountDialogController.accountForm.$invalid" ng-click="$AccountDialogController.save()"><var:string label:value="Save"/></md-button>
</div>
</md-dialog>
</var:component>
+102 -66
View File
@@ -7,23 +7,31 @@
xmlns:label="OGo:label"
xmlns:rsrc="OGo:url"
xmlns:uix="OGo:uix"><var:string var:value="doctype" const:escapeHTML="NO" />
<md-dialog flex="50">
<md-dialog flex="50" flex-sm="100">
<form id="mainForm" var:href="ownPath">
<md-toolbar class="md-padding">
<div class="md-toolbar-tools">
<md-icon class="material-icons sg-icon-toolbar-bg">filter_list</md-icon>
<md-input-container>
<label><var:string label:value="Filter name:"/></label>
<input class="md-title" type="text" ng-model="filter.name" required="required"/>
</md-input-container>
<md-button type="button" class="sg-icon-button " ng-click="cancel()">
<md-icon>close</md-icon>
</md-button>
</div>
</md-toolbar>
<md-dialog-content>
<script type="text/javascript">
var filterId = '<var:string value="filterId"/>';
var sieveFolderEncoding = '<var:string value="sieveFolderEncoding"/>';
var mailTags = <var:string value="labels" const:escapeHTML="NO"/>;
</script>
<form id="mainForm" var:href="ownPath">
<div id="filterNameContainer" class="container">
<md-input-container>
<label><var:string label:value="Filter name:"/></label>
<input const:name="filterName" type="text" ng-model="filter.name"/>
</md-input-container>
</div>
<div id="filterMatchContainer" class="container">
<var:string label:value="For incoming messages that"/>
<md-select const:name="matchType" const:id="matchType" ng-model="filter.match">
<div layout="row" layout-align="start center">
<div><var:string label:value="For incoming messages that"/></div>
<md-input-container class="md-flex">
<md-select ng-model="filter.match" required="required">
<md-option const:value="all">
<var:string label:value="match all of the following rules:"/>
</md-option>
@@ -34,107 +42,135 @@
<var:string label:value="match all messages"/>
</md-option>
</md-select>
</div>
</md-input-container>
</div>
<!-- CONDITIONS -->
<div id="filterRulesContainer" class="container">
<div id="filterRules"><!-- empty -->
<md-list>
<md-list-item ng-repeat="rule in filter.rules">
<!-- CONDITIONS -->
<div layout="column">
<div id="filterRules"><!-- empty -->
<md-list>
<md-list-item ng-repeat="rule in filter.rules">
<md-input-container flex="25">
<md-select ng-model="rule.field">
<md-option ng-value="key" ng-repeat="(key, value) in fieldLabels">{{ value }}</md-option>
</md-select>
</md-input-container>
<input type="text" ng-show="rule.field ==
'header'" ng-model="rule.custom_header"/>
<md-input-container flex="25" ng-show="rule.field == 'header'">
<input type="text" ng-model="rule.custom_header"/>
</md-input-container>
<md-select ng-show="rule.field =='size'" ng-model="rule.operator">
<md-input-container flex="25" ng-show="rule.field == 'size'">
<md-select ng-model="rule.operator">
<md-option ng-value="key" ng-repeat="(key, value) in numberOperatorLabels">
{{ value }}
</md-option>
</md-select>
</md-input-container>
<md-select ng-show="rule.field !='size'" ng-model="rule.operator">
<md-input-container flex="25" ng-show="rule.field != 'size'">
<md-select ng-model="rule.operator">
<md-option ng-value="key" ng-repeat="(key, value) in textOperatorLabels">
{{ value }}
</md-option>
</md-select>
</md-input-container>
<md-input-container class="md-flex">
<input type="text" ng-model="rule.value"/>
</md-input-container>
<md-button class="sg-icon-button" type="button"
ng-click="removeMailFilterRule($index)">
<md-icon>remove</md-icon>
</md-button>
<md-button class="sg-icon-button" type="button"
ng-click="removeMailFilterRule($index)">
<md-icon>remove_circle</md-icon>
</md-button>
</md-list-item>
</md-list>
</div>
<md-button class="sg-icon-button" type="button"
ng-click="addMailFilterRule($event)">
<md-icon>add</md-icon>
</md-button>
</md-list-item>
</md-list>
</div>
<!-- ACTIONS -->
<div id="filterActionsContainer" class="container">
<var:string label:value="Perform these actions:"/><br/>
<div id="filterActions"><!-- empty --></div>
<div layout="row" layout-align="end center">
<md-button class="sg-icon-button" type="button"
ng-click="addMailFilterRule($event)">
<md-icon>add_circle</md-icon>
</md-button>
</div>
</div>
<md-list>
<md-list-item ng-repeat="action in filter.actions">
<!-- ACTIONS -->
<div layout="column">
<var:string label:value="Perform these actions:"/>
<div id="filterActions"><!-- empty --></div>
<md-list>
<md-list-item layout="row" layout-align="space-between center"
ng-repeat="action in filter.actions">
<md-input-container>
<md-select ng-model="action.method">
<md-option ng-value="key" ng-repeat="(key, value) in methodLabels">{{ value }}</md-option>
</md-select>
</md-input-container>
<!-- FORWARD MESSAGE TO -->
<input type="text" ng-show="action.method == 'redirect'" ng-model="action.argument"/>
<!-- FORWARD MESSAGE TO -->
<md-input-container ng-show="action.method == 'redirect'" >
<input type="text" ng-model="action.argument"/>
</md-input-container>
<!-- DISCARD -->
<!-- nada -->
<!-- DISCARD -->
<!-- nada -->
<!-- KEEP -->
<!-- nada -->
<!-- KEEP -->
<!-- nada -->
<!-- SEND REJECT MESSAGE -->
<input type="text" ng-show="action.method == 'reject'" ng-model="action.argument"/>
<!-- SEND REJECT MESSAGE -->
<md-input-container ng-show="action.method == 'reject'">
<input type="text" ng-model="action.argument"/>
</md-input-container>
<!-- FILE INTO -->
<md-select ng-show="action.method == 'fileinto'" ng-model="action.argument">
<!-- FILE INTO -->
<md-input-container ng-show="action.method == 'fileinto'" >
<md-select ng-model="action.argument">
<md-option ng-value="item.path" ng-repeat="item in mailboxes">
{{ item.name }}
</md-option>
</md-select>
</md-input-container>
<!-- FLAG WITH -->
<md-select ng-show="action.method == 'addflag'" ng-model="action.argument" >
<!-- FLAG WITH -->
<md-input-container ng-show="action.method == 'addflag'" >
<md-select ng-model="action.argument" >
<md-option value="seen"><var:string label:value="Seen"/></md-option>
<md-option value="deleted"><var:string label:value="Deleted"/></md-option>
<md-option value="answered"><var:string label:value="Answered"/></md-option>
<md-option value="flagged"><var:string label:value="Flagged"/></md-option>
<md-option value="junk"><var:string label:value="Junk"/></md-option>
<md-option value="not_junk"><var:string label:value="Not Junk"/></md-option>
<md-option ng-value="key" ng-repeat="(key, value) in labels">{{ value[0] }}</md-option>
</md-select>
</md-input-container>
<!-- STOP -->
<!-- nada -->
<!-- STOP -->
<!-- nada -->
<md-button class="sg-icon-button" type="button"
ng-click="removeMailFilterAction($index)">
<md-icon>remove</md-icon>
</md-button>
<md-button class="sg-icon-button" type="button"
ng-click="removeMailFilterAction($index)">
<md-icon>remove_circle</md-icon>
</md-button>
</md-list-item>
</md-list>
</md-list>
<div layout="row" layout-align="end center">
<md-button class="sg-icon-button" type="button"
ng-click="addMailFilterAction($event)">
<md-icon>add</md-icon>
<md-icon>add_circle</md-icon>
</md-button>
</div>
<div class="md-actions" layout="row">
<md-button type="button" ng-click="cancel()">Cancel</md-button>
<md-button type="button" class="md-primary" ng-click="save()">Save</md-button>
</div>
</form>
</md-dialog-content>
</md-dialog-content>
<div class="md-actions">
<md-button type="button" ng-click="save()"><var:string label:value="Save"/></md-button>
</div>
</form>
</md-dialog>
</container>
File diff suppressed because it is too large. Load diff
@@ -4,33 +4,39 @@
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label">
<md-dialog flex="60">
<md-dialog flex="60" flex-sm="100">
<form name="eventForm" ng-submit="editor.save(eventForm)">
<md-toolbar class="md-padding" ng-class="editor.component.getClassName('bg')">
<md-toolbar class="sg-padded" ng-class="editor.component.getClassName('bg')">
<div class="md-toolbar-tools">
<!-- summary -->
<md-icon ng-if="editor.component.classification == 'confidential'">visibility_off</md-icon>
<md-icon ng-if="editor.component.classification == 'private'">vpn_key</md-icon>
<md-input-container md-no-float="true">
<md-input-container>
<label><var:string label:value="Title"/></label>
<input type="text"
class="md-title"
label:placeholder="Title"
ng-model="editor.component.summary"/>
ng-model="editor.component.summary"
required="required"/>
</md-input-container>
<md-button class="md-icon-button" ng-click="editor.cancel()">
<md-icon aria-label="Close dialog">close</md-icon>
</md-button>
</div>
</md-toolbar>
<!--<md-chips>
<md-chip ng-repeat="category in editor.component.categories">{{category}}</md-chip>
</md-chips>-->
<md-dialog-content>
<!-- location -->
<md-input-container>
<label><var:string label:value="Location"/></label>
<input type="text" ng-model="editor.component.location"/>
</md-input-container>
<!-- calendar -->
<md-input-container>
<label><var:string label:value="Calendar"/></label>
<md-select ng-model="editor.component.destinationCalendar">
<md-option ng-repeat="calendar in ::editor.calendars"
ng-value="calendar.id">{{calendar.name}}</md-option>
</md-select>
</md-input-container>
<!-- categories -->
<md-chips ng-model="editor.component.categories">
<md-autocomplete
@@ -41,14 +47,6 @@
<span md-highlight-text="editor.categories.searchText">{{category}}</span>
</md-autocomplete>
</md-chips>
<!-- calendar -->
<md-input-container>
<label><var:string label:value="Calendar"/></label>
<md-select ng-model="editor.component.destinationCalendar">
<md-option ng-repeat="calendar in ::editor.calendars"
ng-value="calendar.id">{{calendar.name}}</md-option>
</md-select>
</md-input-container>
<!-- classification -->
<md-radio-group layout="row" layout-align="space-around center"
ng-model="editor.component.classification">
@@ -63,8 +61,8 @@
</md-radio-button>
</md-radio-group>
<!-- priority -->
<div layout="row" layout-align="center center">
<div flex="20"><var:string label:value="Priority"/></div>
<div class="pseudo-input-container">
<label class="pseudo-input-label"><var:string label:value="Priority"/></label>
<md-slider class="md-flex"
md-discrete="true"
ng-model="editor.component.priority"
@@ -98,15 +96,17 @@
<var:string label:value="Send Appointment Notifications"/>
</md-checkbox>
<!-- start + end -->
<div layout="row">
<md-input-container>
<label><var:string label:value="From"/></label>
<input type="text" ng-model="editor.component.startDate"/>
</md-input-container>
<md-input-container>
<label><var:string label:value="To"/></label>
<input type="text" ng-model="editor.component.endDate"/>
</md-input-container>
<div layout="column">
<div layout="row" layout-align="start center">
<span><var:string label:value="From"/></span>
<md-datepicker ng-model="editor.component.start"> <!-- date picker--></md-datepicker>
<sg-timepicker ng-model="editor.component.start"><!-- time picker --></sg-timepicker>
</div>
<div layout="row" layout-align="start center">
<span><var:string label:value="To"/></span>
<md-datepicker ng-model="editor.component.end"> <!-- date picker--></md-datepicker>
<sg-timepicker ng-model="editor.component.end"><!-- time picker --></sg-timepicker>
</div>
</div>
<!-- attach urls -->
<div class="attr" ng-repeat="attach in editor.component.attachUrls">
@@ -188,7 +188,7 @@
md-search-text="editor.searchText"
md-selected-item-change="editor.addAttendee(card)"
md-items="card in editor.cardFilter(editor.searchText)"
md-item-text="user.empty"
md-item-text="card.empty"
md-min-length="3"
md-delay="300"
md-no-cache="true"
@@ -243,7 +243,8 @@
<md-button type="button" ng-click="editor.component.$reset()">
<var:string label:value="Reset"/>
</md-button>
<md-button class="md-primary md-hue-3" type="submit">
<md-button class="md-primary md-hue-3" type="submit"
ng-disabled="editor.eventForm.$invalid">
<var:string label:value="Save"/>
</md-button>
</div>
@@ -144,7 +144,7 @@
class="md-flex"
md-input-name="delegatedTo"
md-search-text="editor.component.delegatedTo"
md-selected-item="editor.cardToAdd"
md-selected-item="cardToAdd"
md-items="card in editor.cardFilter(editor.component.delegatedTo)"
md-item-text="card.$$email"
md-min-length="3"
+26 -24
View File
@@ -18,28 +18,30 @@
<li><var:string label:value="Copy event to my calendar"/></li>
<li><var:string label:value="View Raw Source"/></li>
</ul>
</div>-->
<div layout="row" layout-align="center">
<h5>
<md-button
class="sg-icon-button"
label:aria-label="Previous Day"
var:date="prevDayQueryParameters.day"
ng-click="calendar.changeView($event)"><md-icon>chevron_left</md-icon></md-button>
<var:string value="currentDayName" />
<md-button
class="sg-icon-button"
label:aria-label="Next Day"
var:date="nextDayQueryParameters.day"
ng-click="calendar.changeView($event)"><md-icon>chevron_right</md-icon></md-button>
</h5>
</div>
<div id="calendarContent">
<var:component
className="UIxCalDayTable"
startDate="startDate"
const:CSSClass="dayOverview"
const:numberOfDays="1"
const:currentView="dayview"/>
</div>
</div>-->
<md-content>
<div layout="row" layout-align="center">
<h5>
<md-button
class="sg-icon-button"
label:aria-label="Previous Day"
var:date="prevDayQueryParameters.day"
ng-click="calendar.changeView($event)"><md-icon>chevron_left</md-icon></md-button>
<var:string value="currentDayName" />
<md-button
class="sg-icon-button"
label:aria-label="Next Day"
var:date="nextDayQueryParameters.day"
ng-click="calendar.changeView($event)"><md-icon>chevron_right</md-icon></md-button>
</h5>
</div>
<div id="calendarContent">
<var:component
className="UIxCalDayTable"
startDate="startDate"
const:CSSClass="dayOverview"
const:numberOfDays="1"
const:currentView="dayview"/>
</div>
</md-content>
</container>
+50 -25
View File
@@ -138,15 +138,12 @@
</div>
-->
<main class="view md-layout-fill" ui-view="calendars" layout="row" ng-controller="navController"><!-- calendars list --></main>
<main class="view layout-fill" ui-view="calendars" layout="row" ng-controller="navController"><!-- calendars list --></main>
<script type = "text/ng-template" id="UIxCalMainFrame">
<!-- calendars colors -->
<div sg-folder-stylesheet="true"
ng-repeat="calendar in calendars.service.$calendars"
ng-model="calendar"><!-- stylesheet --></div>
<div sg-folder-stylesheet="true"
ng-repeat="calendar in calendars.service.$subscriptions"
ng-repeat="calendar in calendars.service.$findAll()"
ng-model="calendar"><!-- stylesheet --></div>
<!-- Sidenav -->
<md-sidenav id="left-sidenav" class="md-sidenav-left md-whiteframe-z1" md-component-id="left" md-is-locked-open="isGtMedium" layout="column">
@@ -308,18 +305,20 @@
</md-sidenav>
<!-- Main section -->
<section layout="column" class="sg-app-content md-layout-fill"
<section layout="column" class="sg-app-content layout-fill"
ng-controller="CalendarListController as list">
<md-toolbar layout="column" layout-align="space-between start" class="md-tall toolbar-main">
<div class="md-toolbar-tools md-toolbar-tools-top sg-padded" layout="row" layout-align="space-between start">
<!-- <var:component className="UIxTopnavToolbarTemplate" /> -->
<div class="sg-toolbar-group-2">
<div class="md-toolbar-tools md-toolbar-tools-top" layout="row" layout-align="space-between start">
<var:component className="UIxTopnavToolbarTemplate" />
<!-- <div class="sg-toolbar-group-2">
<md-button class="sg-icon-button" label:aria-label="Search">
<md-icon>search</md-icon>
</md-button>
</div>
</div> -->
</div><!-- .md-toolbar-tools -->
<div class="md-toolbar-tools" layout="row" layout-align="space-between center">
<div class="md-toolbar-tools" layout="row"
layout-align="space-between center"
ng-show="list.component.$selectedCount() == 0">
<!-- sort/filter mode -->
<div class="view-list" layout="row" layout-align="space-between center"
ng-hide="list.mode.search">
@@ -475,16 +474,18 @@
</md-menu-item>
<md-menu-divider><!-- divider --></md-menu-divider>
<md-menu-item ng-if="list.componentType == 'events'">
<md-button ng-click="list.component.$filter(list.componentType)">
<md-button ng-href="#">
<md-checkbox
ng-change="list.component.$filter(list.componentType)"
ng-model="list.component.$queryEvents.asc"
ng-true-value="0"
ng-false-value="1"><var:string label:value="Descending Order"/></md-checkbox>
</md-button>
</md-menu-item>
<md-menu-item ng-if="list.componentType == 'tasks'">
<md-button ng-click="list.component.$filter(list.componentType)">
<md-button ng-href="#">
<md-checkbox
ng-change="list.component.$filter(list.componentType)"
ng-model="list.component.$queryTasks.asc"
ng-true-value="0"
ng-false-value="1"><var:string label:value="Descending Order"/></md-checkbox>
@@ -492,6 +493,9 @@
</md-menu-item>
</md-menu-content>
</md-menu>
<md-button class="sg-icon-button" ng-click="list.component.$filter(list.componentType)">
<md-icon>refresh</md-icon>
</md-button>
</div>
</div>
<!-- search mode -->
@@ -514,7 +518,7 @@
</md-input-container>
</div>
<!-- day/week/month views -->
<div class="sg-padded">
<div>
<a class="sg-icon-button md-button"
label:aria-label="Day"
href="#/calendar/day"><md-icon>view_day</md-icon></a>
@@ -526,6 +530,19 @@
href="#/calendar/month"><md-icon>view_module</md-icon></a>
</div>
</div>
<div layout="row" layout-align="start center" ng-show="list.component.$selectedCount() > 0">
<md-button class="sg-icon-button" ng-click="list.unselectComponents()">
<md-icon>arrow_back</md-icon>
</md-button>
<label>{{list.component.$selectedCount()}} selected</label>
<md-button class="sg-icon-button" ng-click="list.selectAll()">
<md-tooltip md-direction="left"><var:string label:value="Select All"/></md-tooltip>
<md-icon>select_all</md-icon>
</md-button>
<md-button class="sg-icon-button" ng-click="list.confirmDeleteSelectedComponents()">
<md-icon>delete</md-icon>
</md-button>
</div>
</md-toolbar>
<md-content layout="row" class="md-flex">
<md-content class="view-list md-flex" layout="column">
@@ -534,24 +551,28 @@
<!-- Events list -->
<md-tab label:label="Events"
md-on-select="list.selectComponentType('events')">
<md-list>
<md-list-item class="md-clickable md-2-line"
<md-list class="sg-section-list">
<md-list-item class="md-2-line"
ng-repeat="event in list.component.$events"
ng-click="list.openEvent($event, event)">
<i class="md-avatar" ng-class="event.getClassName('bg')" ><!-- calendar color --></i>
<div class="sg-selected-avatar" ng-show="event.selected"
ng-click="event.selected = !event.selected">
<!-- selected avatar -->
</div>
<i class="md-tile-left" ng-class="event.getClassName('bg')" ng-show="!event.selected" ng-click="event.selected = !event.selected"><!-- calendar color --></i>
<div class="md-list-item-text">
<h3>{{event.c_title}}</h3>
<p>{{event.c_location}}</p>
<p class="md-secondary" layout="row">
{{event.formatted_startdate}}
<span class="sg-tile-icons">
<i class="sg-tile-icons">
<md-icon ng-show="event.c_iscycle">repeat</md-icon>
<md-icon ng-show="event.c_nextalarm">alarm</md-icon>
</span>
</i>
</p>
</div>
</md-list-item>
<md-list-item ng-if="list.component.$events.length == 0">
<md-list-item disabled="disabled" ng-if="list.component.$events.length == 0">
<p class="sg-md-caption"><var:string label:value="No events for selected criteria"/></p>
</md-list-item>
</md-list>
@@ -559,11 +580,15 @@
<!-- Tasks list -->
<md-tab label:label="Tasks"
md-on-select="list.selectComponentType('tasks')">
<md-list>
<md-list-item class="md-clickable md-2-line"
<md-list class="sg-section-list">
<md-list-item class="md-2-line"
ng-repeat="task in list.component.$tasks"
ng-click="list.openTask($event, task)">
<i class="md-avatar" ng-class="task.getClassName('bg')" ><!-- calendar color --></i>
<div class="sg-selected-avatar" ng-show="task.selected"
ng-click="task.selected = !task.selected">
<!-- selected avatar -->
</div>
<i class="md-tile-left" ng-class="task.getClassName('bg')" ng-show="!task.selected" ng-click="task.selected = !task.selected"><!-- calendar color --></i>
<div class="md-list-item-text">
<h3>{{task.c_title}}</h3>
<p class="md-secondary" layout="row">
@@ -575,7 +600,7 @@
</p>
</div>
</md-list-item>
<md-list-item ng-if="list.component.$tasks.length == 0">
<md-list-item disabled="disabled" ng-if="list.component.$tasks.length == 0">
<p class="sg-md-caption"><var:string label:value="No tasks for selected criteria"/></p>
</md-list-item>
</md-list>
@@ -587,7 +612,7 @@
<md-icon>add</md-icon>
</md-button>
</md-content>
<md-content class="view-detail sg-padded" ui-view="calendarView"><!-- calendar view --></md-content>
<div id="detailView" hide-sm="hide-sm" class="view-detail" layout="column" ui-view="calendarView"><!-- calendar view --></div>
</md-content>
</section>
</script>
+23 -19
View File
@@ -21,21 +21,22 @@
</ul>
</div>
<div layout="row" layout-align="center">
<h5>
<md-button
class="sg-icon-button"
label:aria-label="Previous Month"
var:date="prevMonthQueryParameters.day"
ng-click="calendar.changeView($event)"><md-icon>chevron_left</md-icon></md-button>
<var:string value="monthNameOfThisMonth" />
<md-button
class="sg-icon-button"
label:aria-label="Next Month"
var:date="nextMonthQueryParameters.day"
ng-click="calendar.changeView($event)"><md-icon>chevron_right</md-icon></md-button>
</h5>
</div>
<md-content>
<div layout="row" layout-align="center">
<h5>
<md-button
class="sg-icon-button"
label:aria-label="Previous Month"
var:date="prevMonthQueryParameters.day"
ng-click="calendar.changeView($event)"><md-icon>chevron_left</md-icon></md-button>
<var:string value="monthNameOfThisMonth" />
<md-button
class="sg-icon-button"
label:aria-label="Next Month"
var:date="nextMonthQueryParameters.day"
ng-click="calendar.changeView($event)"><md-icon>chevron_right</md-icon></md-button>
</h5>
</div>
<div class="monthView">
<div layout="row" layout-align="center center">
<var:foreach list="headerDaysToDisplay" item="currentTableDay">
@@ -55,13 +56,16 @@
var:day-number="currentDayNumber"
var:id="currentDayId">
<div layout="column" layout-fill="true">
<div class="sg-calendar-tile-header"><var:string value="dayHeaderNumber"/></div>
<sg-calendar-month-day
sg-blocks="calendar.blocks"
var:sg-day="currentTableDay.shortDateString"><!-- day's events --></sg-calendar-month-day></div>
<div class="sg-calendar-tile-header"><var:string value="dayHeaderNumber"/></div>
<sg-calendar-month-day
sg-blocks="calendar.blocks"
sg-click="list.openEvent(event, component)"
var:sg-day="currentTableDay.shortDateString"><!-- day's events --></sg-calendar-month-day>
</div>
</md-grid-tile>
</var:foreach>
</var:foreach>
</md-grid-list>
</div>
</md-content>
</container>
+25 -23
View File
@@ -6,27 +6,29 @@
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label">
<div layout="row" layout-align="center">
<h5>
<md-button
class="sg-icon-button"
label:aria-label="Previous Week"
var:date="prevWeekQueryParameters.day"
ng-click="calendar.changeView($event)"><md-icon>chevron_left</md-icon></md-button>
<var:string value="currentWeekName" />
<md-button
class="sg-icon-button"
label:aria-label="Next Week"
var:date="nextWeekQueryParameters.day"
ng-click="calendar.changeView($event)"><md-icon>chevron_right</md-icon></md-button>
</h5>
</div>
<div id="calendarContent">
<var:component
className="UIxCalDayTable"
startDate="startDate"
const:CSSClass="weekOverview"
const:numberOfDays="7"
const:currentView="weekview"/>
</div>
<md-content>
<div layout="row" layout-align="center">
<h5>
<md-button
class="sg-icon-button"
label:aria-label="Previous Week"
var:date="prevWeekQueryParameters.day"
ng-click="calendar.changeView($event)"><md-icon>chevron_left</md-icon></md-button>
<var:string value="currentWeekName" />
<md-button
class="sg-icon-button"
label:aria-label="Next Week"
var:date="nextWeekQueryParameters.day"
ng-click="calendar.changeView($event)"><md-icon>chevron_right</md-icon></md-button>
</h5>
</div>
<div id="calendarContent">
<var:component
className="UIxCalDayTable"
startDate="startDate"
const:CSSClass="weekOverview"
const:numberOfDays="7"
const:currentView="weekview"/>
</div>
</md-content>
</container>
@@ -7,14 +7,15 @@
xmlns:label="OGo:label"
>
<md-dialog flex="40">
<md-dialog flex="60" flex-sm="100">
<md-toolbar class="sg-padded">
<md-icon class="material-icons sg-icon-toolbar-bg">link</md-icon>
<div class="md-toolbar-tools">
<div class="pseudo-input-container md-flex">
<label class="pseudo-input-label"><var:string label:value="Links to this Calendar"/></label>
<div class="pseudo-input-field sg-md-title"><var:string value="calendar.displayName"/></div>
<div class="pseudo-input-field sg-md-title">{{links.calendar.name}}</div>
</div>
<md-button class="md-icon-button" ng-click="close()">
<md-button class="md-icon-button" ng-click="links.close()">
<md-icon label:aria-label="Close">close</md-icon>
</md-button>
</div>
@@ -24,50 +25,45 @@
<div layout="column" layout-padding="layout-padding">
<div layout="column" layout-padding="layout-padding">
<md-input-container ng-show="links.calendar.isWebCalendar">
<label><var:string label:value="URL"/></label>
<input type="text" ng-model="links.calendar.urls.webCalendarURL" ng-readonly="true"/>
</md-input-container>
<var:if condition="isWebCalendar">
<md-input-container>
<label><var:string label:value="URL"/></label>
<input var:value="webCalendarURL" ng-readonly="true"/>
</md-input-container>
</var:if>
<var:if condition="isWebCalendar" const:negate="YES">
<div ng-hide="links.calendar.isWebCalendar">
<div class="sg-md-title"><var:string label:value="Authenticated User Access"/></div>
<md-input-container>
<label><var:string label:value="CalDAV URL"/></label>
<input var:value="calDavURL" ng-readonly="true"/>
<input type="text" ng-model="links.calendar.urls.calDavURL" ng-readonly="true"/>
</md-input-container>
<md-input-container>
<label><var:string label:value="WebDAV ICS URL"/></label>
<input var:value="webDavICSURL" ng-readonly="true"/>
<input type="text" ng-model="links.calendar.urls.webDavICSURL" ng-readonly="true"/>
</md-input-container>
<md-input-container>
<label><var:string label:value="WebDAV XML URL"/></label>
<input var:value="webDavXMLURL" ng-readonly="true"/>
<input type="text" ng-model="links.calendar.urls.webDavXMLURL" ng-readonly="true"/>
</md-input-container>
</var:if>
</div>
</div>
<var:if condition="isPublicAccessEnabled">
<var:if condition="isWebCalendar" const:negate="YES">
<div layout="column" layout-padding="layout-padding">
<div layout="column" layout-padding="layout-padding" ng-hide="links.calendar.isWebCalendar">
<div class="sg-md-title"><var:string label:value="Public Access"/></div>
<md-input-container>
<label><var:string label:value="CalDAV URL"/></label>
<input var:value="publicCalDavURL" ng-readonly="true"/>
<input type="text" ng-model="links.calendar.urls.publicCalDavURL" ng-readonly="true"/>
</md-input-container>
<md-input-container>
<label><var:string label:value="WebDAV ICS URL"/></label>
<input var:value="publicWebDavICSURL" ng-readonly="true"/>
<input type="text" ng-model="links.calendar.urls.publicWebDavICSURL" ng-readonly="true"/>
</md-input-container>
<md-input-container>
<label><var:string label:value="WebDAV XML URL"/></label>
<input var:value="publicWebDavXMLURL" ng-readonly="true"/>
<input type="text" ng-model="links.calendar.urls.publicWebDavXMLURL" ng-readonly="true"/>
</md-input-container>
</div>
</var:if>
</var:if>
</div>
</md-dialog-content>
@@ -6,7 +6,7 @@
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label"
>
<md-dialog>
<md-dialog flex-sm="100">
<md-toolbar ng-style="{ 'background-color': properties.calendar.color }">
<div class="md-toolbar-tools sg-padded--right">
@@ -22,15 +22,19 @@
<md-input-container class="md-input-number">
<input type="number" label:aria-label="Every" ng-model="editor.component.repeat.interval"/>
</md-input-container>
<var:string label:value="Week(s)"/> <var:string label:value="On"/>
<var:string label:value="Week(s)"/>
</div>
<div class="pseudo-input-container layout-padding" layout="row" layout-align="center center">
<label class="pseudo-input-label"><var:string label:value="On"/></label>
<md-grid-list md-cols="7" md-row-height="2em" md-gutter="0.5em"
flex="50" flex-md="80" flex-sm="100"
sg-toggle-grid="editor.component.repeat.days"
sg-toggle-grid-attr="day">
<var:foreach list="shortWeekDaysList" item="item">
<md-grid-tile var:value="valueForWeekDay"><var:string value="labelForWeekDay"/></md-grid-tile>
</var:foreach>
</md-grid-list>
</div>
<md-grid-list md-cols="7" md-row-height="1:1" md-gutter="0.5em"
sg-toggle-grid="editor.component.repeat.days"
sg-toggle-grid-attr="day">
<var:foreach list="shortWeekDaysList" item="item">
<md-grid-tile var:value="valueForWeekDay"><var:string value="labelForWeekDay"/></md-grid-tile>
</var:foreach>
</md-grid-list>
</div>
<!-- monthly -->
@@ -46,40 +50,43 @@
<md-radio-button value="bymonthday">
<var:string label:value="Recur on day(s)"/>
</md-radio-button>
<md-grid-list md-cols="7" md-row-height="1:1" md-gutter="0.5em"
sg-toggle-grid="editor.component.repeat.monthdays">
<md-grid-tile value="1">1</md-grid-tile>
<md-grid-tile value="2">2</md-grid-tile>
<md-grid-tile value="3">3</md-grid-tile>
<md-grid-tile value="4">4</md-grid-tile>
<md-grid-tile value="5">5</md-grid-tile>
<md-grid-tile value="6">6</md-grid-tile>
<md-grid-tile value="7">7</md-grid-tile>
<md-grid-tile value="8">8</md-grid-tile>
<md-grid-tile value="9">9</md-grid-tile>
<md-grid-tile value="10">10</md-grid-tile>
<md-grid-tile value="11">11</md-grid-tile>
<md-grid-tile value="12">12</md-grid-tile>
<md-grid-tile value="13">13</md-grid-tile>
<md-grid-tile value="14">14</md-grid-tile>
<md-grid-tile value="15">15</md-grid-tile>
<md-grid-tile value="16">16</md-grid-tile>
<md-grid-tile value="17">17</md-grid-tile>
<md-grid-tile value="18">18</md-grid-tile>
<md-grid-tile value="19">19</md-grid-tile>
<md-grid-tile value="20">20</md-grid-tile>
<md-grid-tile value="21">21</md-grid-tile>
<md-grid-tile value="22">22</md-grid-tile>
<md-grid-tile value="23">23</md-grid-tile>
<md-grid-tile value="24">24</md-grid-tile>
<md-grid-tile value="25">25</md-grid-tile>
<md-grid-tile value="26">26</md-grid-tile>
<md-grid-tile value="27">27</md-grid-tile>
<md-grid-tile value="28">28</md-grid-tile>
<md-grid-tile value="29">29</md-grid-tile>
<md-grid-tile value="30">30</md-grid-tile>
<md-grid-tile value="31">31</md-grid-tile>
</md-grid-list>
<div layout="row" layout-align="start center">
<md-grid-list md-cols="7" md-row-height="2em" md-gutter="0.5em"
flex="50" flex-md="80" flex-sm="100"
sg-toggle-grid="editor.component.repeat.monthdays">
<md-grid-tile value="1">1</md-grid-tile>
<md-grid-tile value="2">2</md-grid-tile>
<md-grid-tile value="3">3</md-grid-tile>
<md-grid-tile value="4">4</md-grid-tile>
<md-grid-tile value="5">5</md-grid-tile>
<md-grid-tile value="6">6</md-grid-tile>
<md-grid-tile value="7">7</md-grid-tile>
<md-grid-tile value="8">8</md-grid-tile>
<md-grid-tile value="9">9</md-grid-tile>
<md-grid-tile value="10">10</md-grid-tile>
<md-grid-tile value="11">11</md-grid-tile>
<md-grid-tile value="12">12</md-grid-tile>
<md-grid-tile value="13">13</md-grid-tile>
<md-grid-tile value="14">14</md-grid-tile>
<md-grid-tile value="15">15</md-grid-tile>
<md-grid-tile value="16">16</md-grid-tile>
<md-grid-tile value="17">17</md-grid-tile>
<md-grid-tile value="18">18</md-grid-tile>
<md-grid-tile value="19">19</md-grid-tile>
<md-grid-tile value="20">20</md-grid-tile>
<md-grid-tile value="21">21</md-grid-tile>
<md-grid-tile value="22">22</md-grid-tile>
<md-grid-tile value="23">23</md-grid-tile>
<md-grid-tile value="24">24</md-grid-tile>
<md-grid-tile value="25">25</md-grid-tile>
<md-grid-tile value="26">26</md-grid-tile>
<md-grid-tile value="27">27</md-grid-tile>
<md-grid-tile value="28">28</md-grid-tile>
<md-grid-tile value="29">29</md-grid-tile>
<md-grid-tile value="30">30</md-grid-tile>
<md-grid-tile value="31">31</md-grid-tile>
</md-grid-list>
</div>
<div layout="row" layout-align="start center">
<md-radio-button value="byday">
<var:string label:value="The"/>
@@ -111,27 +118,30 @@
</md-input-container>
<var:string label:value="Year(s)"/>
</div>
<md-grid-list md-cols="6" md-row-height="1:1" md-gutter="0.5em"
sg-toggle-grid="editor.component.repeat.months">
<var:foreach list="yearlyMonthList" item="item">
<md-grid-tile var:value="valueForYearlyMonth"><var:string value="item"/></md-grid-tile>
</var:foreach>
</md-grid-list>
<div layout="row" layout-align="start center">
<md-checkbox ng-model="editor.component.repeat.year.byday">
<var:string label:value="The"/>
</md-checkbox>
<md-select ng-model="editor.component.repeat.month.occurrence">
<var:foreach list="monthlyRepeatList" item="item">
<md-option var:value="valueForMonthlyRepeat"><var:string value="itemText"/></md-option>
</var:foreach>
</md-select>
<md-select ng-model="editor.component.repeat.month.day">
<var:foreach list="yearlyDayList" item="item">
<md-option var:value="valueForYearlyDay"><var:string value="item"/></md-option>
</var:foreach>
</md-select>
</div>
<div layout="row" layout-align="start center">
<md-grid-list md-cols="6" md-row-height="2em" md-gutter="0.5em"
flex="50" flex-md="80" flex-sm="100"
sg-toggle-grid="editor.component.repeat.months">
<var:foreach list="yearlyMonthList" item="item">
<md-grid-tile var:value="valueForYearlyMonth"><var:string value="item"/></md-grid-tile>
</var:foreach>
</md-grid-list>
</div>
<div layout="row" layout-align="start center">
<md-checkbox ng-model="editor.component.repeat.year.byday">
<var:string label:value="The"/>
</md-checkbox>
<md-select ng-model="editor.component.repeat.month.occurrence">
<var:foreach list="monthlyRepeatList" item="item">
<md-option var:value="valueForMonthlyRepeat"><var:string value="itemText"/></md-option>
</var:foreach>
</md-select>
<md-select ng-model="editor.component.repeat.month.day">
<var:foreach list="yearlyDayList" item="item">
<md-option var:value="valueForYearlyDay"><var:string value="item"/></md-option>
</var:foreach>
</md-select>
</div>
</div>
</container>
@@ -4,7 +4,7 @@
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label">
<div layout="row" layout-align="start center">
<div layout="row" layout-align="start center" layout-wrap="layout-wrap">
<md-input-container class="md-input-number">
<input type="number" label:aria-label="quantity" ng-model="editor.component.alarm.quantity"/>
</md-input-container>
@@ -4,18 +4,19 @@
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label">
<md-dialog flex="60">
<md-dialog flex="60" flex-sm="100">
<form name="eventForm" ng-submit="editor.save(eventForm)">
<md-toolbar class="md-padding" ng-class="editor.component.getClassName('bg')">
<div class="md-toolbar-tools">
<md-toolbar ng-class="editor.component.getClassName('bg')">
<div class="md-toolbar-tools sg-padded">
<!-- summary -->
<md-icon ng-if="editor.component.classification == 'confidential'">visibility_off</md-icon>
<md-icon ng-if="editor.component.classification == 'private'">vpn_key</md-icon>
<md-input-container md-no-float="true">
<md-input-container>
<label><var:string label:value="Title"/></label>
<input type="text"
class="md-title"
label:placeholder="Title"
ng-model="editor.component.summary"/>
ng-model="editor.component.summary"
required="required"/>
</md-input-container>
<md-button class="md-icon-button" ng-click="editor.cancel()">
<md-icon aria-label="Close dialog">close</md-icon>
@@ -79,15 +80,35 @@
<var:string label:value="All day Event"/>
</md-checkbox>
<!-- start + end -->
<div layout="row">
<md-input-container>
<label><var:string label:value="Start"/></label>
<input type="text" ng-model="editor.component.startDate"/>
</md-input-container>
<md-input-container>
<label><var:string label:value="Due"/></label>
<input type="text" ng-model="editor.component.dueDate"/>
</md-input-container>
<div class="section" layout="column">
<div layout="row" layout-align="start center" ng-show="editor.component.start">
<label><var:string label:value="From"/></label>
<md-datepicker ng-model="editor.component.start"> <!-- date picker--></md-datepicker>
<sg-timepicker ng-model="editor.component.start"><!-- time picker --></sg-timepicker>
<md-button class="sg-icon-button" type="button" ng-click="editor.component.$deleteStartDate()">
<md-icon>remove_circle</md-icon>
</md-button>
</div>
<div class="md-layout-margin" layout="row" layout-align="start center" ng-hide="editor.component.start">
<md-button class="sg-icon-button" type="button" ng-click="editor.component.$addStartDate()">
<md-icon>add_circle</md-icon>
</md-button>
<label class="button-label"><var:string label:value="Add From"/></label>
</div>
</div>
<div layout="row" layout-align="start center" ng-show="editor.component.due">
<label><var:string label:value="Due"/></label>
<md-datepicker ng-model="editor.component.due"> <!-- date picker--></md-datepicker>
<sg-timepicker ng-model="editor.component.due"><!-- time picker --></sg-timepicker>
<md-button class="sg-icon-button" type="button" ng-click="editor.component.$deleteDueDate()">
<md-icon>remove_circle</md-icon>
</md-button>
</div>
<div class="md-layout-margin" layout="row" layout-align="start center" ng-hide="editor.component.due">
<md-button class="sg-icon-button" type="button" ng-click="editor.component.$addDueDate()">
<md-icon>add_circle</md-icon>
</md-button>
<label class="button-label"><var:string label:value="Add Due"/></label>
</div>
<!-- status -->
<div layout="row">
@@ -206,7 +227,8 @@
<md-button type="button" ng-click="editor.component.$reset()">
<var:string label:value="Reset"/>
</md-button>
<md-button class="md-primary md-hue-3" type="submit">
<md-button class="md-primary md-hue-3" type="submit"
ng-disabled="editor.eventForm.$invalid">
<var:string label:value="Save"/>
</md-button>
</div>
@@ -4,20 +4,20 @@
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label">
<md-dialog flex="40">
<md-toolbar class="md-padding" ng-class="viewer.component.getClassName('bg')">
<md-dialog flex="40" flex-md="60" flex-sm="100">
<md-toolbar class="md-padding" ng-class="editor.component.getClassName('bg')">
<div class="md-toolbar-tools">
<md-icon class="material-icons sg-icon-toolbar-bg">assignment_turned_in</md-icon>
<h2 class="md-flex">
<div class="sg-md-title md-flex">
<!-- classification -->
<md-icon ng-if="viewer.component.classification == 'confidential'">visibility_off</md-icon>
<md-icon ng-if="viewer.component.classification == 'private'">vpn_key</md-icon>
<md-icon ng-if="editor.component.classification == 'confidential'">visibility_off</md-icon>
<md-icon ng-if="editor.component.classification == 'private'">vpn_key</md-icon>
<!-- summary -->
{{viewer.component.summary}}
{{editor.component.summary}}
<!-- priority -->
<md-icon ng-repeat="i in viewer.component.priority | range">star</md-icon>
</h2>
<md-button class="md-icon-button" ng-click="viewer.close()">
<md-icon ng-repeat="i in editor.component.priority | range">star</md-icon>
</div>
<md-button class="md-icon-button" ng-click="editor.close()">
<md-icon aria-label="Close dialog">close</md-icon>
</md-button>
</div>
@@ -25,77 +25,77 @@
<md-dialog-content>
<md-list>
<!-- categories -->
<md-list-item ng-show="viewer.component.categories.length > 0">
<md-chips ng-model="viewer.component.categories" readonly="true">
<md-list-item ng-show="editor.component.categories.length > 0">
<md-chips class="sg-readonly" ng-model="editor.component.categories" readonly="true">
<md-chip-template>
<span>{{$chip}}</span>
</md-chip-template>
</md-chips>
</md-list-item>
<!-- location -->
<md-list-item ng-show="viewer.component.location">
<md-list-item ng-show="editor.component.location">
<md-icon>place</md-icon>
<p>{{viewer.component.location}}</p>
<p>{{editor.component.location}}</p>
</md-list-item>
<!-- calendar -->
<md-list-item>
<md-icon>event</md-icon>
<p>{{viewer.component.calendar}}</p>
<p>{{editor.component.calendar}}</p>
</md-list-item>
<!-- start/due dates -->
<md-list-item class="md-2-line">
<md-icon>access_time</md-icon>
<div layout="row" class="md-flex">
<div class="md-list-item-text" ng-show="viewer.component.startDate">
<div class="md-list-item-text" ng-show="editor.component.startDate">
<p><var:string label:value="Start"/></p>
<h3>
{{viewer.component.localizedStartDate}}
<span ng-hide="viewer.component.isAllDay"> {{viewer.component.localizedStartTime}}</span>
{{editor.component.localizedStartDate}}
<span ng-hide="editor.component.isAllDay"> {{editor.component.localizedStartTime}}</span>
</h3>
</div>
<div class="md-list-item-text" ng-show="viewer.component.dueDate">
<div class="md-list-item-text" ng-show="editor.component.dueDate">
<p><var:string label:value="Due Date"/></p>
<h3>
{{viewer.component.localizedDueDate}}
<span ng-hide="viewer.component.isAllDay">{{viewer.component.localizedDueTime}}</span>
{{editor.component.localizedDueDate}}
<span ng-hide="editor.component.isAllDay">{{editor.component.localizedDueTime}}</span>
</h3>
</div>
</div>
</md-list-item>
<!-- status -->
<md-list-item ng-show="viewer.component.status == 'completed'">
<md-list-item ng-show="editor.component.status == 'completed'">
<md-icon>check</md-icon>
<p>{{viewer.component.localizedCompletedDate}} {{viewer.component.localizedCompletedTime}}</p>
<p>{{editor.component.localizedCompletedDate}} {{editor.component.localizedCompletedTime}}</p>
</md-list-item>
<md-list-item ng-show="viewer.component.enablePercentComplete()">
<md-list-item ng-show="editor.component.enablePercentComplete()">
<md-icon>call_made</md-icon>
<p>{{viewer.component.percentComplete}} %</p>
<p>{{editor.component.percentComplete}} %</p>
</md-list-item>
<!-- attach urls -->
<md-list-item ng-show="viewer.component.attachUrls.length > 0">
<md-list-item ng-repeat="url in editor.component.attachUrls">
<md-icon>link</md-icon>
<p ng-repeat="url in viewer.component.attachUrls"><a ng-href="url.value">{{url.value}}</a></p>
<p><a ng-href="url.value">{{url.value}}</a></p>
</md-list-item>
<!-- comment -->
<md-list-item ng-show="viewer.component.comment">
<md-list-item ng-show="editor.component.comment">
<md-icon>mode_comment</md-icon>
<p>{{viewer.component.comment}}</p>
<p>{{editor.component.comment}}</p>
</md-list-item>
<!-- repeat -->
<md-list-item ng-show="viewer.component.repeat">
<md-list-item ng-show="editor.component.repeat">
<md-icon>repeat</md-icon>
<p><!-- viewer.component.repeat.toString() --></p>
<p><!-- editor.component.repeat.toString() --></p>
</md-list-item>
<!-- reminder -->
<md-list-item ng-show="viewer.component.$hasAlarm">
<md-list-item ng-show="editor.component.$hasAlarm">
<md-icon>alarm</md-icon>
<p><!-- viewer.component.alarm.toString() --></p>
<p><!-- editor.component.alarm.toString() --></p>
</md-list-item>
</md-list>
</md-dialog-content>
<!-- edit -->
<div class="md-actions">
<md-button ng-click="viewer.edit()">
<div class="md-actions" ng-show="editor.component.isEditable()">
<md-button ng-click="editor.edit()">
<var:string label:value="Edit"/>
</md-button>
</div>
+78 -194
View File
@@ -6,38 +6,31 @@
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"
>
xmlns:label="OGo:label">
<var:string var:value="doctype" const:escapeHTML="NO" />
<var:if condition="hideFrame" const:negate="YES"
>
<html const:xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en" const:lang="en">
<var:if condition="hideFrame" const:negate="YES">
<html const:xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" const:lang="en">
<head>
<title>
<var:string value="title" />
</title>
<meta name="hideFrame" var:content="hideFrame" />
<meta name="description" content="SOGo Web Interface" />
<meta name="author" content="SKYRIX Software AG/Inverse inc." />
<meta name="author" content="Inverse inc." />
<meta name="robots" content="stop" />
<meta name="build" var:content="buildDate" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="mailto:support@inverse.ca" rev="made" />
<link rel="shortcut icon" var:href="siteFavicon" type="image/x-icon" />
<link type="text/css" rel="stylesheet" rsrc:href="css/styles.css" />
<var:if condition="hasPageSpecificCSS"
>
<link type="text/css" rel="stylesheet" var:href="pageCSSURL"
/>
<var:if condition="hasPageSpecificCSS">
<link type="text/css" rel="stylesheet" var:href="pageCSSURL"/>
</var:if>
<var:foreach list="additionalCSSFiles" item="item"
>
<link type="text/css" rel="stylesheet" var:href="item"
/>
<var:foreach list="additionalCSSFiles" item="item">
<link type="text/css" rel="stylesheet" var:href="item"/>
</var:foreach>
<var:if-ie const:lte="9"
>
<var:if-ie const:lte="9">
<link type="text/css" rel="stylesheet" rsrc:href="iefixes.css" />
<script type="text/javascript" rsrc:src="XMLHttpRequest.js"><!-- space --></script>
</var:if-ie>
@@ -49,152 +42,26 @@
<div id="popupFrame" style="display: none;">
<iframe width="100%" height="100%" src="/SOGo/loading"><!-- space --></iframe>
</div>
</var:if
>
<var:if condition="shortUserNameForDisplay" const:value="anonymous"
const:negate="YES">
<var:if condition="hasUserSettingsKeys"
>
</var:if>
<var:if condition="shortUserNameForDisplay" const:value="anonymous" const:negate="YES">
<var:if condition="hasUserSettingsKeys">
<script type="text/javascript">
var UserSettings =<var:string value="userSettings" const:escapeHTML="NO" />;
</script
>
</script>
</var:if>
<var:if condition="hasUserDefaultsKeys">
<script type="text/javascript">
var UserDefaults =<var:string value="userDefaults" const:escapeHTML="NO" />;
</script
>
</var:if
>
</var:if
>
<var:if condition="isPopup" const:negate="YES"
>
<var:if condition="isUIxDebugEnabled"
><!-- space --></var:if>
</script>
</var:if>
</var:if>
<var:if condition="isPopup" const:negate="YES">
<var:if condition="isUIxDebugEnabled"><!-- space --></var:if>
</var:if>
<!-- MAIN CONTENT ROW -->
<!-- MAIN CONTENT ROW -->
<var:component-content />
<!-- BOTTOM-SHEET -->
<script type="text/ng-template" id="bottomSheetTemplate.html">
<md-bottom-sheet class="md-list">
<md-list>
<var:if condition="shortUserNameForDisplay" const:value="anonymous" const:negate="YES">
<!-- CALENDAR -->
<var:if condition="userHasCalendarAccess">
<var:if condition="isCalendar">
<md-list-item>
<md-button class="md-list-item-content" ng-disabled="true">
<md-icon>event</md-icon>
<var:string label:value="Calendar"/>
</md-button>
</md-list-item>
</var:if>
<var:if condition="isCalendar" const:negate="YES">
<md-list-item>
<md-button class="md-list-item-content" var:href="relativeCalendarPath">
<md-icon>event</md-icon>
<var:string label:value="Calendar"/>
</md-button>
</md-list-item>
</var:if>
</var:if>
<!-- CONTACTS -->
<var:if condition="isContacts">
<md-list-item>
<md-button class="md-list-item-content" ng-disabled="true">
<md-icon>contacts</md-icon>
<var:string label:value="Address Book"/>
</md-button>
</md-list-item>
</var:if>
<var:if condition="isContacts" const:negate="YES">
<md-list-item>
<md-button class="md-list-item-content" var:href="relativeContactsPath">
<md-icon>contacts</md-icon>
<var:string label:value="Address Book"/>
</md-button>
</md-list-item>
</var:if>
<!-- MAIL -->
<var:if condition="userHasMailAccess">
<var:if condition="isMail">
<md-list-item>
<md-button class="md-list-item-content" ng-disabled="true">
<md-icon>email</md-icon>
<var:string label:value="Mail"/>
</md-button>
</md-list-item>
</var:if>
<var:if condition="isMail" const:negate="YES">
<md-list-item>
<md-button class="md-list-item-content" var:href="relativeMailPath">
<md-icon>email</md-icon>
<var:string label:value="Mail"/>
</md-button>
</md-list-item>
</var:if>
</var:if>
<!-- PREFERENCES -->
<var:if condition="isPreferences">
<md-list-item>
<md-button class="md-list-item-content" ng-disabled="true">
<md-icon>settings</md-icon>
<var:string label:value="Preferences"/>
</md-button>
</md-list-item>
</var:if>
<var:if condition="isPreferences" const:negate="YES">
<md-list-item>
<md-button class="md-list-item-content" var:ng-href="relativePreferencesPath">
<md-icon>settings</md-icon>
<var:string label:value="Preferences"/>
</md-button>
</md-list-item>
</var:if>
<!-- ADMINISTRATION -->
<var:if condition="isSuperUser">
<var:if condition="isAdministration">
<md-list-item>
<md-button class="md-list-item-content" ng-disabled="true">
<md-icon>settings_applications</md-icon>
<var:string label:value="Administration"/>
</md-button>
</md-list-item>
</var:if>
<var:if condition="isAdministration" const:negate="YES">
<md-list-item>
<md-button class="md-list-item-content" var:href="relativeAdministrationPath">
<md-icon>settings_applications</md-icon>
<var:string label:value="Administration"/>
</md-button>
</md-list-item>
</var:if>
</var:if>
<!-- DISCONNECT -->
<var:if condition="canLogoff">
<md-list-item>
<md-button class="md-list-item-content" var:ng-href="logoffPath">
<md-icon>settings_power</md-icon>
<var:string label:value="Disconnect"/>
</md-button>
</md-list-item>
</var:if>
</var:if>
</md-list>
</md-bottom-sheet>
</script>
<var:if condition="shortUserNameForDisplay" const:value="anonymous" const:negate="YES">
</var:if>
@@ -202,75 +69,92 @@
<script type="text/javascript">
var ApplicationBaseURL = '<var:string value="modulePath" />';
var ResourcesURL = '<var:string value="applicationPath" />.woa/WebServerResources';
var minimumSearchLength =<var:string value="minimumSearchLength" />;
<var:if condition="isSuperUser"
>var IsSuperUser = true;
var minimumSearchLength = <var:string value="minimumSearchLength" />;
<var:if condition="isSuperUser">
var IsSuperUser = true;
</var:if>
<var:if condition="isSuperUser" const:negate="YES"
>var IsSuperUser = false;
<var:if condition="isSuperUser" const:negate="YES">
var IsSuperUser = false;
</var:if>
<var:if condition="usesCASAuthentication"
>var usesCASAuthentication = true;
<var:if condition="usesCASAuthentication">
var usesCASAuthentication = true;
</var:if>
<var:if condition="usesCASAuthentication" const:negate="YES"
>var usesCASAuthentication = false;
<var:if condition="usesCASAuthentication" const:negate="YES">
var usesCASAuthentication = false;
</var:if>
<var:if condition="shortUserNameForDisplay" const:value="anonymous"
const:negate="YES"
>var UserFolderURL = '<var:string value="userFolderPath" const:escapeHTML="NO" />';
<var:if condition="shortUserNameForDisplay" const:value="anonymous" const:negate="YES">
var UserFolderURL = '<var:string value="userFolderPath" const:escapeHTML="NO" />';
var UserLogin = '<var:string value="shortUserNameForDisplay" const:escapeHTML="NO" />';
var UserIdentification = '<var:string value="userIdentification" const:escapeHTML="NO" />';
var UserEmail = '<var:string value="userEmail" const:escapeHTML="NO" />';
var UserLanguage = '<var:string value="userLanguage" const:escapeHTML="NO" />';
// Application paths
var UserCalendarPath = '';
<var:if condition="userHasCalendarAccess">
UserCalendarPath = '<var:string value="relativeCalendarPath" const:escapeHTML="NO" />';
</var:if>
var UserContactsPath = '<var:string value="relativeContactsPath" const:escapeHTML="NO" />';
var UserMailPath = '';
<var:if condition="userHasMailAccess">
UserMailPath = '<var:string value="relativeMailPath" const:escapeHTML="NO" />';
</var:if>
var UserPreferencesPath = '<var:string value="relativePreferencesPath" const:escapeHTML="NO" />';
<var:if condition="isSuperUser">
var UserAdministrationPath = '<var:string value="relativeAdministrationPath" const:escapeHTML="NO" />';
</var:if>
var UserLogoffPath = '';
<var:if condition="canLogoff">
UserLogoffPath = '<var:string value="logoffPath" const:escapeHTML="NO" />';
</var:if>
</var:if>
// This is the equivalent of an AJAX call to /SOGo/so/_UserLogin_/date
var currentDay = <var:string value="currentDayDescription" const:escapeHTML="NO"/>;
// This is the equivalent of an AJAX call to /SOGo/so/_UserLogin_/date
var currentDay = <var:string value="currentDayDescription" const:escapeHTML="NO"/>;
<var:string value="commonLocalizableStrings" const:escapeHTML="NO" />
<var:string value="productLocalizableStrings" const:escapeHTML="NO" />
</script>
<script type="text/javascript" rsrc:src="js/vendor/lodash.min.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/vendor/angular.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/vendor/angular-animate.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/vendor/angular-sanitize.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/vendor/angular-aria.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/vendor/angular.min.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/vendor/angular-animate.min.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/vendor/angular-sanitize.min.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/vendor/angular-aria.min.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/vendor/angular-material.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/vendor/angular-ui-router.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/Common/utils.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/vendor/angular-ui-router.min.js"><!-- space --></script>
<var:if condition="hasProductSpecificJavaScript">
<script type="text/javascript"
var:src="productJavaScriptURL"><!-- space --></script>
<script type="text/javascript" var:src="productJavaScriptURL"><!-- space --></script>
</var:if>
<var:if condition="hasPageSpecificJavaScript">
<script type="text/javascript"
var:src="pageJavaScriptURL"><!-- space --></script>
<script type="text/javascript" var:src="pageJavaScriptURL"><!-- space --></script>
</var:if>
<var:foreach list="additionalJSFiles" item="item"
>
<var:foreach list="additionalJSFiles" item="item">
<script type="text/javascript" var:src="item"><!-- space --></script>
</var:foreach>
<var:foreach list="systemAdditionalJSFiles" item="item"
>
<var:foreach list="systemAdditionalJSFiles" item="item">
<script type="text/javascript" var:src="item"><!-- space --></script>
</var:foreach>
</var:if>
<var:if condition="isCompatibleBrowser" const:negate="YES"
>
<var:component className="SOGoBrowsersPanel"
/>
<var:if condition="isCompatibleBrowser" const:negate="YES">
<var:component className="SOGoBrowsersPanel"/>
</var:if>
<noscript>
<div class="javascriptPopupBackground">
</div>
<div class="javascriptMessagePseudoWindow noJavascriptErrorMessage">
<var:string label:value="noJavascriptError"
/>
<br />
<br
/>
<a class="button" var:href="page.context.uri"
>
<var:string label:value="noJavascriptRetry"
/>
<var:string label:value="noJavascriptError"/>
<br/>
<br/>
<a class="button" var:href="page.context.uri">
<var:string label:value="noJavascriptRetry"/>
</a>
</div>
</noscript>
+10 -23
View File
@@ -3,27 +3,14 @@
xmlns="http://www.w3.org/1999/xhtml"
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label"
>
<md-toolbar class="md-tall">
<div class="md-toolbar-tools sg-padded" layout="row" layout-align="start start">
<md-button class="sg-icon-button"
label:aria-label="Apps"
ng-click="openBottomSheet()">
<md-icon>apps</md-icon>
</md-button>
<span class="sg-logo"><!-- logo.svg --></span>
</div>
<div class="md-toolbar-tools sg-padded" layout="row">
<div layout="column">
<p class="sg-md-title">{{activeUser.identification}}</p>
<!--fixme: implement this with the proper model and actions -->
<!-- <md-select class="sg-logout" ng-model="accounts.options" var:placeholder="shortUserNameForDisplay">
<md-option var:value="relativePreferencesPath"><var:string label:value="Preferences"/></md-option>
<var:if condition="canLogoff"><md-option var:value="logoffPath"><var:string label:value="Disconnect" /></md-option></var:if>
</md-select> -->
</div>
</div>
</md-toolbar>
xmlns:label="OGo:label">
<md-toolbar class="md-tall sg-padded">
<div layout="column" layout-align="center start">
<sg-avatar-image class="md-tile-left"
sg-email="activeUser.email"
size="48"><!-- avatar --></sg-avatar-image>
<p class="sg-md-title">{{activeUser.identification}}</p>
<p class="md-caption">{{activeUser.email}}</p>
</div>
</md-toolbar>
</container>
+52 -3
View File
@@ -3,19 +3,68 @@
xmlns="http://www.w3.org/1999/xhtml"
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label"
>
xmlns:label="OGo:label">
<div class="sg-toolbar-group-1" layout="row">
<md-button ng-click="toggleLeft()"
class="hide-gt-md sg-button-navicon sg-icon-button"
label:aria-label="Toggle Menu">
<md-icon>menu</md-icon>
</md-button>
<div class="sg-date-group" layout="column" layout-align="space-between end">
<div class="sg-date-group sg-padded" layout="column" layout-align="space-between end">
<p class="sg-day" ng-bind="currentDay.weekday"><!-- weekday --></p>
<p class="sg-month" ng-bind="currentDay.month"><!-- month --></p>
<p class="sg-year" ng-bind="currentDay.year"><!-- year --></p>
</div>
<p class="sg-md-display-3 sg-date-today" ng-bind="currentDay.day"><!-- day --></p>
</div>
<div class="sg-toolbar-group-last">
<md-menu layout-fill="layout-fill">
<md-button label:aria-label="Modules"
class="sg-icon-button" ng-click="$mdOpenMenu($event)">
<md-icon>apps</md-icon>
</md-button>
<md-menu-content width="8">
<div class="md-toolbar-tools sg-padded" layout="row"
layout-align="space-around start">
<md-button class="sg-icon-button"
ng-show="activeUser.path.calendar.length"
ng-disabled="baseURL.endsWith('/Calendar')"
ng-href="{{activeUser.path.calendar}}">
<md-icon>event</md-icon>
<md-tooltip><var:string label:value="Calendar"/></md-tooltip>
</md-button>
<md-button class="sg-icon-button"
ng-disabled="baseURL.endsWith('/Contacts')"
ng-href="{{activeUser.path.contacts}}">
<md-icon>contacts</md-icon>
<md-tooltip><var:string label:value="Address Book"/></md-tooltip>
</md-button>
<md-button class="sg-icon-button"
ng-show="activeUser.path.mail.length"
ng-disabled="baseURL.endsWith('/Mail')"
ng-href="{{activeUser.path.mail}}">
<md-icon>email</md-icon>
<md-tooltip><var:string label:value="Mail"/></md-tooltip>
</md-button>
<md-button class="sg-icon-button"
ng-disabled="baseURL.endsWith(activeUser.login)"
ng-href="{{activeUser.path.preferences}}">
<md-icon>settings</md-icon>
<md-tooltip><var:string label:value="Preferences"/></md-tooltip>
</md-button>
<md-button class="sg-icon-button" ng-show="activeUser.isSuperUser" ng-href="{{activeUser.path.administration}}">
<md-icon>settings_applications</md-icon>
<md-tooltip><var:string label:value="Administration"/></md-tooltip>
</md-button>
<md-button class="sg-icon-button" ng-show="activeUser.path.logoff.length" ng-href="{{activeUser.path.logoff}}">
<md-icon>settings_power</md-icon>
<md-tooltip><var:string label:value="Disconnect"/></md-tooltip>
</md-button>
</div>
</md-menu-content>
</md-menu>
</div>
</container>
+10 -7
View File
@@ -4,7 +4,9 @@ include ../common.make
WEBSERVER_RESOURCE_DIRS = css fonts img js
build-static-files:
.DEFAULT_GOAL := all
dev:
npm install --production
@if [ ! -e angular-material/src ]; then \
git submodule init; \
@@ -17,9 +19,10 @@ build-static-files:
fi
grunt --stack build
all :: build-static-files
all:
@echo "The 'all' target does nothing. Use 'make dev' to generate static files and 'make install' to install them."
install ::
install:
@if [ -L "$(SOGO_WEBSERVERRESOURCESDIR)" ]; then \
echo "$(SOGO_WEBSERVERRESOURCESDIR) is a symbolic link (for development?). Installation skipped."; \
else \
@@ -27,13 +30,13 @@ install ::
cp -r $(WEBSERVER_RESOURCE_DIRS) $(SOGO_WEBSERVERRESOURCESDIR)/; \
fi
check ::
check:
clean ::
clean:
distclean :: clean
distclean: clean
uninstall ::
uninstall:
@if [ -L "$(SOGO_WEBSERVERRESOURCESDIR)" ]; then \
echo "$(SOGO_WEBSERVERRESOURCESDIR) is a symbolic link (for development?). Uninstallation skipped."; \
else \
+7 -6
View File
@@ -1,14 +1,15 @@
module.exports = function(grunt) {
var js_files = {
'js/Common.js': ['js/Common/Common.app.js', 'js/Common/*.filter.js', 'js/Common/*Controller.js', 'js/Common/*.service.js', 'js/Common/*.directive.js'],
'js/Common.js': ['js/Common/Common.app.js', 'js/Common/*.filter.js', 'js/Common/*Controller.js', 'js/Common/*.service.js', 'js/Common/*.directive.js', 'js/Common/utils.js'],
'js/Main.js': ['js/Main/Main.app.js'],
'js/Scheduler.services.js': ['js/Scheduler/*.service.js'],
'js/Scheduler.js': ['js/Scheduler.app.js', 'js/Scheduler/*Controller.js', 'js/Scheduler/*.directive.js'],
'js/Scheduler.js': ['js/Scheduler/Scheduler.app.js', 'js/Scheduler/*Controller.js', 'js/Scheduler/*.directive.js'],
'js/Contacts.services.js': ['js/Contacts/*.service.js'],
'js/Contacts.js': ['js/Contacts.app.js', 'js/Contacts/*Controller.js', 'js/Contacts/*.directive.js'],
'js/Mailer.services.js': ['js/Mailer/*.service.js'],
'js/Mailer.js': ['js/Mailer.app.js', 'js/Mailer/*Controller.js', 'js/Mailer/*.directive.js'],
'js/Contacts.js': ['js/Contacts/Contacts.app.js', 'js/Contacts/*Controller.js', 'js/Contacts/*.directive.js'],
'js/Mailer.services.js': ['js/Mailer/*.service.js', 'js/Mailer/*Controller.js'],
'js/Mailer.js': ['js/Mailer/Mailer.app.js', 'js/Mailer/*.directive.js'],
'js/Preferences.services.js': ['js/Preferences/*.service.js'],
'js/Preferences.js': ['js/Preferences.app.js', 'js/Preferences/*Controller.js']
'js/Preferences.js': ['js/Preferences/Preferences.app.js', 'js/Preferences/*Controller.js']
};
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
+4 -4
View File
@@ -2,9 +2,9 @@
"name": "sogo-web",
"private": true,
"devDependencies": {
"angular": "1.3.x",
"angular-animate": "1.3.x",
"angular-sanitize": "1.3.x",
"angular": "1.4.x",
"angular-animate": "1.4.x",
"angular-sanitize": "1.4.x",
"angular-ui-router": "latest",
"angular-file-upload": "latest",
"angular-material": "master",
@@ -12,6 +12,6 @@
"breakpoint-sass": ">=2.4.2"
},
"resolutions": {
"angular": "1.3.x"
"angular": "1.4.x"
}
}
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../scss/components/_palette.scss","../scss/core/reset.scss","styles.css","../scss/core/angular.scss","../scss/styles.scss","../angular-material/src/core/style/structure.scss","../angular-material/src/core/style/mixins.scss","../scss/core/variables.scss","../scss/core/structure.scss","../angular-material/src/core/services/layout/layout.scss","../scss/core/layout.scss","../bower_components/breakpoint-sass/stylesheets/_breakpoint.scss","../angular-material/src/core/style/typography.scss","../scss/core/typography.scss","../scss/core/_functions.scss","../angular-material/src/components/autocomplete/autocomplete.scss","../scss/components/autocomplete/autocomplete.scss","../scss/components/backdrop/_extends.scss","../angular-material/src/components/backdrop/backdrop.scss","../scss/components/backdrop/backdrop.scss","../scss/components/button/_extends.scss","../angular-material/src/components/button/button.scss","../scss/components/button/button.scss","../scss/components/card/_extends.scss","../angular-material/src/components/card/card.scss","../scss/components/card/card.scss","../scss/components/checkbox/_extends.scss","../angular-material/src/components/checkbox/checkbox.scss","../scss/components/checkbox/checkbox.scss","../scss/components/chips/_extends.scss","../angular-material/src/components/chips/chips.scss","../scss/components/chips/chips.scss","../scss/components/content/_extends.scss","../angular-material/src/components/content/content.scss","../scss/components/content/content.scss","../scss/components/datepicker/_extends-calendar.scss","../angular-material/src/components/datepicker/calendar.scss","../scss/components/datepicker/_extends-datePicker.scss","../angular-material/src/components/datepicker/datePicker.scss","../scss/components/datepicker/datePicker.scss","../scss/components/dialog/_extends.scss","../angular-material/src/components/dialog/dialog.scss","../scss/components/dialog/dialog.scss","../scss/components/divider/_extends.scss","../angular-material/src/components/divider/divider.scss","../scss/components/fabSpeedDial/_extends.scss","../angular-material/src/components/fabSpeedDial/fabSpeedDial.scss","../angular-material/src/components/gridList/grid-list.scss","../scss/components/gridList/grid-list.scss","../scss/components/icon/_extends.scss","../angular-material/src/components/icon/icon.scss","../scss/components/icon/icon.scss","../scss/components/input/_extends.scss","../angular-material/src/components/input/input.scss","../scss/components/input/input.scss","../scss/components/list/_extends.scss","../angular-material/src/components/list/list.scss","../scss/components/list/list.scss","../scss/components/menu/_extends.scss","../angular-material/src/components/menu/menu.scss","../scss/components/progressCircular/_extends.scss","../angular-material/src/components/progressCircular/progress-circular.scss","../scss/components/progressCircular/progress-circular.scss","../scss/components/radioButton/_extends.scss","../angular-material/src/components/radioButton/radio-button.scss","../scss/components/select/_extends.scss","../angular-material/src/components/select/select.scss","../scss/components/select/select.scss","../scss/components/sidenav/_extends.scss","../angular-material/src/components/sidenav/sidenav.scss","../scss/components/sidenav/sidenav.scss","../scss/components/slider/_extends.scss","../angular-material/src/components/slider/slider.scss","../scss/components/sticky/_extends.scss","../angular-material/src/components/sticky/sticky.scss","../scss/components/subheader/_extends.scss","../angular-material/src/components/subheader/subheader.scss","../scss/components/subheader/subheader.scss","../scss/components/switch/_extends.scss","../angular-material/src/components/switch/switch.scss","../scss/components/tabs/_extends.scss","../angular-material/src/components/tabs/tabs.scss","../scss/components/toast/_extends.scss","../angular-material/src/components/toast/toast.scss","../scss/components/toolbar/_extends.scss","../angular-material/src/components/toolbar/toolbar.scss","../scss/components/toolbar/toolbar.scss","../scss/components/tooltip/_extends.scss","../angular-material/src/components/tooltip/tooltip.scss","../scss/components/virtualRepeat/_extends.scss","../angular-material/src/components/virtualRepeat/virtual-repeater.scss","../scss/components/virtualRepeat/virtual-repeat.scss","../scss/components/whiteframe/_extends.scss","../angular-material/src/components/whiteframe/whiteframe.scss","../scss/components/timepicker/timepicker.scss","../scss/components/pseudo-input/pseudo-input.scss","../scss/views/LoginUI.scss","../scss/views/MailerUI.scss","../scss/views/ContactsUI.scss","../scss/views/MessageEditorUI.scss","../scss/views/SchedulerUI.scss","../scss/views/_view.scss","../scss/core/base_styles/_base_style.scss"],"names":[],"mappings":"AAEA;;;;;GAKG;AAqYiC;EAE9B,eAzSO,EAwSP;;AAEA;EAEA,0BA5SO,EA2SP;;AAJ8B;EAE9B,eAxSO,EAuSP;;AAEA;EAEA,0BA3SO,EA0SP;;AAJ8B;EAE9B,eAvSOLine truncated
+3
View File
@@ -0,0 +1,3 @@
function l(){var key=arguments[0],value=key;labels[key]?value=labels[key]:clabels[key]&&(value=clabels[key]);for(var i=1,j=0;i<arguments.length;i++,j++)value=value.replace("%{"+j+"}",arguments[i]);return value}!function(){"use strict";function configure($mdThemingProvider){$mdThemingProvider.definePalette("sogo-green",{50:"eaf5e9",100:"cbe5c8",200:"aad6a5",300:"88c781",400:"66b86a",500:"56b04c",600:"4da143",700:"388e3c",800:"367d2e",900:"225e1b",A100:"b9f6ca",A200:"69f0ae",A400:"00e676",A700:"00c853",contrastDefaultColor:"dark",contrastDarkColors:"50 100 200",contrastLightColors:"300 400 500 600 700 800 900"}),$mdThemingProvider.definePalette("sogo-blue",{50:"f0faf9",100:"e1f5f3",200:"ceebe8",300:"bfe0dd",400:"b2d6d3",500:"a1ccc8",600:"8ebfbb",700:"7db3b0",800:"639997",900:"4d8080",A100:"d4f7fa",A200:"c3f5fa",A400:"53e3f0",A700:"00b0c0",contrastDefaultColor:"light",contrastDarkColors:["50","100","200"],contrastLightColors:["300","400","500","600","700","800","900","A100","A200","A400","A700"]}),$mdThemingProvider.definePalette("sogo-paper",{50:"fcf7f8",100:"f7f1dc",200:"ede5ca",300:"e6d8ba",400:"e2d2a3",500:"d6c48d",600:"baa870",700:"857545",800:"524517",900:"433809",1000:"000000",A100:"ffffff",A200:"eeeeee",A400:"bdbdbd",A700:"616161",contrastDefaultColor:"dark",contrastLightColors:"800 900"}),$mdThemingProvider.theme("default").primaryPalette("sogo-blue",{"default":"300","hue-1":"100","hue-2":"400","hue-3":"A700"}).accentPalette("sogo-green",{"default":"500","hue-1":"200","hue-2":"300","hue-3":"A700"}).backgroundPalette("sogo-paper",{"default":"100","hue-1":"200","hue-2":"50","hue-3":"500"})}angular.module("SOGo.Common",["ngMaterial"]).value("sgSettings",{baseURL:function(){return ApplicationBaseURL||null},activeUser:function(param){var settings={login:UserLogin||null,identification:UserIdentification||null,email:UserEmail||null,language:UserLanguage||null,folderURL:UserFolderURL||null,isSuperUser:IsSuperUser||null,path:{calendar:UserCalendarPath||null,contacts:UserContactsPath||null,mail:UserMailPath||null,preferences:UserPreferencesPath||null,administration:IsSuperUser?UserAdministrationPath:null,logoff:UserLogoffPath||null}};return param?settings[param]:settings}}).constant("sgColors",{selection:["#FFFFFF","#330033","#C0C0C0","#999999","#666666","#333333","#000000","#FFCCCC","#FF6666","#FF0000","#CC0000","#990000","#660000","#330000","#FFCC99","#FF9966","#FF9900","#FF6600","#CC6600","#993300","#663300","#FFFF99","#FFFF66","#FFCC66","#FFCC33","#CC9933","#996633","#663333","#FFFFCC","#FFFF33","#FFFF00","#FFCC00","#999900","#666600","#333300","#CCCCCC","#66FF99","#33FF33","#33CC00","#009900","#006600","#003300","#99FFFF","#33FFFF","#66CCCC","#00CCCC","#339999","#336666","#003333","#CCFFFF","#66FFFF","#33CCFF","#3366FF","#3333FF","#000099","#000066","#CCCCFF","#9999FF","#6666CC","#6633FF","#6600CC","#333399","#330099","#FFCCFF","#FF99FF","#CC66CC","#CC33CC","#993399","#663366","#99FF99"]}).constant("sgConstant",{sm:"(max-width: 600px)","gt-sm":"(min-width: 600px)",md:"(min-width: 600px) and (max-width: 1024px)","gt-md":"(min-width: 1025px)",lg:"(min-width: 1024px) and (max-width: 1280px)","gt-lg":"(min-width: 1280px)"}).config(configure),configure.$inject=["$mdThemingProvider"]}(),function(){"use strict";function decodeUri($window){return $window.decodeURIComponent}decodeUri.$inject=["$window"],angular.module("SOGo.Common").filter("decodeUri",decodeUri)}(),function(){"use strict";function encodeUri($window){return $window.encodeURIComponent}encodeUri.$inject=["$window"],angular.module("SOGo.Common").filter("encodeUri",encodeUri)}(),function(){"use strict";function loc(){return l}angular.module("SOGo.Common").filter("loc",loc)}(),function(){"use strict";function range(){return function(n){for(var res=[],i=0;i<parseInt(n);i++)res.push(i);return res}}angular.module("SOGo.Common").filter("range",range)}(),function(){"use strict";function AclController($mdDialog,Dialog,usersWithACL,User,folder){function userFilter($query){return User.$filter($query,folder.$acl.users)}function closeModal(){folder.$acl.$resetUsersRights(),$mdDialog.hide()}function saveModal(){folder.$acl.$saveUsersRights().then(function(){$mdDialog.hide()},function(data,status){Dialog.alert(l("Warning"),l("An error occured please try again."))})}function confirmChange(user){var confirmation=user.$confirmRights();confirmation&&(vm.confirmation.showing=!0,vm.confirmation.message=confirmation)}function removeUser(user){folder.$acl.$removeUser(user.uid)["catch"](function(data,status){Dialog.alert(l("Warning"),l("An error occured please try again."))})}function addUser(data){data&&folder.$acl.$addUser(data).then(function(){vm.userToAdd="",vm.searchText=""},function(error){Dialog.alert(l("Warning"),error)})}function selectUser(user){vm.selectedUser==user?vm.selectedUser=null:(vm.selectedUser=user,vm.selectedUser.$rights())}var vm=this;vm.users=usersWithACL,vm.folder=folder,vm.selectedUser=null,vm.userToAdd="",vm.searchText="",vm.userFilter=userFilter,Line truncated
return{template:'<div class="sg-time-pane"><div class="hours-pane"><div ng-repeat="hoursBigLine in hours" layout="row" layout-sm="column" ><div ng-repeat="hoursLine in hoursBigLine" layout="row" class="hours" ><div flex layout="row" layout-align="center center" ng-repeat="hour in hoursLine"><md-button class="hourBtn md-fab md-mini" id="{{hour.id}}" ng-click="hourClickHandler(hour.displayName)">{{hour.displayName}}</md-button></div></div></div></div><div ng-repeat="minutesLine in min5" layout="row" class="min5" ng-show="is5min()"><div flex layout="row" layout-align="center center" ng-repeat="minute in minutesLine"><md-button class="minuteBtn md-fab md-mini" id="{{minute.id}}" ng-click="minuteClickHandler(minute.displayName)">{{minute.displayName}}</md-button></div></div><div ng-repeat="minutesLine in min1" layout="row" class="min1" ng-hide="is5min()"><div flex layout="row" layout-align="center center" ng-repeat="minute in minutesLine"><md-button class="minuteBtn md-fab md-mini" id="{{minute.id}}" ng-click="minuteClickHandler(minute.displayName)">{{minute.displayName}}</md-button></div></div><div flex layout="row" layout-align="center center" class="toggle-pane"><md-button class="toggleBtn md-fab md-mini" ng-bind="getToggleBtnLbl()" ng-click="toggleManual5min()"></md-button></div></div>',scope:{},require:["ngModel","sgTimePane"],controller:TimePaneCtrl,controllerAs:"ctrl",bindToController:!0,link:function(scope,element,attrs,controllers){var ngModelCtrl=controllers[0],sgTimePaneCtrl=controllers[1];console.log(element);var timePaneElement=element;sgTimePaneCtrl.configureNgModel(ngModelCtrl,sgTimePaneCtrl,timePaneElement)}}}function TimePaneCtrl($element,$attrs,$scope,$animate,$q,$mdConstant,$mdTheming,$$mdDateUtil,$mdDateLocale,$mdInkRipple,$mdUtil){var m;this.$scope=$scope,this.$element=$element,this.timePaneElement=$element[0].querySelector(".sg-time-pane"),this.$animate=$animate,this.$q=$q,this.$mdInkRipple=$mdInkRipple,this.$mdUtil=$mdUtil,this.keyCode=$mdConstant.KEY_CODE,this.dateUtil=$$mdDateUtil,this.id=nextTimePaneUniqueId++,this.ngModelCtrl=null,this.selectedTime=null,this.displayTime=null,this.isInitialized=!1,$scope.hours=[],$scope.hours[0]=[],$scope.hours[0][0]=[],$scope.hours[0][1]=[],$scope.hours[1]=[],$scope.hours[1][0]=[],$scope.hours[1][1]=[];for(var i=0;6>i;i++)$scope.hours[0][0][i]={id:"tp-"+this.id+"-hour-"+i,displayName:10>i?"0"+i:""+i,selected:!1},$scope.hours[0][1][i]={id:"tp-"+this.id+"-hour-"+(i+6),displayName:10>i+6?"0"+(i+6):""+(i+6),selected:!1},$scope.hours[1][0][i]={id:"tp-"+this.id+"-hour-"+(i+12),displayName:""+(i+12),selected:!1},$scope.hours[1][1][i]={id:"tp-"+this.id+"-hour-"+(i+18),displayName:""+(i+18),selected:!1};for($scope.min5=[],$scope.min5[0]=[],$scope.min5[1]=[],i=0;6>i;i++)m=5*i,$scope.min5[0][i]={id:"tp-"+this.id+"-minute5-"+m,displayName:10>m?":0"+m:":"+m,selected:!0},$scope.min5[1][i]={id:"tp-"+this.id+"-minute5-"+(m+30),displayName:":"+(m+30),selected:!1};for($scope.min1=[],i=0;12>i;i++){$scope.min1[i]=[];for(var ii=0;5>ii;ii++)m=5*i+ii,$scope.min1[i][ii]={id:"tp-"+this.id+"-minute-"+m,displayName:10>m?":0"+m:":"+m,selected:!0}}$scope.show5min=!0,$scope.getToggleBtnLbl=function(){return $scope.is5min()?">>":"<<"},$scope.toggleManual5min=function(){$scope.manual5min=!$scope.is5min()},$scope.is5min=function(){return $scope.manual5min===!0||$scope.manual5min===!1?$scope.manual5min:$scope.show5min},$attrs.tabindex||$element.attr("tabindex","-1");var self=this;this.hourClickHandler=function(displayVal){var updated=new Date(self.displayTime).setHours(Number(displayVal));self.setNgModelValue(updated,"hours")},$scope.hourClickHandler=this.hourClickHandler,this.minuteClickHandler=function(displayVal){var val=displayVal.substr(1),updated=new Date(self.displayTime).setMinutes(Number(val));self.setNgModelValue(updated,"minutes")},$scope.minuteClickHandler=this.minuteClickHandler,this.attachTimePaneEventListeners()}angular.module("SOGo.Common").directive("sgTimePane",timePaneDirective);var SELECTED_TIME_CLASS2="md-primary",FOCUSED_TIME_CLASS="md-focus",nextTimePaneUniqueId=0;TimePaneCtrl.$inject=["$element","$attrs","$scope","$animate","$q","$mdConstant","$mdTheming","$$mdDateUtil","$mdDateLocale","$mdInkRipple","$mdUtil"],TimePaneCtrl.prototype.configureNgModel=function(ngModelCtrl,sgTimePaneCtrl,timePaneElement){this.ngModelCtrl=ngModelCtrl;var self=this;ngModelCtrl.$render=function(){self.changeSelectedTime(self.ngModelCtrl.$viewValue,sgTimePaneCtrl,timePaneElement)}},TimePaneCtrl.prototype.changeSelectedTime=function(date,sgTimePaneCtrl,timePaneElement){var previousSelectedTime=this.selectedTime;this.selectedTime=new Date(date),this.changeDisplayTime(date).then(function(){if(previousSelectedTime){var prevH=previousSelectedTime.getHours(),prevHCell=document.getElementById("tp-"+sgTimePaneCtrl.id+"-hour-"+prevH);prevHCell&&(prevHCell.classList.remove(SELECTED_TIME_CLASS2),prevHCell.setAttribute("aria-selected","false"));var prevM=previousSelectedTime.getMinutes(),prevMCeLine truncated
//# sourceMappingURL=Common.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Common.js","sources":["Common/utils.js","Common/Common.app.js","Common/decodeUri.filter.js","Common/encodeUri.filter.js","Common/loc.filter.js","Common/range.filter.js","Common/AclController.js","Common/navController.js","Common/Acl.service.js","Common/Alarm.service.js","Common/Authentication.service.js","Common/Dialog.service.js","Common/Gravatar.service.js","Common/Resource.service.js","Common/User.service.js","Common/sgFocus.service.js","Common/sgAvatarImage.directive.js","Common/sgColorPicker.directive.js","Common/sgCompile.directive.js","Common/sgEnter.directive.js","Common/sgEscape.directive.js","Common/sgFocus.directive.js","Common/sgFolderStylesheet.directive.js","Common/sgSearch.directive.js","Common/sgSubscribe.directive.js","Common/sgTarget.directive.js","Common/sgTimepicker.directive.js","Common/sgToggleGrid.directive.js"],"names":["l","key","arguments","value","labels","clabels","i","j","length","replace","configure","$mdThemingProvider","definePalette","50","100","200","300","400","500","600","700","800","900","A100","A200","A400","A700","contrastDefaultColor","contrastDarkColors","contrastLightColors","1000","theme","primaryPalette","default","hue-1","hue-2","hue-3","accentPalette","backgroundPalette","angular","module","baseURL","ApplicationBaseURL","activeUser","param","settings","login","UserLogin","identification","UserIdentification","email","UserEmail","language","UserLanguage","folderURL","UserFolderURL","isSuperUser","IsSuperUser","path","calendar","UserCalendarPath","contacts","UserContactsPath","mail","UserMailPath","preferences","UserPreferencesPath","administration","UserAdministrationPath","logoff","UserLogoffPath","constant","selection","sm","gt-sm","md","gt-md","lg","gt-lg","config","$inject","decodeUri","$window","decodeURIComponent","filter","encodeUri","encodeURIComponent","loc","range","n","res","parseInt","push","AclController","$mdDialog","Dialog","usersWithACL","User","folder","userFilter","$query","$filter","$acl","users","closeModal","$resetUsersRights","hide","saveModal","$saveUsersRights","then","data","status","alert","confirmChange","user","confirmation","$confirmRights","vm","showing","message","removeUser","$removeUser","uid","addUser","$addUser","userToAdd","searchText","error","selectUser","selectedUser","$rights","this","controller","navController","$scope","$timeout","$interval","$http","$mdSidenav","$mdBottomSheet","$mdMedia","$log","sgConstant","sgSettings","Alarm","currentDay","window","get","success","secondsBeforeTomorrow","toggleLeft","toggle","debug","toggleRight","openBottomSheet","show","parent","element","document","getElementById","templateUrl","toggleDetailView","detail","toggleClass","$watch","newVal","isGtMedium","getAlarms","Acl","folderId","factory","$q","Settings","Resource","extend","$$resource","$User","prototype","$users","_this","deferred","defer","resolve","promise","fetch","response","forEach","canSubscribeUser","isSubscribed","wasSubscribed","bind","_","indexOf","pluck","userClass","isGroup","reject","splice","$resetRights","deferredSave","deferredSubscribe","action","$rightsAreDirty","$omit","save","$shadowRights","copy","rights","uids","join","all","currentAlarm","AlarmService","$mdToast","$toast","now","Date","browserTime","Math","floor","getTime","alarms","sort","a","b","x","y","next","pop","utc","url","alarmTime","delay","f","showAlarm","cancel","AlarmController","scope","reminder","ok","position","hideDelay","template","summary","locals","Authentication","readCookie","name","foundCookie","prefix","pairs","currentPair","start","cookie","split","charAt","substr","readLoginCookie","loginValues","base64decode","redirectUrl","username","domain","userName","address","baseAddress","altBaseAddress","parts","hostpart","protocol","newAddress","location","href","startsWith","endsWith","getService","passwordPolicyConfig","service","d","password","rememberLogin","loginSuffix","method","loginCookie","expire","grace","msg","perr","LDAPPasswordPolicyError","PolicyNoError","PolicyAccountLocked","changePassword","newPassword","PolicyPasswordModNotAllowed","PolicyInsufficientPasswordQuality","PolicyPasswordTooShort","PolicyPasswordTooYoung","PolicyPasswordInHistory","formatted","PolicyPasswordUnknown","PolicyPasswordSystemUnknown","$get","PolicyPasswordChangeUnsupported","PolicyPasswordExpired","PolicyChangeAfterReset","PolicyMustSupplyOldPassword","provider","DialogService","$modal","title","content","confirm","options","prompt","label","PromptDialogController","o","body","clickOutsideToClose","escapeToClose","inputType","Gravatar","size","hash","s","substring","md5","_http","_q","_path","_activeUser","$factory","userResource","compact","escape","params","flatten","newguid","create","post","id","newValue","remove","userData","init","$gravatar","search","excludedUsers","when","results","index","compareUids","find","each","isUndefined","$$shortFormat","$shortFormat","$$image","image","c_email","empty","fullname","cn","no_email"Line truncated
@@ -48,7 +48,7 @@
else {
return Acl.$$resource.fetch(this.folderId, 'acls').then(function(response) {
_this.users = [];
// console.debug(JSON.stringify(users, undefined, 2));
//console.debug(JSON.stringify(users, undefined, 2));
angular.forEach(response.users, function(data) {
user = new Acl.$User(data);
user.canSubscribeUser = user.isSubscribed;
@@ -78,7 +78,7 @@
}
}
angular
.module('SOGo.Common')
.controller('AclController', AclController);
angular
.module('SOGo.Common')
.controller('AclController', AclController);
})();
@@ -58,66 +58,67 @@
Alarm.showAlarm = function(url) {
var _this = this;
this.$$resource.fetch(url, '?resetAlarm=yes');
this.$toast.show({
position: 'top right',
hideDelay: 0,
template: [
'<md-toast>',
' <md-input-container>',
' <md-select ng-model="reminder">',
' <md-option value=5>',
l('5 minutes'),
' </md-option>',
' <md-option value=10>',
l('10 minutes'),
' </md-option>',
' <md-option value=15>',
l('15 minutes'),
' </md-option>',
' <md-option value=30>',
l('30 minutes'),
' </md-option>',
' <md-option value=45>',
l('45 minutes'),
' </md-option>',
' <md-option value=60>',
l('1 hour'),
' </md-option>',
' <md-option value=1440>',
l('1 day'),
' </md-option>',
' </md-select>',
' </md-input-container>',
' <md-button ng-click="cancel()">',
l('Cancel'),
' </md-button>',
' <md-button ng-click="ok()">',
l('Ok'),
' </md-button>',
'</md-toast>'
this.$$resource.fetch(url, '?resetAlarm=yes').then(function(data) {
_this.$toast.show({
position: 'top right',
hideDelay: 0,
template: [
'<md-toast>',
' <md-input-container>',
' <label style="color: white;">' + data.summary + '</label>',
' <md-select ng-model="reminder">',
' <md-option value=5>',
l('5 minutes'),
' </md-option>',
' <md-option value=10>',
l('10 minutes'),
' </md-option>',
' <md-option value=15>',
l('15 minutes'),
' </md-option>',
' <md-option value=30>',
l('30 minutes'),
' </md-option>',
' <md-option value=45>',
l('45 minutes'),
' </md-option>',
' <md-option value=60>',
l('1 hour'),
' </md-option>',
' <md-option value=1440>',
l('1 day'),
' </md-option>',
' </md-select>',
' </md-input-container>',
' <md-button ng-click="cancel()">',
l('Cancel'),
' </md-button>',
' <md-button ng-click="ok()">',
l('Ok'),
' </md-button>',
'</md-toast>'
].join(''),
locals: {
url: url
},
controller: AlarmController
locals: {
url: url
},
controller: AlarmController
});
/**
* @ngInject
*/
AlarmController.$inject = ['scope', '$mdToast', 'url'];
function AlarmController(scope, $mdToast, url) {
scope.reminder = '10';
scope.cancel = function() {
$mdToast.hide();
};
scope.ok = function() {
_this.$$resource.fetch(url, 'view?snoozeAlarm=' + scope.reminder);
$mdToast.hide();
};
}
});
/**
* @ngInject
*/
AlarmController.$inject = ['scope', '$mdToast', 'url'];
function AlarmController(scope, $mdToast, url) {
scope.reminder = '10';
scope.cancel = function() {
$mdToast.hide();
};
scope.ok = function() {
_this.$$resource.fetch(url, 'view?snoozeAlarm=' + scope.reminder);
$mdToast.hide();
};
}
};
/**
@@ -26,9 +26,6 @@
// TODO: convert to a Factory recipe?
.provider('Authentication', Authentication);
/**
* @ngInject
*/
function Authentication() {
function readCookie(name) {
var foundCookie, prefix, pairs, i, currentPair, start;
@@ -91,9 +88,11 @@
this.$get = getService;
/**
* @ngInject
*/
getService.$inject = ['$q', '$http', 'passwordPolicyConfig'];
function getService($q, $http, passwordPolicyConfig) {
var _this = this, service;
service = {
+10 -1
View File
@@ -12,9 +12,18 @@
var settings = {
login: UserLogin || null,
identification: UserIdentification || null,
email: UserEmail || null,
language: UserLanguage || null,
folderURL: UserFolderURL || null,
isSuperUser: IsSuperUser || null
isSuperUser: IsSuperUser || null,
path: {
calendar: UserCalendarPath || null,
contacts: UserContactsPath || null,
mail: UserMailPath || null,
preferences: UserPreferencesPath || null,
administration: (IsSuperUser ? UserAdministrationPath : null),
logoff: UserLogoffPath || null
}
};
if (param)
return settings[param];
@@ -31,12 +31,12 @@
* @param {string} content
* @returns a promise that resolves if the user has clicked on the 'OK' button
*/
Dialog.confirm = function(title, content) {
Dialog.confirm = function(title, content, options) {
var confirm = this.$modal.confirm()
.title(title)
.content(content)
.ok(l('OK'))
.cancel(l('Cancel'));
.ok((options && options.ok)? options.ok : l('OK'))
.cancel((options && options.cancel)? options.cancel : l('Cancel'));
return this.$modal.show(confirm);
};
@@ -12,10 +12,16 @@
*/
function Gravatar() {
return function(email, size) {
var hash, s = size;
var x, y, hash, s = size;
if (!email) {
return '';
}
x = email.indexOf('<');
if (x >= 0) {
y = email.indexOf('>', x);
if (y > x)
email = email.substring(x+1,y);
}
if (!size) {
s = 48; // default to 48 pixels
}
@@ -45,6 +45,7 @@
*/
User.$filter = function(search, excludedUsers) {
var param = {search: search};
var _this = this;
if (!search) {
// No query specified
@@ -60,7 +61,7 @@
return User.$$resource.fetch(null, 'usersSearch', param).then(function(response) {
var results, index, user,
compareUids = function(data) {
return this.uid == data.uid;
return _this.uid == data.uid;
};
if (excludedUsers) {
// Remove excluded users from response
@@ -13,6 +13,7 @@
function navController($scope, $timeout, $interval, $http, $mdSidenav, $mdBottomSheet, $mdMedia, $log, sgConstant, sgSettings, Alarm) {
$scope.activeUser = sgSettings.activeUser();
$scope.baseURL = sgSettings.baseURL();
// Show current day in top bar
$scope.currentDay = window.currentDay;
@@ -0,0 +1,743 @@
(function() {
'use strict';
angular
.module('SOGo.Common')
.directive('sgTimePane', timePaneDirective);
function timePaneDirective() {
return {
template:
'<div class="sg-time-pane">' +
'<div class="hours-pane"><div ng-repeat="hoursBigLine in hours" layout="row" layout-sm="column" ><div ng-repeat="hoursLine in hoursBigLine" layout="row" class="hours" >' +
'<div flex layout="row" layout-align="center center" ng-repeat="hour in hoursLine">' +
'<md-button class="hourBtn md-fab md-mini" id="{{hour.id}}" ng-click="hourClickHandler(hour.displayName)">{{hour.displayName}}</md-button>'+
'</div></div></div></div>' +
'<div ng-repeat="minutesLine in min5" layout="row" class="min5" ng-show="is5min()">' +
'<div flex layout="row" layout-align="center center" ng-repeat="minute in minutesLine">' +
'<md-button class="minuteBtn md-fab md-mini" id="{{minute.id}}" ng-click="minuteClickHandler(minute.displayName)">{{minute.displayName}}</md-button>'+
'</div></div>' +
'<div ng-repeat="minutesLine in min1" layout="row" class="min1" ng-hide="is5min()">' +
'<div flex layout="row" layout-align="center center" ng-repeat="minute in minutesLine">' +
'<md-button class="minuteBtn md-fab md-mini" id="{{minute.id}}" ng-click="minuteClickHandler(minute.displayName)">{{minute.displayName}}</md-button>' +
'</div></div>' +
'<div flex layout="row" layout-align="center center" class="toggle-pane"><md-button class="toggleBtn md-fab md-mini" ng-bind="getToggleBtnLbl()" ng-click="toggleManual5min()"></md-button></div></div>',
scope: {},
require: ['ngModel', 'sgTimePane'],
controller: TimePaneCtrl,
controllerAs: 'ctrl',
bindToController: true,
link: function(scope, element, attrs, controllers) {
var ngModelCtrl = controllers[0];
var sgTimePaneCtrl = controllers[1];
console.log(element);
var timePaneElement = element;
sgTimePaneCtrl.configureNgModel(ngModelCtrl,sgTimePaneCtrl, timePaneElement);
}
};
}
/** Class applied to the selected hour or minute cell/. */
//var SELECTED_TIME_CLASS = 'md-calendar-selected-date';
//var SELECTED_TIME_CLASS1 = 'md-raised';
var SELECTED_TIME_CLASS2 = 'md-primary';
/** Class applied to the focused hour or minute cell/. */
var FOCUSED_TIME_CLASS = 'md-focus';
/** Next identifier for calendar instance. */
var nextTimePaneUniqueId = 0;
function TimePaneCtrl($element, $attrs, $scope, $animate, $q, $mdConstant,
$mdTheming, $$mdDateUtil, $mdDateLocale, $mdInkRipple, $mdUtil) {
var m;
this.$scope = $scope;
this.$element = $element;
this.timePaneElement = $element[0].querySelector('.sg-time-pane');
this.$animate = $animate;
this.$q = $q;
this.$mdInkRipple = $mdInkRipple;
this.$mdUtil = $mdUtil;
this.keyCode = $mdConstant.KEY_CODE;
this.dateUtil = $$mdDateUtil;
this.id = nextTimePaneUniqueId++;
this.ngModelCtrl = null;
this.selectedTime = null;
this.displayTime = null;
this.isInitialized = false;
$scope.hours=[];
$scope.hours[0]=[];
$scope.hours[0][0]=[];
$scope.hours[0][1]=[];
$scope.hours[1]=[];
$scope.hours[1][0]=[];
$scope.hours[1][1]=[];
for(var i=0; i<6; i++){
$scope.hours[0][0][i] = {id:'tp-'+this.id+'-hour-'+i, displayName:i<10?"0"+i:""+i, selected:false};
$scope.hours[0][1][i] = {id:'tp-'+this.id+'-hour-'+(i+6),displayName:(i+6)<10?"0"+(i+6):""+(i+6), selected:false};
$scope.hours[1][0][i] = {id:'tp-'+this.id+'-hour-'+(i+12), displayName:""+(i+12), selected:false};
$scope.hours[1][1][i] = {id:'tp-'+this.id+'-hour-'+(i+18), displayName:""+(i+18), selected:false};
}
$scope.min5=[];
$scope.min5[0]=[];
$scope.min5[1]=[];
for(i=0; i<6; i++){
m=i*5;
$scope.min5[0][i] = {id:'tp-'+this.id+'-minute5-'+m, displayName:m<10?":0"+m:":"+m, selected:true};
$scope.min5[1][i] = {id:'tp-'+this.id+'-minute5-'+(m+30), displayName:":"+(m+30), selected:false};
}
$scope.min1=[];
for(i=0; i<12; i++){
$scope.min1[i]=[];
for(var ii=0; ii<5; ii++){
m=i*5 + ii;
$scope.min1[i][ii] = {id:'tp-'+this.id+'-minute-'+m, displayName:m<10?":0"+m:":"+m, selected:true};
}
}
$scope.show5min=true;
$scope.getToggleBtnLbl = function() {
return ($scope.is5min()) ? '>>' : '<<';
};
$scope.toggleManual5min = function() {
$scope.manual5min = !$scope.is5min();
};
$scope.is5min=function(){
if($scope.manual5min === true || $scope.manual5min === false) {
return $scope.manual5min;
}
else {
return $scope.show5min;
}
};
if (!$attrs.tabindex) {
$element.attr('tabindex', '-1');
}
var self = this;
this.hourClickHandler = function(displayVal) {
var updated = new Date(self.displayTime).setHours(Number(displayVal));
self.setNgModelValue(updated, 'hours');
};
$scope.hourClickHandler = this.hourClickHandler;
this.minuteClickHandler = function(displayVal) {
//remove leading ':'
var val = displayVal.substr(1);
var updated = new Date(self.displayTime).setMinutes(Number(val));
self.setNgModelValue(updated, 'minutes');
};
$scope.minuteClickHandler = this.minuteClickHandler;
this.attachTimePaneEventListeners();
}
TimePaneCtrl.$inject = ["$element", "$attrs", "$scope", "$animate", "$q", "$mdConstant", "$mdTheming", "$$mdDateUtil", "$mdDateLocale", "$mdInkRipple", "$mdUtil"];
TimePaneCtrl.prototype.configureNgModel = function(ngModelCtrl, sgTimePaneCtrl, timePaneElement) {
this.ngModelCtrl = ngModelCtrl;
var self = this;
ngModelCtrl.$render = function() {
self.changeSelectedTime(self.ngModelCtrl.$viewValue, sgTimePaneCtrl, timePaneElement);
};
};
/**
* Change the selected date in the time (ngModel value has already been changed).
*/
TimePaneCtrl.prototype.changeSelectedTime = function(date, sgTimePaneCtrl, timePaneElement) {
var self = this;
var previousSelectedTime = this.selectedTime;
this.selectedTime = new Date(date);
this.changeDisplayTime(date).then(function() {
// Remove the selected class from the previously selected date, if any.
if (previousSelectedTime) {
var prevH = previousSelectedTime.getHours();
var prevHCell = document.getElementById('tp-'+sgTimePaneCtrl.id+'-hour-'+prevH);
if (prevHCell) {
prevHCell.classList.remove(SELECTED_TIME_CLASS2);
prevHCell.setAttribute('aria-selected', 'false');
}
var prevM = previousSelectedTime.getMinutes();
var prevMCell = document.getElementById('tp-'+sgTimePaneCtrl.id+'-minute-'+prevM);
if (prevMCell) {
prevMCell.classList.remove(SELECTED_TIME_CLASS2);
prevMCell.setAttribute('aria-selected', 'false');
}
var prevM5Cell = document.getElementById('tp-'+sgTimePaneCtrl.id+'-minute5-'+prevM);
if (prevM5Cell) {
prevM5Cell.classList.remove(SELECTED_TIME_CLASS2);
prevM5Cell.setAttribute('aria-selected', 'false');
}
}
// Apply the select class to the new selected date if it is set.
if (date) {
var d = new Date(date);
var newH = d.getHours();
var mCell, hCell = document.getElementById('tp-'+sgTimePaneCtrl.id+'-hour-'+newH);
if (hCell) {
hCell.classList.add(SELECTED_TIME_CLASS2);
hCell.setAttribute('aria-selected', 'true');
}
var newM = d.getMinutes();
if (newM % 5 === 0) {
sgTimePaneCtrl.$scope.show5min = true;
mCell = document.getElementById('tp-'+sgTimePaneCtrl.id+'-minute5-'+newM);
if (mCell) {
mCell.classList.add(SELECTED_TIME_CLASS2);
mCell.setAttribute('aria-selected', 'true');
}
}
else {
sgTimePaneCtrl.$scope.show5min = false;
}
mCell = document.getElementById('tp-'+sgTimePaneCtrl.id+'-minute-'+newM);
if (mCell) {
mCell.classList.add(SELECTED_TIME_CLASS2);
mCell.setAttribute('aria-selected', 'true');
}
}
});
};
TimePaneCtrl.prototype.changeDisplayTime = function(date) {
var d = new Date(date);
if (!this.isInitialized) {
this.buildInitialTimePaneDisplay();
return this.$q.when();
}
if (!this.dateUtil.isValidDate(d)) {
return this.$q.when();
}
this.displayTime = d;
return this.$q.when();
};
TimePaneCtrl.prototype.buildInitialTimePaneDisplay = function() {
this.displayTime = this.selectedTime || this.today;
this.isInitialized = true;
};
TimePaneCtrl.prototype.attachTimePaneEventListeners = function() {
// Keyboard interaction.
this.$element.on('keydown', angular.bind(this, this.handleKeyEvent));
};
/*** User input handling ***/
/**
* Handles a key event in the calendar with the appropriate action. The action will either
* be to select the focused date or to navigate to focus a new date.
* @param {KeyboardEvent} event
*/
TimePaneCtrl.prototype.handleKeyEvent = function(event) {
var self = this;
this.$scope.$apply(function() {
// Capture escape and emit back up so that a wrapping component
// (such as a time-picker) can decide to close.
if (event.which == self.keyCode.ESCAPE || event.which == self.keyCode.TAB) {
self.$scope.$emit('md-time-pane-close');
if (event.which == self.keyCode.TAB) {
event.preventDefault();
}
return;
}
// Remaining key events fall into two categories: selection and navigation.
// Start by checking if this is a selection event.
if (event.which === self.keyCode.ENTER) {
self.setNgModelValue(self.displayTime, 'enter');
event.preventDefault();
return;
}
// Selection isn't occuring, so the key event is either navigation or nothing.
/*var date = self.getFocusDateFromKeyEvent(event);
if (date) {
event.preventDefault();
event.stopPropagation();
// Since this is a keyboard interaction, actually give the newly focused date keyboard
// focus after the been brought into view.
self.changeDisplayTime(date).then(function () {
self.focus(date);
});
}*/
});
};
/**
* Sets the ng-model value for the time pane and emits a change event.
* @param {Date} date
*/
TimePaneCtrl.prototype.setNgModelValue = function(date, mode) {
this.$scope.$emit('sg-time-pane-change', {date:date, changed:mode});
this.ngModelCtrl.$setViewValue(date);
this.ngModelCtrl.$render();
};
/**
* Focus the cell corresponding to the given date.
* @param {Date=} opt_date
*/
TimePaneCtrl.prototype.focus = function(opt_date, sgTimePaneCtrl) {
var date = opt_date || this.selectedTime || this.today;
var previousFocus = this.timePaneElement.querySelector('.md-focus');
if (previousFocus) {
previousFocus.classList.remove(FOCUSED_TIME_CLASS);
}
if (date) {
var newH = date.getHours();
var hCell = document.getElementById('tp-'+sgTimePaneCtrl.id+'-hour-'+newH);
if (hCell) {
hCell.classList.add(FOCUSED_TIME_CLASS);
hCell.focus();
}
}
};
})();
(function() {
'use strict';
angular.module('SOGo.Common')
.directive('sgTimepicker', timePickerDirective);
/**
* @ngdoc directive
* @name mdTimepicker
* @module material.components.timepicker
*
* @param {Date} ng-model The component's model. Expects a JavaScript Date object.
* @param {expression=} ng-change Expression evaluated when the model value changes.
* @param {boolean=} disabled Whether the timepicker is disabled.
*
* @description
* `<sg-timepicker>` is a component used to select a single time.
* For information on how to configure internationalization for the time picker,
* see `$mdTimeLocaleProvider`.
*
* @usage
* <hljs lang="html">
* <sg-timepicker ng-model="birthday"></sg-timepicker>
* </hljs>
*
*/
function timePickerDirective() {
return {
template:
// Buttons are not in the tab order because users can open the hours pane via keyboard
// interaction on the text input, and multiple tab stops for one component (picker)
// may be confusing.
'<md-button class="sg-timepicker-button md-icon-button" type="button" ' +
'tabindex="-1" aria-hidden="true" ' +
'ng-click="ctrl.openTimePane($event)">' +
//'<md-icon class="md-timepicker-calendar-icon" md-svg-icon="md-calendar"></md-icon>' +
'<i class="material-icons">access_time</i>' +
'</md-button>' +
'<div class="sg-timepicker-input-container" ' +
'ng-class="{\'sg-timepicker-focused\': ctrl.isFocused}">' +
'<input class="sg-timepicker-input" aria-haspopup="true" ' +
'ng-focus="ctrl.setFocused(true)" ng-blur="ctrl.setFocused(false)">' +
'<md-button type="button" md-no-ink ' +
'class="sg-timepicker-triangle-button md-icon-button" ' +
'ng-click="ctrl.openTimePane($event)" ' +
'aria-label="{{::ctrl.dateLocale.msgOpenCalendar}}">' +
'<div class="sg-timepicker-expand-triangle"></div>' +
'</md-button>' +
'</div>' +
// This pane will be detached from here and re-attached to the document body.
'<div class="sg-timepicker-time-pane md-whiteframe-z1">' +
'<div class="sg-timepicker-input-mask">' +
'<div class="sg-timepicker-input-mask-opaque"></div>' +
'</div>' +
'<div class="sg-timepicker-time">' +
'<sg-time-pane role="dialog" aria-label="{{::ctrl.dateLocale.msgCalendar}}" ' +
'ng-model="ctrl.time" ng-if="ctrl.isTimeOpen"></sg-time-pane>' +
'</div>' +
'</div>',
require: ['ngModel', 'sgTimepicker'],
scope: {
placeholder: '@mdPlaceholder'
},
controller: TimePickerCtrl,
controllerAs: 'ctrl',
bindToController: true,
link: function(scope, element, attr, controllers) {
var ngModelCtrl = controllers[0];
var mdTimePickerCtrl = controllers[1];
mdTimePickerCtrl.configureNgModel(ngModelCtrl);
}
};
}
/** Additional offset for the input's `size` attribute, which is updated based on its content. */
var EXTRA_INPUT_SIZE = 3;
/** Class applied to the container if the date is invalid. */
var INVALID_CLASS = 'sg-timepicker-invalid';
/** Default time in ms to debounce input event by. */
var DEFAULT_DEBOUNCE_INTERVAL = 500;
/**
* Controller for sg-timepicker.
*
* ngInject @constructor
*/
function TimePickerCtrl($scope, $element, $attrs, $compile, $timeout, $mdConstant, $mdTheming,
$mdUtil, $mdDateLocale, $$mdDateUtil, $$rAF) {
/** @final */
this.$compile = $compile;
/** @final */
this.$timeout = $timeout;
/** @final */
this.dateLocale = $mdDateLocale;
/** @final */
this.dateUtil = $$mdDateUtil;
/** @final */
this.$mdConstant = $mdConstant;
/* @final */
this.$mdUtil = $mdUtil;
/** @final */
this.$$rAF = $$rAF;
/** @type {!angular.NgModelController} */
this.ngModelCtrl = null;
/** @type {HTMLInputElement} */
this.inputElement = $element[0].querySelector('input');
/** @type {HTMLElement} */
this.inputContainer = $element[0].querySelector('.sg-timepicker-input-container');
/** @type {HTMLElement} Floating time pane. */
this.timePane = $element[0].querySelector('.sg-timepicker-time-pane');
/** @type {HTMLElement} Time icon button. */
this.timeButton = $element[0].querySelector('.sg-timepicker-button');
/**
* Element covering everything but the input in the top of the floating calendar pane.
* @type {HTMLElement}
*/
this.inputMask = $element[0].querySelector('.sg-timepicker-input-mask-opaque');
/** @final {!angular.JQLite} */
this.$element = $element;
/** @final {!angular.Attributes} */
this.$attrs = $attrs;
/** @final {!angular.Scope} */
this.$scope = $scope;
/** @type {Date} */
this.date = null;
/** @type {boolean} */
this.isFocused = false;
/** @type {boolean} */
this.isDisabled = false;
this.setDisabled($element[0].disabled || angular.isString($attrs.disabled));
/** @type {boolean} Whether the date-picker's calendar pane is open. */
this.isTimeOpen = false;
/**
* Element from which the calendar pane was opened. Keep track of this so that we can return
* focus to it when the pane is closed.
* @type {HTMLElement}
*/
this.timePaneOpenedFrom = null;
this.timePane.id = 'sg-time-pane' + $mdUtil.nextUid();
$mdTheming($element);
/** Pre-bound click handler is saved so that the event listener can be removed. */
this.bodyClickHandler = angular.bind(this, this.handleBodyClick);
// Unless the user specifies so, the datepicker should not be a tab stop.
// This is necessary because ngAria might add a tabindex to anything with an ng-model
// (based on whether or not the user has turned that particular feature on/off).
if (!$attrs.tabindex) {
$element.attr('tabindex', '-1');
}
this.installPropertyInterceptors();
this.attachChangeListeners();
this.attachInteractionListeners();
var self = this;
$scope.$on('$destroy', function() {
self.detachTimePane();
});
}
TimePickerCtrl.$inject = ["$scope", "$element", "$attrs", "$compile", "$timeout", "$mdConstant", "$mdTheming",
"$mdUtil", "$mdDateLocale", "$$mdDateUtil", "$$rAF"];
/**
* Sets up the controller's reference to ngModelController.
* @param {!angular.NgModelController} ngModelCtrl
*/
TimePickerCtrl.prototype.configureNgModel = function(ngModelCtrl) {
this.ngModelCtrl = ngModelCtrl;
var self = this;
ngModelCtrl.$render = function() {
self.time = self.ngModelCtrl.$viewValue;
self.inputElement.value = self.formatTime(self.time);
self.resizeInputElement();
};
};
TimePickerCtrl.prototype.formatTime = function(time) {
var t = new Date(time);
if(t) {
var h= t.getHours();
var m= t.getMinutes();
return (h<10?('0'+ h) : h) + ':' + (m<10?'0'+ m : m);
}
else return '';
};
/**
* Attach event listeners for both the text input and the md-time.
* Events are used instead of ng-model so that updates don't infinitely update the other
* on a change. This should also be more performant than using a $watch.
*/
TimePickerCtrl.prototype.attachChangeListeners = function() {
var self = this;
self.$scope.$on('sg-time-pane-change', function(event, data) {
var time = new Date(data.date);
self.ngModelCtrl.$setViewValue(time);
self.time = time;
self.inputElement.value = self.formatTime(self.time);
if(data.changed == 'minutes') {
self.closeTimePane();
}
self.resizeInputElement();
self.inputContainer.classList.remove(INVALID_CLASS);
});
var ngElement = angular.element(self.inputElement);
ngElement.on('input', angular.bind(self, self.resizeInputElement));
ngElement.on('input', self.$mdUtil.debounce(self.handleInputEvent,
DEFAULT_DEBOUNCE_INTERVAL, self));
};
/** Attach event listeners for user interaction. */
TimePickerCtrl.prototype.attachInteractionListeners = function() {
var self = this;
var $scope = this.$scope;
var keyCodes = this.$mdConstant.KEY_CODE;
// Add event listener through angular so that we can triggerHandler in unit tests.
angular.element(self.inputElement).on('keydown', function(event) {
if (event.altKey && event.keyCode == keyCodes.DOWN_ARROW) {
self.openTimePane(event);
$scope.$digest();
}
});
$scope.$on('md-time-close', function() {
self.closeTimePane();
});
};
/**
* Capture properties set to the time-picker and imperitively handle internal changes.
* This is done to avoid setting up additional $watches.
*/
TimePickerCtrl.prototype.installPropertyInterceptors = function() {
var self = this;
if (this.$attrs.ngDisabled) {
// The expression is to be evaluated against the directive element's scope and not
// the directive's isolate scope.
this.$element.scope().$watch(this.$attrs.ngDisabled, function(isDisabled) {
self.setDisabled(isDisabled);
});
}
Object.defineProperty(this, 'placeholder', {
get: function() { return self.inputElement.placeholder; },
set: function(value) { self.inputElement.placeholder = value || ''; }
});
};
/**
* Sets whether the date-picker is disabled.
* @param {boolean} isDisabled
*/
TimePickerCtrl.prototype.setDisabled = function(isDisabled) {
this.isDisabled = isDisabled;
this.inputElement.disabled = isDisabled;
this.timeButton.disabled = isDisabled;
};
/**
* Resizes the input element based on the size of its content.
*/
TimePickerCtrl.prototype.resizeInputElement = function() {
this.inputElement.size = this.inputElement.value.length + EXTRA_INPUT_SIZE;
};
/**
* Sets the model value if the user input is a valid time.
* Adds an invalid class to the input element if not.
*/
TimePickerCtrl.prototype.handleInputEvent = function(self) {
var inputString = this.inputElement.value;
var arr = inputString.split(':');
if(arr.length < 2) {return;}
var h=Number(arr[0]);
var m=Number(arr[1]);
var newVal = new Date(this.time);
if (h && h>=0 && h<=23 && m && m>=0 && m<= 59 && angular.isDate(newVal)) {
newVal.setHours(h);
newVal.setMinutes(m);
this.ngModelCtrl.$setViewValue(newVal);
this.time = newVal;
this.inputContainer.classList.remove(INVALID_CLASS);
}
else {
// If there's an input string, it's an invalid time.
this.inputContainer.classList.toggle(INVALID_CLASS, inputString);
}
};
/** Position and attach the floating calendar to the document. */
TimePickerCtrl.prototype.attachTimePane = function() {
var timePane = this.timePane;
this.$element.addClass('sg-timepicker-open');
var elementRect = this.inputContainer.getBoundingClientRect();
var bodyRect = document.body.getBoundingClientRect();
timePane.style.left = (elementRect.left - bodyRect.left) + 'px';
timePane.style.top = (elementRect.top - bodyRect.top) + 'px';
document.body.appendChild(this.timePane);
// The top of the calendar pane is a transparent box that shows the text input underneath.
// Since the pane is flowing, though, the page underneath the pane *adjacent* to the input is
// also shown unless we cover it up. The inputMask does this by filling up the remaining space
// based on the width of the input.
this.inputMask.style.left = elementRect.width + 'px';
// Add CSS class after one frame to trigger open animation.
this.$$rAF(function() {
timePane.classList.add('md-pane-open');
});
};
/** Detach the floating time pane from the document. */
TimePickerCtrl.prototype.detachTimePane = function() {
this.$element.removeClass('sg-timepicker-open');
this.timePane.classList.remove('md-pane-open');
if (this.timePane.parentNode) {
// Use native DOM removal because we do not want any of the angular state of this element
// to be disposed.
this.timePane.parentNode.removeChild(this.timePane);
}
};
/**
* Open the floating time pane.
* @param {Event} event
*/
TimePickerCtrl.prototype.openTimePane = function(event) {
if (!this.isTimeOpen && !this.isDisabled) {
this.isTimeOpen = true;
this.timePaneOpenedFrom = event.target;
this.attachTimePane();
this.focusTime();
// Because the time pane is attached directly to the body, it is possible that the
// rest of the component (input, etc) is in a different scrolling container, such as
// an md-content. This means that, if the container is scrolled, the pane would remain
// stationary. To remedy this, we disable scrolling while the time pane is open, which
// also matches the native behavior for things like `<select>` on Mac and Windows.
this.$mdUtil.disableScrollAround(this.timePane);
// Attach click listener inside of a timeout because, if this open call was triggered by a
// click, we don't want it to be immediately propogated up to the body and handled.
var self = this;
this.$mdUtil.nextTick(function() {
document.body.addEventListener('click', self.bodyClickHandler);
}, false);
}
};
/** Close the floating time pane. */
TimePickerCtrl.prototype.closeTimePane = function() {
this.isTimeOpen = false;
this.detachTimePane();
this.timePaneOpenedFrom.focus();
this.timePaneOpenedFrom = null;
this.$mdUtil.enableScrolling();
document.body.removeEventListener('click', this.bodyClickHandler);
};
/** Gets the controller instance for the time in the floating pane. */
TimePickerCtrl.prototype.getTimePaneCtrl = function() {
return angular.element(this.timePane.querySelector('sg-time-pane')).controller('sgTimePane');
};
/** Focus the time in the floating pane. */
TimePickerCtrl.prototype.focusTime = function() {
// Use a timeout in order to allow the time to be rendered, as it is gated behind an ng-if.
var self = this;
this.$mdUtil.nextTick(function() {
var ctrl = self.getTimePaneCtrl();
self.getTimePaneCtrl().focus(null, ctrl);
}, false);
};
/**
* Sets whether the input is currently focused.
* @param {boolean} isFocused
*/
TimePickerCtrl.prototype.setFocused = function(isFocused) {
this.isFocused = isFocused;
};
/**
* Handles a click on the document body when the floating time pane is open.
* Closes the floating time pane if the click is not inside of it.
* @param {MouseEvent} event
*/
TimePickerCtrl.prototype.handleBodyClick = function(event) {
if (this.isTimeOpen) {
// TODO(jelbourn): way want to also include the md-datepicker itself in this check.
var isInTime = this.$mdUtil.getClosest(event.target, 'sg-time-pane');
if (!isInTime) {
this.closeTimePane();
}
this.$scope.$digest();
}
};
})();
+5 -7
View File
@@ -80,7 +80,7 @@ String.prototype.md5 = function() {
String.prototype.capitalize = function() {
return this.charAt(0).toUpperCase() + this.slice(1);
}
};
String.prototype.asDate = function () {
var newDate;
@@ -129,7 +129,7 @@ String.prototype.formatTime = function(hours, minutes) {
};
Date.prototype.daysUpTo = function(otherDate) {
var days = new Array();
var days = [];
var day1 = this.getTime();
var day2 = otherDate.getTime();
@@ -155,10 +155,8 @@ String.prototype.isValidEmail = function() {
};
String.prototype.asCSSIdentifier = function() {
var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ',' , ' '
, "'", '&', '\\+' ];
var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_CO_',
'_SP_', '_SQ_', '_AM_', '_P_' ];
var characters = [ '_' , '\\.', '#' , '@' , '\\*', ':' , ',' , ' ', "'", '&', '\\+' ];
var escapeds = [ '_U_', '_D_', '_H_', '_A_', '_S_', '_C_', '_CO_', '_SP_', '_SQ_', '_AM_', '_P_' ];
var newString = this;
for (var i = 0; i < characters.length; i++) {
@@ -231,7 +229,7 @@ Date.prototype.beginOfDay = function() {
beginOfDay.setMilliseconds(0);
return beginOfDay;
}
};
Date.prototype.beginOfWeek = function() {
var offset = firstDayOfWeek - this.getDay();
+2
View File
@@ -0,0 +1,2 @@
!function(){"use strict";function configure($stateProvider,$urlRouterProvider){$stateProvider.state("app",{url:"/addressbooks","abstract":!0,views:{addressbooks:{templateUrl:"UIxContactFoldersView",controller:"AddressBooksController",controllerAs:"app"}},resolve:{stateAddressbooks:stateAddressbooks}}).state("app.addressbook",{url:"/:addressbookId",views:{addressbook:{templateUrl:"addressbook",controller:"AddressBookController",controllerAs:"addressbook"}},resolve:{stateAddressbook:stateAddressbook}}).state("app.addressbook.new",{url:"/{contactType:(?:card|list)}/new",views:{card:{templateUrl:"UIxContactEditorTemplate",controller:"CardController",controllerAs:"editor"}},resolve:{stateCard:stateNewCard}}).state("app.addressbook.card",{url:"/:cardId","abstract":!0,views:{card:{template:"<ui-view/>"}},resolve:{stateCard:stateCard}}).state("app.addressbook.card.view",{url:"/view",views:{"card@app.addressbook":{templateUrl:"UIxContactViewTemplate",controller:"CardController",controllerAs:"editor"}}}).state("app.addressbook.card.editor",{url:"/edit",views:{"card@app.addressbook":{templateUrl:"UIxContactEditorTemplate",controller:"CardController",controllerAs:"editor"}}}),$urlRouterProvider.otherwise("/addressbooks/personal")}function stateAddressbooks(AddressBook){return AddressBook.$findAll(window.contactFolders)}function stateAddressbook($stateParams,AddressBook){return AddressBook.$find($stateParams.addressbookId).$futureAddressBookData}function stateNewCard($stateParams,stateAddressbook,Card){var tag="v"+$stateParams.contactType,card=new Card({pid:$stateParams.addressbookId,c_component:tag});return stateAddressbook.selectedCard=!0,card}function stateCard($stateParams,stateAddressbook){return stateAddressbook.selectedCard=$stateParams.cardId,stateAddressbook.$getCard($stateParams.cardId)}function runBlock($rootScope){$rootScope.$on("$routeChangeError",function(event,current,previous,rejection){console.error(event,current,previous,rejection)})}angular.module("SOGo.ContactsUI",["ngSanitize","ui.router","angularFileUpload","ck","SOGo.Common","SOGo.PreferencesUI","SOGo.MailerUI"]).config(configure).run(runBlock),configure.$inject=["$stateProvider","$urlRouterProvider"],stateAddressbooks.$inject=["AddressBook"],stateAddressbook.$inject=["$stateParams","AddressBook"],stateNewCard.$inject=["$stateParams","stateAddressbook","Card"],stateCard.$inject=["$stateParams","stateAddressbook"],runBlock.$inject=["$rootScope"]}(),function(){"use strict";function AddressBookController($scope,$q,$state,$timeout,$mdDialog,focus,Account,Card,AddressBook,Dialog,Settings,stateAddressbooks,stateAddressbook){function selectCard(card){$state.go("app.addressbook.card.view",{addressbookId:stateAddressbook.id,cardId:card.id})}function newComponent(ev){function ComponentDialogController(scope,$mdDialog,$state,addressbookId){scope.create=function(type){$mdDialog.hide(),$state.go("app.addressbook.new",{addressbookId:addressbookId,contactType:type})}}$mdDialog.show({parent:angular.element(document.body),targetEvent:ev,clickOutsideToClose:!0,escapeToClose:!0,template:['<md-dialog aria-label="'+l("Create component")+'">'," <md-dialog-content>",' <div layout="column">'," <md-button ng-click=\"create('card')\">"," "+l("Contact")," </md-button>"," <md-button ng-click=\"create('list')\">"," "+l("List")," </md-button>"," </div>"," </md-dialog-content>","</md-dialog>"].join(""),locals:{addressbookId:vm.selectedFolder.id},controller:ComponentDialogController}),ComponentDialogController.$inject=["scope","$mdDialog","$state","addressbookId"]}function notSelectedComponent(currentCard,type){return currentCard&&currentCard.c_component==type&&!currentCard.selected}function unselectCards(){_.each(vm.selectedFolder.cards,function(card){card.selected=!1})}function confirmDeleteSelectedCards(){Dialog.confirm(l("Warning"),l("Are you sure you want to delete the selected contacts?")).then(function(){var selectedCards=_.filter(vm.selectedFolder.cards,function(card){return card.selected});vm.selectedFolder.$deleteCards(selectedCards),delete vm.selectedFolder.selectedCard},function(data,status){})}function saveSelectedCards(){var selectedCards=_.filter(vm.selectedFolder.cards,function(card){return card.selected}),selectedUIDs=_.pluck(selectedCards,"id");window.location.href=ApplicationBaseURL+"/"+vm.selectedFolder.id+"/export?uid="+selectedUIDs.join("&uid=")}function copySelectedCards(folder){var selectedCards=_.filter(vm.selectedFolder.cards,function(card){return card.selected});vm.selectedFolder.$copyCards(selectedCards,folder).then(function(){},function(error){Dialog.alert(l("Error"),error)})}function selectAll(){_.each(vm.selectedFolder.cards,function(card){card.selected=!0})}function sort(field){vm.selectedFolder.$filter("",{sort:field})}function sortedBy(field){return AddressBook.$query.sort==field}function cancelSearch(){vm.mode.search=!1,vm.selectedFolder.$filter("")}function newMessage($event,recipients){Account.$findALine truncated
//# sourceMappingURL=Contacts.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Contacts.js","sources":["Contacts/Contacts.app.js","Contacts/AddressBookController.js","Contacts/AddressBooksController.js","Contacts/CardController.js","Contacts/sgAddress.directive.js"],"names":["configure","$stateProvider","$urlRouterProvider","state","url","abstract","views","addressbooks","templateUrl","controller","controllerAs","resolve","stateAddressbooks","addressbook","stateAddressbook","card","stateCard","stateNewCard","template","card@app.addressbook","otherwise","AddressBook","$findAll","window","contactFolders","$stateParams","$find","addressbookId","$futureAddressBookData","Card","tag","contactType","pid","c_component","selectedCard","cardId","$getCard","runBlock","$rootScope","$on","event","current","previous","rejection","console","error","angular","module","config","run","$inject","AddressBookController","$scope","$q","$state","$timeout","$mdDialog","focus","Account","Dialog","Settings","selectCard","go","id","newComponent","ev","ComponentDialogController","scope","create","type","hide","show","parent","element","document","body","targetEvent","clickOutsideToClose","escapeToClose","l","join","locals","vm","selectedFolder","notSelectedComponent","currentCard","selected","unselectCards","_","each","cards","confirmDeleteSelectedCards","confirm","then","selectedCards","filter","$deleteCards","data","status","saveSelectedCards","selectedUIDs","pluck","location","href","ApplicationBaseURL","copySelectedCards","folder","$copyCards","alert","selectAll","sort","field","$filter","sortedBy","$query","cancelSearch","mode","search","newMessage","$event","recipients","accounts","account","o","$getMailboxes","mailboxes","$newMessage","message","stateAccounts","stateMessage","stateRecipients","newMessageWithRecipient","recipient","fn","full","newMessageWithSelectedCards","promises","c_mail","length","push","c_cn","isDefined","refs","ref","email","$futureCardData","all","this","AddressBooksController","$mdToast","FileUploader","User","select","editMode","value","newAddressbook","prompt","name","isEditable","isRemote","owner","UserLogin","$add","edit","originalAddressbook","extend","$omit","revertEditing","save","$rename","confirmDelete","service","isSubscription","$delete","importCards","CardsImportDialogController","uploader","onProgressItem","item","progress","debug","onSuccessItem","response","headers","JSON","stringify","undefined","simple","content","imported","position","hideDelay","$reload","onCancelItem","onErrorItem","close","upload","uploadAll","exportCards","showLinks","LinksDialogController","share","$acl","$users","usersWithACL","users","subscribeToFolder","addressbookData","$subscribe","activeUser","CardController","addOrgUnit","i","$addOrgUnit","addBirthday","birthday","Date","addEmail","$addEmail","addPhone","$addPhone","addUrl","$addUrl","addAddress","$addAddress","addMember","$addMember","userFilter","excludedCards","dry","excludeLists","$cards","form","$valid","$save","indexOf","copy","err","log","reset","$reset","cancel","isNew","$fullname","ok","reject","exportCard","currentFolder","viewRawSource","$$resource","post","CardRawSourceDialogController","allEmailTypes","$EMAIL_TYPES","allTelTypes","$TEL_TYPES","allUrlTypes","$URL_TYPES","allAddressTypes","$ADDRESS_TYPES","categories","userFilterResults","sgAddress","restrict","addressLines","lines","locality_region","street","street2","locality","region","country","postalcode","directive"],"mappings":"CAGA,WACE,YAUA,SAASA,WAAUC,eAAgBC,oBACjCD,eACGE,MAAM,OACLC,IAAK,gBACLC,YAAU,EACVC,OACEC,cACEC,YAAa,wBACbC,WAAY,yBACZC,aAAc,QAGlBC,SACEC,kBAAmBA,qBAGtBT,MAAM,mBACLC,IAAK,kBACLE,OACEO,aACEL,YAAa,cACbC,WAAY,wBACZC,aAAc,gBAGlBC,SACEG,iBAAkBA,oBAGrBX,MAAM,uBACLC,IAAK,mCACLE,OACES,MACEP,YAAa,2BACbC,WAAY,iBACZC,aAAc,WAGlBC,SACEK,UAAWC,gBAGdd,MAAM,wBACLC,IAAK,WACLC,YAAU,EACVC,OACES,MACEG,SAAU,eAGdP,SACEK,UAAWA,aAGdb,MAAM,6BACLC,IAAK,QACLE,OACEa,wBACEX,YAAa,yBACbC,WAAY,iBACZC,aAAc,aAInBP,MAAM,+BACLC,IAAK,QACLE,OACEa,wBACEX,YAAa,2BACbC,WAAY,iBACZC,aAAc,aAMtBR,mBAAmBkB,UAAU,0BAO/B,QAASR,mBAAkBS,aACzB,MAAOA,aAAYC,SAASC,OAAOC,gBAOrC,QAASV,kBAAiBW,aAAcJ,aACtC,MAAOA,aAAYK,MAAMD,aAAaE,eAAeC,uBAOvD,QAASX,cAAaQ,aAAcX,iBAAkBe,MACpD,GAAIC,KAAM,IAAML,aAAaM,YACzBhB,KAAO,GAAIc,OAAOG,IAAKP,aAAaE,cAAeM,YAAaH,KAEpE,OADAhB,kBAAiBoB,cAAe,EACzBnB,KAOT,QAASC,WAAUS,aAAcX,kBAE/B,MADAA,kBAAiBoB,aAAeT,aAAaU,OACtCrB,iBAAiBsB,SAASX,aAAaU,QAOhD,QAASE,UAASC,YAChBA,WAAWC,IAAI,oBAAqB,SAASC,MAAOC,QAASC,SAAUC,WACrEC,QAAQC,MAAML,MAAOC,QAASC,SAAUC,aAjI5CG,QAAQC,OAAO,mBAAoB,aAAc,YAAa,oBAAqB,KAAM,cAAe,qBAAsB,kBAC3HC,OAAOhD,WACPiD,IAAIZ,UAKPrC,UAAUkD,SAAW,iBAAkB,sBAmFvCtC,kBAAkBsC,SAAW,eAQ7BpC,iBAAiBoC,SAAW,eAAgB,eAQ5CjC,aAAaiC,SAAW,eAAgB,mBAAoB,QAW5DlC,UAAUkC,SAAW,eAAgB,oBASrCb,SAASa,SAAW,iBClItB,WACE,YAMA,SAASC,uBAAsBC,OAAQC,GAAIC,OAAQC,SAAUC,UAAWC,MAAOC,QAAS7B,KAAMR,YAAasC,OAAQC,SAAUhD,kBAAmBE,kBAsB9I,QAAS+C,YAAW9C,MAClBuC,OAAOQ,GAAG,6BAA8BnC,cAAeb,iBAAiBiD,GAAI5B,OAAQpB,KAAKgD,KAG3F,QAASC,cAAaC,IA8BpB,QAASC,2BAA0BC,MAAOX,UAAWF,OAAQ3B,eAC3DwC,MAAMC,OAAS,SAASC,MACtBLine truncated
@@ -0,0 +1,2 @@
!function(){"use strict";function AddressBook(futureAddressBookData){if("function"!=typeof futureAddressBookData.then)if(this.init(futureAddressBookData),this.name&&!this.id){var newAddressBookData=AddressBook.$$resource.create("createFolder",this.name);this.$unwrap(newAddressBookData)}else this.id&&(this.$acl=new AddressBook.$$Acl("Contacts/"+this.id));else this.$unwrap(futureAddressBookData)}AddressBook.$factory=["$q","$timeout","$log","sgSettings","Resource","Card","Acl","Preferences",function($q,$timeout,$log,Settings,Resource,Card,Acl,Preferences){return angular.extend(AddressBook,{$q:$q,$timeout:$timeout,$log:$log,$$resource:new Resource(Settings.activeUser("folderURL")+"Contacts",Settings.activeUser()),$Card:Card,$$Acl:Acl,$Preferences:Preferences,$query:{search:"name_or_address",value:"",sort:"c_cn",asc:1},activeUser:Settings.activeUser(),selectedFolder:null,$refreshTimeout:null}),Preferences.ready().then(function(){Preferences.settings.Contact.SortingState&&(AddressBook.$query.sort=Preferences.settings.Contact.SortingState[0],AddressBook.$query.asc=parseInt(Preferences.settings.Contact.SortingState[1]))}),AddressBook}];try{angular.module("SOGo.ContactsUI")}catch(e){angular.module("SOGo.ContactsUI",["SOGo.Common"])}angular.module("SOGo.ContactsUI").factory("AddressBook",AddressBook.$factory),AddressBook.$filterAll=function(search,options,excludedCards){var params={search:search};if(!search)return AddressBook.$cards=[],AddressBook.$q.when(AddressBook.$cards);if(angular.isUndefined(AddressBook.$cards))AddressBook.$cards=[];else if(AddressBook.$query==search)return AddressBook.$q.when(AddressBook.$cards);return AddressBook.$query=search,angular.extend(params,options),AddressBook.$$resource.fetch(null,"allContactSearch",params).then(function(response){var results,card,index,compareIds=function(data){return this.id==data.id};for(results=excludedCards?_.filter(response.contacts,function(data){return _.isUndefined(_.find(excludedCards,compareIds,data))}):response.contacts,index=AddressBook.$cards.length-1;index>=0;index--)card=AddressBook.$cards[index],_.isUndefined(_.find(results,compareIds,card))&&AddressBook.$cards.splice(index,1);return _.each(results,function(data,index){if(_.isUndefined(_.find(AddressBook.$cards,compareIds,data))){var card=new AddressBook.$Card(data,search);AddressBook.$cards.splice(index,0,card)}}),AddressBook.$cards})},AddressBook.$add=function(addressbook){var list,sibling,i;list=addressbook.isSubscription?this.$subscriptions:this.$addressbooks,sibling=_.find(list,function(o){return"personal"==addressbook.id||"personal"!=o.id&&1===o.name.localeCompare(addressbook.name)}),i=sibling?_.indexOf(_.pluck(list,"id"),sibling.id):1,list.splice(i,0,addressbook)},AddressBook.$findAll=function(data){var _this=this;return data&&(this.$addressbooks=[],this.$subscriptions=[],this.$remotes=[],angular.forEach(data,function(o,i){var addressbook=new AddressBook(o);addressbook.isRemote?_this.$remotes.push(addressbook):addressbook.isSubscription?_this.$subscriptions.push(addressbook):_this.$addressbooks.push(addressbook)})),_.union(this.$addressbooks,this.$subscriptions,this.$remotes)},AddressBook.$find=function(addressbookId){var futureAddressBookData=AddressBook.$Preferences.ready().then(function(){return AddressBook.$$resource.fetch(addressbookId,"view",AddressBook.$query)});return new AddressBook(futureAddressBookData)},AddressBook.$subscribe=function(uid,path){var _this=this;return AddressBook.$$resource.userResource(uid).fetch(path,"subscribe").then(function(addressbookData){var addressbook=new AddressBook(addressbookData);return _.isUndefined(_.find(_this.$subscriptions,function(o){return o.id==addressbookData.id}))&&AddressBook.$add(addressbook),addressbook})},AddressBook.prototype.init=function(data){this.$isLoading=!0,this.$cards=[],this.cards=[],angular.extend(this,data),this.isOwned=AddressBook.activeUser.isSuperUser||this.owner==AddressBook.activeUser.login,this.isSubscription=!this.isRemote&&this.owner!=AddressBook.activeUser.login},AddressBook.prototype.$id=function(){return this.id?AddressBook.$q.when(this.id):this.$futureAddressBookData.then(function(addressbook){return addressbook.id})},AddressBook.prototype.$selectedCount=function(){var count;return count=0,this.cards&&(count=_.filter(this.cards,function(card){return card.selected}).length),count},AddressBook.prototype.$startRefreshTimeout=function(){var _this=this;AddressBook.$refreshTimeout&&AddressBook.$timeout.cancel(AddressBook.$refreshTimeout),AddressBook.$Preferences.ready().then(function(){var refreshViewCheck=AddressBook.$Preferences.defaults.SOGoRefreshViewCheck;if(refreshViewCheck&&"manually"!=refreshViewCheck){var f=angular.bind(_this,AddressBook.prototype.$reload);AddressBook.$refreshTimeout=AddressBook.$timeout(f,1e3*refreshViewCheck.timeInterval())}})},AddressBook.prototype.$reload=function(){return this.$startRefreshTimeout(),this.$filter()},AddressBook.prototype.$filter=function(search,options,excludedCards){var _tLine truncated
//# sourceMappingURL=Contacts.services.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Contacts.services.js","sources":["Contacts/AddressBook.service.js","Contacts/Card.service.js"],"names":["AddressBook","futureAddressBookData","then","this","init","name","id","newAddressBookData","$$resource","create","$unwrap","$acl","$$Acl","$factory","$q","$timeout","$log","Settings","Resource","Card","Acl","Preferences","angular","extend","activeUser","$Card","$Preferences","$query","search","value","sort","asc","selectedFolder","$refreshTimeout","ready","settings","Contact","SortingState","parseInt","module","e","factory","$filterAll","options","excludedCards","params","$cards","when","isUndefined","fetch","response","results","card","index","compareIds","data","_","filter","contacts","find","length","splice","each","$add","addressbook","list","sibling","i","isSubscription","$subscriptions","$addressbooks","o","localeCompare","indexOf","pluck","$findAll","_this","$remotes","forEach","isRemote","push","union","$find","addressbookId","$subscribe","uid","path","userResource","addressbookData","prototype","$isLoading","cards","isOwned","isSuperUser","owner","login","$id","$futureAddressBookData","$selectedCount","count","selected","$startRefreshTimeout","cancel","refreshViewCheck","defaults","SOGoRefreshViewCheck","f","bind","$reload","timeInterval","$filter","dry","isDefined","oldIndex","removedCards","findIndex","$rename","$save","$delete","promise","d","defer","remove","resolve","status","reject","$deleteCards","uids","map","post","difference","$copyCards","folder","save","$omit","$getCard","cardId","fullCard","cachedCard","$futureCardData","isError","isObject","key","futureCardData","partial","pid","newCardData","newguid","isNew","$TEL_TYPES","$EMAIL_TYPES","$URL_TYPES","$ADDRESS_TYPES","Gravatar","$gravatar","SOGoContactsCategories","$categories","join","$unwrapCollection","filterCategories","query","re","RegExp","category","collection","refs","$$fullname","$fullname","$$email","$preferredEmail","$$image","image","empty","action","c_component","$shadowData","attribute","names","fn","c_cn","c_givenname","nickname","c_sn","c_org","emails","$description","description","title","role","orgUnits","unit","org","email","test","type","$shortFormat","fullname","$birthday","birthday","getFullYear","getMonth","getDate","$isCard","$isList","$addOrgUnit","orgUnit","$addCategory","categories","$addEmail","$addPhone","phones","$addUrl","url","urls","$addAddress","postoffice","street","street2","locality","region","country","postalcode","addresses","$addMember","$reset","reference","Date","deep","copy","getTime","toString","desc"],"mappings":"CAEA,WACE,YAOA,SAASA,aAAYC,uBAEnB,GAA0C,kBAA/BA,uBAAsBC,KAE/B,GADAC,KAAKC,KAAKH,uBACNE,KAAKE,OAASF,KAAKG,GAAI,CAEzB,GAAIC,oBAAqBP,YAAYQ,WAAWC,OAAO,eAAgBN,KAAKE,KAC5EF,MAAKO,QAAQH,wBAENJ,MAAKG,KACZH,KAAKQ,KAAO,GAAIX,aAAYY,MAAM,YAAcT,KAAKG,SAKvDH,MAAKO,QAAQT,uBASjBD,YAAYa,UAAY,KAAM,WAAY,OAAQ,aAAc,WAAY,OAAQ,MAAO,cAAe,SAASC,GAAIC,SAAUC,KAAMC,SAAUC,SAAUC,KAAMC,IAAKC,aAqBpK,MApBAC,SAAQC,OAAOvB,aACbc,GAAIA,GACJC,SAAUA,SACVC,KAAMA,KACNR,WAAY,GAAIU,UAASD,SAASO,WAAW,aAAe,WAAYP,SAASO,cACjFC,MAAON,KACPP,MAAOQ,IACPM,aAAcL,YACdM,QAASC,OAAQ,kBAAmBC,MAAO,GAAIC,KAAM,OAAQC,IAAK,GAClEP,WAAYP,SAASO,aACrBQ,eAAgB,KAChBC,gBAAiB,OAGnBZ,YAAYa,QAAQhC,KAAK,WACnBmB,YAAYc,SAASC,QAAQC,eAC/BrC,YAAY2B,OAAOG,KAAOT,YAAYc,SAASC,QAAQC,aAAa,GACpErC,YAAY2B,OAAOI,IAAMO,SAASjB,YAAYc,SAASC,QAAQC,aAAa,OAGzErC,aAOT,KACEsB,QAAQiB,OAAO,mBAEjB,MAAMC,GACJlB,QAAQiB,OAAO,mBAAoB,gBAErCjB,QAAQiB,OAAO,mBACZE,QAAQ,cAAezC,YAAYa,UAUtCb,YAAY0C,WAAa,SAASd,OAAQe,QAASC,eACjD,GAAIC,SAAUjB,OAAQA,OAEtB,KAAKA,OAGH,MADA5B,aAAY8C,UACL9C,YAAYc,GAAGiC,KAAK/C,YAAY8C,OAEzC,IAAIxB,QAAQ0B,YAAYhD,YAAY8C,QAElC9C,YAAY8C,cAET,IAAI9C,YAAY2B,QAAUC,OAE7B,MAAO5B,aAAYc,GAAGiC,KAAK/C,YAAY8C,OAMzC,OAJA9C,aAAY2B,OAASC,OAErBN,QAAQC,OAAOsB,OAAQF,SAEhB3C,YAAYQ,WAAWyC,MAAM,KAAM,mBAAoBJ,QAAQ3C,KAAK,SAASgD,UAClF,GAAIC,SAASC,KAAMC,MACfC,WAAa,SAASC,MACpB,MAAOpD,MAAKG,IAAMiD,KAAKjD,GAY7B,KARE6C,QAFEP,cAEQY,EAAEC,OAAOP,SAASQ,SAAU,SAASH,MAC7C,MAAOC,GAAER,YAAYQ,EAAEG,KAAKf,cAAeU,WAAYC,SAI/CL,SAASQ,SAGhBL,MAAQrD,YAAY8C,OAAOc,OAAS,EAAGP,OAAS,EAAGA,QACtDD,KAAOpD,YAAY8C,OAAOO,OACtBG,EAAER,YAAYQ,EAAEG,KAAKR,QAASG,WAAYF,QAC5CpD,YAAY8C,OAAOe,OAAOR,MAAO,EAUrC,OANAG,GAAEM,KAAKX,QAAS,SAASI,KAAMF,OAC7B,GAAIG,EAAER,YAAYQ,EAAEG,KAAK3D,YAAY8C,OAAQQ,WAAYC,OAAQ,CAC/D,GAAIH,MAAO,GAAIpD,aAAYyB,MAAM8B,KAAM3B,OACvC5B,aAAY8C,OAAOe,OAAOR,MAAO,EAAGD,SAGjCpD,YAAY8C,UASvB9C,YAAY+D,KAAO,SAASC,aAE1B,GAAIC,MAAMC,QAASC,CAEnBF,MAAOD,YAAYI,eAAgBjE,KAAKkE,eAAiBlE,KAAKmE,cAC9DJ,QAAUV,EAAEG,KAAKM,KAAM,SAASM,GAC9B,MAA0B,YAAlBP,YAAY1D,IACH,YAARiE,EAAEjE,IACyC,IAA3CiE,EAAElE,KAAKmE,cAAcR,YAAY3D,QAE5C8D,EAAID,QAAUV,EAAEiB,QAAQjB,EAAEkB,MAAMT,KAAM,MAAOC,QAAQ5D,IAAM,EAC3D2D,KAAKJ,OAAOM,EAAG,EAAGH,cASpBhE,YAAY2E,SAAW,SAASpB,MAC9B,GAAIqB,OAAQzE,IAgBZ,OAfIoD,QACFpD,KAAKmE,iBACLnE,KAAKkE,kBACLlE,KAAK0E,YAELvD,QAAQwD,QAAQvB,KAAM,SAASgB,EAAGJ,GAChC,GAAIH,aAAc,GAAIhE,aAAYuE,EAC9BP,aAAYe,SACdH,MAAMC,SAASG,KAAKhB,aACbA,YAAYI,eACnBQ,MAAMP,eAAeW,KAAKhB,aAE1BY,MAAMN,cAAcU,KAAKhB,gBAGxBR,EAAEyB,MAAM9E,KLine truncated
Loaded 100 of 196 files, more files were not shown because too many files have changed in this diff. Show more