(js) New file structure for Angular modules

JavaScript files are now merged by the 'js' Grunt task.
pull/91/head
Francis Lachapelle 2015-05-05 22:06:13 -04:00
parent b1ff1d4365
commit 1dc5f0d412
65 changed files with 2995 additions and 2397 deletions

View File

@ -6,7 +6,7 @@
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label"
const:userDefaultsKeys="SOGoContactsCategories"
const:jsFiles="Common/user-model.js, Common/acl-model.js, Common/resource.js, Contacts/card-model.js, Contacts/addressbook-model.js"
const:jsFiles="Contacts.app.js, Contacts.js, Common.js, Contacts.js"
className="UIxPageFrame"
title="name">
<script type="text/javascript">
@ -319,7 +319,7 @@
<md-button class="iconButton md-fab md-fab-bottom-right md-accent"
label:aria-label="New Contact"
ng-click="newComponent()">
ng-click="newComponent($event)">
<i class="md-icon-add"><!--icon--></i>
</md-button>
</div>

View File

@ -8,44 +8,44 @@
xmlns:uix="OGo:uix">
<md-dialog flex="50" flex-sm="100">
<md-dialog-content>
<md-input-container md-no-float="md-no-float" layout="row">
<i class="md-icon-search"><!--icon--></i>
<input ng-model="searchString" type="text" placeholder="Name" sg-user-typeahead="sg-user-typeahead"/>
<h2 class="md-headline"><var:string label:value="Subscribe"/></h2>
<md-input-container>
<label><i class="md-icon-search"><!--icon--></i><var:string label:value="Search"/></label>
<input type="input"
autocomplete="off"
ng-model="vm.searchText"
ng-model-options="vm.searchTextOptions"
ng-change="vm.onChange()"/>
</md-input-container>
<md-list>
<md-list-item
layout="column"
ng-repeat="user in users">
<md-button class="sg-expandable" ng-click="selectUser($index)">
<div layout="row" layout-align="space-between center"
<md-list-item layout="column"
ng-repeat="user in vm.users">
<md-button class="sg-expandable" ng-click="vm.selectUser($index)">
<div layout="row" layout-align="start center"
layout-fill="true">
<div class="sg-avatar"><!-- normal-user --></div>
<span class="sg-item-name">{{user.$shortFormat()}}</span>
<span class="md-display-1">
<i class="md-icon-expand-more"
ng-show="user.uid != selectedUser.uid"><!--icon--></i>
<i class="md-icon-expand-less"
ng-show="user.uid == selectedUser.uid"><!--icon--></i>
<span class="md-secondary md-display-1">
<i ng-class="{ 'md-icon-expand-more': user.uid != vm.selectedUser.uid,
'md-icon-expand-less': user.uid == vm.selectedUser.uid }"><!--icon--></i>
</span>
</div>
</md-button>
<md-list layout-fill="true" ng-if="user.uid == selectedUser.uid">
<md-list layout-fill="true"
ng-show="user == vm.selectedUser">
<md-list-item ng-show="user.$$folders.length == 0">
<i class="md-icon-warning"><!-- no subscription --></i>
<var:string label:value="No possible subscription"/>
</md-list-item>
<md-list-item
layout="row"
layout-fill="true"
ng-repeat="folder in user.$$folders">
<i class="md-icon-contacts md-padding"
ng-show="folder.type == 'Contact'"><!--icon--></i>
<i class="md-icon-today md-padding"
ng-show="folder.type == 'Appointment'"><!--icon--></i>
<md-list-item layout="row" layout-fill="true"
ng-repeat="folder in user.$$folders">
<i class="md-padding"
ng-class="{ 'md-icon-contacts': folder.type == 'Contact',
'md-icon-today': folder.type == 'Appointment' }"><!--icon--></i>
<p class="md-flex sg-item-name">{{folder.displayName}}</p>
<md-button
class="md-raised"
ng-click="selectFolder(folder)">Subscribe</md-button>
ng-click="vm.selectFolder(folder)"><var:string label:value="Subscribe"/></md-button>
</md-list-item>
</md-list>
<md-divider ng-if="!$last"><!-- divider --></md-divider>

View File

@ -9,7 +9,7 @@
title="title"
const:userDefaultsKeys="SOGoMailMessageCheck,SOGoRefreshViewCheck,SOGoMailSortByThreads,SOGoMailListViewColumnsOrder,SOGoMailDisplayRemoteInlineImages,SOGoMailComposeMessageType,SOGoMailReplyPlacement,SOGoMailLabelsColors"
const:userSettingsKeys="Mail"
const:jsFiles="Common/resource.js, Common/user-model.js, Common/acl-model.js, Contacts/card-model.js, Contacts/addressbook-model.js, Mailer/message-model.js, Mailer/mailbox-model.js, Mailer/account-model.js, vendor/ckeditor/ckeditor.js, vendor/ckeditor/ck.js, vendor/angular-file-upload.js">
const:jsFiles="Mailer.app.js, Common.js, Contacts.js, Mailer.js, vendor/ckeditor/ckeditor.js, vendor/ckeditor/ck.js, vendor/angular-file-upload.js">
<script type="text/javascript">
var mailAccounts =<var:string value="mailAccounts" const:escapeHTML="NO" />;
var userNames =<var:string value="userNames" const:escapeHTML="NO" />;

View File

@ -9,7 +9,7 @@
xmlns:label="OGo:label"
className="UIxPageFrame"
title="title"
const:jsFiles="Common/resource.js,Common/SOGoAuthentication.js,Mailer/mailbox-model.js,Mailer/message-model.js,Preferences/preferences-model.js,Common/user-model.js">
const:jsFiles="Preferences.app.js, Common.js, Mailer.js, Preferences.js">
<main class="view md-layout-fill" ui-view="preferences" layout="row"
ng-controller="navController"><!-- preferences --> </main>
@ -59,7 +59,7 @@
</md-toolbar>
<form name="preferencesForm"
ng-submit="save()">
ng-submit="app.save()">
<div ui-view="module"><!-- view --></div>
<md-button class="" type="submit">
<var:string label:value="Save" />
@ -89,7 +89,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Language :"/></div>
<div>
<md-select ng-model="preferences.defaults.SOGoLanguage">
<md-select ng-model="app.preferences.defaults.SOGoLanguage">
<var:foreach list="languages" item="item">
<md-option var:value="item">
<var:string value="languageText"/>
@ -102,7 +102,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Current Time Zone :"/></div>
<div>
<md-select ng-model="preferences.defaults.SOGoTimeZone">
<md-select ng-model="app.preferences.defaults.SOGoTimeZone">
<var:foreach list="timeZonesList" item="item">
<md-option var:value="item">
<var:string value="item"/>
@ -115,7 +115,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Short Date Format :"/></div>
<div>
<md-select ng-model="preferences.defaults.SOGoShortDateFormat">
<md-select ng-model="app.preferences.defaults.SOGoShortDateFormat">
<var:foreach list="shortDateFormatsList" item="item">
<md-option var:value="item">
<var:string value="itemShortDateFormatText"/>
@ -134,7 +134,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Long Date Format :"/></div>
<div>
<md-select ng-model="preferences.defaults.SOGoLongDateFormat">
<md-select ng-model="app.preferences.defaults.SOGoLongDateFormat">
<var:foreach list="longDateFormatsList" item="item">
<md-option var:value="item">
<var:string value="itemLongDateFormatText"/>
@ -152,7 +152,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Time Format :"/></div>
<div>
<md-select ng-model="preferences.defaults.SOGoTimeFormat">
<md-select ng-model="app.preferences.defaults.SOGoTimeFormat">
<var:foreach list="timeFormatsList" item="item">
<md-option var:value="item">
<var:string value="itemTimeFormatText"/>
@ -169,7 +169,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Default Module:"/></div>
<md-select ng-model="preferences.defaults.SOGoLoginModule">
<md-select ng-model="app.preferences.defaults.SOGoLoginModule">
<var:foreach list="availableModules" item="item">
<md-option var:value="item">
<var:string value="itemModuleText"/>
@ -181,7 +181,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Refresh View :"/></div>
<div>
<md-select ng-model="preferences.defaults.SOGoRefreshViewCheck">
<md-select ng-model="app.preferences.defaults.SOGoRefreshViewCheck">
<var:foreach list="refreshViewList" item="item">
<md-option var:value="item">
<var:string value="itemRefreshViewCheckText"/>
@ -243,7 +243,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Week begins on :" /></div>
<div>
<md-select ng-model="preferences.defaults.SOGoFirstDayOfWeek">
<md-select ng-model="app.preferences.defaults.SOGoFirstDayOfWeek">
<var:foreach list="daysList" item="item">
<md-option var:value="item">
<var:string value="itemWeekStartDay"/>
@ -261,7 +261,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Day start time :" /></div>
<div>
<md-select ng-model="preferences.defaults.SOGoDayStartTime">
<md-select ng-model="app.preferences.defaults.SOGoDayStartTime">
<var:foreach list="hoursList" item="item">
<md-option var:value="item">
<var:string value="item"/>
@ -279,7 +279,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Day end time :" /></div>
<div>
<md-select ng-model="preferences.defaults.SOGoDayEndTime">
<md-select ng-model="app.preferences.defaults.SOGoDayEndTime">
<var:foreach list="hoursList" item="item">
<md-option var:value="item">
<var:string value="item"/>
@ -296,7 +296,7 @@
<div layout="row" layout-align="space-around center">
<md-checkbox
ng-model="preferences.defaults.SOGoBusyOffHours"
ng-model="app.preferences.defaults.SOGoBusyOffHours"
ng-true-value="1"
ng-false-value="0">
<var:string label:value="Show time as busy outside working hours"/>
@ -306,7 +306,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="First week of year :"/></div>
<div>
<md-select ng-model="preferences.defaults.SOGoFirstWeekOfYear">
<md-select ng-model="app.preferences.defaults.SOGoFirstWeekOfYear">
<var:foreach list="firstWeekList" item="item">
<md-option var:value="item">
<var:string value="itemFirstWeekText"/>
@ -324,7 +324,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Default calendar :" /></div>
<div>
<md-select ng-model="preferences.defaults.SOGoDefaultCalendar">
<md-select ng-model="app.preferences.defaults.SOGoDefaultCalendar">
<var:foreach list="defaultCalendarList" item="item">
<md-option var:value="item">
<var:string value="itemCalendarText"/>
@ -342,7 +342,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Default events classification :" /></div>
<div>
<md-select ng-model="preferences.defaults.SOGoCalendarEventsDefaultClassification">
<md-select ng-model="app.preferences.defaults.SOGoCalendarEventsDefaultClassification">
<var:foreach list="calendarClassificationsList" item="item">
<md-option var:value="item">
<var:string value="itemClassificationText"/>
@ -360,7 +360,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Default tasks classification :"/></div>
<div>
<md-select ng-model="preferences.defaults.SOGoCalendarTasksDefaultClassification">
<md-select ng-model="app.preferences.defaults.SOGoCalendarTasksDefaultClassification">
<var:foreach list="calendarClassificationsList" item="item">
<md-option var:value="item">
<var:string value="itemClassificationText"/>
@ -378,7 +378,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Default reminder :"/></div>
<div>
<md-select ng-model="preferences.defaults.SOGoCalendarDefaultReminder">
<md-select ng-model="app.preferences.defaults.SOGoCalendarDefaultReminder">
<var:foreach list="reminderValues" item="item">
<md-option var:value="item">
<var:string value="itemReminderText"/>
@ -410,7 +410,7 @@
track by $index">
<i class="md-avatar" ng-style="{'background-color': '{{preferences.defaults.SOGoCalendarCategoriesColors[item]}}'}"><!-- category color --></i>
<md-input-container>
<input type="text" ng-model="preferences.defaults.SOGoCalendarCategories[$index]"/>
<input type="text" ng-model="app.preferences.defaults.SOGoCalendarCategories[$index]"/>
</md-input-container>
<md-button
ng-click="removeCalendarCategory($index)"
@ -435,7 +435,7 @@
<!-- <div id="calendarAppointmentsInvitationsView"
class="tab"> -->
<md-checkbox
ng-model="preferences.settings.Calendar.PreventInvitations"
ng-model="app.preferences.settings.Calendar.PreventInvitations"
ng-true-value="1"
ng-false-value="0">
<var:string label:value="Prevent from being invited to appointments"/>
@ -446,7 +446,7 @@
<var:string label:value="White list for appointment invitations:"/>
</label>
<md-contact-chips
ng-model="preferences.settings.Calendar.PreventInvitationsWhitelist"
ng-model="app.preferences.settings.Calendar.PreventInvitationsWhitelist"
md-contacts="userFilter($query)"
md-contact-name="shortFormat"
md-contact-image="image"
@ -487,7 +487,7 @@
preferences.defaults.SOGoContactsCategories
track by $index">
<md-input-container>
<input type="text" ng-model="preferences.defaults.SOGoContactsCategories[$index]"/>
<input type="text" ng-model="app.preferences.defaults.SOGoContactsCategories[$index]"/>
</md-input-container>
<md-button
ng-click="removeContactCategory($index)"
@ -524,7 +524,7 @@
<div role="tabpanel" aria-labelledby="mailGeneralView" id="mailGeneralView-content">
<md-checkbox
ng-model="preferences.defaults.SOGoMailShowSubscribedFoldersOnly"
ng-model="app.preferences.defaults.SOGoMailShowSubscribedFoldersOnly"
ng-true-value="1"
ng-false-value="0"
label:aria-label="Show subscribed mailboxes only">
@ -532,7 +532,7 @@
</md-checkbox>
<md-checkbox
ng-model="preferences.defaults.SOGoMailSortByThreads"
ng-model="app.preferences.defaults.SOGoMailSortByThreads"
ng-true-value="1"
ng-false-value="0"
label:aria-label="Sort messages by threads">
@ -541,7 +541,7 @@
<div layout="row" layout-align="space-around center">
<md-checkbox
ng-model="preferences.defaults.SOGoMailAddOutgoingAddresses"
ng-model="app.preferences.defaults.SOGoMailAddOutgoingAddresses"
ng-true-value="1"
ng-false-value="0"
label:arial-label="When sending mail, add unknown recipients to my">
@ -562,7 +562,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Forward messages:"/></div>
<div>
<md-select ng-model="preferences.defaults.SOGoMailMessageForwarding">
<md-select ng-model="app.preferences.defaults.SOGoMailMessageForwarding">
<var:foreach list="messageForwardingList" item="item">
<md-option var:value="item">
<var:string value="itemMessageForwardingText"/>
@ -575,7 +575,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="When replying to a message:"/></div>
<div>
<md-select ng-model="preferences.defaults.SOGoMailReplyPlacement">
<md-select ng-model="app.preferences.defaults.SOGoMailReplyPlacement">
<var:foreach list="replyPlacementList" item="item">
<md-option var:value="item">
<var:string value="itemReplyPlacementText"/>
@ -588,7 +588,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="And place my signature"/></div>
<div>
<md-select ng-model="preferences.defaults.SOGoMailSignaturePlacement">
<md-select ng-model="app.preferences.defaults.SOGoMailSignaturePlacement">
<var:foreach list="signaturePlacementList" item="item">
<md-option var:value="item">
<var:string value="itemSignaturePlacementText"/>
@ -601,7 +601,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Compose messages in"/></div>
<div>
<md-select ng-model="preferences.defaults.SOGoMailComposeMessageType">
<md-select ng-model="app.preferences.defaults.SOGoMailComposeMessageType">
<var:foreach list="composeMessagesType" item="item">
<md-option var:value="item">
<var:string value="itemComposeMessagesText"/>
@ -614,7 +614,7 @@
<div layout="row" layout-align="space-around center">
<div><var:string label:value="Display remote inline images"/></div>
<div>
<md-select ng-model="preferences.defaults.SOGoMailDisplayRemoteInlineImages">
<md-select ng-model="app.preferences.defaults.SOGoMailDisplayRemoteInlineImages">
<var:foreach list="displayRemoteInlineImages" item="item">
<md-option var:value="item">
<var:string value="itemDisplayRemoteInlineImagesText"/>
@ -650,13 +650,13 @@
preferences.defaults.SOGoSieveFilters
track by $index">
<md-checkbox
ng-model="preferences.defaults.SOGoSieveFilters[$index].active"
ng-model="app.preferences.defaults.SOGoSieveFilters[$index].active"
ng-true-value="1"
ng-false-value="0">
<!-- active or not-->
</md-checkbox>
<md-input-container>
<input type="text" ng-model="preferences.defaults.SOGoSieveFilters[$index].name"/>
<input type="text" ng-model="app.preferences.defaults.SOGoSieveFilters[$index].name"/>
</md-input-container>
<md-button ng-click="editMailFilter($index)"
type="button"
@ -729,7 +729,7 @@
track by $index">
<md-input-container>
<input type="text"
ng-model="preferences.defaults.AuxiliaryMailAccounts[$index].name"
ng-model="app.preferences.defaults.AuxiliaryMailAccounts[$index].name"
ng-readonly="$index == 0"/>
</md-input-container>
<md-button ng-click="editMailAccount($index)"
@ -770,7 +770,7 @@
<div role="tabpanel" aria-labelledby="mailVacationView" id="mailVacationView-content">
<md-checkbox
ng-model="preferences.defaults.Vacation.enabled"
ng-model="app.preferences.defaults.Vacation.enabled"
ng-true-value="1"
ng-false-value="0"
label:aria-label="Enable vacation auto reply">
@ -853,7 +853,7 @@
<div role="tabpanel" aria-labelledby="mailForwardView" id="mailForwardView-content">
<div id="forwardView" class="tab">
<md-checkbox
ng-model="preferences.defaults.Forward.enabled"
ng-model="app.preferences.defaults.Forward.enabled"
ng-true-value="1"
ng-false-value="0">
<var:string label:value="Forward incoming messages"/>
@ -863,7 +863,7 @@
<label><var:string label:value="Email addresses (separated by commas) :"/><br/>
<textarea const:name="forwardAddress"
const:id="forwardAddress"
ng-model="preferences.defaults.Forward.forwardAddress" />
ng-model="app.preferences.defaults.Forward.forwardAddress" />
</label><br/>
<md-checkbox

View File

@ -7,10 +7,10 @@
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"
className="UIxPageFrame"
const:userDefaultsKeys="SOGoRefreshViewCheck, SOGoCalendarCategoriesColors,SOGoDefaultCalendar"
const:userSettingsKeys="Calendar,ShowCompletedTasks"
const:jsFiles="Common/resource.js, Common/user-model.js, Common/acl-model.js, Contacts/card-model.js, Contacts/addressbook-model.js, Appointments/component-model.js, Appointments/calendar-model.js"
title="title">
title="title"
const:userDefaultsKeys="SOGoRefreshViewCheck, SOGoCalendarCategoriesColors, SOGoDefaultCalendar"
const:userSettingsKeys="Calendar, ShowCompletedTasks"
const:jsFiles="Scheduler.app.js, Scheduler.js, Common.js, Contacts.js">
<script type="text/javascript">
var firstDayOfWeek = <var:string value="firstDayOfWeek"/>;
var dayStartHour = <var:string value="dayStartHour"/>;

View File

@ -10,10 +10,8 @@
title="title"
const:toolbar="none"
const:popup="YES">
<div class="calendarUserRights" layout="column">
<div class="calendarUserRights" layout="column">
<!-- <var:foreach list="rightTypes" item="currentRightType">
<div><span><var:string value="currentRightTypeLabel"/></span>
<var:popup list="objectRights" item="currentRight"
@ -24,9 +22,9 @@
/></div>
</var:foreach>
-->
<div layout="row" layout-align="space-around center">
<div layout="row" layout-align="space-between center">
<var:string label:value="Public"/>
<md-select ng-model="selectedUser.rights.Public">
<md-select flex="50" ng-model="selectedUser.rights.Public">
<var:foreach list="objectRights" item="currentRight">
<md-option var:value="currentRight">
<var:string value="currentRightLabel"/>
@ -35,9 +33,9 @@
</md-select>
</div>
<div layout="row" layout-align="space-around center">
<div layout="row" layout-align="space-between center">
<var:string label:value="Confidential"/>
<md-select ng-model="selectedUser.rights.Confidential">
<md-select flex="50" ng-model="selectedUser.rights.Confidential">
<var:foreach list="objectRights" item="currentRight">
<md-option var:value="currentRight">
<var:string value="currentRightLabel"/>
@ -46,9 +44,9 @@
</md-select>
</div>
<div layout="row" layout-align="space-around center">
<div layout="row" layout-align="space-between center">
<var:string label:value="Private"/>
<md-select ng-model="selectedUser.rights.Private">
<md-select flex="50" ng-model="selectedUser.rights.Private">
<var:foreach list="objectRights" item="currentRight">
<md-option var:value="currentRight">
<var:string value="currentRightLabel"/>
@ -56,7 +54,7 @@
</var:foreach>
</md-select>
</div>
<md-checkbox name="canCreateObjects"
ng-model="selectedUser.rights.canCreateObjects"
ng-change="confirmChange(selectedUser)"
@ -65,7 +63,7 @@
ng-false-value="0">
<var:string label:value="This person can create objects in my calendar." />
</md-checkbox>
<md-checkbox name="canEraseObjects"
ng-model="selectedUser.rights.canEraseObjects"
ng-change="confirmChange(selectedUser)"

View File

@ -6,15 +6,16 @@
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label"
xmlns:uix="OGo:uix">
<md-dialog>
<md-dialog flex="50" flex-sm="100">
<md-dialog-content>
<md-subheader><var:string label:value="Access Rights"/> - {{stateAddressbook.name}}</md-subheader>
<h2 class="md-headline"><var:string label:value="Access Rights"/> - {{folder.name}}</h2>
<md-list>
<md-list-item ng-repeat="user in users | orderBy:['userClass', 'displayName']">
<div layout="column">
<md-list-item
ng-repeat="user in users | orderBy:['userClass', 'displayName']"
ng-click="selectUser(user)">
<div layout="column" layout-fill="true">
<div layout="row">
<md-button ng-click="selectUser(user)">
<md-button>
<div layout="row" layout-align="space-between center"
layout-fill="true">
<span class="card-picture" ng-switch="user.userClass">
@ -28,11 +29,11 @@
ng-click="removeUser(user)"
type="button"
layout="row" layout-align="end center"
ng-hide="user.$isSpecial()">
ng-hide="user.uid != selectedUser.uid || user.$isSpecial()">
<div class="md-icon-delete"><!-- delete --></div>
</md-button>
</div>
<span id="AccessRightList" ng-show="user==selectedUser">
<span id="AccessRightList" ng-show="user.uid == selectedUser.uid">
<md-checkbox ng-model="user.isSubscribed"
arial-label="Subscribe User"
ng-disabled="user.wasSubscribed"
@ -52,25 +53,26 @@
</div>
<md-divider><!-- divider --></md-divider>
</md-list-item>
<md-autocomplete
md-selected-item="userToAdd"
md-search-text="searchText"
md-selected-item-change="addUser(user)"
md-items="user in userFilter(searchText)"
md-item-text="user.shortFormat"
md-min-length="0"
placeholder="Add">
<span md-highlight-text="searchText" md-highlight-flags="^i">{{user.shortFormat}}</span>
</md-autocomplete>
<md-list-item>
<md-autocomplete
class="md-flex"
md-selected-item="userToAdd"
md-search-text="searchText"
md-selected-item-change="addUser(user)"
md-items="user in userFilter(searchText)"
md-item-text="user.shortFormat"
md-min-length="0"
placeholder="Add">
<span md-highlight-text="searchText" md-highlight-flags="^i">{{user.shortFormat}}</span>
</md-autocomplete>
</md-list-item>
</md-list>
<div id="aclButtons">
<md-button ng-click="closeModal()"><var:string label:value="Close"/></md-button>
<md-button ng-click="saveModal()"><var:string label:value="Save"/></md-button>
</div>
</md-dialog-content>
<div class="md-actions">
<md-button ng-click="closeModal()"><var:string label:value="Close"/></md-button>
<md-button class="md-primitive" ng-click="saveModal()"><var:string label:value="Save"/></md-button>
</div>
</md-dialog>
</container>

View File

@ -226,8 +226,6 @@
<script type="text/javascript" rsrc:src="js/vendor/angular-recursion.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/vendor/angular-vs-repeat.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/Common/utils.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/Common/ui.js"><!-- space --></script>
<script type="text/javascript" rsrc:src="js/Common/ui-desktop.js"><!-- space --></script>
<var:if condition="hasProductSpecificJavaScript">
<script type="text/javascript"

View File

@ -1,10 +1,16 @@
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/Scheduler.js': ['js/Scheduler/*.js'],
'js/Contacts.js': ['js/Contacts/*.js'],
'js/Mailer.js': ['js/Mailer/*.js'],
'js/Preferences.js': ['js/Preferences/*service.js', 'js/Preferences/*Controller.js']
};
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
sass: {
options: {
sourceMap: true,
// require: 'SassyJSON',
noCache: true,
includePaths: ['scss/',
'bower_components/breakpoint-sass/stylesheets/'
@ -42,6 +48,20 @@ module.exports = function(grunt) {
src: 'css/styles.css'
}
},
concat_sourcemap: {
dist: {
options: {
sourcesContent: false
},
files: js_files
},
dev: {
options: {
sourcesContent: true
},
files: js_files
}
},
watch: {
grunt: {
files: ['Gruntfile.js']
@ -49,12 +69,17 @@ module.exports = function(grunt) {
sass: {
files: 'scss/**/*.scss',
tasks: ['sass']
},
js: {
files: Object.keys(js_files).map(function(key) { return js_files[key]; }),
tasks: ['js']
}
}
});
grunt.loadNpmTasks('grunt-sass');
grunt.loadNpmTasks('grunt-postcss');
grunt.loadNpmTasks('grunt-concat-sourcemap');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.task.registerTask('static', function() {
@ -119,7 +144,10 @@ module.exports = function(grunt) {
}
*/
});
grunt.task.registerTask('build', ['static', 'sass', 'postcss:dist']);
grunt.task.registerTask('build', ['static', 'concat_sourcemap:dist', 'sass', 'postcss:dist']);
// Tasks for developers
grunt.task.registerTask('default', ['watch']);
grunt.task.registerTask('css', ['sass', 'postcss:dev']);
grunt.task.registerTask('default', ['build', 'watch']);
grunt.task.registerTask('js', ['concat_sourcemap:dev']);
grunt.task.registerTask('dev', ['css', 'js']);
};

View File

@ -15,7 +15,7 @@
* @desc The factory we'll use to register with Angular.
* @return the Acl constructor
*/
Acl.factory = ['$q', '$timeout', 'sgSettings', 'sgResource', 'sgUser', function($q, $timeout, Settings, Resource, User) {
Acl.factory = ['$q', '$timeout', 'sgSettings', 'Resource', 'User', function($q, $timeout, Settings, Resource, User) {
angular.extend(Acl, {
$q: $q,
$timeout: $timeout,
@ -30,7 +30,7 @@
* @module SOGo.Common
* @desc Factory registration of User in Angular module.
*/
angular.module('SOGo.Common').factory('sgAcl', Acl.factory);
angular.module('SOGo.Common').factory('Acl', Acl.factory);
/**
* @function $users

View File

@ -81,12 +81,12 @@
newAddress = baseAddress;
}
if (/theme=mobile/.test(window.location.search)) {
newAddress = baseAddress + '/Contacts' + '?theme=mobile';
}
else {
newAddress = baseAddress + '/Contacts';
}
// if (/theme=mobile/.test(window.location.search)) {
// newAddress = baseAddress + '/Contacts' + '?theme=mobile';
// }
// else {
// newAddress = baseAddress + '/Contacts';
// }
return newAddress;
};

View File

@ -0,0 +1,104 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
angular.module('SOGo.Common', ['ngMaterial', 'RecursionHelper'])
// md break-points values are hard-coded in angular-material/src/core/util/constant.js
// $mdMedia has a built-in support for those values but can also evaluate others
// For some reasons, angular-material's break-points don't match the specs
// Here we define values according to specs
.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 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'
});
// Default theme definition
// .primaryColor will soon be deprecated in favor of primaryPalette (already on dev builds https://groups.google.com/forum/m/#!topic/ngmaterial/-sXR8CYBMPg)
$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'
});
}
})();

View File

@ -0,0 +1,125 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* @name Dialog
* @constructor
*/
function Dialog() {
}
/**
* @name alert
* @desc Show an alert dialog box with a single "OK" button
* @param {string} title
* @param {string} content
*/
Dialog.alert = function(title, content) {
var alert = this.$modal.alert()
.title(title)
.content(content)
.ok(l('OK'));
this.$modal.show(alert);
};
/**
* @name confirm
* @desc Show a confirmation dialog box with buttons 'Cancel' and 'OK'
* @param {string} title
* @param {string} content
* @returns a promise that resolves if the user has clicked on the 'OK' button
*/
Dialog.confirm = function(title, content) {
var d = this.$q.defer(),
confirm = this.$modal.confirm()
.title(title)
.content(content)
.ok(l('OK'))
.cancel(l('Cancel'));
this.$modal.show(confirm).then(function() {
d.resolve();
}, function() {
d.reject();
});
return d.promise;
};
/**
* @name prompt
* @desc Show a primpt dialog box with a input text field and the 'Cancel' and 'OK' buttons
* @param {string} title
* @param {string} label
* @param {object} [options] - use a different input type by setting 'inputType'
* @returns a promise that resolves with the input field value
*/
Dialog.prompt = function(title, label, options) {
var o = options || {},
d = this.$q.defer();
this.$modal.show({
parent: angular.element(document.body),
clickOutsideToClose: true,
escapeToClose: true,
template: [
'<md-dialog flex="50" flex-sm="100">',
' <md-dialog-content layout="column">',
' <h2 class="md-title" ng-bind="title"></h2>',
' <md-input-container>',
' <label>' + label + '</label>',
' <input type="' + (o.inputType || 'text') + '"',
' aria-label="' + title + '"',
' ng-model="name" required="required"/>',
' </md-input-container>',
' </md-dialog-content>',
' <div class="md-actions">',
' <md-button ng-click="cancel()">',
' ' + l('Cancel'),
' </md-button>',
' <md-button class="md-primary" ng-click="ok()" ng-disabled="!name.length">',
' ' + l('OK'),
' </md-button>',
' </div>',
'</md-dialog>'
].join(''),
controller: PromptDialogController
});
/**
* @ngInject
*/
function PromptDialogController(scope, $mdDialog) {
scope.title = title;
scope.name = "";
scope.cancel = function() {
d.reject();
$mdDialog.hide();
}
scope.ok = function() {
d.resolve(scope.name);
$mdDialog.hide();
}
}
return d.promise;
};
/**
* @memberof Dialog
* @desc The factory we'll register as Dialog in the Angular module SOGo.Common
* @ngInject
*/
DialogService.$inject = ['$q', '$mdDialog'];
function DialogService($q, $mdDialog) {
angular.extend(Dialog, { $q: $q , $modal: $mdDialog });
return Dialog; // return constructor
};
/* Factory registration in Angular module */
angular
.module('SOGo.Common')
.factory('Dialog', DialogService);
})();

View File

@ -38,7 +38,7 @@
* @module SOGo.Common
* @desc Factory registration of Resource in Angular module.
*/
angular.module('SOGo.Common').factory('sgResource', Resource.$factory);
angular.module('SOGo.Common').factory('Resource', Resource.$factory);
/**
* @function userResource

View File

@ -23,7 +23,7 @@
* @desc The factory we'll use to register with Angular.
* @return the User constructor
*/
User.factory = ['$q', 'sgSettings', 'sgResource', function($q, Settings, Resource) {
User.factory = ['$q', 'sgSettings', 'Resource', function($q, Settings, Resource) {
angular.extend(User, {
$q: $q,
$$resource: new Resource(Settings.activeUser.folderURL, Settings.activeUser)
@ -36,7 +36,7 @@
* @module SOGo.Common
* @desc Factory registration of User in Angular module.
*/
angular.module('SOGo.Common').factory('sgUser', User.factory);
angular.module('SOGo.Common').factory('User', User.factory);
/**
* @memberof User
@ -46,6 +46,8 @@
*/
User.$filter = function(search) {
var param = {search: search};
if (!search)
return User.$q.when([]);
return User.$$resource.fetch(null, 'usersSearch', param).then(function(response) {
var results = [];
angular.forEach(response.users, function(data) {
@ -53,6 +55,7 @@
var user = new User(data);
results.push(user);
});
User.$users = results;
return results;
});
};

View File

@ -0,0 +1,19 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/**
* @type {angular.Module}
*/
(function () {
'use strict';
/**
* @ngInject
*/
decodeUri.$inject = ['$window'];
function decodeUri($window) {
return $window.decodeURIComponent;
}
angular.module('SOGo.Common')
.filter('decodeUri', decodeUri);
})();

View File

@ -0,0 +1,19 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/**
* @type {angular.Module}
*/
(function () {
'use strict';
/**
* @ngInject
*/
encodeUri.$inject = ['$window'];
function encodeUri($window) {
return $window.encodeURIComponent;
}
angular.module('SOGo.Common')
.filter('encodeUri', encodeUri);
})();

View File

@ -0,0 +1,18 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/**
* @type {angular.Module}
*/
(function () {
'use strict';
/**
* @ngInject
*/
function loc() {
return l;
}
angular.module('SOGo.Common')
.filter('loc', loc);
})();

View File

@ -0,0 +1,57 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/**
* @type {angular.Module}
*/
(function () {
'use strict';
/**
* @ngInject
*/
navController.$inject = ['$scope', '$timeout', '$interval', '$http', '$mdSidenav', '$mdBottomSheet', '$mdMedia', '$log', 'sgConstant'];
function navController($scope, $timeout, $interval, $http, $mdSidenav, $mdBottomSheet, $mdMedia, $log, sgConstant) {
// Show current day in top bar
$scope.currentDay = window.currentDay;
$timeout(function() {
// Update date when day ends
$interval(function() {
$http.get('../date').success(function(data) {
$scope.currentDay = data;
});
}, 24 * 3600 * 1000);
}, window.secondsBeforeTomorrow * 1000);
$scope.toggleLeft = function () {
$mdSidenav('left').toggle()
.then(function () {
$log.debug("toggle left is done");
});
};
$scope.toggleRight = function () {
$mdSidenav('right').toggle()
.then(function () {
$log.debug("toggle RIGHT is done");
});
};
$scope.openBottomSheet = function() {
$mdBottomSheet.show({
parent: angular.element(document.getElementById('left-sidenav')),
templateUrl: 'bottomSheetTemplate.html'
});
};
$scope.toggleDetailView = function() {
var detail = angular.element(document.getElementById('detailView'));
detail.toggleClass('sg-close');
};
$scope.$watch(function() {
return $mdMedia(sgConstant['gt-md']);
}, function(newVal) {
$scope.isGtMedium = newVal;
});
}
angular.module('SOGo.Common')
.controller('navController', navController);
})();

View File

@ -0,0 +1,32 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* sgEnter - A directive evaluated when the enter key is pressed
* @memberof SOGo.Common
* @ngInject
* @example:
<input type="text"
sg-enter="save($index)" />
*/
function sgEnter() {
var ENTER_KEY = 13;
return function(scope, element, attrs) {
element.bind("keydown keypress", function(event) {
if (event.which === ENTER_KEY) {
scope.$apply(function() {
scope.$eval(attrs.sgEnter);
});
event.preventDefault();
}
});
};
}
angular
.module('SOGo.Common')
.directive('sgEnter', sgEnter);
})();

View File

@ -0,0 +1,29 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* sgEscape - A directive evaluated when the escape key is pressed
* @memberof SOGo.Common
* @ngInject
* @example:
<input type="text"
sg-escape="revertEditing($index)" />
*/
function sgEscape() {
var ESCAPE_KEY = 27;
return function(scope, elem, attrs) {
elem.bind('keydown', function(event) {
if (event.keyCode === ESCAPE_KEY) {
scope.$apply(attrs.sgEscape);
}
});
};
}
angular
.module('SOGo.Common')
.directive('sgEscape', sgEscape);
})();

View File

@ -0,0 +1,30 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* sgFocusOn - A directive that sets the focus on its element when the specified string is broadcasted
* @memberof SOGo.Common
* @see {@link SOGo.Common.sgFocus}
* @ngInject
* @example:
<input type="text"
sg-focus-on="username" />
*/
function sgFocusOn() {
return function(scope, elem, attr) {
scope.$on('sgFocusOn', function(e, name) {
if (name === attr.sgFocusOn) {
elem[0].focus();
elem[0].select();
}
});
};
}
angular
.module('SOGo.Common')
.directive('sgFocusOn', sgFocusOn);
})();

View File

@ -0,0 +1,25 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* sgFocus - A service to set the focus on the element associated to a specific string
* @memberof SOGo.Common
* @param {string} name - the string identifier of the element
* @see {@link SOGo.Common.sgFocusOn}
* @ngInject
*/
sgFocus.$inject = ['$rootScope', '$timeout'];
function sgFocus($rootScope, $timeout) {
return function(name) {
$timeout(function() {
$rootScope.$broadcast('sgFocusOn', name);
});
}
}
angular
.module('SOGo.Common')
.factory('sgFocus', sgFocus);
})();

View File

@ -0,0 +1,44 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/*
* sgFolderStylesheet - Add CSS stylesheet for folder (addressbook or calendar)
* @memberof SOGo.Common
* @restrict attribute
* @param {object} ngModel - the object literal describing the folder (an Addressbook or Calendar instance)
* @example:
<div sg-folder-stylesheet="true"
ng-repeat="calendar in calendars.list"
ng-model="calendar" />
</div>
*/
function sgFolderStylesheet() {
return {
restrict: 'A',
require: 'ngModel',
scope: {
ngModel: '='
},
template: [
'<style type="text/css">',
' .bg-folder{{ ngModel.id }} {',
' background-color: {{ ngModel.color }} !important;',
' }',
' .fg-folder{{ ngModel.id }} {',
' color: {{ ngModel.color }} !important;',
' }',
' .checkbox-folder{{ ngModel.id }}.md-checked .md-icon {',
' background-color: {{ ngModel.color }} !important;',
' }',
'</style>'
].join('')
}
}
angular
.module('SOGo.Common')
.directive('sgFolderStylesheet', sgFolderStylesheet);
})();

View File

@ -0,0 +1,40 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* sgGravatarImage - A simple Gravatar directive (based on http://blog.lingohub.com/2014/08/better-ux-with-angularjs-directives/)
* @memberof SOGo.Common
* @example:
<sg-gravatar-image email="test@email.com" size="50"></sg-gravatar-image>
*/
function sgGravatarImage() {
return {
restrict: 'AE',
replace: true,
required: 'email',
template: '<img ng-src="https://www.gravatar.com/avatar/{{hash}}?s={{size}}&d=wavatar" />',
link: function(scope, element, attrs) {
attrs.$observe('email', function(value) {
if (!value) { return; }
// MD5 (Message-Digest Algorithm) by WebToolkit
var md5=function(s){function L(k,d){return(k<<d)|(k>>>(32-d));}function K(G,k){var I,d,F,H,x;F=(G&2147483648);H=(k&2147483648);I=(G&1073741824);d=(k&1073741824);x=(G&1073741823)+(k&1073741823);if(I&d){return(x^2147483648^F^H);}if(I|d){if(x&1073741824){return(x^3221225472^F^H);}else{return(x^1073741824^F^H);}}else{return(x^F^H);}}function r(d,F,k){return(d&F)|((~d)&k);}function q(d,F,k){return(d&k)|(F&(~k));}function p(d,F,k){return(d^F^k);}function n(d,F,k){return(F^(d|(~k)));}function u(G,F,aa,Z,k,H,I){G=K(G,K(K(r(F,aa,Z),k),I));return K(L(G,H),F);}function f(G,F,aa,Z,k,H,I){G=K(G,K(K(q(F,aa,Z),k),I));return K(L(G,H),F);}function D(G,F,aa,Z,k,H,I){G=K(G,K(K(p(F,aa,Z),k),I));return K(L(G,H),F);}function t(G,F,aa,Z,k,H,I){G=K(G,K(K(n(F,aa,Z),k),I));return K(L(G,H),F);}function e(G){var Z;var F=G.length;var x=F+8;var k=(x-(x%64))/64;var I=(k+1)*16;var aa=Array(I-1);var d=0;var H=0;while(H<F){Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=(aa[Z]|(G.charCodeAt(H)<<d));H++;}Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=aa[Z]|(128<<d);aa[I-2]=F<<3;aa[I-1]=F>>>29;return aa;}function B(x){var k="",F="",G,d;for(d=0;d<=3;d++){G=(x>>>(d*8))&255;F="0"+G.toString(16);k=k+F.substr(F.length-2,2);}return k;}function J(k){k=k.replace(/rn/g,"n");var d="";for(var F=0;F<k.length;F++){var x=k.charCodeAt(F);if(x<128){d+=String.fromCharCode(x);}else{if((x>127)&&(x<2048)){d+=String.fromCharCode((x>>6)|192);d+=String.fromCharCode((x&63)|128);}else{d+=String.fromCharCode((x>>12)|224);d+=String.fromCharCode(((x>>6)&63)|128);d+=String.fromCharCode((x&63)|128);}}}return d;}var C=Array();var P,h,E,v,g,Y,X,W,V;var S=7,Q=12,N=17,M=22;var A=5,z=9,y=14,w=20;var o=4,m=11,l=16,j=23;var U=6,T=10,R=15,O=21;s=J(s);C=e(s);Y=1732584193;X=4023233417;W=2562383102;V=271733878;for(P=0;P<C.length;P+=16){h=Y;E=X;v=W;g=V;Y=u(Y,X,W,V,C[P+0],S,3614090360);V=u(V,Y,X,W,C[P+1],Q,3905402710);W=u(W,V,Y,X,C[P+2],N,606105819);X=u(X,W,V,Y,C[P+3],M,3250441966);Y=u(Y,X,W,V,C[P+4],S,4118548399);V=u(V,Y,X,W,C[P+5],Q,1200080426);W=u(W,V,Y,X,C[P+6],N,2821735955);X=u(X,W,V,Y,C[P+7],M,4249261313);Y=u(Y,X,W,V,C[P+8],S,1770035416);V=u(V,Y,X,W,C[P+9],Q,2336552879);W=u(W,V,Y,X,C[P+10],N,4294925233);X=u(X,W,V,Y,C[P+11],M,2304563134);Y=u(Y,X,W,V,C[P+12],S,1804603682);V=u(V,Y,X,W,C[P+13],Q,4254626195);W=u(W,V,Y,X,C[P+14],N,2792965006);X=u(X,W,V,Y,C[P+15],M,1236535329);Y=f(Y,X,W,V,C[P+1],A,4129170786);V=f(V,Y,X,W,C[P+6],z,3225465664);W=f(W,V,Y,X,C[P+11],y,643717713);X=f(X,W,V,Y,C[P+0],w,3921069994);Y=f(Y,X,W,V,C[P+5],A,3593408605);V=f(V,Y,X,W,C[P+10],z,38016083);W=f(W,V,Y,X,C[P+15],y,3634488961);X=f(X,W,V,Y,C[P+4],w,3889429448);Y=f(Y,X,W,V,C[P+9],A,568446438);V=f(V,Y,X,W,C[P+14],z,3275163606);W=f(W,V,Y,X,C[P+3],y,4107603335);X=f(X,W,V,Y,C[P+8],w,1163531501);Y=f(Y,X,W,V,C[P+13],A,2850285829);V=f(V,Y,X,W,C[P+2],z,4243563512);W=f(W,V,Y,X,C[P+7],y,1735328473);X=f(X,W,V,Y,C[P+12],w,2368359562);Y=D(Y,X,W,V,C[P+5],o,4294588738);V=D(V,Y,X,W,C[P+8],m,2272392833);W=D(W,V,Y,X,C[P+11],l,1839030562);X=D(X,W,V,Y,C[P+14],j,4259657740);Y=D(Y,X,W,V,C[P+1],o,2763975236);V=D(V,Y,X,W,C[P+4],m,1272893353);W=D(W,V,Y,X,C[P+7],l,4139469664);X=D(X,W,V,Y,C[P+10],j,3200236656);Y=D(Y,X,W,V,C[P+13],o,681279174);V=D(V,Y,X,W,C[P+0],m,3936430074);W=D(W,V,Y,X,C[P+3],l,3572445317);X=D(X,W,V,Y,C[P+6],j,76029189);Y=D(Y,X,W,V,C[P+9],o,3654602809);V=D(V,Y,X,W,C[P+12],m,3873151461);W=D(W,V,Y,X,C[P+15],l,530742520);X=D(X,W,V,Y,C[P+2],j,3299628645);Y=t(Y,X,W,V,C[P+0],U,4096336452);V=t(V,Y,X,W,C[P+7],T,1126891415);W=t(W,V,Y,X,C[P+14],R,2878612391);X=t(X,W,V,Y,C[P+5],O,4237533241);Y=t(Y,X,W,V,C[P+12],U,1700485571);V=t(V,Y,X,W,C[P+3],T,2399980690);W=t(W,V,Y,X,C[P+10],R,4293915773);X=t(X,W,V,Y,C[P+1],O,2240044497);Y=t(Y,X,W,V,C[P+8],U,1873313359);V=t(V,Y,X,W,C[P+15],T,4264355552);W=t(W,V,Y,X,C[P+6],R,2734768916);X=t(X,W,V,Y,C[P+13],O,1309151649);Y=t(Y,X,W,V,C[P+4],U,4149444226);V=t(V,Y,X,W,C[P+11],T,3174756917);W=t(W,V,Y,X,C[P+2],R,718787259);X=t(X,W,V,Y,C[P+9],O,3951481745);Y=K(Y,h);X=K(X,E);W=K(W,v);V=K(V,g);}var i=B(Y)+B(X)+B(W)+B(V);return i.toLowerCase();};
scope.hash = md5(value.toLowerCase());
scope.size = attrs.size;
if (angular.isUndefined(scope.size)) {
scope.size = 60; // default to 60 pixels
}
});
}
};
}
angular
.module('SOGo.Common')
.directive('sgGravatarImage', sgGravatarImage);
})();

View File

@ -0,0 +1,101 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/*
* sgSearch - Search within a list of items
* @memberof SOGo.Common
* @restrict attribute
* @param {function} sgSearch - the function to call when performing a search.
* Two variables are available: searchField and searchText.
* @example:
<div sg-search="mailbox.$filter({ sort: 'date', asc: false }, [{ searchBy: searchField, searchInput: searchText }])">
<md-input-container>
<input name="search" type="search"/>
</md-input-container>
<md-select class="sg-toolbar-sort md-contrast-light">
<md-option value="subject">Subject</md-option>
<md-option value="sender">sender</md-option>
</md-select>
</div>
*/
sgSearch.$inject = ['$compile'];
function sgSearch($compile) {
return {
restrict: 'A',
controller: 'sgSearchController',
controllerAs: '$sgSearchController',
// See http://stackoverflow.com/questions/19224028/add-directives-from-directive-in-angularjs
// for reasons of using terminal and priority
terminal: true,
priority: 1000,
scope: {
doSearch: '&sgSearch'
},
compile: compile
};
function compile(tElement, tAttr) {
var mdInputEl = tElement.find('md-input-container'),
inputEl = tElement.find('input'),
selectEl = tElement.find('md-select');
inputEl.attr('ng-model', '$sgSearchController.searchText');
inputEl.attr('ng-model-options', '$sgSearchController.searchTextOptions');
if (selectEl) {
selectEl.attr('ng-model', '$sgSearchController.searchField');
selectEl.attr('ng-change', '$sgSearchController.onChange()');
}
return function postLink(scope, iElement, iAttr, controller) {
$compile(mdInputEl)(scope);
if (selectEl)
$compile(selectEl)(scope);
$compile(tElement.find('md-button'))(scope.$parent);
scope.$watch('$sgSearchController.searchText', angular.bind(controller, controller.onChange));
}
}
}
/**
* @ngInject
*/
sgSearchController.$inject = ['$scope', '$element'];
function sgSearchController($scope, $element) {
// Controller variables
this.previous = { searchText: '', searchField: '' };
this.searchText = '';
this.searchField = $element.find('md-option').attr('value'); // defaults to first option
// Model options
this.searchTextOptions = {
updateOn: 'default blur',
debounce: {
default: 300,
blur: 0
}
};
// Method to call on data changes
this.onChange = function(value) {
if (typeof this.searchText != 'undefined') {
if (this.searchText != this.previous.searchText || this.searchField != this.previous.searchField) {
if (this.searchText.length > 2 || this.searchText.length == 0) {
// See https://github.com/angular/angular.js/issues/7635
// for why we need to use $scope here
$scope.doSearch({ searchText: this.searchText, searchField: this.searchField });
}
this.previous = { searchText: this.searchText, searchField: this.searchField };
}
}
};
}
angular
.module('SOGo.Common')
.controller('sgSearchController', sgSearchController)
.directive('sgSearch', sgSearch);
})();

View File

@ -0,0 +1,93 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/*
* sgSubscribe - Common subscription widget
* @restrict class or attribute
* @param {string} sgSubscribe - the folder type
* @param {function} sgSubscribeOnSelect - the function to call when subscribing to a folder.
* One variable is available: folderData.
* @ngInject
* @example:
<md-button sg-subscribe="contact" sg-subscribe-on-select="subscribeToFolder">Subscribe ..</md-button>
*/
sgSubscribe.$inject = ['User'];
function sgSubscribe(User) {
return {
restrict: 'A',
scope: {
folderType: '@sgSubscribe',
onFolderSelect: '&sgSubscribeOnSelect'
},
replace: false,
bindToController: true,
controller: sgSubscribeDialogController,
controllerAs: 'vm',
link: link
}
sgSubscribeDialogController.$inject = ['$mdDialog'];
function sgSubscribeDialogController($mdDialog) {
var vm = this;
vm.showDialog = function() {
$mdDialog.show({
templateUrl: '../Contacts/UIxContactsUserFolders',
clickOutsideToClose: true,
//scope: vm,
//preserveScope: true,
locals: {
folderType: vm.folderType,
onFolderSelect: vm.onFolderSelect
//User: User
},
controller: sgSubscribeController,
controllerAs: 'vm'
});
/**
* @ngInject
*/
sgSubscribeController.$inject = ['folderType', 'onFolderSelect', 'User'];
function sgSubscribeController(folderType, onFolderSelect, User) {
var vm = this;
vm.selectedUser = null;
vm.searchTextOptions = {
updateOn: 'default blur',
debounce: {
default: 300,
blur: 0
}
};
vm.onChange = function() {
User.$filter(vm.searchText).then(function(matches) {
vm.users = matches;
});
};
vm.selectUser = function(i) {
// Fetch folders of specific type for selected user
vm.users[i].$folders(folderType).then(function() {
vm.selectedUser = vm.users[i];
});
};
// Callback upon subscription to a folder
vm.selectFolder = function(folder) {
onFolderSelect({folderData: folder});
};
}
};
}
function link(scope, element, attrs, controller) {
var inputEl = element.find('input');
element.on('click', controller.showDialog);
}
}
angular
.module('SOGo.Common')
.directive('sgSubscribe', sgSubscribe);
})();

View File

@ -4,234 +4,15 @@
(function() {
'use strict';
/**
* @name Dialog
* @constructor
*/
function Dialog() {
}
/**
* @name alert
* @desc Show an alert dialog box with a single "OK" button
* @param {string} title
* @param {string} content
*/
Dialog.alert = function(title, content) {
var alert = this.$modal.alert()
.title(title)
.content(content)
.ok(l('OK'));
this.$modal.show(alert);
};
/**
* @name confirm
* @desc Show a confirmation dialog box with buttons 'Cancel' and 'OK'
* @param {string} title
* @param {string} content
* @returns a promise that resolves if the user has clicked on the 'OK' button
*/
Dialog.confirm = function(title, content) {
var d = this.$q.defer(),
confirm = this.$modal.confirm()
.title(title)
.content(content)
.ok(l('OK'))
.cancel(l('Cancel'));
this.$modal.show(confirm).then(function() {
d.resolve();
}, function() {
d.reject();
});
return d.promise;
};
/**
* @name prompt
* @desc Show a primpt dialog box with a input text field and the 'Cancel' and 'OK' buttons
* @param {string} title
* @param {string} label
* @param {object} [options] - use a different input type by setting 'inputType'
* @returns a promise that resolves with the input field value
*/
Dialog.prompt = function(title, label, options) {
var o = options || {},
d = this.$q.defer();
this.$modal.show({
parent: angular.element(document.body),
clickOutsideToClose: true,
escapeToClose: true,
template: [
'<md-dialog flex="30" flex-sm="100">',
' <md-dialog-content layout="column">',
' <h2 class="md-title" ng-bind="title"></h2>',
' <md-input-container>',
' <label>' + label + '</label>',
' <input type="' + (o.inputType || 'text') + '"',
' aria-label="' + title + '"',
' ng-model="name" required="required"/>',
' </md-input-container>',
' </md-dialog-content>',
' <div class="md-actions">',
' <md-button ng-click="cancel()">',
' ' + l('Cancel'),
' </md-button>',
' <md-button class="md-primary" ng-click="ok()" ng-disabled="!name.length">',
' ' + l('OK'),
' </md-button>',
' </div>',
'</md-dialog>'
].join(''),
controller: PromptDialogController
});
function PromptDialogController(scope, $mdDialog) {
scope.title = title;
scope.name = "";
scope.cancel = function() {
d.reject();
$mdDialog.hide();
}
scope.ok = function() {
d.resolve(scope.name);
$mdDialog.hide();
}
}
return d.promise;
};
/**
* @memberof Dialog
* @desc The factory we'll register as sgDialog in the Angular module SOGo.UIDesktop
*/
Dialog.$factory = ['$q', '$mdDialog', function($q, $mdDialog) {
angular.extend(Dialog, { $q: $q , $modal: $mdDialog });
return Dialog; // return constructor
}];
/* Angular module instanciation */
angular.module('SOGo.UIDesktop', ['ngMaterial', 'RecursionHelper'])
/* Factory registration in Angular module */
.factory('sgDialog', Dialog.$factory)
/**
* sgEnter - A directive evaluated when the enter key is pressed
* @memberof SOGo.UIDesktop
* @example:
<input type="text"
sg-enter="save($index)" />
*/
.directive('sgEnter', function() {
var ENTER_KEY = 13;
return function(scope, element, attrs) {
element.bind("keydown keypress", function(event) {
if (event.which === ENTER_KEY) {
scope.$apply(function() {
scope.$eval(attrs.sgEnter);
});
event.preventDefault();
}
});
};
})
/**
* sgEscape - A directive evaluated when the escape key is pressed
* @memberof SOGo.UIDesktop
* @example:
<input type="text"
sg-escape="revertEditing($index)" />
*/
.directive('sgEscape', function() {
var ESCAPE_KEY = 27;
return function(scope, elem, attrs) {
elem.bind('keydown', function(event) {
if (event.keyCode === ESCAPE_KEY) {
scope.$apply(attrs.sgEscape);
}
});
};
})
/**
* sgFocusOn - A directive that sets the focus on its element when the specified string is broadcasted
* @memberof SOGo.UIDesktop
* @see {@link SOGo.UIDesktop.sgFocus}
* @example:
<input type="text"
sg-focus-on="username" />
*/
.directive('sgFocusOn', function() {
return function(scope, elem, attr) {
scope.$on('sgFocusOn', function(e, name) {
if (name === attr.sgFocusOn) {
elem[0].focus();
elem[0].select();
}
});
};
})
/**
* sgGravatarImage - A simple Gravatar directive (based on http://blog.lingohub.com/2014/08/better-ux-with-angularjs-directives/)
* @memberof SOGo.UIDesktop
* @example:
<sg-gravatar-image email="test@email.com" size="50"></sg-gravatar-image>
*/
.directive('sgGravatarImage', function () {
return {
restrict: 'AE',
replace: true,
required: 'email',
template: '<img ng-src="https://www.gravatar.com/avatar/{{hash}}?s={{size}}&d=wavatar" />',
link: function (scope, element, attrs) {
attrs.$observe('email', function (value) {
if(!value) { return; }
// MD5 (Message-Digest Algorithm) by WebToolkit
var md5=function(s){function L(k,d){return(k<<d)|(k>>>(32-d));}function K(G,k){var I,d,F,H,x;F=(G&2147483648);H=(k&2147483648);I=(G&1073741824);d=(k&1073741824);x=(G&1073741823)+(k&1073741823);if(I&d){return(x^2147483648^F^H);}if(I|d){if(x&1073741824){return(x^3221225472^F^H);}else{return(x^1073741824^F^H);}}else{return(x^F^H);}}function r(d,F,k){return(d&F)|((~d)&k);}function q(d,F,k){return(d&k)|(F&(~k));}function p(d,F,k){return(d^F^k);}function n(d,F,k){return(F^(d|(~k)));}function u(G,F,aa,Z,k,H,I){G=K(G,K(K(r(F,aa,Z),k),I));return K(L(G,H),F);}function f(G,F,aa,Z,k,H,I){G=K(G,K(K(q(F,aa,Z),k),I));return K(L(G,H),F);}function D(G,F,aa,Z,k,H,I){G=K(G,K(K(p(F,aa,Z),k),I));return K(L(G,H),F);}function t(G,F,aa,Z,k,H,I){G=K(G,K(K(n(F,aa,Z),k),I));return K(L(G,H),F);}function e(G){var Z;var F=G.length;var x=F+8;var k=(x-(x%64))/64;var I=(k+1)*16;var aa=Array(I-1);var d=0;var H=0;while(H<F){Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=(aa[Z]|(G.charCodeAt(H)<<d));H++;}Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=aa[Z]|(128<<d);aa[I-2]=F<<3;aa[I-1]=F>>>29;return aa;}function B(x){var k="",F="",G,d;for(d=0;d<=3;d++){G=(x>>>(d*8))&255;F="0"+G.toString(16);k=k+F.substr(F.length-2,2);}return k;}function J(k){k=k.replace(/rn/g,"n");var d="";for(var F=0;F<k.length;F++){var x=k.charCodeAt(F);if(x<128){d+=String.fromCharCode(x);}else{if((x>127)&&(x<2048)){d+=String.fromCharCode((x>>6)|192);d+=String.fromCharCode((x&63)|128);}else{d+=String.fromCharCode((x>>12)|224);d+=String.fromCharCode(((x>>6)&63)|128);d+=String.fromCharCode((x&63)|128);}}}return d;}var C=Array();var P,h,E,v,g,Y,X,W,V;var S=7,Q=12,N=17,M=22;var A=5,z=9,y=14,w=20;var o=4,m=11,l=16,j=23;var U=6,T=10,R=15,O=21;s=J(s);C=e(s);Y=1732584193;X=4023233417;W=2562383102;V=271733878;for(P=0;P<C.length;P+=16){h=Y;E=X;v=W;g=V;Y=u(Y,X,W,V,C[P+0],S,3614090360);V=u(V,Y,X,W,C[P+1],Q,3905402710);W=u(W,V,Y,X,C[P+2],N,606105819);X=u(X,W,V,Y,C[P+3],M,3250441966);Y=u(Y,X,W,V,C[P+4],S,4118548399);V=u(V,Y,X,W,C[P+5],Q,1200080426);W=u(W,V,Y,X,C[P+6],N,2821735955);X=u(X,W,V,Y,C[P+7],M,4249261313);Y=u(Y,X,W,V,C[P+8],S,1770035416);V=u(V,Y,X,W,C[P+9],Q,2336552879);W=u(W,V,Y,X,C[P+10],N,4294925233);X=u(X,W,V,Y,C[P+11],M,2304563134);Y=u(Y,X,W,V,C[P+12],S,1804603682);V=u(V,Y,X,W,C[P+13],Q,4254626195);W=u(W,V,Y,X,C[P+14],N,2792965006);X=u(X,W,V,Y,C[P+15],M,1236535329);Y=f(Y,X,W,V,C[P+1],A,4129170786);V=f(V,Y,X,W,C[P+6],z,3225465664);W=f(W,V,Y,X,C[P+11],y,643717713);X=f(X,W,V,Y,C[P+0],w,3921069994);Y=f(Y,X,W,V,C[P+5],A,3593408605);V=f(V,Y,X,W,C[P+10],z,38016083);W=f(W,V,Y,X,C[P+15],y,3634488961);X=f(X,W,V,Y,C[P+4],w,3889429448);Y=f(Y,X,W,V,C[P+9],A,568446438);V=f(V,Y,X,W,C[P+14],z,3275163606);W=f(W,V,Y,X,C[P+3],y,4107603335);X=f(X,W,V,Y,C[P+8],w,1163531501);Y=f(Y,X,W,V,C[P+13],A,2850285829);V=f(V,Y,X,W,C[P+2],z,4243563512);W=f(W,V,Y,X,C[P+7],y,1735328473);X=f(X,W,V,Y,C[P+12],w,2368359562);Y=D(Y,X,W,V,C[P+5],o,4294588738);V=D(V,Y,X,W,C[P+8],m,2272392833);W=D(W,V,Y,X,C[P+11],l,1839030562);X=D(X,W,V,Y,C[P+14],j,4259657740);Y=D(Y,X,W,V,C[P+1],o,2763975236);V=D(V,Y,X,W,C[P+4],m,1272893353);W=D(W,V,Y,X,C[P+7],l,4139469664);X=D(X,W,V,Y,C[P+10],j,3200236656);Y=D(Y,X,W,V,C[P+13],o,681279174);V=D(V,Y,X,W,C[P+0],m,3936430074);W=D(W,V,Y,X,C[P+3],l,3572445317);X=D(X,W,V,Y,C[P+6],j,76029189);Y=D(Y,X,W,V,C[P+9],o,3654602809);V=D(V,Y,X,W,C[P+12],m,3873151461);W=D(W,V,Y,X,C[P+15],l,530742520);X=D(X,W,V,Y,C[P+2],j,3299628645);Y=t(Y,X,W,V,C[P+0],U,4096336452);V=t(V,Y,X,W,C[P+7],T,1126891415);W=t(W,V,Y,X,C[P+14],R,2878612391);X=t(X,W,V,Y,C[P+5],O,4237533241);Y=t(Y,X,W,V,C[P+12],U,1700485571);V=t(V,Y,X,W,C[P+3],T,2399980690);W=t(W,V,Y,X,C[P+10],R,4293915773);X=t(X,W,V,Y,C[P+1],O,2240044497);Y=t(Y,X,W,V,C[P+8],U,1873313359);V=t(V,Y,X,W,C[P+15],T,4264355552);W=t(W,V,Y,X,C[P+6],R,2734768916);X=t(X,W,V,Y,C[P+13],O,1309151649);Y=t(Y,X,W,V,C[P+4],U,4149444226);V=t(V,Y,X,W,C[P+11],T,3174756917);W=t(W,V,Y,X,C[P+2],R,718787259);X=t(X,W,V,Y,C[P+9],O,3951481745);Y=K(Y,h);X=K(X,E);W=K(W,v);V=K(V,g);}var i=B(Y)+B(X)+B(W)+B(V);return i.toLowerCase();};
scope.hash = md5(value.toLowerCase());
scope.size = attrs.size;
if(angular.isUndefined(scope.size)) {
scope.size = 60; // default to 60 pixels
}
});
}
};
})
/**
* sgFocus - A service to set the focus on the element associated to a specific string
* @memberof SOGo.UIDesktop
* @param {string} name - the string identifier of the element
* @see {@link SOGo.UIDesktop.sgFocusOn}
*/
.factory('sgFocus', ['$rootScope', '$timeout', function($rootScope, $timeout) {
return function(name) {
$timeout(function() {
$rootScope.$broadcast('sgFocusOn', name);
});
}
}])
angular.module('SOGo.Common')
/*
* sgFolderTree - Provides hierarchical folders tree
* @memberof SOGo.UIDesktop
* @memberof SOGo.Common
* @restrict element
* @param {object} sgRoot
* @param {object} sgFolder
* @param {function} sgSetFolder
* @param {function} sgSelectFolder
* @see https://github.com/marklagendijk/angular-recursion
* @example:
@ -248,29 +29,30 @@
folder: '=sgFolder',
selectFolder: '=sgSelectFolder'
},
template:
'<md-list-item>' +
' <md-item-content layout="row" layout-align="start center" flex>' +
' <i class="md-icon-folder"></i>' +
' <button class="md-button md-flex sg-item-name">{{folder.name}}</button>' +
template: [
'<md-list-item>',
' <md-item-content layout="row" layout-align="start center" flex>',
' <i class="md-icon-folder"></i>',
' <button class="md-button md-flex sg-item-name">{{folder.name}}</button>',
' <md-input-container class="md-flex md-tile-content ng-hide">'+
' <input type="text"' +
' ng-model="folder.name"' +
' ng-blur="save()"' +
' sg-enter="save()"' +
' sg-escape="revert()"/>' +
' </md-input-container>' +
' <span class="icon ng-hide" ng-cloak="ng-cloak">' +
' <a class="icon" href="#"' +
' dropdown-toggle="#folderProperties"' +
' options="align:right"><i class="md-icon-more-vert"></i></a>' +
' </span>' +
' </md-item-content>' +
'</md-list-item>' +
'<sg-folder-tree ng-repeat="child in folder.children track by child.path"' +
' data-sg-root="root"' +
' data-sg-folder="child"' +
' data-sg-select-folder="selectFolder"></sg-folder-tree>',
' <input type="text"',
' ng-model="folder.name"',
' ng-blur="save()"',
' sg-enter="save()"',
' sg-escape="revert()"/>',
' </md-input-container>',
' <span class="icon ng-hide" ng-cloak="ng-cloak">',
' <a class="icon" href="#"',
' dropdown-toggle="#folderProperties"',
' options="align:right"><i class="md-icon-more-vert"></i></a>',
' </span>',
' </md-item-content>',
'</md-list-item>',
'<sg-folder-tree ng-repeat="child in folder.children track by child.path"',
' sg-root="root"',
' sg-folder="child"',
' sg-select-folder="selectFolder"></sg-folder-tree>'
].join(''),
compile: function(element) {
return RecursionHelper.compile(element, function(scope, iElement, iAttrs, controller, transcludeFn) {
var level, link, inputContainer, input, edit;
@ -378,7 +160,7 @@
/*
* sgDropdownContentToggle - Provides dropdown content functionality
* @memberof SOGo.UIDesktop
* @memberof SOGo.Common
* @restrict class or attribute
* @see https://github.com/pineconellc/angular-foundation/blob/master/src/dropdownToggle/dropdownToggle.js
* @example:
@ -496,57 +278,7 @@
*/
/*
* sgSubscribe - Common subscription widget
* @restrict class or attribute
* @param {String} sgSubscribe - the folder type
* @param {Function} sgSubscribeOnSelect - the function to call when subscribing to a folder
* @example:
<md-button sg-subscribe="contact" sg-subscribe-on-select="subscribeToFolder">Subscribe ..</md-button>
*/
.directive('sgSubscribe', [function() {
console.debug('registering sgSubscribe');
return {
restrict: 'A',
scope: {
folderType: '@sgSubscribe',
onFolderSelect: '&sgSubscribeOnSelect'
},
replace: false,
bindToController: true,
controllerAs: 'vm',
controller: ['$scope', '$mdDialog', function($scope, $mdDialog) {
var vm = this;
vm.showDialog = function() {
$mdDialog.show({
templateUrl: '../Contacts/UIxContactsUserFolders',
clickOutsideToClose: true,
locals: {
folderType: vm.folderType,
onFolderSelect: vm.onFolderSelect
},
controller: function($scope, folderType, onFolderSelect) {
$scope.selectUser = function(i) {
// Fetch folders of specific type for selected user
$scope.users[i].$folders(folderType).then(function() {
$scope.selectedUser = $scope.users[i];
});
};
$scope.selectFolder = function(folder) {
onFolderSelect({folderData: folder});
};
}
});
};
}],
link: function(scope, element, attrs, controller) {
element.on('click', controller.showDialog);
}
};
}])
/*
* sgUserTypeahead - Typeahead of users, used internally by sgSubscribe
* UserTypeahead - Typeahead of users, used internally by sgSubscribe
* @restrict attribute
* @param {String} sgModel - the folder type
* @param {Function} sgSubscribeOnSelect - the function to call when subscribing to a folder
@ -555,7 +287,7 @@
<div sg-subscribe="contact" sg-subscribe-on-select="subscribeToFolder"></div>
*/
.directive('sgUserTypeahead', ['$parse', '$q', '$timeout', 'sgUser', function($parse, $q, $timeout, User) {
.directive('sgUserTypeahead', ['$parse', '$q', '$timeout', 'User', function($parse, $q, $timeout, User) {
return {
restrict: 'A',
require: 'ngModel',
@ -648,272 +380,4 @@
};
}])
/*
* sgSearch - Search within a list of items
* @memberof SOGo.UIDesktop
* @restrict attribute
* @param {function} sgSearch - the function to call when performing a search.
* Two variables are available: searchField and searchText.
* @example:
<div sg-search="mailbox.$filter({ sort: 'date', asc: false }, [{ searchBy: searchField, searchInput: searchText }])">
<md-input-container>
<input name="search" type="search"/>
</md-input-container>
<md-select class="sg-toolbar-sort md-contrast-light">
<md-option value="subject">Subject</md-option>
<md-option value="sender">sender</md-option>
</md-select>
</div>
*/
.directive('sgSearch', ['$compile', function($compile) {
return {
restrict: 'A',
controller: 'sgSearchController',
controllerAs: '$sgSearchController',
// See http://stackoverflow.com/questions/19224028/add-directives-from-directive-in-angularjs
// for reasons of using terminal and priority
terminal: true,
priority: 1000,
scope: {
doSearch: '&sgSearch'
},
compile: compile
};
function compile(tElement, tAttr) {
var mdInputEl = tElement.find('md-input-container'),
inputEl = tElement.find('input'),
selectEl = tElement.find('md-select');
inputEl.attr('ng-model', '$sgSearchController.searchText');
inputEl.attr('ng-model-options', '$sgSearchController.searchTextOptions');
selectEl.attr('ng-model', '$sgSearchController.searchField');
selectEl.attr('ng-change', '$sgSearchController.onChange()');
return function postLink(scope, iElement, iAttr, controller) {
$compile(mdInputEl)(scope);
$compile(selectEl)(scope);
$compile(tElement.find('md-button'))(scope.$parent);
scope.$watch('$sgSearchController.searchText', angular.bind(controller, controller.onChange));
}
}
}])
.controller('sgSearchController', ['$scope', '$element', function($scope, $element) {
// Controller variables
this.previous = { searchText: '', searchField: '' };
this.searchText = '';
this.searchField = $element.find('md-option').attr('value'); // defaults to first option
// Model options
this.searchTextOptions = {
updateOn: 'default blur',
debounce: {
default: 300,
blur: 0
}
};
// Method to call on data changes
this.onChange = function(value) {
if (typeof this.searchText != 'undefined') {
if (this.searchText != this.previous.searchText || this.searchField != this.previous.searchField) {
if (this.searchText.length > 2 || this.searchText.length == 0) {
// See https://github.com/angular/angular.js/issues/7635
// for why we need to use $scope here
$scope.doSearch({ searchText: this.searchText, searchField: this.searchField });
}
this.previous = { searchText: this.searchText, searchField: this.searchField };
}
}
};
}])
/*
* sgFolderStylesheet - Add CSS stylesheet for folder (addressbook or calendar)
* @memberof SOGo.UIDesktop
* @restrict attribute
* @param {object} ngModel - the object literal describing the folder (an Addressbook or Calendar instance)
* @example:
<div sg-folder-stylesheet="true"
ng-repeat="calendar in calendars.list"
ng-model="calendar" />
</div>
*/
.directive('sgFolderStylesheet', [function() {
return {
restrict: 'A',
require: 'ngModel',
scope: {
ngModel: '='
},
template: [
'<style type="text/css">',
' .bg-folder{{ ngModel.id }} {',
' background-color: {{ ngModel.color }} !important;',
' }',
' .fg-folder{{ ngModel.id }} {',
' color: {{ ngModel.color }} !important;',
' }',
' .checkbox-folder{{ ngModel.id }}.md-checked .md-icon {',
' background-color: {{ ngModel.color }} !important;',
' }',
'</style>'
].join('')
}
}])
/*
* sgCalendarDayTable - Build list of blocks for a specific day
* @memberof SOGo.UIDesktop
* @restrict element
* @param {object} sgBlocks - the events blocks definitions for the current view
* @param {string} sgDay - the day of the events to display
* @example:
<sg-calendar-day-table
sg-blocks="calendar.blocks"
sg-day="20150330" />
*/
.directive('sgCalendarDayTable', [function() {
return {
restrict: 'E',
scope: {
blocks: '=sgBlocks',
day: '@sgDay'
},
template:
'<sg-calendar-day-block class="event draggable"' +
' ng-repeat="block in blocks[day]"' +
' sg-block="block"/>',
};
}])
/*
* sgCalendarDayBlock - An event block to be displayed in a week
* @memberof SOGo.UIDesktop
* @restrict element
* @param {object} sgBlock - the event block definition
* @example:
<sg-calendar-day-block
ng-repeat="block in blocks[day]"
sg-block="block"/>
*/
.directive('sgCalendarDayBlock', [function() {
return {
restrict: 'E',
scope: {
block: '=sgBlock'
},
replace: true,
template: [
'<div class="event draggable">',
' <div class="eventInside">',
' <div class="gradient">',
' </div>',
' <div class="text">{{ block.component.c_title }}',
' <span class="icons">',
' <i ng-if="block.component.c_nextalarm" class="md-icon-alarm"></i>',
' <i ng-if="block.component.c_classification == 1" class="md-icon-visibility-off"></i>',
' <i ng-if="block.component.c_classification == 2" class="md-icon-vpn-key"></i>',
' </span></div>',
' </div>',
' <div class="topDragGrip"></div>',
' <div class="bottomDragGrip"></div>',
'</div>'
].join(''),
link: link
};
function link(scope, iElement, attrs) {
// Compute overlapping (5%)
var pc = 100 / scope.block.siblings,
left = scope.block.position * pc,
right = 100 - (scope.block.position + 1) * pc;
if (pc < 100) {
if (left > 0)
left -= 5;
if (right > 0)
right -= 5;
}
// Set position
iElement.css('left', left + '%');
iElement.css('right', right + '%');
iElement.addClass('starts' + scope.block.start);
iElement.addClass('lasts' + scope.block.length);
iElement.addClass('bg-folder' + scope.block.component.c_folder);
}
}])
/*
* sgCalendarMonthDay - Build list of blocks for a specific day in a month
* @memberof SOGo.UIDesktop
* @restrict element
* @param {object} sgBlocks - the events blocks definitions for the current view
* @param {string} sgDay - the day of the events to display
* @example:
<sg-calendar-monh-day
sg-blocks="calendar.blocks"
sg-day="20150408" />
*/
.directive('sgCalendarMonthDay', [function() {
return {
restrict: 'E',
scope: {
blocks: '=sgBlocks',
day: '@sgDay'
},
replace: true,
template:
'<sg-calendar-month-event' +
' ng-repeat="block in blocks[day]"' +
' sg-block="block"/>',
};
}])
/*
* sgCalendarMonthEvent - An event block to be displayed in a month
* @memberof SOGo.UIDesktop
* @restrict element
* @param {object} sgBlock - the event block definition
* @example:
<sg-calendar-month-event
ng-repeat="block in blocks[day]"
sg-block="block"/>
*/
.directive('sgCalendarMonthEvent', [function() {
return {
restrict: 'E',
scope: {
block: '=sgBlock'
},
replace: true,
template:
'<div class="sg-event">' +
' <span ng-if="!block.component.c_isallday">{{ block.starthour }} - </span>' +
' {{ block.component.c_title }}' +
' <span class="icons">' +
' <i ng-if="block.component.c_nextalarm" class="md-icon-alarm"></i>' +
' <i ng-if="block.component.c_classification == 1" class="md-icon-visibility-off"></i>' +
' <i ng-if="block.component.c_classification == 2" class="md-icon-vpn-key"></i>' +
' </span>' +
' <div class="leftDragGrip"></div>' +
' <div class="rightDragGrip"></div>' +
' </div>' +
'</div>',
link: link
};
function link(scope, iElement, attrs) {
iElement.addClass('bg-folder' + scope.block.component.c_folder);
}
}]);
})();

View File

@ -1,161 +0,0 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/**
* The common SOGo UI, app module
*
* @type {angular.Module}
*/
(function () {
'use strict';
angular.module('SOGo.UI', ['ngMaterial', 'ngAnimate'])
// md break-points values are hard-coded in angular-material/src/core/util/constant.js
// $mdMedia has a built-in support for those values but can also evaluate others
// For some reasons, angular-material's break-points don't match the specs
// Here we define values according to specs
.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(['$mdThemingProvider', function ($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'
});
// Default theme definition
// .primaryColor will soon be deprecated in favor of primaryPalette (already on dev builds https://groups.google.com/forum/m/#!topic/ngmaterial/-sXR8CYBMPg)
$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'
});
}])
.filter('encodeUri', function ($window) {
return $window.encodeURIComponent;
})
.filter('decodeUri', function ($window) {
return $window.decodeURIComponent;
})
.filter('loc', function () {
return l;
})
.controller('navController', ['$scope', '$timeout', '$interval', '$http', '$mdSidenav', '$mdBottomSheet', '$mdMedia', '$log', 'sgConstant', function ($scope, $timeout, $interval, $http, $mdSidenav, $mdBottomSheet, $mdMedia, $log, sgConstant) {
// Show current day in top bar
$scope.currentDay = window.currentDay;
$timeout(function() {
// Update date when day ends
$interval(function() {
$http.get('../date').success(function(data) {
$scope.currentDay = data;
});
}, 24 * 3600 * 1000);
}, window.secondsBeforeTomorrow * 1000);
$scope.toggleLeft = function () {
$mdSidenav('left').toggle()
.then(function () {
$log.debug("toggle left is done");
});
};
$scope.toggleRight = function () {
$mdSidenav('right').toggle()
.then(function () {
$log.debug("toggle RIGHT is done");
});
};
$scope.openBottomSheet = function() {
$mdBottomSheet.show({
parent: angular.element(document.getElementById('left-sidenav')),
templateUrl: 'bottomSheetTemplate.html'
});
};
$scope.toggleDetailView = function() {
var detail = angular.element(document.getElementById('detailView'));
detail.toggleClass('sg-close');
};
$scope.$watch(function() {
return $mdMedia(sgConstant['gt-md']);
},
function(newVal) {
$scope.isGtMedium = newVal;
});
}]);
})();

View File

@ -0,0 +1,112 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* JavaScript for SOGoContacts */
(function() {
'use strict';
angular.module('SOGo.Common', []);
angular.module('SOGo.ContactsUI', ['ngSanitize', 'ui.router', 'vs-repeat', 'SOGo.Common'])
.constant('sgSettings', {
baseURL: ApplicationBaseURL,
activeUser: {
login: UserLogin,
identification: UserIdentification,
language: UserLanguage,
folderURL: UserFolderURL,
isSuperUser: IsSuperUser
}
})
.config(configure);
/**
* @ngInject
*/
configure.$inject = ['$stateProvider', '$urlRouterProvider'];
function configure($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: '/addressbooks',
abstract: true,
views: {
addressbooks: {
templateUrl: 'UIxContactFoldersView', // UI/Templates/Contacts/UIxContactFoldersView.wox
controller: 'AddressBooksController'
}
},
resolve: {
stateAddressbooks: ['AddressBook', function(AddressBook) {
return AddressBook.$findAll(window.contactFolders);
}]
}
})
.state('app.addressbook', {
url: '/:addressbookId',
views: {
addressbook: {
templateUrl: 'addressbook',
controller: 'AddressBookController'
}
},
resolve: {
stateAddressbook: ['$stateParams', 'AddressBook', function($stateParams, AddressBook) {
return AddressBook.$find($stateParams.addressbookId);
}]
}
})
.state('app.addressbook.new', {
url: '/{contactType:(?:card|list)}/new',
views: {
card: {
templateUrl: 'UIxContactEditorTemplate', // UI/Templates/Contacts/UIxContactEditorTemplate.wox
controller: 'CardController'
}
},
resolve: {
stateCard: ['$stateParams', 'stateAddressbook', 'Card', function($stateParams, stateAddressbook, Card) {
var tag = 'v' + $stateParams.contactType,
card = new Card({ pid: $stateParams.addressbookId, tag: tag });
return card;
}]
}
})
.state('app.addressbook.card', {
url: '/:cardId',
abstract: true,
views: {
card: {
template: '<ui-view/>'
}
},
resolve: {
stateCard: ['$stateParams', 'stateAddressbook', function($stateParams, stateAddressbook) {
return stateAddressbook.$getCard($stateParams.cardId);
}]
}
})
.state('app.addressbook.card.view', {
url: '/view',
views: {
'card@app.addressbook': {
templateUrl: 'UIxContactViewTemplate', // UI/Templates/Contacts/UIxContactViewTemplate.wox
controller: 'CardController'
}
}
})
.state('app.addressbook.card.editor', {
url: '/edit',
views: {
'card@app.addressbook': {
templateUrl: 'UIxContactEditorTemplate', // UI/Templates/Contacts/UIxContactEditorTemplate.wox
controller: 'CardController'
}
}
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/addressbooks/personal');
}
})();

View File

@ -32,7 +32,7 @@
* @desc The factory we'll use to register with Angular
* @returns the AddressBook constructor
*/
AddressBook.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'sgResource', 'sgCard', 'sgAcl', function($q, $timeout, $log, Settings, Resource, Card, Acl) {
AddressBook.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'Resource', 'Card', 'Acl', function($q, $timeout, $log, Settings, Resource, Card, Acl) {
angular.extend(AddressBook, {
$q: $q,
$timeout: $timeout,
@ -48,7 +48,7 @@
/* Factory registration in Angular module */
angular.module('SOGo.ContactsUI')
.factory('sgAddressBook', AddressBook.$factory);
.factory('AddressBook', AddressBook.$factory);
/**
* @memberof AddressBook

View File

@ -0,0 +1,56 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* @ngInject
*/
AddressBookController.$inject = ['$state', '$scope', '$rootScope', '$stateParams', '$timeout', '$mdDialog', 'sgFocus', 'Card', 'AddressBook', 'Dialog', 'sgSettings', 'stateAddressbooks', 'stateAddressbook'];
function AddressBookController($state, $scope, $rootScope, $stateParams, $timeout, $mdDialog, focus, Card, AddressBook, Dialog, Settings, stateAddressbooks, stateAddressbook) {
var currentAddressbook;
$rootScope.currentFolder = stateAddressbook;
$scope.newComponent = function(ev) {
$mdDialog.show({
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose: true,
escapeToClose: true,
template: [
'<md-dialog aria-label="Create component">',
' <md-content>',
' <div layout="column">',
' <md-button ng-click="createContact()">',
' ' + l('Contact'),
' </md-button>',
' <md-button ng-click="createList()">',
' ' + l('List'),
' </md-button>',
' </div>',
' </md-content>',
'</md-dialog>'
].join(''),
locals: {
state: $state
},
controller: ComponentDialogController
});
function ComponentDialogController(scope, $mdDialog, state) {
scope.createContact = function() {
state.go('app.addressbook.new', { addressbookId: $scope.currentFolder.id, contactType: 'card' });
$mdDialog.hide();
}
scope.createList = function() {
state.go('app.addressbook.new', { addressbookId: $scope.currentFolder.id, contactType: 'list' });
$mdDialog.hide();
}
}
};
}
angular
.module('SOGo.ContactsUI')
.controller('AddressBookController', AddressBookController);
})();

View File

@ -0,0 +1,183 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* @ngInject
*/
AddressBooksController.$inject = ['$state', '$scope', '$rootScope', '$stateParams', '$timeout', '$q', '$mdDialog', 'sgFocus', 'Card', 'AddressBook', 'Dialog', 'sgSettings', 'User', 'stateAddressbooks'];
function AddressBooksController($state, $scope, $rootScope, $stateParams, $timeout, $q, $mdDialog, focus, Card, AddressBook, Dialog, Settings, User, stateAddressbooks) {
var currentAddressbook;
$scope.activeUser = Settings.activeUser;
$scope.service = AddressBook;
// $scope functions
$scope.select = function(folder) {
$scope.editMode = false;
$state.go('app.addressbook', {addressbookId: folder.id});
};
$scope.newAddressbook = function() {
Dialog.prompt(l('New addressbook'),
l('Name of new addressbook'))
.then(function(name) {
var addressbook = new AddressBook(
{
name: name,
isEditable: true,
isRemote: false,
owner: UserLogin
}
);
AddressBook.$add(addressbook);
});
};
$scope.edit = function(index, folder) {
if (!folder.isRemote) {
$scope.editMode = folder.id;
$scope.originalAddressbook = angular.extend({}, folder.$omit());
focus('addressBookName_' + folder.id);
}
};
$scope.revertEditing = function(folder) {
folder.name = $scope.originalAddressbook.name;
$scope.editMode = false;
};
$scope.save = function(folder) {
var name = folder.name;
if (name && name.length > 0 && name != $scope.originalAddressbook.name) {
folder.$rename(name)
.then(function(data) {
$scope.editMode = false;
}, function(data, status) {
Dialog.alert(l('Warning'), data);
});
}
};
$scope.confirmDelete = function() {
if ($scope.currentFolder.isSubscription) {
// Unsubscribe without confirmation
$rootScope.currentFolder.$delete()
.then(function() {
$rootScope.currentFolder = null;
$state.go('app.addressbook', { addressbookId: 'personal' });
}, function(data, status) {
Dialog.alert(l('An error occured while deleting the addressbook "%{0}".',
$rootScope.currentFolder.name),
l(data.error));
});
}
else {
Dialog.confirm(l('Warning'), l('Are you sure you want to delete the addressbook <em>%{0}</em>?',
$scope.currentFolder.name))
.then(function() {
$rootScope.currentFolder.$delete()
.then(function() {
$rootScope.currentFolder = null;
}, function(data, status) {
Dialog.alert(l('An error occured while deleting the addressbook "%{0}".',
$rootScope.currentFolder.name),
l(data.error));
});
});
}
};
$scope.importCards = function() {
};
$scope.exportCards = function() {
window.location.href = ApplicationBaseURL + '/' + $scope.currentFolder.id + '/exportFolder';
};
$scope.share = function(folder) {
if (folder.id != $scope.currentFolder.id) {
// Counter the possibility to click on the "hidden" secondary button
$scope.select(folder);
return;
}
$mdDialog.show({
templateUrl: $scope.currentFolder.id + '/UIxAclEditor', // UI/Templates/UIxAclEditor.wox
controller: AddressBookACLController,
clickOutsideToClose: true,
escapeToClose: true,
locals: {
usersWithACL: $scope.currentFolder.$acl.$users(),
User: User,
stateAddressbook: $scope.currentFolder,
q: $q
}
});
function AddressBookACLController($scope, $mdDialog, usersWithACL, User, stateAddressbook, q) {
$scope.users = usersWithACL; // ACL users
$scope.stateAddressbook = stateAddressbook;
$scope.userToAdd = '';
$scope.searchText = '';
$scope.userFilter = function($query) {
var deferred = q.defer();
User.$filter($query).then(function(results) {
deferred.resolve(results)
});
return deferred.promise;
};
$scope.closeModal = function() {
stateAddressbook.$acl.$resetUsersRights(); // cancel changes
$mdDialog.hide();
};
$scope.saveModal = function() {
stateAddressbook.$acl.$saveUsersRights().then(function() {
$mdDialog.hide();
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'));
});
};
$scope.confirmChange = function(user) {
var confirmation = user.$confirmRights();
if (confirmation) {
Dialog.confirm(l('Warning'), confirmation).then(function(res) {
if (!res)
user.$resetRights(true);
});
}
};
$scope.removeUser = function(user) {
stateAddressbook.$acl.$removeUser(user.uid).then(function() {
if (user.uid == $scope.selectedUser.uid) {
$scope.selectedUser = null;
}
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
};
$scope.addUser = function(data) {
stateAddressbook.$acl.$addUser(data).then(function() {
$scope.userToAdd = '';
$scope.searchText = '';
}, function(error) {
Dialog.alert(l('Warning'), error);
});
};
$scope.selectUser = function(user) {
// Check if it is a different user
if ($scope.selectedUser != user) {
$scope.selectedUser = user;
$scope.selectedUser.$rights();
}
};
};
};
/**
* subscribeToFolder - Callback of sgSubscribe directive
*/
$scope.subscribeToFolder = function(addressbookData) {
console.debug('subscribeToFolder ' + addressbookData.owner + addressbookData.name);
AddressBook.$subscribe(addressbookData.owner, addressbookData.name).catch(function(data) {
Dialog.alert(l('Warning'), l('An error occured please try again.'));
});
};
}
angular
.module('SOGo.ContactsUI')
.controller('AddressBooksController', AddressBooksController);
})();

View File

@ -25,9 +25,6 @@
if (!this.shortFormat)
this.shortFormat = this.$shortFormat();
// FIXME
this.image = "http://www.gravatar.com/avatar/asdasdasdasd?d=identicon";
}
else {
// The promise will be unwrapped first
@ -45,7 +42,7 @@
* @desc The factory we'll use to register with Angular.
* @returns the Card constructor
*/
Card.$factory = ['$timeout', 'sgSettings', 'sgResource', function($timeout, Settings, Resource) {
Card.$factory = ['$timeout', 'sgSettings', 'Resource', function($timeout, Settings, Resource) {
angular.extend(Card, {
$$resource: new Resource(Settings.activeUser.folderURL + 'Contacts', Settings.activeUser),
$timeout: $timeout,
@ -60,34 +57,7 @@
* @desc Factory registration of Card in Angular module.
*/
angular.module('SOGo.ContactsUI')
.factory('sgCard', Card.$factory)
/**
* @name sgAddress
* @memberof ContactsUI
* @desc Directive to format a postal address.
*/
.directive('sgAddress', function() {
return {
restrict: 'A',
scope: { data: '=sgAddress' },
controller: ['$scope', function($scope) {
$scope.addressLines = function(data) {
var lines = [],
locality_region = [];
if (data.street) lines.push(data.street);
if (data.street2) lines.push(data.street2);
if (data.locality) locality_region.push(data.locality);
if (data.region) locality_region.push(data.region);
if (locality_region.length > 0) lines.push(locality_region.join(', '));
if (data.country) lines.push(data.country);
if (data.postalcode) lines.push(data.postalcode);
return lines.join('<br>');
};
}],
template: '<address ng-bind-html="addressLines(data)"></address>'
}
});
.factory('Card', Card.$factory)
/**
* @memberof Card

View File

@ -0,0 +1,103 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* Controller to view and edit a card
* @ngInject
*/
CardController.$inject = ['$scope', '$rootScope', '$timeout', 'AddressBook', 'Card', 'Dialog', 'sgFocus', '$state', '$stateParams', 'stateCard'];
function CardController($scope, $rootScope, $timeout, AddressBook, Card, Dialog, focus, $state, $stateParams, stateCard) {
$rootScope.card = stateCard;
$scope.allEmailTypes = Card.$EMAIL_TYPES;
$scope.allTelTypes = Card.$TEL_TYPES;
$scope.allUrlTypes = Card.$URL_TYPES;
$scope.allAddressTypes = Card.$ADDRESS_TYPES;
$scope.categories = {};
$scope.addOrgUnit = function() {
var i = $scope.card.$addOrgUnit('');
focus('orgUnit_' + i);
};
$scope.addEmail = function() {
var i = $scope.card.$addEmail('');
focus('email_' + i);
};
$scope.addPhone = function() {
var i = $scope.card.$addPhone('');
focus('phone_' + i);
};
$scope.addUrl = function() {
var i = $scope.card.$addUrl('', '');
focus('url_' + i);
};
$scope.addAddress = function() {
var i = $scope.card.$addAddress('', '', '', '', '', '', '', '');
focus('address_' + i);
};
$scope.addMember = function() {
var i = $scope.card.$addMember('');
focus('ref_' + i);
};
$scope.save = function(form) {
if (form.$valid) {
$scope.card.$save()
.then(function(data) {
var i = _.indexOf(_.pluck($scope.currentFolder.cards, 'id'), $scope.card.id);
if (i < 0) {
// New card; reload contacts list and show addressbook in which the card has been created
$rootScope.currentFolder = AddressBook.$find(data.pid);
}
else {
// Update contacts list with new version of the Card object
$rootScope.currentFolder.cards[i] = angular.copy($scope.card);
}
$state.go('app.addressbook.card.view', { cardId: $scope.card.id });
}, function(data, status) {
console.debug('failed');
});
}
};
$scope.reset = function() {
$scope.card.$reset();
};
$scope.cancel = function() {
$scope.card.$reset();
if ($scope.card.isNew) {
// Cancelling the creation of a card
$scope.card = null;
$state.go('app.addressbook', { addressbookId: $scope.currentFolder.id });
}
else {
// Cancelling the edition of an existing card
$state.go('app.addressbook.card.view', { cardId: $scope.card.id });
}
};
$scope.confirmDelete = function(card) {
Dialog.confirm(l('Warning'),
l('Are you sure you want to delete the card of %{0}?', card.$fullname()))
.then(function() {
// User confirmed the deletion
card.$delete()
.then(function() {
// Remove card from list of addressbook
$rootScope.currentFolder.cards = _.reject($rootScope.currentFolder.cards, function(o) {
return o.id == card.id;
});
// Remove card object from scope
$scope.card = null;
$state.go('app.addressbook', { addressbookId: $scope.currentFolder.id });
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured while deleting the card "%{0}".',
card.$fullname()));
});
});
};
}
angular
.module('SOGo.ContactsUI')
.controller('CardController', CardController);
})();

View File

@ -0,0 +1,37 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* @name sgAddress
* @memberof ContactsUI
* @desc Directive to format a postal address.
* @ngInject
*/
function sgAddress() {
return {
restrict: 'A',
scope: { data: '=sgAddress' },
controller: ['$scope', function($scope) {
$scope.addressLines = function(data) {
var lines = [],
locality_region = [];
if (data.street) lines.push(data.street);
if (data.street2) lines.push(data.street2);
if (data.locality) locality_region.push(data.locality);
if (data.region) locality_region.push(data.region);
if (locality_region.length > 0) lines.push(locality_region.join(', '));
if (data.country) lines.push(data.country);
if (data.postalcode) lines.push(data.postalcode);
return lines.join('<br>');
};
}],
template: '<address ng-bind-html="addressLines(data)"></address>'
}
}
angular
.module('SOGo.Common')
.directive('sgAddress', sgAddress);
})();

View File

@ -1,410 +0,0 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* JavaScript for SOGoContacts */
(function() {
'use strict';
angular.module('SOGo.Common', []);
angular.module('SOGo.ContactsUI', ['ngSanitize', 'ui.router', 'vs-repeat', 'SOGo.Common', 'SOGo.UI', 'SOGo.UIDesktop'])
.constant('sgSettings', {
baseURL: ApplicationBaseURL,
activeUser: {
login: UserLogin,
identification: UserIdentification,
language: UserLanguage,
folderURL: UserFolderURL,
isSuperUser: IsSuperUser
}
})
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: '/addressbooks',
abstract: true,
views: {
addressbooks: {
templateUrl: 'UIxContactFoldersView', // UI/Templates/Contacts/UIxContactFoldersView.wox
controller: 'AddressBooksCtrl'
}
},
resolve: {
stateAddressbooks: ['sgAddressBook', function(AddressBook) {
return AddressBook.$findAll(window.contactFolders);
}]
}
})
.state('app.addressbook', {
url: '/:addressbookId',
views: {
addressbook: {
templateUrl: 'addressbook',
controller: 'AddressBookCtrl'
}
},
resolve: {
stateAddressbook: ['$stateParams', 'sgAddressBook', function($stateParams, AddressBook) {
return AddressBook.$find($stateParams.addressbookId);
}]
}
})
.state('app.addressbook.new', {
url: '/:contactType/new',
views: {
card: {
templateUrl: 'UIxContactEditorTemplate', // UI/Templates/Contacts/UIxContactEditorTemplate.wox
controller: 'CardCtrl'
}
},
resolve: {
stateCard: ['$stateParams', 'stateAddressbook', 'sgCard', function($stateParams, stateAddressbook, Card) {
var tag = 'v' + $stateParams.contactType,
card = new Card({ pid: $stateParams.addressbookId, tag: tag });
return card;
}]
}
})
.state('app.addressbook.card', {
url: '/:cardId',
abstract: true,
views: {
card: {
template: '<ui-view/>'
}
},
resolve: {
stateCard: ['$stateParams', 'stateAddressbook', function($stateParams, stateAddressbook) {
return stateAddressbook.$getCard($stateParams.cardId);
}]
}
})
.state('app.addressbook.card.view', {
url: '/view',
views: {
'card@app.addressbook': {
templateUrl: 'UIxContactViewTemplate', // UI/Templates/Contacts/UIxContactViewTemplate.wox
controller: 'CardCtrl'
}
}
})
.state('app.addressbook.card.editor', {
url: '/edit',
views: {
'card@app.addressbook': {
templateUrl: 'UIxContactEditorTemplate', // UI/Templates/Contacts/UIxContactEditorTemplate.wox
controller: 'CardCtrl'
}
}
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/addressbooks/personal');
}])
.controller('AddressBooksCtrl', ['$state', '$scope', '$rootScope', '$stateParams', '$timeout', '$q', '$mdDialog', 'sgFocus', 'sgCard', 'sgAddressBook', 'sgDialog', 'sgSettings', 'sgUser', 'stateAddressbooks', function($state, $scope, $rootScope, $stateParams, $timeout, $q, $mdDialog, focus, Card, AddressBook, Dialog, Settings, User, stateAddressbooks) {
var currentAddressbook;
$scope.activeUser = Settings.activeUser;
$scope.service = AddressBook;
// $scope functions
$scope.select = function(folder) {
$scope.editMode = false;
$rootScope.card = null;
$state.go('app.addressbook', {addressbookId: folder.id});
};
$scope.newAddressbook = function() {
Dialog.prompt(l('New addressbook'),
l('Name of new addressbook'))
.then(function(name) {
var addressbook = new AddressBook(
{
name: name,
isEditable: true,
isRemote: false,
owner: UserLogin
}
);
AddressBook.$add(addressbook);
});
};
$scope.edit = function(index, folder) {
if (!folder.isRemote) {
$scope.editMode = folder.id;
$scope.originalAddressbook = angular.extend({}, folder.$omit());
focus('addressBookName_' + folder.id);
}
};
$scope.revertEditing = function(folder) {
folder.name = $scope.originalAddressbook.name;
$scope.editMode = false;
};
$scope.save = function(folder) {
var name = folder.name;
if (name && name.length > 0 && name != $scope.originalAddressbook.name) {
folder.$rename(name)
.then(function(data) {
$scope.editMode = false;
}, function(data, status) {
Dialog.alert(l('Warning'), data);
});
}
};
$scope.confirmDelete = function() {
if ($scope.currentFolder.isSubscription) {
// Unsubscribe without confirmation
$rootScope.currentFolder.$delete()
.then(function() {
$rootScope.currentFolder = null;
$state.go('app.addressbook', { addressbookId: 'personal' });
}, function(data, status) {
Dialog.alert(l('An error occured while deleting the addressbook "%{0}".',
$rootScope.currentFolder.name),
l(data.error));
});
}
else {
Dialog.confirm(l('Warning'), l('Are you sure you want to delete the addressbook <em>%{0}</em>?',
$scope.currentFolder.name))
.then(function() {
$rootScope.currentFolder.$delete()
.then(function() {
$rootScope.currentFolder = null;
}, function(data, status) {
Dialog.alert(l('An error occured while deleting the addressbook "%{0}".',
$rootScope.currentFolder.name),
l(data.error));
});
});
}
};
$scope.importCards = function() {
};
$scope.exportCards = function() {
window.location.href = ApplicationBaseURL + '/' + $scope.currentFolder.id + '/exportFolder';
};
$scope.share = function(folder) {
if (folder.id != $scope.currentFolder.id) {
// Counter the possibility to click on the "hidden" secondary button
$scope.select(folder);
return;
}
$mdDialog.show({
templateUrl: $scope.currentFolder.id + '/UIxAclEditor', // UI/Templates/UIxAclEditor.wox
controller: AddressBookACLController,
clickOutsideToClose: true,
escapeToClose: true,
locals: {
usersWithACL: $scope.currentFolder.$acl.$users(),
User: User,
stateAddressbook: $scope.currentFolder,
q: $q
}
});
function AddressBookACLController($scope, $mdDialog, usersWithACL, User, stateAddressbook, q) {
$scope.users = usersWithACL; // ACL users
$scope.stateAddressbook = stateAddressbook;
$scope.userToAdd = '';
$scope.searchText = '';
$scope.userFilter = function($query) {
var deferred = q.defer();
User.$filter($query).then(function(results) {
deferred.resolve(results)
});
return deferred.promise;
};
$scope.closeModal = function() {
stateAddressbook.$acl.$resetUsersRights(); // cancel changes
$mdDialog.hide();
};
$scope.saveModal = function() {
stateAddressbook.$acl.$saveUsersRights().then(function() {
$mdDialog.hide();
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'));
});
};
$scope.confirmChange = function(user) {
var confirmation = user.$confirmRights();
if (confirmation) {
Dialog.confirm(l('Warning'), confirmation).then(function(res) {
if (!res)
user.$resetRights(true);
});
}
};
$scope.removeUser = function(user) {
stateAddressbook.$acl.$removeUser(user.uid).then(function() {
if (user.uid == $scope.selectedUser.uid) {
$scope.selectedUser = null;
}
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
};
$scope.addUser = function(data) {
stateAddressbook.$acl.$addUser(data).then(function() {
$scope.userToAdd = '';
$scope.searchText = '';
}, function(error) {
Dialog.alert(l('Warning'), error);
});
};
$scope.selectUser = function(user) {
// Check if it is a different user
if ($scope.selectedUser != user) {
$scope.selectedUser = user;
$scope.selectedUser.$rights();
}
};
};
};
/**
* subscribeToFolder - Callback of sgSubscribe directive
*/
$scope.subscribeToFolder = function(addressbookData) {
console.debug('subscribeToFolder ' + addressbookData.owner + addressbookData.name);
AddressBook.$subscribe(addressbookData.owner, addressbookData.name).catch(function(data) {
Dialog.alert(l('Warning'), l('An error occured please try again.'));
});
};
}])
.controller('AddressBookCtrl', ['$state', '$scope', '$rootScope', '$stateParams', '$timeout', '$mdDialog', 'sgFocus', 'sgCard', 'sgAddressBook', 'sgDialog', 'sgSettings', 'stateAddressbooks', 'stateAddressbook', function($state, $scope, $rootScope, $stateParams, $timeout, $mdDialog, focus, Card, AddressBook, Dialog, Settings, stateAddressbooks, stateAddressbook) {
var currentAddressbook;
$rootScope.currentFolder = stateAddressbook;
$scope.newComponent = function(ev) {
$mdDialog.show({
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose: true,
escapeToClose: true,
template:
'<md-dialog aria-label="Create component">' +
' <md-content>' +
' <div layout="column">' +
' <md-button ng-click="createContact()">' +
' Contact' +
' </md-button>' +
' <md-button ng-click="createList()">' +
' List' +
' </md-button>' +
' </div>' +
' </md-content>' +
'</md-dialog>',
locals: {
state: $state
},
controller: ComponentDialogController
});
function ComponentDialogController(scope, $mdDialog, state) {
scope.createContact = function() {
state.go('app.addressbook.new', { addressbookId: $scope.currentFolder.id, contactType: 'card' });
$mdDialog.hide();
}
scope.createList = function() {
state.go('app.addressbook.new', { addressbookId: $scope.currentFolder.id, contactType: 'list' });
$mdDialog.hide();
}
}
};
}])
/**
* Controller to view and edit a card
*/
.controller('CardCtrl', ['$scope', '$rootScope', '$timeout', 'sgAddressBook', 'sgCard', 'sgDialog', 'sgFocus', '$state', '$stateParams', 'stateCard', function($scope, $rootScope, $timeout, AddressBook, Card, Dialog, focus, $state, $stateParams, stateCard) {
$rootScope.card = stateCard;
$scope.allEmailTypes = Card.$EMAIL_TYPES;
$scope.allTelTypes = Card.$TEL_TYPES;
$scope.allUrlTypes = Card.$URL_TYPES;
$scope.allAddressTypes = Card.$ADDRESS_TYPES;
$scope.categories = {};
$scope.addOrgUnit = function() {
var i = $scope.card.$addOrgUnit('');
focus('orgUnit_' + i);
};
$scope.addEmail = function() {
var i = $scope.card.$addEmail('');
focus('email_' + i);
};
$scope.addPhone = function() {
var i = $scope.card.$addPhone('');
focus('phone_' + i);
};
$scope.addUrl = function() {
var i = $scope.card.$addUrl('', '');
focus('url_' + i);
};
$scope.addAddress = function() {
var i = $scope.card.$addAddress('', '', '', '', '', '', '', '');
focus('address_' + i);
};
$scope.addMember = function() {
var i = $scope.card.$addMember('');
focus('ref_' + i);
};
$scope.save = function(form) {
if (form.$valid) {
$scope.card.$save()
.then(function(data) {
var i = _.indexOf(_.pluck($scope.currentFolder.cards, 'id'), $scope.card.id);
if (i < 0) {
// New card; reload contacts list and show addressbook in which the card has been created
$rootScope.currentFolder = AddressBook.$find(data.pid);
}
else {
// Update contacts list with new version of the Card object
$rootScope.currentFolder.cards[i] = angular.copy($scope.card);
}
$state.go('app.addressbook.card.view', { cardId: $scope.card.id });
}, function(data, status) {
console.debug('failed');
});
}
};
$scope.reset = function() {
$scope.card.$reset();
};
$scope.cancel = function() {
$scope.card.$reset();
if ($scope.card.isNew) {
// Cancelling the creation of a card
$scope.card = null;
$state.go('app.addressbook', { addressbookId: $scope.currentFolder.id });
}
else {
// Cancelling the edition of an existing card
$state.go('app.addressbook.card.view', { cardId: $scope.card.id });
}
};
$scope.confirmDelete = function(card) {
Dialog.confirm(l('Warning'),
l('Are you sure you want to delete the card of %{0}?', card.$fullname()))
.then(function() {
// User confirmed the deletion
card.$delete()
.then(function() {
// Remove card from list of addressbook
$rootScope.currentFolder.cards = _.reject($rootScope.currentFolder.cards, function(o) {
return o.id == card.id;
});
// Remove card object from scope
$scope.card = null;
$state.go('app.addressbook', { addressbookId: $scope.currentFolder.id });
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured while deleting the card "%{0}".',
card.$fullname()));
});
});
};
}]);
})();

View File

@ -0,0 +1,185 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* JavaScript for SOGo.MailerUI module */
(function() {
'use strict';
angular.module('SOGo.ContactsUI', []);
angular.module('SOGo.MailerUI', ['ngSanitize', 'ui.router', 'vs-repeat', 'ck', 'angularFileUpload', 'SOGo.Common', 'SOGo.ContactsUI', 'ngAnimate'])
.constant('sgSettings', {
baseURL: ApplicationBaseURL,
activeUser: {
login: UserLogin,
identification: UserIdentification,
language: UserLanguage,
folderURL: UserFolderURL,
isSuperUser: IsSuperUser
}
})
.config(configure)
.run(runBlock);
/**
* @ngInject
*/
configure.$inject = ['$stateProvider', '$urlRouterProvider'];
function configure($stateProvider, $urlRouterProvider) {
$stateProvider
.state('mail', {
url: '/Mail',
views: {
mailboxes: {
templateUrl: 'UIxMailMainFrame', // UI/Templates/MailerUI/UIxMailMainFrame.wox
controller: 'MailboxesController'
}
},
resolve: {
stateAccounts: ['$q', 'Account', function($q, Account) {
var accounts = Account.$findAll(mailAccounts);
var promises = [];
// Fetch list of mailboxes for each account
angular.forEach(accounts, function(account, i) {
var mailboxes = account.$getMailboxes();
promises.push(mailboxes.then(function(objects) {
return account;
}));
});
return $q.all(promises);
}]
}
})
.state('mail.account', {
url: '/:accountId',
abstract: true,
views: {
mailbox: {
template: '<ui-view/>',
}
},
resolve: {
stateAccount: ['$stateParams', 'stateAccounts', function($stateParams, stateAccounts) {
return _.find(stateAccounts, function(account) {
return account.id == $stateParams.accountId;
});
}]
}
})
.state('mail.account.mailbox', {
url: '/:mailboxId',
views: {
'mailbox@mail': {
templateUrl: 'UIxMailFolderTemplate', // UI/Templates/MailerUI/UIxMailFolderTemplate.wox
controller: 'MailboxController'
}
},
resolve: {
stateMailbox: ['$stateParams', 'stateAccount', 'decodeUriFilter', function($stateParams, stateAccount, decodeUriFilter) {
var mailboxId = decodeUriFilter($stateParams.mailboxId);
// Recursive find function
var _find = function(mailboxes) {
var mailbox = _.find(mailboxes, function(o) {
return o.path == mailboxId;
});
if (!mailbox) {
angular.forEach(mailboxes, function(o) {
if (!mailbox && o.children && o.children.length > 0) {
mailbox = _find(o.children);
}
});
}
return mailbox;
};
return _find(stateAccount.$mailboxes);
}],
stateMessages: ['stateMailbox', function(stateMailbox) {
return stateMailbox.$filter();
}]
}
})
.state('mail.account.mailbox.message', {
url: '/:messageId',
views: {
message: {
templateUrl: 'UIxMailViewTemplate', // UI/Templates/MailerUI/UIxMailViewTemplate.wox
controller: 'MessageController'
}
},
resolve: {
stateMessage: ['encodeUriFilter', '$stateParams', '$state', 'stateMailbox', 'stateMessages', function(encodeUriFilter, $stateParams, $state, stateMailbox, stateMessages) {
var message = _.find(stateMessages, function(messageObject) {
return messageObject.uid == $stateParams.messageId;
});
if (message)
return message.$reload();
else
// Message not found
$state.go('mail.account.mailbox', { accountId: stateMailbox.$account.id, mailboxId: encodeUriFilter(stateMailbox.path) });
}]
}
})
.state('mail.account.mailbox.message.edit', {
url: '/edit',
views: {
'mailbox@mail': {
templateUrl: 'UIxMailEditor', // UI/Templates/MailerUI/UIxMailEditor.wox
controller: 'MessageEditorController'
}
},
resolve: {
stateContent: ['stateMessage', function(stateMessage) {
return stateMessage.$editableContent();
}]
}
})
.state('mail.account.mailbox.message.action', {
url: '/{actionName:(?:reply|replyall|forward)}',
views: {
'mailbox@mail': {
templateUrl: 'UIxMailEditor', // UI/Templates/MailerUI/UIxMailEditor.wox
controller: 'MessageEditorController'
}
}
})
.state('mail.newMessage', {
url: '/new',
views: {
mailbox: {
templateUrl: 'UIxMailEditor', // UI/Templates/MailerUI/UIxMailEditor.wox
controller: 'MessageEditorController'
}
},
resolve: {
stateMessage: ['stateAccounts', function(stateAccounts) {
if (stateAccounts.length > 0) {
return stateAccounts[0].$newMessage();
}
}]
}
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/Mail');
// Set default configuration for tags input
// tagsInputConfigProvider.setDefaults('tagsInput', {
// addOnComma: false,
// replaceSpacesWithDashes: false,
// allowedTagsPattern: /([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)/i
// });
}
/**
* @ngInject
*/
runBlock.$inject = ['$rootScope'];
function runBlock($rootScope) {
$rootScope.$on('$routeChangeError', function(event, current, previous, rejection) {
console.error(event, current, previous, rejection)
})
}
})();

View File

@ -31,7 +31,7 @@
* @desc The factory we'll use to register with Angular
* @returns the Account constructor
*/
Account.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'sgResource', 'sgMailbox', 'sgMessage', function($q, $timeout, $log, Settings, Resource, Mailbox, Message) {
Account.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'Resource', 'Mailbox', 'Message', function($q, $timeout, $log, Settings, Resource, Mailbox, Message) {
angular.extend(Account, {
$q: $q,
$timeout: $timeout,
@ -46,7 +46,7 @@
/* Factory registration in Angular module */
angular.module('SOGo.MailerUI')
.factory('sgAccount', Account.$factory);
.factory('Account', Account.$factory);
/**
* @memberof Account
@ -70,7 +70,7 @@
* @function $getMailboxes
* @memberof Account.prototype
* @desc Fetch the list of mailboxes for the current account.
* @param {object} [options] - force a reload
* @param {object} [options] - force a reload by setting 'reload' to true
* @returns a promise of the HTTP operation
*/
Account.prototype.$getMailboxes = function(options) {

View File

@ -9,6 +9,7 @@
* @param {object} futureMailboxData - either an object literal or a promise
*/
function Mailbox(account, futureMailboxData) {
this.$isLoading = false;
this.$account = account;
// Data is immediately available
if (typeof futureMailboxData.then !== 'function') {
@ -37,7 +38,7 @@
* @desc The factory we'll use to register with Angular
* @returns the Mailbox constructor
*/
Mailbox.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'sgResource', 'sgMessage', 'sgMailbox_PRELOAD', function($q, $timeout, $log, Settings, Resource, Message, PRELOAD) {
Mailbox.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'Resource', 'Message', 'sgMailbox_PRELOAD', function($q, $timeout, $log, Settings, Resource, Message, PRELOAD) {
angular.extend(Mailbox, {
$q: $q,
$timeout: $timeout,
@ -61,7 +62,7 @@
SIZE: 100
})
/* Factory registration in Angular module */
.factory('sgMailbox', Mailbox.$factory);
.factory('Mailbox', Mailbox.$factory);
/**
* @memberof Mailbox
@ -180,6 +181,7 @@
});
}
this.$isLoading = true;
futureMailboxData = Mailbox.$$resource.post(this.id, 'view', options);
return this.$unwrap(futureMailboxData);
@ -388,7 +390,7 @@
/**
* @function $newMailbox
* @memberof Account.prototype
* @memberof Mailbox.prototype
* @desc Create a new mailbox on the server and refresh the list of mailboxes.
* @returns a promise of the HTTP operations
*/
@ -435,6 +437,8 @@
_this.uidsMap = {};
if (_this.uids) {
Mailbox.$log.debug('unwrapping ' + data.uids.length + ' messages');
// First entry of 'headers' are keys
headers = _.invoke(_this.headers[0], 'toLowerCase');
_this.headers.splice(0, 1);
@ -469,6 +473,7 @@
});
}
Mailbox.$log.debug('mailbox ' + _this.id + ' ready');
_this.$isLoading = false;
deferred.resolve(_this.$messages);
});
}, function(data) {

View File

@ -0,0 +1,20 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* @ngInject
*/
MailboxController.$inject = ['$scope', '$rootScope', '$stateParams', 'stateAccount', 'stateMailbox', '$timeout', 'sgFocus', 'Dialog', 'Account', 'Mailbox'];
function MailboxController($scope, $rootScope, $stateParams, stateAccount, stateMailbox, $timeout, focus, Dialog, Account, Mailbox) {
$scope.account = stateAccount;
$rootScope.mailbox = stateMailbox;
$rootScope.currentFolder = stateMailbox;
}
angular
.module('SOGo.MailerUI')
.controller('MailboxController', MailboxController);
})();

View File

@ -0,0 +1,74 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* @ngInject
*/
MailboxesController.$inject = ['$scope', '$rootScope', '$stateParams', '$state', '$timeout', 'sgFocus', 'encodeUriFilter', 'Dialog', 'sgSettings', 'Account', 'Mailbox', 'stateAccounts'];
function MailboxesController($scope, $rootScope, $stateParams, $state, $timeout, focus, encodeUriFilter, Dialog, Settings, Account, Mailbox, stateAccounts) {
$scope.activeUser = Settings.activeUser;
$scope.accounts = stateAccounts;
$scope.newFolder = function(parentFolder) {
Dialog.prompt(l('New folder'),
l('Enter the new name of your folder :'))
.then(function(name) {
parentFolder.$newMailbox(parentFolder.id, name);
});
};
$scope.editFolder = function(folder) {
$scope.editMode = folder.path;
focus('mailboxName_' + folder.path);
};
$scope.revertEditing = function(folder) {
folder.$reset();
$scope.editMode = false;
};
$scope.selectFolder = function(account, folder) {
if ($scope.editMode == folder.path)
return;
$rootScope.currentFolder = folder;
$scope.editMode = false;
$rootScope.message = null;
$state.go('mail.account.mailbox', { accountId: account.id, mailboxId: encodeUriFilter(folder.path) });
};
$scope.saveFolder = function(folder) {
folder.$rename();
};
$scope.exportMails = function() {
window.location.href = ApplicationBaseURL + '/' + $rootScope.currentFolder.id + '/exportFolder';
};
$scope.confirmDelete = function(folder) {
if (folder.path != $scope.currentFolder.path) {
// Counter the possibility to click on the "hidden" secondary button
$scope.selectFolder(folder.$account, folder);
return;
}
Dialog.confirm(l('Confirmation'), l('Do you really want to move this folder into the trash ?'))
.then(function() {
folder.$delete()
.then(function() {
$rootScope.currentFolder = null;
$state.go('mail');
}, function(data, status) {
Dialog.alert(l('An error occured while deleting the mailbox "%{0}".', folder.name),
l(data.error));
});
});
};
if ($state.current.name == 'mail' && $scope.accounts.length > 0 && $scope.accounts[0].$mailboxes.length > 0) {
// Redirect to first mailbox of first account if no mailbox is selected
var account = $scope.accounts[0];
var mailbox = account.$mailboxes[0];
$state.go('mail.account.mailbox', { accountId: account.id, mailboxId: encodeUriFilter(mailbox.path) });
}
}
angular
.module('SOGo.MailerUI')
.controller('MailboxesController', MailboxesController);
})();

View File

@ -34,7 +34,7 @@
* @desc The factory we'll use to register with Angular
* @returns the Message constructor
*/
Message.$factory = ['$q', '$timeout', '$log', '$sce', 'sgSettings', 'sgResource', function($q, $timeout, $log, $sce, Settings, Resource) {
Message.$factory = ['$q', '$timeout', '$log', '$sce', 'sgSettings', 'Resource', function($q, $timeout, $log, $sce, Settings, Resource) {
angular.extend(Message, {
$q: $q,
$timeout: $timeout,
@ -52,7 +52,7 @@
/* Factory registration in Angular module */
angular.module('SOGo.MailerUI')
.factory('sgMessage', Message.$factory);
.factory('Message', Message.$factory);
/**
* @function filterTags

View File

@ -0,0 +1,40 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* @ngInject
*/
MessageController.$inject = ['$scope', '$rootScope', '$stateParams', '$state', 'stateAccount', 'stateMailbox', 'stateMessage', '$timeout', 'encodeUriFilter', 'sgFocus', 'Dialog', 'Account', 'Mailbox'];
function MessageController($scope, $rootScope, $stateParams, $state, stateAccount, stateMailbox, stateMessage, $timeout, encodeUriFilter, focus, Dialog, Account, Mailbox) {
$rootScope.message = stateMessage;
$scope.tags = {};
$scope.addOrRemoveTag = function(operation, tag) {
if (tag) {
stateMessage.$addOrRemoveTag(operation, tag);
}
};
$scope.markAsFlaggedOrUnflagged = function() {
var operation = (stateMessage.isflagged ? 'remove' : 'add');
stateMessage.$markAsFlaggedOrUnflagged(operation).then(function() {
stateMessage.isflagged = !stateMessage.isflagged;
});
};
$scope.doDelete = function() {
stateMailbox.$deleteMessages([stateMessage.uid]).then(function() {
// Remove card from list of addressbook
stateMailbox.$messages = _.reject(stateMailbox.$messages, function(o) {
return o.uid == stateMessage.uid;
});
// Remove card object from scope
$rootScope.message = null;
$state.go('mail.account.mailbox', { accountId: stateAccount.id, mailboxId: encodeUriFilter(stateMailbox.path) });
});
};
}
angular
.module('SOGo.MailerUI')
.controller('MessageController', MessageController);
})();

View File

@ -0,0 +1,87 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* @ngInject
*/
MessageEditorController.$inject = ['$scope', '$rootScope', '$stateParams', '$state', '$q', 'FileUploader', 'stateAccounts', 'stateMessage', '$timeout', 'encodeUriFilter', 'sgFocus', 'Dialog', 'Account', 'Mailbox', 'AddressBook'];
function MessageEditorController($scope, $rootScope, $stateParams, $state, $q, FileUploader, stateAccounts, stateMessage, $timeout, encodeUriFilter, focus, Dialog, Account, Mailbox, AddressBook) {
$scope.autocomplete = {to: {}, cc: {}, bcc: {}};
$scope.hideCc = true;
$scope.hideBcc = true;
$scope.hideAttachments = true;
if ($stateParams.actionName == 'reply') {
stateMessage.$reply().then(function(msgObject) {
console.debug("foo");
$scope.message = msgObject;
$scope.hideCc = (!msgObject.editable.cc || msgObject.editable.cc.length == 0);
$scope.hideBcc = (!msgObject.editable.bcc || msgObject.editable.bcc.length == 0);
$scope.hideAttachments = true;
});
}
else if ($stateParams.actionName == 'replyall') {
stateMessage.$replyAll().then(function(msgObject) {
$scope.message = msgObject;
$scope.hideCc = (!msgObject.editable.cc || msgObject.editable.cc.length == 0);
$scope.hideBcc = (!msgObject.editable.bcc || msgObject.editable.bcc.length == 0);
$scope.hideAttachments = true;
});
}
else if ($stateParams.actionName == 'forward') {
stateMessage.$forward().then(function(msgObject) {
$scope.message = msgObject;
$scope.hideCc = true;
$scope.hideBcc = true;
$scope.hideAttachments = (!msgObject.editable.attachmentAttrs || msgObject.editable.attachmentAttrs.length == 0);
});
}
else if (angular.isDefined(stateMessage)) {
$scope.message = stateMessage;
}
$scope.identities = _.pluck(_.flatten(_.pluck(stateAccounts, 'identities')), 'full');
$scope.cancel = function() {
if ($scope.mailbox)
$state.go('mail.account.mailbox', { accountId: $scope.mailbox.$account.id, mailboxId: encodeUriFilter($scope.mailbox.path) });
else
$state.go('mail');
};
$scope.send = function(message) {
message.$send().then(function(data) {
$rootScope.message = null;
$state.go('mail');
}, function(data) {
console.debug('failure ' + JSON.stringify(data, undefined, 2));
});
};
$scope.userFilter = function($query) {
var deferred = $q.defer();
AddressBook.$filterAll($query).then(function(results) {
deferred.resolve(_.invoke(results, '$shortFormat', $query));
});
return deferred.promise;
};
$scope.uploader = new FileUploader({
url: stateMessage.$absolutePath({asDraft: true}) + '/save',
autoUpload: true,
alias: 'attachments',
onProgressItem: function(item, progress) {
console.debug(item); console.debug(progress);
},
onSuccessItem: function(item, response, status, headers) {
stateMessage.$setUID(response.uid);
stateMessage.$reload();
console.debug(item); console.debug('success = ' + JSON.stringify(response, undefined, 2));
},
onErrorItem: function(item, response, status, headers) {
console.debug(item); console.debug('error = ' + JSON.stringify(response, undefined, 2));
}
});
}
angular
.module('SOGo.MailerUI')
.controller('MessageEditorController', MessageEditorController);
})();

View File

@ -1,342 +0,0 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* JavaScript for SOGo.MailerUI module */
(function() {
'use strict';
angular.module('SOGo.Common', []);
angular.module('SOGo.ContactsUI', []);
angular.module('SOGo.MailerUI', ['ngSanitize', 'ui.router', 'vs-repeat', 'ck', 'angularFileUpload', 'SOGo.Common', 'SOGo.UI', 'SOGo.UIDesktop', 'SOGo.ContactsUI', 'ngAnimate'])
.constant('sgSettings', {
baseURL: ApplicationBaseURL,
activeUser: {
login: UserLogin,
identification: UserIdentification,
language: UserLanguage,
folderURL: UserFolderURL,
isSuperUser: IsSuperUser
}
})
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('mail', {
url: '/Mail',
views: {
mailboxes: {
templateUrl: 'UIxMailMainFrame', // UI/Templates/MailerUI/UIxMailMainFrame.wox
controller: 'MailboxesCtrl'
}
},
resolve: {
stateAccounts: ['$q', 'sgAccount', function($q, Account) {
var accounts = Account.$findAll(mailAccounts);
var promises = [];
// Fetch list of mailboxes for each account
angular.forEach(accounts, function(account, i) {
var mailboxes = account.$getMailboxes();
promises.push(mailboxes.then(function(objects) {
return account;
}));
});
return $q.all(promises);
}]
}
})
.state('mail.account', {
url: '/:accountId',
abstract: true,
views: {
mailbox: {
template: '<ui-view/>',
}
},
resolve: {
stateAccount: ['$stateParams', 'stateAccounts', function($stateParams, stateAccounts) {
return _.find(stateAccounts, function(account) {
return account.id == $stateParams.accountId;
});
}]
}
})
.state('mail.account.mailbox', {
url: '/:mailboxId',
views: {
'mailbox@mail': {
templateUrl: 'UIxMailFolderTemplate', // UI/Templates/MailerUI/UIxMailFolderTemplate.wox
controller: 'MailboxCtrl'
}
},
resolve: {
stateMailbox: ['$stateParams', 'stateAccount', 'decodeUriFilter', function($stateParams, stateAccount, decodeUriFilter) {
var mailboxId = decodeUriFilter($stateParams.mailboxId);
// Recursive find function
var _find = function(mailboxes) {
var mailbox = _.find(mailboxes, function(o) {
return o.path == mailboxId;
});
if (!mailbox) {
angular.forEach(mailboxes, function(o) {
if (!mailbox && o.children && o.children.length > 0) {
mailbox = _find(o.children);
}
});
}
return mailbox;
};
return _find(stateAccount.$mailboxes);
}],
stateMessages: ['stateMailbox', function(stateMailbox) {
return stateMailbox.$filter();
}]
}
})
.state('mail.account.mailbox.message', {
url: '/:messageId',
views: {
message: {
templateUrl: 'UIxMailViewTemplate', // UI/Templates/MailerUI/UIxMailViewTemplate.wox
controller: 'MessageCtrl'
}
},
resolve: {
stateMessage: ['encodeUriFilter', '$stateParams', '$state', 'stateMailbox', 'stateMessages', function(encodeUriFilter, $stateParams, $state, stateMailbox, stateMessages) {
var message = _.find(stateMessages, function(messageObject) {
return messageObject.uid == $stateParams.messageId;
});
if (message)
return message.$reload();
else
// Message not found
$state.go('mail.account.mailbox', { accountId: stateMailbox.$account.id, mailboxId: encodeUriFilter(stateMailbox.path) });
}]
}
})
.state('mail.account.mailbox.message.edit', {
url: '/edit',
views: {
'mailbox@mail': {
templateUrl: 'UIxMailEditor', // UI/Templates/MailerUI/UIxMailEditor.wox
controller: 'MessageEditorCtrl'
}
},
resolve: {
stateContent: ['stateMessage', function(stateMessage) {
return stateMessage.$editableContent();
}]
}
})
.state('mail.account.mailbox.message.action', {
url: '/{actionName:(?:reply|replyall|forward)}',
views: {
'mailbox@mail': {
templateUrl: 'UIxMailEditor', // UI/Templates/MailerUI/UIxMailEditor.wox
controller: 'MessageEditorCtrl'
}
}
})
.state('mail.newMessage', {
url: '/new',
views: {
mailbox: {
templateUrl: 'UIxMailEditor', // UI/Templates/MailerUI/UIxMailEditor.wox
controller: 'MessageEditorCtrl'
}
},
resolve: {
stateMessage: ['stateAccounts', function(stateAccounts) {
if (stateAccounts.length > 0) {
return stateAccounts[0].$newMessage();
}
}]
}
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/Mail');
// Set default configuration for tags input
// tagsInputConfigProvider.setDefaults('tagsInput', {
// addOnComma: false,
// replaceSpacesWithDashes: false,
// allowedTagsPattern: /([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)/i
// });
}])
.run(function($rootScope) {
$rootScope.$on('$routeChangeError', function(event, current, previous, rejection) {
console.error(event, current, previous, rejection)
})
})
.controller('MailboxesCtrl', ['$scope', '$rootScope', '$stateParams', '$state', '$timeout', 'sgFocus', 'encodeUriFilter', 'sgDialog', 'sgSettings', 'sgAccount', 'sgMailbox', 'stateAccounts', function($scope, $rootScope, $stateParams, $state, $timeout, focus, encodeUriFilter, Dialog, Settings, Account, Mailbox, stateAccounts) {
$scope.activeUser = Settings.activeUser;
$scope.accounts = stateAccounts;
$scope.newFolder = function(parentFolder) {
Dialog.prompt(l('New folder'),
l('Enter the new name of your folder :'))
.then(function(name) {
parentFolder.$newMailbox(parentFolder.id, name);
});
};
$scope.editFolder = function(folder) {
$scope.editMode = folder.path;
focus('mailboxName_' + folder.path);
};
$scope.revertEditing = function(folder) {
folder.$reset();
$scope.editMode = false;
};
$scope.selectFolder = function(account, folder) {
if ($scope.editMode == folder.path)
return;
$rootScope.currentFolder = folder;
$scope.editMode = false;
$rootScope.message = null;
$state.go('mail.account.mailbox', { accountId: account.id, mailboxId: encodeUriFilter(folder.path) });
};
$scope.saveFolder = function(folder) {
folder.$rename();
};
$scope.exportMails = function() {
window.location.href = ApplicationBaseURL + '/' + $rootScope.currentFolder.id + '/exportFolder';
};
$scope.confirmDelete = function(folder) {
if (folder.path != $scope.currentFolder.path) {
// Counter the possibility to click on the "hidden" secondary button
$scope.selectFolder(folder.$account, folder);
return;
}
Dialog.confirm(l('Confirmation'), l('Do you really want to move this folder into the trash ?'))
.then(function() {
folder.$delete()
.then(function() {
$rootScope.currentFolder = null;
$state.go('mail');
}, function(data, status) {
Dialog.alert(l('An error occured while deleting the mailbox "%{0}".', folder.name),
l(data.error));
});
});
};
if ($state.current.name == 'mail' && $scope.accounts.length > 0 && $scope.accounts[0].$mailboxes.length > 0) {
// Redirect to first mailbox of first account if no mailbox is selected
var account = $scope.accounts[0];
var mailbox = account.$mailboxes[0];
$state.go('mail.account.mailbox', { accountId: account.id, mailboxId: encodeUriFilter(mailbox.path) });
}
}])
.controller('MailboxCtrl', ['$scope', '$rootScope', '$stateParams', 'stateAccount', 'stateMailbox', '$timeout', 'sgFocus', 'sgDialog', 'sgAccount', 'sgMailbox', function($scope, $rootScope, $stateParams, stateAccount, stateMailbox, $timeout, focus, Dialog, Account, Mailbox) {
$scope.account = stateAccount;
$rootScope.mailbox = stateMailbox;
$rootScope.currentFolder = stateMailbox;
}])
.controller('MessageCtrl', ['$scope', '$rootScope', '$stateParams', '$state', 'stateAccount', 'stateMailbox', 'stateMessage', '$timeout', 'encodeUriFilter', 'sgFocus', 'sgDialog', 'sgAccount', 'sgMailbox', function($scope, $rootScope, $stateParams, $state, stateAccount, stateMailbox, stateMessage, $timeout, encodeUriFilter, focus, Dialog, Account, Mailbox) {
$rootScope.message = stateMessage;
$scope.tags = {};
$scope.addOrRemoveTag = function(operation, tag) {
if (tag) {
stateMessage.$addOrRemoveTag(operation, tag);
}
};
$scope.markAsFlaggedOrUnflagged = function() {
var operation = (stateMessage.isflagged ? 'remove' : 'add');
stateMessage.$markAsFlaggedOrUnflagged(operation).then(function() {
stateMessage.isflagged = !stateMessage.isflagged;
});
};
$scope.doDelete = function() {
stateMailbox.$deleteMessages([stateMessage.uid]).then(function() {
// Remove card from list of addressbook
stateMailbox.$messages = _.reject(stateMailbox.$messages, function(o) {
return o.uid == stateMessage.uid;
});
// Remove card object from scope
$rootScope.message = null;
$state.go('mail.account.mailbox', { accountId: stateAccount.id, mailboxId: encodeUriFilter(stateMailbox.path) });
});
};
}])
.controller('MessageEditorCtrl', ['$scope', '$rootScope', '$stateParams', '$state', '$q', 'FileUploader', 'stateAccounts', 'stateMessage', '$timeout', 'encodeUriFilter', 'sgFocus', 'sgDialog', 'sgAccount', 'sgMailbox', 'sgAddressBook', function($scope, $rootScope, $stateParams, $state, $q, FileUploader, stateAccounts, stateMessage, $timeout, encodeUriFilter, focus, Dialog, Account, Mailbox, AddressBook) {
$scope.autocomplete = {to: {}, cc: {}, bcc: {}};
$scope.hideCc = true;
$scope.hideBcc = true;
$scope.hideAttachments = true;
if ($stateParams.actionName == 'reply') {
stateMessage.$reply().then(function(msgObject) {
console.debug("foo");
$scope.message = msgObject;
$scope.hideCc = (!msgObject.editable.cc || msgObject.editable.cc.length == 0);
$scope.hideBcc = (!msgObject.editable.bcc || msgObject.editable.bcc.length == 0);
$scope.hideAttachments = true;
});
}
else if ($stateParams.actionName == 'replyall') {
stateMessage.$replyAll().then(function(msgObject) {
$scope.message = msgObject;
$scope.hideCc = (!msgObject.editable.cc || msgObject.editable.cc.length == 0);
$scope.hideBcc = (!msgObject.editable.bcc || msgObject.editable.bcc.length == 0);
$scope.hideAttachments = true;
});
}
else if ($stateParams.actionName == 'forward') {
stateMessage.$forward().then(function(msgObject) {
$scope.message = msgObject;
$scope.hideCc = true;
$scope.hideBcc = true;
$scope.hideAttachments = (!msgObject.editable.attachmentAttrs || msgObject.editable.attachmentAttrs.length == 0);
});
}
else if (angular.isDefined(stateMessage)) {
$scope.message = stateMessage;
}
$scope.identities = _.pluck(_.flatten(_.pluck(stateAccounts, 'identities')), 'full');
$scope.cancel = function() {
if ($scope.mailbox)
$state.go('mail.account.mailbox', { accountId: $scope.mailbox.$account.id, mailboxId: encodeUriFilter($scope.mailbox.path) });
else
$state.go('mail');
};
$scope.send = function(message) {
message.$send().then(function(data) {
$rootScope.message = null;
$state.go('mail');
}, function(data) {
console.debug('failure ' + JSON.stringify(data, undefined, 2));
});
};
$scope.userFilter = function($query) {
var deferred = $q.defer();
AddressBook.$filterAll($query).then(function(results) {
deferred.resolve(_.invoke(results, '$shortFormat', $query));
});
return deferred.promise;
};
$scope.uploader = new FileUploader({
url: stateMessage.$absolutePath({asDraft: true}) + '/save',
autoUpload: true,
alias: 'attachments',
onProgressItem: function(item, progress) {
console.debug(item); console.debug(progress);
},
onSuccessItem: function(item, response, status, headers) {
stateMessage.$setUID(response.uid);
stateMessage.$reload();
console.debug(item); console.debug('success = ' + JSON.stringify(response, undefined, 2));
},
onErrorItem: function(item, response, status, headers) {
console.debug(item); console.debug('error = ' + JSON.stringify(response, undefined, 2));
}
});
}]);
})();

View File

@ -0,0 +1,88 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* JavaScript for SOGoPreferences */
(function() {
'use strict';
angular.module('SOGo.ContactsUI', []);
angular.module('SOGo.MailerUI', []);
angular.module('SOGo.PreferencesUI', ['ngSanitize', 'ui.router', 'SOGo.Common', 'SOGo.MailerUI', 'SOGo.ContactsUI', 'SOGo.Authentication'])
.constant('sgSettings', {
baseURL: ApplicationBaseURL,
activeUser: {
login: UserLogin,
identification: UserIdentification,
language: UserLanguage,
folderURL: UserFolderURL,
isSuperUser: IsSuperUser
}
})
.config(configure);
/**
* @ngInject
*/
configure.$inject = ['$stateProvider', '$urlRouterProvider'];
function configure($stateProvider, $urlRouterProvider) {
$stateProvider
.state('preferences', {
abstract: true,
views: {
preferences: {
templateUrl: 'preferences.html',
controller: 'PreferencesController',
controllerAs: 'app'
}
},
resolve: {
statePreferences: statePreferences
}
})
.state('preferences.general', {
url: '/general',
views: {
module: {
templateUrl: 'generalPreferences.html'
}
}
})
.state('preferences.calendars', {
url: '/calendars',
views: {
module: {
templateUrl: 'calendarsPreferences.html'
}
}
})
.state('preferences.addressbooks', {
url: '/addressbooks',
views: {
module: {
templateUrl: 'addressbooksPreferences.html'
}
}
})
.state('preferences.mailer', {
url: '/mailer',
views: {
module: {
templateUrl: 'mailerPreferences.html'
}
}
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/general');
}
/**
* @ngInject
*/
statePreferences.$inject = ['Preferences'];
function statePreferences(Preferences) {
return new Preferences();
}
})();

View File

@ -0,0 +1,32 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* JavaScript for SOGoPreferences */
(function() {
'use strict';
/**
* @ngInject
*/
AccountDialogController.$inject = ['$scope', '$mdDialog', 'account', 'accountId', 'mailCustomFromEnabled'];
function AccountDialogController($scope, $mdDialog, account, accountId, mailCustomFromEnabled) {
$scope.account = account;
$scope.accountId = accountId;
$scope.customFromIsReadonly = function() {
if (accountId > 0)
return false;
return !mailCustomFromEnabled;
};
$scope.cancel = function() {
$mdDialog.cancel();
};
$scope.save = function() {
$mdDialog.hide();
};
}
angular
.module('SOGo.PreferencesUI')
.controller('AccountDialogController', AccountDialogController);
})();

View File

@ -0,0 +1,98 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* JavaScript for SOGoPreferences */
(function() {
'use strict';
/**
* @ngInject
*/
FiltersDialogController.$inject = ['$scope', '$mdDialog', 'filter', 'mailboxes', 'labels'];
function FiltersDialogController($scope, $mdDialog, filter, mailboxes, labels) {
$scope.filter = filter;
$scope.mailboxes = mailboxes;
$scope.labels = labels;
$scope.fieldLabels = {
"subject": l("Subject"),
"from": l("From"),
"to": l("To"),
"cc": l("Cc"),
"to_or_cc": l("To or Cc"),
"size": l("Size (Kb)"),
"header": l("Header"),
"body": l("Body")
};
$scope.methodLabels = {
"addflag": l("Flag the message with:"),
"discard": l("Discard the message"),
"fileinto": l("File the message in:"),
"keep": l("Keep the message"),
"redirect": l("Forward the message to:"),
"reject": l("Send a reject message:"),
"vacation": l("Send a vacation message"),
"stop": l("Stop processing filter rules")
};
$scope.numberOperatorLabels = {
"under": l("is under"),
"over": l("is over")
};
$scope.textOperatorLabels = {
"is": l("is"),
"is_not": l("is not"),
"contains": l("contains"),
"contains_not": l("does not contain"),
"matches": l("matches"),
"matches_not": l("does not match"),
"regex": l("matches regex"),
"regex_not": l("does not match regex")
};
$scope.flagLabels = {
"seen": l("Seen"),
"deleted": l("Deleted"),
"answered": l("Answered"),
"flagged": l("Flagged"),
"junk": l("Junk"),
"not_junk": l("Not Junk")
};
$scope.cancel = function() {
$mdDialog.cancel();
};
$scope.save = function() {
$mdDialog.hide();
};
$scope.addMailFilterRule = function(event) {
if (!$scope.filter.rules)
$scope.filter.rules = [];
$scope.filter.rules.push({});
};
$scope.removeMailFilterRule = function(index) {
$scope.filter.rules.splice(index, 1);
};
$scope.addMailFilterAction = function(event) {
if (!$scope.filter.actions)
$scope.filter.actions = [];
$scope.filter.actions.push({});
};
$scope.removeMailFilterAction = function(index) {
$scope.filter.actions.splice(index, 1);
};
}
angular
.module('SOGo.PreferencesUI')
.controller('FiltersDialogController', FiltersDialogController);
})();

View File

@ -13,45 +13,45 @@
this.defaults = {};
this.settings = {};
this.mailboxes = Preferences.$Mailbox.$find({ id: 0 });
Preferences.$$resource.fetch("jsonDefaults").then(function(data) {
Preferences.$timeout(function() {
this.mailboxes = Preferences.$Mailbox.$find({ id: 0 });
Preferences.$$resource.fetch("jsonDefaults").then(function(data) {
Preferences.$timeout(function() {
// We swap $key -> _$key to avoid an Angular bug (https://github.com/angular/angular.js/issues/6266)
var labels = _.object(_.map(data.SOGoMailLabelsColors, function(value, key) {
if (key.charAt(0) == '$')
return ['_' + key, value];
return [key, value];
}));
data.SOGoMailLabelsColors = labels;
// We swap $key -> _$key to avoid an Angular bug (https://github.com/angular/angular.js/issues/6266)
var labels = _.object(_.map(data.SOGoMailLabelsColors, function(value, key) {
if (key.charAt(0) == '$')
return ['_' + key, value];
return [key, value];
}));
// We convert our list of autoReplyEmailAddresses/forwardAddress into a string.
if (data.Vacation && data.Vacation.autoReplyEmailAddresses)
data.Vacation.autoReplyEmailAddresses = data.Vacation.autoReplyEmailAddresses.join(",");
data.SOGoMailLabelsColors = labels;
if (data.Forward && data.Forward.forwardAddress)
data.Forward.forwardAddress = data.Forward.forwardAddress.join(",");
angular.extend(_this.defaults, data);
});
});
Preferences.$$resource.fetch("jsonSettings").then(function(data) {
Preferences.$timeout(function() {
// We convert our list of autoReplyEmailAddresses/forwardAddress into a string.
if (data.Vacation && data.Vacation.autoReplyEmailAddresses)
data.Vacation.autoReplyEmailAddresses = data.Vacation.autoReplyEmailAddresses.join(",");
// We convert our PreventInvitationsWhitelist hash into a array of user
if (data.Calendar && data.Calendar.PreventInvitationsWhitelist)
data.Calendar.PreventInvitationsWhitelist = _.map(data.Calendar.PreventInvitationsWhitelist, function(value, key) {
return new Preferences.$User({uid: key, shortFormat: value});
});
else
data.Calendar.PreventInvitationsWhitelist = [];
angular.extend(_this.settings, data);
});
});
if (data.Forward && data.Forward.forwardAddress)
data.Forward.forwardAddress = data.Forward.forwardAddress.join(",");
angular.extend(_this.defaults, data);
});
});
Preferences.$$resource.fetch("jsonSettings").then(function(data) {
Preferences.$timeout(function() {
// We convert our PreventInvitationsWhitelist hash into a array of user
if (data.Calendar && data.Calendar.PreventInvitationsWhitelist)
data.Calendar.PreventInvitationsWhitelist = _.map(data.Calendar.PreventInvitationsWhitelist, function(value, key) {
return new Preferences.$User({uid: key, shortFormat: value});
});
else
data.Calendar.PreventInvitationsWhitelist = [];
angular.extend(_this.settings, data);
});
});
}
/**
@ -59,7 +59,7 @@
* @desc The factory we'll use to register with Angular
* @returns the Preferences constructor
*/
Preferences.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'sgResource', 'sgMailbox', 'sgUser', function($q, $timeout, $log, Settings, Resource, Mailbox, User) {
Preferences.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'Resource', 'Mailbox', 'User', function($q, $timeout, $log, Settings, Resource, Mailbox, User) {
angular.extend(Preferences, {
$q: $q,
$timeout: $timeout,
@ -75,14 +75,14 @@
/* Factory registration in Angular module */
angular.module('SOGo.PreferencesUI')
.factory('sgPreferences', Preferences.$factory);
.factory('Preferences', Preferences.$factory);
/**
* @function $save
* @memberof Preferences.prototype
* @desc Save the preferences to the server.
*/
Preferences.prototype.$save = function() {
/*Preferences.prototype.$save = function() {
var _this = this;
console.debug("save in model...");
@ -94,7 +94,7 @@
//_this.$shadowData = _this.$omit(true);
return data;
});
};
};*/
/**
* @function $omit
@ -103,7 +103,7 @@
* @param {Boolean} [deep] - make a deep copy if true
* @return an object literal copy of the Preferences instance
*/
Preferences.prototype.$omit = function(deep) {
/*Preferences.prototype.$omit = function(deep) {
var preferences = {};
angular.forEach(this, function(value, key) {
if (key != 'constructor' && key[0] != '$') {
@ -139,6 +139,6 @@
}
return preferences;
};
};*/
})();

View File

@ -0,0 +1,198 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* JavaScript for SOGoPreferences */
(function() {
'use strict';
/**
* @ngInject
*/
PreferencesController.$inject = ['$scope', '$timeout', '$q', '$mdDialog', 'Preferences', 'User', 'statePreferences', 'Authentication'];
function PreferencesController($scope, $timeout, $q, $mdDialog, Preferences, User, statePreferences, Authentication) {
var vm = this;
vm.preferences = statePreferences;
vm.passwords = { newPassword: null, newPasswordConfirmation: null };
vm.addCalendarCategory = addCalendarCategory;
vm.removeCalendarCategory = removeCalendarCategory;
vm.addContactCategory = addContactCategory;
vm.removeContactCategory = removeContactCategory;
vm.addMailAccount = addMailAccount;
vm.editMailAccount = editMailAccount;
vm.removeMailAccount = removeMailAccount;
vm.addMailLabel = addMailLabel;
vm.removeMailLabel = removeMailLabel;
vm.addMailFilter = addMailFilter;
vm.editMailFilter = editMailFilter;
vm.removeMailFilter = removeMailFilter;
vm.userFilter = userFilter;
vm.save = save;
vm.canChangePassword = canChangePassword;
vm.changePassword = changePassword;
function addCalendarCategory() {
vm.preferences.defaults.SOGoCalendarCategoriesColors["New category"] = "#aaa";
vm.preferences.defaults.SOGoCalendarCategories.push("New category");
}
function removeCalendarCategory(index) {
var key = vm.preferences.defaults.SOGoCalendarCategories[index];
vm.preferences.defaults.SOGoCalendarCategories.splice(index, 1);
delete vm.preferences.defaults.SOGoCalendarCategoriesColors[key];
}
function addContactCategory() {
vm.preferences.defaults.SOGoContactsCategories.push("");
}
function removeContactCategory(index) {
vm.preferences.defaults.SOGoContactsCategories.splice(index, 1);
}
function addMailAccount(ev) {
var account;
vm.preferences.defaults.AuxiliaryMailAccounts.push({});
account = _.last(vm.preferences.defaults.AuxiliaryMailAccounts);
account['name'] = "New account";
account['identities'] = [];
account['identities'][0] = {};
account['identities'][0]['fullName'] = "";
account['identities'][0]['email'] = "";
account['receipts'] = {};
account['receipts']['receiptAction'] = "ignore";
account['receipts']['receiptNonRecipientAction'] = "ignore";
account['receipts']['receiptOutsideDomainAction'] = "ignore";
account['receipts']['receiptAnyAction'] = "ignore";
$mdDialog.show({
controller: 'AccountDialogController',
templateUrl: 'editAccount?account=new',
targetEvent: ev,
locals: {
account: account,
accountId: (vm.preferences.defaults.AuxiliaryMailAccounts.length-1),
mailCustomFromEnabled: window.mailCustomFromEnabled
}
});
}
function editMailAccount(index) {
var account = vm.preferences.defaults.AuxiliaryMailAccounts[index];
$mdDialog.show({
controller: 'AccountDialogController',
templateUrl: 'editAccount?account=' + index,
targetEvent: null,
locals: {
account: account,
accountId: index,
mailCustomFromEnabled: window.mailCustomFromEnabled
}
}).then(function() {
vm.preferences.defaults.AuxiliaryMailAccounts[index] = account;
});
}
function removeMailAccount(index) {
vm.preferences.defaults.AuxiliaryMailAccounts.splice(index, 1);
}
function addMailLabel() {
vm.preferences.defaults.SOGoMailLabelsColors["new_label"] = ["New label", "#aaa"];
}
function removeMailLabel(key) {
delete vm.preferences.defaults.SOGoMailLabelsColors[key];
}
function addMailFilter(ev) {
if (!vm.preferences.defaults.SOGoSieveFilters)
vm.preferences.defaults.SOGoSieveFilters = [];
vm.preferences.defaults.SOGoSieveFilters.push({});
var filter = _.last(vm.preferences.defaults.SOGoSieveFilters);
$mdDialog.show({
controller: 'FiltersDialogController',
templateUrl: 'editFilter?filter=new',
targetEvent: ev,
locals: {
filter: filter,
mailboxes: vm.preferences.mailboxes,
labels: vm.preferences.defaults.SOGoMailLabelsColors
}
});
}
function editMailFilter(index) {
var filter = angular.copy(vm.preferences.defaults.SOGoSieveFilters[index]);
$mdDialog.show({
controller: 'FiltersDialogController',
templateUrl: 'editFilter?filter=' + index,
targetEvent: null,
locals: {
filter: filter,
mailboxes: vm.preferences.mailboxes,
labels: vm.preferences.defaults.SOGoMailLabelsColors
}
}).then(function() {
vm.preferences.defaults.SOGoSieveFilters[index] = filter;
});
}
function removeMailFilter(index) {
vm.preferences.defaults.SOGoSieveFilters.splice(index, 1);
}
function userFilter($query) {
var deferred = $q.defer();
User.$filter($query).then(function(results) {
deferred.resolve(results)
});
return deferred.promise;
}
function save() {
vm.preferences.$save();
}
function canChangePassword() {
if (vm.passwords.newPassword && vm.passwords.newPassword.length > 0 &&
vm.passwords.newPasswordConfirmation && vm.passwords.newPasswordConfirmation.length &&
vm.passwords.newPassword == vm.passwords.newPasswordConfirmation)
return true;
return false;
}
function changePassword() {
Authentication.changePassword(vm.passwords.newPassword).then(function() {
var alert = $mdDialog.alert({
title: l('Password'),
content: l('The password was changed successfully.'),
ok: 'OK'
});
$mdDialog.show( alert )
.finally(function() {
alert = undefined;
});
}, function(msg) {
var alert = $mdDialog.alert({
title: l('Password'),
content: msg,
ok: 'OK'
});
$mdDialog.show( alert )
.finally(function() {
alert = undefined;
});
});
}
}
angular
.module('SOGo.PreferencesUI')
.controller('PreferencesController', PreferencesController);
})();

View File

@ -1,317 +0,0 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* JavaScript for SOGoPreferences */
(function() {
'use strict';
angular.module('SOGo.Common', []);
angular.module('SOGo.ContactsUI', []);
angular.module('SOGo.MailerUI', []);
angular.module('SOGo.PreferencesUI', ['ngSanitize', 'ui.router', 'SOGo.Common', 'SOGo.MailerUI', 'SOGo.UIDesktop', 'SOGo.UI', 'SOGo.ContactsUI', 'SOGo.Authentication'])
.constant('sgSettings', {
baseURL: ApplicationBaseURL,
activeUser: {
login: UserLogin,
identification: UserIdentification,
language: UserLanguage,
folderURL: UserFolderURL,
isSuperUser: IsSuperUser
}
})
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('preferences', {
abstract: true,
views: {
preferences: {
templateUrl: 'preferences.html',
controller: 'PreferencesCtrl'
}
},
resolve: {
statePreferences: ['sgPreferences', function(Preferences) {
return new Preferences();
}]
}
})
.state('preferences.general', {
url: '/general',
views: {
module: {
templateUrl: 'generalPreferences.html'
}
}
})
.state('preferences.calendars', {
url: '/calendars',
views: {
module: {
templateUrl: 'calendarsPreferences.html'
}
}
})
.state('preferences.addressbooks', {
url: '/addressbooks',
views: {
module: {
templateUrl: 'addressbooksPreferences.html'
}
}
})
.state('preferences.mailer', {
url: '/mailer',
views: {
module: {
templateUrl: 'mailerPreferences.html'
}
}
})
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/general');
}])
.controller('PreferencesCtrl', ['$scope', '$timeout', '$q', '$mdDialog', 'sgPreferences', 'sgUser', 'statePreferences', 'Authentication', function($scope, $timeout, $q, $mdDialog, Preferences, User, statePreferences, Authentication) {
$scope.preferences = statePreferences;
$scope.addCalendarCategory = function() {
$scope.preferences.defaults.SOGoCalendarCategoriesColors["New category"] = "#aaa";
$scope.preferences.defaults.SOGoCalendarCategories.push("New category");
}
$scope.removeCalendarCategory = function(index) {
var key = $scope.preferences.defaults.SOGoCalendarCategories[index];
$scope.preferences.defaults.SOGoCalendarCategories.splice(index, 1);
delete $scope.preferences.defaults.SOGoCalendarCategoriesColors[key];
}
$scope.addContactCategory = function() {
$scope.preferences.defaults.SOGoContactsCategories.push("");
};
$scope.removeContactCategory = function(index) {
$scope.preferences.defaults.SOGoContactsCategories.splice(index, 1);
}
$scope.addMailAccount = function(ev) {
$scope.preferences.defaults.AuxiliaryMailAccounts.push({});
var account = _.last($scope.preferences.defaults.AuxiliaryMailAccounts);
account['name'] = "New account";
account['identities'] = [];
account['identities'][0] = {};
account['identities'][0]['fullName'] = "";
account['identities'][0]['email'] = "";
account['receipts'] = {};
account['receipts']['receiptAction'] = "ignore";
account['receipts']['receiptNonRecipientAction'] = "ignore";
account['receipts']['receiptOutsideDomainAction'] = "ignore";
account['receipts']['receiptAnyAction'] = "ignore";
$mdDialog.show({
controller: AccountDialogCtrl,
templateUrl: 'editAccount?account=new',
targetEvent: ev,
locals: { account: account,
accountId: ($scope.preferences.defaults.AuxiliaryMailAccounts.length-1),
mailCustomFromEnabled: window.mailCustomFromEnabled}
});
};
$scope.editMailAccount = function(index) {
var account = $scope.preferences.defaults.AuxiliaryMailAccounts[index];
$mdDialog.show({
controller: AccountDialogCtrl,
templateUrl: 'editAccount?account=' + index,
targetEvent: null,
locals: { account: account,
accountId: index,
mailCustomFromEnabled: window.mailCustomFromEnabled}
}).then(function() {
$scope.preferences.defaults.AuxiliaryMailAccounts[index] = account;
});
};
$scope.removeMailAccount = function(index) {
$scope.preferences.defaults.AuxiliaryMailAccounts.splice(index, 1);
};
$scope.addMailLabel = function() {
$scope.preferences.defaults.SOGoMailLabelsColors["new_label"] = ["New label", "#aaa"];
};
$scope.removeMailLabel = function(key) {
delete $scope.preferences.defaults.SOGoMailLabelsColors[key];
};
$scope.addMailFilter = function(ev) {
if (!$scope.preferences.defaults.SOGoSieveFilters)
$scope.preferences.defaults.SOGoSieveFilters = [];
$scope.preferences.defaults.SOGoSieveFilters.push({});
var filter = _.last($scope.preferences.defaults.SOGoSieveFilters);
$mdDialog.show({
controller: FiltersDialogCtrl,
templateUrl: 'editFilter?filter=new',
targetEvent: ev,
locals: { filter: filter,
mailboxes: $scope.preferences.mailboxes,
labels: $scope.preferences.defaults.SOGoMailLabelsColors}
});
};
$scope.editMailFilter = function(index) {
var filter = angular.copy($scope.preferences.defaults.SOGoSieveFilters[index]);
$mdDialog.show({
controller: FiltersDialogCtrl,
templateUrl: 'editFilter?filter=' + index,
targetEvent: null,
locals: { filter: filter,
mailboxes: $scope.preferences.mailboxes,
labels: $scope.preferences.defaults.SOGoMailLabelsColors }
}).then(function() {
$scope.preferences.defaults.SOGoSieveFilters[index] = filter;
});
};
$scope.removeMailFilter = function(index) {
$scope.preferences.defaults.SOGoSieveFilters.splice(index, 1);
};
$scope.userFilter = function($query) {
var deferred = $q.defer();
User.$filter($query).then(function(results) {
deferred.resolve(results)
});
return deferred.promise;
};
$scope.save = function() {
$scope.preferences.$save();
};
$scope.passwords = { newPassword: null, newPasswordConfirmation: null };
$scope.canChangePassword = function() {
if ($scope.passwords.newPassword && $scope.passwords.newPassword.length > 0 &&
$scope.passwords.newPasswordConfirmation && $scope.passwords.newPasswordConfirmation.length &&
$scope.passwords.newPassword == $scope.passwords.newPasswordConfirmation)
return true;
return false;
};
$scope.changePassword = function() {
Authentication.changePassword($scope.passwords.newPassword).then(function() {
var alert = $mdDialog.alert({
title: l('Password'),
content: l('The password was changed successfully.'),
ok: 'OK'
});
$mdDialog.show( alert )
.finally(function() {
alert = undefined;
});
}, function(msg) {
var alert = $mdDialog.alert({
title: l('Password'),
content: msg,
ok: 'OK'
});
$mdDialog.show( alert )
.finally(function() {
alert = undefined;
});
});
};
}]);
function FiltersDialogCtrl($scope, $mdDialog, filter, mailboxes, labels) {
$scope.filter = filter;
$scope.mailboxes = mailboxes;
$scope.labels = labels;
$scope.fieldLabels = { "subject": l("Subject"),
"from": l("From"),
"to": l("To"),
"cc": l("Cc"),
"to_or_cc": l("To or Cc"),
"size": l("Size (Kb)"),
"header": l("Header"),
"body": l("Body") };
$scope.methodLabels = { "addflag": l("Flag the message with:"),
"discard": l("Discard the message"),
"fileinto": l("File the message in:"),
"keep": l("Keep the message"),
"redirect": l("Forward the message to:"),
"reject": l("Send a reject message:"),
"vacation": l("Send a vacation message"),
"stop": l("Stop processing filter rules") };
$scope.numberOperatorLabels = { "under": l("is under"),
"over": l("is over") };
$scope.textOperatorLabels = { "is": l("is"),
"is_not": l("is not"),
"contains": l("contains"),
"contains_not": l("does not contain"),
"matches": l("matches"),
"matches_not": l("does not match"),
"regex": l("matches regex"),
"regex_not": l("does not match regex") };
$scope.flagLabels = { "seen": l("Seen"),
"deleted": l("Deleted"),
"answered": l("Answered"),
"flagged": l("Flagged"),
"junk": l("Junk"),
"not_junk": l("Not Junk") };
$scope.cancel = function() {
$mdDialog.cancel();
};
$scope.save = function() {
$mdDialog.hide();
};
$scope.addMailFilterRule = function(event) {
if (!$scope.filter.rules)
$scope.filter.rules = [];
$scope.filter.rules.push({});
}
$scope.removeMailFilterRule = function(index) {
$scope.filter.rules.splice(index, 1);
};
$scope.addMailFilterAction = function(event) {
if (!$scope.filter.actions)
$scope.filter.actions = [];
$scope.filter.actions.push({});
}
$scope.removeMailFilterAction = function(index) {
$scope.filter.actions.splice(index, 1);
};
}
function AccountDialogCtrl($scope, $mdDialog, account, accountId, mailCustomFromEnabled) {
$scope.account = account;
$scope.accountId = accountId;
$scope.customFromIsReadonly = function() {
if (accountId > 0)
return false;
return !mailCustomFromEnabled;
};
$scope.cancel = function() {
$mdDialog.cancel();
};
$scope.save = function() {
$mdDialog.hide();
};
}
})();

View File

@ -0,0 +1,159 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* JavaScript for SOGo.SchedulerUI module */
(function() {
'use strict';
angular.module('SOGo.ContactsUI', []);
angular.module('SOGo.SchedulerUI', ['ngSanitize', 'ui.router', 'ct.ui.router.extras.sticky', 'ct.ui.router.extras.previous', 'vs-repeat', 'SOGo.Common', 'SOGo.ContactsUI'])
.constant('sgSettings', {
baseURL: ApplicationBaseURL,
activeUser: {
login: UserLogin,
identification: UserIdentification,
language: UserLanguage,
folderURL: UserFolderURL,
isSuperUser: IsSuperUser
}
})
.config(configure)
.run(runBlock);
/**
* @ngInject
*/
configure.$inject = ['$stateProvider', '$urlRouterProvider'];
function configure($stateProvider, $urlRouterProvider) {
$stateProvider
.state('calendars', {
url: '/calendar',
views: {
calendars: {
templateUrl: 'UIxCalMainFrame', // UI/Templates/SchedulerUI/UIxCalMainFrame.wox
controller: 'CalendarsController',
controllerAs: 'calendars'
}
},
resolve: {
stateCalendars: stateCalendars
}
})
.state('calendars.view', {
url: '/{view:(?:day|week|month)}/:day',
sticky: true,
deepStateRedirect: true,
views: {
calendarView: {
templateUrl: function($stateParams) {
// UI/Templates/SchedulerUI/UIxCalDayView.wox or
// UI/Templates/SchedulerUI/UIxCalWeekView.wox or
// UI/Templates/SchedulerUI/UIxCalMonthView.wox
return $stateParams.view + 'view?day=' + $stateParams.day;
},
controller: 'CalendarController',
controllerAs: 'calendar'
}
},
resolve: {
stateEventsBlocks: stateEventsBlocks
}
})
.state('calendars.newComponent', {
url: '/:calendarId/{componentType:(?:appointment|task)}/new',
views: {
componentEditor: {
templateUrl: 'UIxAppointmentEditorTemplate',
controller: 'ComponentController',
controllerAs: 'editor'
}
},
resolve: {
stateComponent: stateNewComponent
}
})
.state('calendars.component', {
url: '/:calendarId/event/:componentId',
views: {
componentEditor: {
templateUrl: 'UIxAppointmentEditorTemplate',
controller: 'ComponentController',
controllerAs: 'editor'
}
},
// onEnter: ['$mdSidenav', function($mdSidenav) {
// $mdSidenav('right').open()
// .then(function() {
// console.debug("toggle RIGHT is done");
// });
// }],
resolve: {
stateComponent: stateComponent
}
});
$urlRouterProvider.when('/calendar/day', function() {
// If no date is specified, show today
var now = new Date();
return '/calendar/day/' + now.getDayString();
})
$urlRouterProvider.when('/calendar/week', function() {
// If no date is specified, show today's week
var now = new Date();
return '/calendar/week/' + now.getDayString();
})
$urlRouterProvider.when('/calendar/month', function() {
// If no date is specified, show today's month
var now = new Date();
return '/calendar/month/' + now.getDayString();
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/calendar');
}
/**
* @ngInject
*/
stateCalendars.$inject = ['Calendar'];
function stateCalendars(Calendar) {
return Calendar.$calendars || Calendar.$findAll(window.calendarsData);
}
/**
* @ngInject
*/
stateEventsBlocks.$inject = ['$stateParams', 'Component'];
function stateEventsBlocks($stateParams, Component) {
return Component.$eventsBlocksForView($stateParams.view, $stateParams.day.asDate());
}
/**
* @ngInject
*/
stateNewComponent.$inject = ['$stateParams', 'Component'];
function stateNewComponent($stateParams, Component) {
var component = new Component({ pid: $stateParams.calendarId, type: $stateParams.componentType });
return component;
}
/**
* @ngInject
*/
stateComponent.$inject = ['$stateParams', 'Calendar'];
function stateComponent($stateParams, Calendar) {
return Calendar.$get($stateParams.calendarId).$getComponent($stateParams.componentId);
}
/**
* @ngInject
*/
runBlock.$inject = ['$rootScope'];
function runBlock($rootScope) {
$rootScope.$on('$routeChangeError', function(event, current, previous, rejection) {
console.error(event, current, previous, rejection);
});
}
})();

View File

@ -26,7 +26,7 @@
* @desc The factory we'll use to register with Angular
* @returns the Calendar constructor
*/
Calendar.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'sgResource', 'sgComponent', 'sgAcl', function($q, $timeout, $log, Settings, Resource, Component, Acl) {
Calendar.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'Resource', 'Component', 'Acl', function($q, $timeout, $log, Settings, Resource, Component, Acl) {
angular.extend(Calendar, {
$q: $q,
$timeout: $timeout,
@ -42,7 +42,7 @@
/* Factory registration in Angular module */
angular.module('SOGo.SchedulerUI')
.factory('sgCalendar', Calendar.$factory);
.factory('Calendar', Calendar.$factory);
/**
* @memberof Calendar

View File

@ -0,0 +1,33 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* @ngInject
*/
CalendarController.$inject = ['$scope', '$state', '$stateParams', '$timeout', '$interval', '$log', 'sgFocus', 'Calendar', 'Component', 'stateEventsBlocks'];
function CalendarController($scope, $state, $stateParams, $timeout, $interval, $log, focus, Calendar, Component, stateEventsBlocks) {
var vm = this;
vm.blocks = stateEventsBlocks;
vm.changeView = changeView;
// Refresh current view when the list of calendars is modified
$scope.$on('calendars:list', function() {
Component.$eventsBlocksForView($stateParams.view, $stateParams.day.asDate()).then(function(data) {
vm.blocks = data;
});
});
// Change calendar's view
function changeView($event) {
var date = angular.element($event.currentTarget).attr('date');
$state.go('calendars.view', { view: $stateParams.view, day: date });
}
}
angular
.module('SOGo.SchedulerUI')
.controller('CalendarController', CalendarController);
})();

View File

@ -0,0 +1,48 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* @ngInject
*/
CalendarListController.$inject = ['$scope', '$rootScope', '$timeout', '$state', 'sgFocus', 'encodeUriFilter', 'Dialog', 'sgSettings', 'Calendar', 'Component', '$mdSidenav'];
function CalendarListController($scope, $rootScope, $timeout, $state, focus, encodeUriFilter, Dialog, Settings, Calendar, Component, $mdSidenav) {
var vm = this;
vm.component = Component;
vm.componentType = null;
vm.selectComponentType = selectComponentType;
vm.newComponent = newComponent;
// TODO: should reflect last state userSettings -> Calendar -> SelectedList
vm.selectedList = 0;
vm.selectComponentType('events');
// Switch between components tabs
function selectComponentType(type, options) {
if (options && options.reload || vm.componentType != type) {
// TODO: save user settings (Calendar.SelectedList)
Component.$filter(type);
vm.componentType = type;
}
}
function newComponent() {
var type = 'appointment';
if (vm.componentType == 'tasks')
type = 'task';
$state.go('calendars.newComponent', { calendarId: 'personal', componentType: type });
}
// Refresh current list when the list of calendars is modified
$scope.$on('calendars:list', function() {
Component.$filter(vm.componentType);
});
}
angular
.module('SOGo.SchedulerUI')
.controller('CalendarListController', CalendarListController);
})();

View File

@ -0,0 +1,179 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* @ngInject
*/
CalendarsController.$inject = ['$scope', '$rootScope', '$stateParams', '$state', '$timeout', '$q', '$mdDialog', '$log', 'sgFocus', 'encodeUriFilter', 'Dialog', 'sgSettings', 'Calendar', 'User', 'stateCalendars'];
function CalendarsController($scope, $rootScope, $stateParams, $state, $timeout, $q, $mdDialog, $log, focus, encodeUriFilter, Dialog, Settings, Calendar, User, stateCalendars) {
var vm = this;
vm.activeUser = Settings.activeUser;
vm.service = Calendar;
vm.newCalendar = newCalendar;
vm.addWebCalendar = addWebCalendar;
vm.confirmDelete = confirmDelete;
vm.share = share;
vm.subscribeToFolder = subscribeToFolder;
// Dispatch the event named 'calendars:list' when a calendar is activated or deactivated or
// when the color of a calendar is changed
$scope.$watch(
function() {
return _.union(
_.map(Calendar.$calendars, function(o) { return _.pick(o, ['id', 'active', 'color']) }),
_.map(Calendar.$subscriptions, function(o) { return _.pick(o, ['id', 'active', 'color']) }),
_.map(Calendar.$webcalendars, function(o) { return _.pick(o, ['id', 'active', 'color']) })
);
},
function(newList, oldList) {
// Identify which calendar has changed
var ids = _.pluck(_.filter(newList, function(o, i) { return !_.isEqual(o, oldList[i]); }), 'id');
if (ids.length > 0) {
$log.debug(ids.join(', ') + ' changed');
_.each(ids, function(id) {
var calendar = Calendar.$get(id);
calendar.$setActivation().then(function() {
$scope.$broadcast('calendars:list');
});
});
}
},
true // compare for object equality
);
function newCalendar(ev) {
Dialog.prompt(l('New calendar'), l('Name of the Calendar'))
.then(function(name) {
var calendar = new Calendar(
{
name: name,
isEditable: true,
isRemote: false,
owner: UserLogin
}
);
Calendar.$add(calendar);
});
}
function addWebCalendar() {
Dialog.prompt(l('Subscribe to a web calendar...'), l('URL of the Calendar'), {inputType: 'url'})
.then(function(url) {
Calendar.$addWebCalendar(url);
});
}
function confirmDelete(folder) {
if (folder.isSubscription) {
// Unsubscribe without confirmation
folder.$delete()
.then(function() {
$scope.$broadcast('calendars:list');
}, function(data, status) {
Dialog.alert(l('An error occured while deleting the addressbook "%{0}".', folder.name),
l(data.error));
});
}
else {
Dialog.confirm(l('Warning'), l('Are you sure you want to delete the addressbook <em>%{0}</em>?', folder.name))
.then(function() {
folder.$delete()
.then(function() {
$scope.$broadcast('calendars:list');
}, function(data, status) {
Dialog.alert(l('An error occured while deleting the addressbook "%{0}".', folder.name),
l(data.error));
});
});
}
}
function share(calendar) {
$mdDialog.show({
templateUrl: calendar.id + '/UIxAclEditor', // UI/Templates/UIxAclEditor.wox
controller: CalendarACLController,
clickOutsideToClose: true,
escapeToClose: true,
locals: {
usersWithACL: calendar.$acl.$users(),
User: User,
folder: calendar
}
});
/**
* @ngInject
*/
CalendarACLController.$inject = ['$scope', '$mdDialog', 'usersWithACL', 'User', 'folder'];
function CalendarACLController($scope, $mdDialog, usersWithACL, User, folder) {
$scope.users = usersWithACL; // ACL users
$scope.folder = folder;
$scope.selectedUser = null;
$scope.userToAdd = '';
$scope.searchText = '';
$scope.userFilter = function($query) {
return User.$filter($query);
};
$scope.closeModal = function() {
folder.$acl.$resetUsersRights(); // cancel changes
$mdDialog.hide();
};
$scope.saveModal = function() {
folder.$acl.$saveUsersRights().then(function() {
$mdDialog.hide();
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'));
});
};
$scope.confirmChange = function(user) {
var confirmation = user.$confirmRights();
if (confirmation) {
Dialog.confirm(l('Warning'), confirmation).then(function(res) {
if (!res)
user.$resetRights(true);
});
}
};
$scope.removeUser = function(user) {
folder.$acl.$removeUser(user.uid).then(function() {
if (user.uid == $scope.selectedUser.uid)
$scope.selectedUser = null;
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
};
$scope.addUser = function(data) {
if (data) {
folder.$acl.$addUser(data).then(function() {
$scope.userToAdd = '';
$scope.searchText = '';
}, function(error) {
Dialog.alert(l('Warning'), error);
});
}
};
$scope.selectUser = function(user) {
// Check if it is a different user
if ($scope.selectedUser != user) {
$scope.selectedUser = user;
$scope.selectedUser.$rights();
}
};
};
};
// Callback of sgSubscribe directive
function subscribeToFolder(calendarData) {
$log.debug('subscribeToFolder ' + calendarData.owner + calendarData.name);
Calendar.$subscribe(calendarData.owner, calendarData.name).catch(function(data) {
Dialog.alert(l('Warning'), l('An error occured please try again.'));
});
}
}
angular
.module('SOGo.SchedulerUI')
.controller('CalendarsController', CalendarsController);
})();

View File

@ -31,7 +31,7 @@
* @desc The factory we'll use to register with Angular
* @returns the Component constructor
*/
Component.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'sgResource', function($q, $timeout, $log, Settings, Resource) {
Component.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'Resource', function($q, $timeout, $log, Settings, Resource) {
angular.extend(Component, {
$q: $q,
$timeout: $timeout,
@ -49,7 +49,7 @@
*/
angular.module('SOGo.SchedulerUI')
/* Factory registration in Angular module */
.factory('sgComponent', Component.$factory);
.factory('Component', Component.$factory);
/**
* @function $filter
@ -331,13 +331,29 @@
* @return an object literal copy of the Component instance
*/
Component.prototype.$omit = function() {
var component = {};
var component = {}, date;
angular.forEach(this, function(value, key) {
if (key != 'constructor' && key[0] != '$') {
component[key] = value;
}
});
component.startTime = component.startDate ? formatTime(component.startDate) : '';
component.endTime = component.endDate ? formatTime(component.endDate) : '';
function formatTime(dateString) {
// YYYY-MM-DDTHH:MM-05:00
var date = new Date(dateString.substring(0,10) + ' ' + dateString.substring(11,16)),
hours = date.getHours(),
minutes = date.getMinutes();
if (hours < 10) hours = '0' + hours;
if (minutes < 10) minutes = '0' + minutes;
return hours + ':' + minutes;
}
return component;
};

View File

@ -0,0 +1,73 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/**
* @ngInject
*/
ComponentController.$inject = ['$scope', '$log', '$timeout', '$state', '$previousState', '$mdSidenav', '$mdDialog', 'Calendar', 'Component', 'stateCalendars', 'stateComponent'];
function ComponentController($scope, $log, $timeout, $state, $previousState, $mdSidenav, $mdDialog, Calendar, Component, stateCalendars, stateComponent) {
var vm = this;
vm.calendars = stateCalendars;
vm.event = stateComponent;
vm.categories = {};
vm.editRecurrence = editRecurrence;
vm.cancel = cancel;
vm.save = save;
// Open sidenav when loading the view;
// Return to previous state when closing the sidenav.
$scope.$on('$viewContentLoaded', function(event) {
$timeout(function() {
$mdSidenav('right').open()
.then(function() {
$scope.$watch($mdSidenav('right').isOpen, function(isOpen, wasOpen) {
if (!isOpen) {
if ($previousState.get())
$previousState.go()
else
$state.go('calendars');
}
});
});
}, 100); // don't ask why
});
function editRecurrence($event) {
$mdDialog.show({
templateUrl: 'editRecurrence', // UI/Templates/SchedulerUI/UIxRecurrenceEditor.wox
controller: RecurrenceController
});
function RecurrenceController() {
}
}
function save(form) {
if (form.$valid) {
vm.event.$save()
.then(function(data) {
$scope.$emit('calendars:list');
$mdSidenav('right').close();
}, function(data, status) {
$log.debug('failed');
});
}
}
function cancel() {
vm.event.$reset();
if (vm.event.isNew) {
// Cancelling the creation of a component
vm.event = null;
}
$mdSidenav('right').close();
}
}
angular
.module('SOGo.SchedulerUI')
.controller('ComponentController', ComponentController);
})();

View File

@ -0,0 +1,69 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/*
* sgCalendarDayBlock - An event block to be displayed in a week
* @memberof SOGo.Common
* @restrict element
* @param {object} sgBlock - the event block definition
* @ngInject
* @example:
<sg-calendar-day-block
ng-repeat="block in blocks[day]"
sg-block="block"/>
*/
function sgCalendarDayBlock() {
return {
restrict: 'E',
scope: {
block: '=sgBlock'
},
replace: true,
template: [
'<div class="event draggable">',
' <div class="eventInside">',
' <div class="gradient">',
' </div>',
' <div class="text">{{ block.component.c_title }}',
' <span class="icons">',
' <i ng-if="block.component.c_nextalarm" class="md-icon-alarm"></i>',
' <i ng-if="block.component.c_classification == 1" class="md-icon-visibility-off"></i>',
' <i ng-if="block.component.c_classification == 2" class="md-icon-vpn-key"></i>',
' </span></div>',
' </div>',
' <div class="topDragGrip"></div>',
' <div class="bottomDragGrip"></div>',
'</div>'
].join(''),
link: link
};
function link(scope, iElement, attrs) {
// Compute overlapping (5%)
var pc = 100 / scope.block.siblings,
left = scope.block.position * pc,
right = 100 - (scope.block.position + 1) * pc;
if (pc < 100) {
if (left > 0)
left -= 5;
if (right > 0)
right -= 5;
}
// Set position
iElement.css('left', left + '%');
iElement.css('right', right + '%');
iElement.addClass('starts' + scope.block.start);
iElement.addClass('lasts' + scope.block.length);
iElement.addClass('bg-folder' + scope.block.component.c_folder);
}
}
angular
.module('SOGo.SchedulerUI')
.directive('sgCalendarDayBlock', sgCalendarDayBlock);
})();

View File

@ -0,0 +1,37 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/*
* sgCalendarDayTable - Build list of blocks for a specific day
* @memberof SOGo.Common
* @restrict element
* @param {object} sgBlocks - the events blocks definitions for the current view
* @param {string} sgDay - the day of the events to display
* @ngInject
* @example:
<sg-calendar-day-table
sg-blocks="calendar.blocks"
sg-day="20150330" />
*/
function sgCalendarDayTable() {
return {
restrict: 'E',
scope: {
blocks: '=sgBlocks',
day: '@sgDay'
},
template: [
'<sg-calendar-day-block class="event draggable"',
' ng-repeat="block in blocks[day]"',
' sg-block="block"/>'
].join('')
};
}
angular
.module('SOGo.SchedulerUI')
.directive('sgCalendarDayTable', sgCalendarDayTable);
})();

View File

@ -0,0 +1,38 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/*
* sgCalendarMonthDay - Build list of blocks for a specific day in a month
* @memberof SOGo.Common
* @restrict element
* @param {object} sgBlocks - the events blocks definitions for the current view
* @param {string} sgDay - the day of the events to display
* @ngInject
* @example:
<sg-calendar-monh-day
sg-blocks="calendar.blocks"
sg-day="20150408" />
*/
function sgCalendarMonthDay() {
return {
restrict: 'E',
scope: {
blocks: '=sgBlocks',
day: '@sgDay'
},
replace: true,
template: [
'<sg-calendar-month-event',
' ng-repeat="block in blocks[day]"',
' sg-block="block"/>'
].join('')
};
}
angular
.module('SOGo.SchedulerUI')
.directive('sgCalendarMonthDay', sgCalendarMonthDay);
})();

View File

@ -0,0 +1,50 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
(function() {
'use strict';
/*
* sgCalendarMonthEvent - An event block to be displayed in a month
* @memberof SOGo.Common
* @restrict element
* @param {object} sgBlock - the event block definition
* @ngInject
* @example:
<sg-calendar-month-event
ng-repeat="block in blocks[day]"
sg-block="block"/>
*/
function sgCalendarMonthEvent() {
return {
restrict: 'E',
scope: {
block: '=sgBlock'
},
replace: true,
template: [
'<div class="sg-event">',
' <span ng-if="!block.component.c_isallday">{{ block.starthour }} - </span>',
' {{ block.component.c_title }}',
' <span class="icons">',
' <i ng-if="block.component.c_nextalarm" class="md-icon-alarm"></i>',
' <i ng-if="block.component.c_classification == 1" class="md-icon-visibility-off"></i>',
' <i ng-if="block.component.c_classification == 2" class="md-icon-vpn-key"></i>',
' </span>',
' <div class="leftDragGrip"></div>',
' <div class="rightDragGrip"></div>',
' </div>',
'</div>'
].join(''),
link: link
};
function link(scope, iElement, attrs) {
iElement.addClass('bg-folder' + scope.block.component.c_folder);
}
}
angular
.module('SOGo.SchedulerUI')
.directive('sgCalendarMonthEvent', sgCalendarMonthEvent);
})();

View File

@ -1,391 +0,0 @@
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* JavaScript for SOGo.SchedulerUI module */
(function() {
'use strict';
angular.module('SOGo.Common', []);
angular.module('SOGo.ContactsUI', []);
angular.module('SOGo.SchedulerUI', ['ngSanitize', 'ui.router', 'ct.ui.router.extras.sticky', 'ct.ui.router.extras.previous', 'vs-repeat', 'SOGo.Common', 'SOGo.UI', 'SOGo.UIDesktop', 'SOGo.ContactsUI'])
.constant('sgSettings', {
baseURL: ApplicationBaseURL,
activeUser: {
login: UserLogin,
identification: UserIdentification,
language: UserLanguage,
folderURL: UserFolderURL,
isSuperUser: IsSuperUser
}
})
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('calendars', {
url: '/calendar',
views: {
calendars: {
templateUrl: 'UIxCalMainFrame', // UI/Templates/SchedulerUI/UIxCalMainFrame.wox
controller: 'CalendarsController',
controllerAs: 'calendars'
}
},
resolve: {
stateCalendars: ['sgCalendar', function(Calendar) {
return Calendar.$calendars || Calendar.$findAll(window.calendarsData);
}]
}
})
.state('calendars.view', {
url: '/{view:(?:day|week|month)}/:day',
sticky: true,
deepStateRedirect: true,
views: {
calendarView: {
templateUrl: function($stateParams) {
// UI/Templates/SchedulerUI/UIxCalDayView.wox or
// UI/Templates/SchedulerUI/UIxCalWeekView.wox or
// UI/Templates/SchedulerUI/UIxCalMonthView.wox
return $stateParams.view + 'view?day=' + $stateParams.day;
},
controller: 'CalendarController',
controllerAs: 'calendar'
}
},
resolve: {
stateEventsBlocks: ['$stateParams', 'sgComponent', function($stateParams, Component) {
return Component.$eventsBlocksForView($stateParams.view, $stateParams.day.asDate());
}]
}
})
.state('calendars.newComponent', {
url: '/:calendarId/{componentType:(?:appointment|task)}/new',
views: {
componentEditor: {
templateUrl: 'UIxAppointmentEditorTemplate',
controller: 'ComponentController',
controllerAs: 'editor'
}
},
resolve: {
stateComponent: ['$stateParams', 'sgComponent', function($stateParams, Component) {
var component = new Component({ pid: $stateParams.calendarId, type: $stateParams.componentType });
return component;
}]
}
})
.state('calendars.component', {
url: '/:calendarId/event/:componentId',
views: {
componentEditor: {
templateUrl: 'UIxAppointmentEditorTemplate',
controller: 'ComponentController',
controllerAs: 'editor'
}
},
resolve: {
stateComponent: ['$stateParams', 'sgCalendar', function($stateParams, Calendar) {
return Calendar.$get($stateParams.calendarId).$getComponent($stateParams.componentId);
}]
}
});
$urlRouterProvider.when('/calendar/day', function() {
// If no date is specified, show today
var now = new Date();
return '/calendar/day/' + now.getDayString();
})
$urlRouterProvider.when('/calendar/week', function() {
// If no date is specified, show today's week
var now = new Date();
return '/calendar/week/' + now.getDayString();
})
$urlRouterProvider.when('/calendar/month', function() {
// If no date is specified, show today's month
var now = new Date();
return '/calendar/month/' + now.getDayString();
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/calendar');
}])
.run(function($rootScope) {
$rootScope.$on('$routeChangeError', function(event, current, previous, rejection) {
console.error(event, current, previous, rejection)
})
})
.controller('CalendarsController', ['$scope', '$rootScope', '$stateParams', '$state', '$timeout', '$q', '$mdDialog', '$log', 'sgFocus', 'encodeUriFilter', 'sgDialog', 'sgSettings', 'sgCalendar', 'sgUser', 'stateCalendars', function($scope, $rootScope, $stateParams, $state, $timeout, $q, $mdDialog, $log, focus, encodeUriFilter, Dialog, Settings, Calendar, User, stateCalendars) {
var vm = this;
vm.activeUser = Settings.activeUser;
vm.service = Calendar;
// Dispatch the event named 'calendars:list' when a calendar is activated or deactivated or
// when the color of a calendar is changed
$scope.$watch(
function() {
return _.union(
_.map(Calendar.$calendars, function(o) { return _.pick(o, ['id', 'active', 'color']) }),
_.map(Calendar.$subscriptions, function(o) { return _.pick(o, ['id', 'active', 'color']) }),
_.map(Calendar.$webcalendars, function(o) { return _.pick(o, ['id', 'active', 'color']) })
);
},
function(newList, oldList) {
// Identify which calendar has changed
var ids = _.pluck(_.filter(newList, function(o, i) { return !_.isEqual(o, oldList[i]); }), 'id');
if (ids.length > 0) {
$log.debug(ids.join(', ') + ' changed');
_.each(ids, function(id) {
var calendar = Calendar.$get(id);
calendar.$setActivation().then(function() {
$scope.$broadcast('calendars:list');
});
});
}
},
true // compare for object equality
);
vm.newCalendar = function(ev) {
Dialog.prompt(l('New calendar'), l('Name of the Calendar'))
.then(function(name) {
var calendar = new Calendar(
{
name: name,
isEditable: true,
isRemote: false,
owner: UserLogin
}
);
Calendar.$add(calendar);
});
};
vm.addWebCalendar = function() {
Dialog.prompt(l('Subscribe to a web calendar...'), l('URL of the Calendar'), {inputType: 'url'})
.then(function(url) {
Calendar.$addWebCalendar(url);
});
};
vm.confirmDelete = function(folder) {
if (folder.isSubscription) {
// Unsubscribe without confirmation
folder.$delete()
.then(function() {
$scope.$broadcast('calendars:list');
}, function(data, status) {
Dialog.alert(l('An error occured while deleting the addressbook "%{0}".', folder.name),
l(data.error));
});
}
else {
Dialog.confirm(l('Warning'), l('Are you sure you want to delete the addressbook <em>%{0}</em>?', folder.name))
.then(function() {
folder.$delete()
.then(function() {
$scope.$broadcast('calendars:list');
}, function(data, status) {
Dialog.alert(l('An error occured while deleting the addressbook "%{0}".', folder.name),
l(data.error));
});
});
}
};
vm.share = function(calendar) {
$mdDialog.show({
templateUrl: calendar.id + '/UIxAclEditor', // UI/Templates/UIxAclEditor.wox
controller: CalendarACLController,
clickOutsideToClose: true,
escapeToClose: true,
locals: {
usersWithACL: calendar.$acl.$users(),
User: User,
stateCalendar: calendar
}
});
function CalendarACLController($scope, $mdDialog, usersWithACL, User, stateCalendar) {
$scope.users = usersWithACL; // ACL users
$scope.stateCalendar = stateCalendar;
$scope.userToAdd = '';
$scope.searchText = '';
$scope.userFilter = function($query) {
return User.$filter($query);
};
$scope.closeModal = function() {
stateCalendar.$acl.$resetUsersRights(); // cancel changes
$mdDialog.hide();
};
$scope.saveModal = function() {
stateCalendar.$acl.$saveUsersRights().then(function() {
$mdDialog.hide();
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'));
});
};
$scope.confirmChange = function(user) {
var confirmation = user.$confirmRights();
if (confirmation) {
Dialog.confirm(l('Warning'), confirmation).then(function(res) {
if (!res)
user.$resetRights(true);
});
}
};
$scope.removeUser = function(user) {
stateCalendar.$acl.$removeUser(user.uid).then(function() {
if (user.uid == $scope.selectedUser.uid) {
$scope.selectedUser = null;
}
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
};
$scope.addUser = function(data) {
stateCalendar.$acl.$addUser(data).then(function() {
$scope.userToAdd = '';
$scope.searchText = '';
}, function(error) {
Dialog.alert(l('Warning'), error);
});
};
$scope.selectUser = function(user) {
// Check if it is a different user
if ($scope.selectedUser != user) {
$scope.selectedUser = user;
$scope.selectedUser.$rights();
}
};
};
};
// Callback of sgSubscribe directive
vm.subscribeToFolder = function(calendarData) {
$log.debug('subscribeToFolder ' + calendarData.owner + calendarData.name);
Calendar.$subscribe(calendarData.owner, calendarData.name).catch(function(data) {
Dialog.alert(l('Warning'), l('An error occured please try again.'));
});
};
}])
.controller('CalendarListController', ['$scope', '$rootScope', '$timeout', '$state', 'sgFocus', 'encodeUriFilter', 'sgDialog', 'sgSettings', 'sgCalendar', 'sgComponent', '$mdSidenav', function($scope, $rootScope, $timeout, $state, focus, encodeUriFilter, Dialog, Settings, Calendar, Component, $mdSidenav) {
var vm = this;
vm.component = Component;
vm.componentType = null;
vm.selectComponentType = selectComponentType;
vm.newComponent = newComponent;
// TODO: should reflect last state userSettings -> Calendar -> SelectedList
vm.selectedList = 0;
vm.selectComponentType('events');
// Switch between components tabs
function selectComponentType(type, options) {
if (options && options.reload || vm.componentType != type) {
// TODO: save user settings (Calendar.SelectedList)
Component.$filter(type);
vm.componentType = type;
}
}
function newComponent() {
var type = 'appointment';
if (vm.componentType == 'tasks')
type = 'task';
$state.go('calendars.newComponent', { calendarId: 'personal', componentType: type });
}
// Refresh current list when the list of calendars is modified
$scope.$on('calendars:list', function() {
Component.$filter(vm.componentType);
});
}])
.controller('CalendarController', ['$scope', '$state', '$stateParams', '$timeout', '$interval', '$log', 'sgFocus', 'sgCalendar', 'sgComponent', 'stateEventsBlocks', function($scope, $state, $stateParams, $timeout, $interval, $log, focus, Calendar, Component, stateEventsBlocks) {
var vm = this;
vm.blocks = stateEventsBlocks;
vm.changeView = changeView;
// Refresh current view when the list of calendars is modified
$scope.$on('calendars:list', function() {
Component.$eventsBlocksForView($stateParams.view, $stateParams.day.asDate()).then(function(data) {
vm.blocks = data;
});
});
// Change calendar's view
function changeView($event) {
var date = angular.element($event.currentTarget).attr('date');
$state.go('calendars.view', { view: $stateParams.view, day: date });
}
}])
.controller('ComponentController', ['$scope', '$log', '$timeout', '$state', '$previousState', '$mdSidenav', '$mdDialog', 'sgCalendar', 'sgComponent', 'stateCalendars', 'stateComponent', function($scope, $log, $timeout, $state, $previousState, $mdSidenav, $mdDialog, Calendar, Component, stateCalendars, stateComponent) {
var vm = this;
vm.calendars = stateCalendars;
vm.event = stateComponent;
vm.categories = {};
vm.editRecurrence = editRecurrence;
vm.cancel = cancel;
vm.save = save;
// Open sidenav when loading the view;
// Return to previous state when closing the sidenav.
$scope.$on('$viewContentLoaded', function(event) {
$timeout(function() {
$mdSidenav('right').open()
.then(function() {
$scope.$watch($mdSidenav('right').isOpen, function(isOpen, wasOpen) {
if (!isOpen) {
if ($previousState.get())
$previousState.go()
else
$state.go('calendars');
}
});
});
}, 100); // don't ask why
});
function editRecurrence($event) {
$mdDialog.show({
templateUrl: 'editRecurrence', // UI/Templates/SchedulerUI/UIxRecurrenceEditor.wox
controller: RecurrenceController
});
function RecurrenceController() {
}
}
function save(form) {
if (form.$valid) {
vm.event.$save()
.then(function(data) {
$scope.$emit('calendars:list');
$mdSidenav('right').close();
}, function(data, status) {
$log.debug('failed');
});
}
}
function cancel() {
vm.event.$reset();
if (vm.event.isNew) {
// Cancelling the creation of a component
vm.event = null;
}
$mdSidenav('right').close();
}
}]);
})();

View File

@ -7,11 +7,11 @@
"csswring": ">=3.0.0",
"grunt": ">=0.4.1",
"grunt-cli": ">=0.1.11",
"grunt-contrib-compass": "^1.0.1",
"grunt-concat-sourcemap": ">=0.4.3",
"grunt-contrib-watch": ">=0.5.3",
"grunt-ng-annotate": ">=0.10.0",
"grunt-postcss": "^0.3.0",
"grunt-sass": ">=0.18.0",
"kss": "^2.0.2",
"sassyjson": "^1.1.8"
"kss": "^2.0.2"
}
}