mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-06-05 10:29:43 +00:00
(js) New file structure for Angular modules
JavaScript files are now merged by the 'js' Grunt task.
This commit is contained in:
@@ -0,0 +1,297 @@
|
||||
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @name Calendar
|
||||
* @constructor
|
||||
* @param {object} futureCalendarData - either an object literal or a promise
|
||||
*/
|
||||
function Calendar(futureCalendarData) {
|
||||
// Data is immediately available
|
||||
this.init(futureCalendarData);
|
||||
if (this.name && !this.id) {
|
||||
// Create a new calendar on the server
|
||||
var newCalendarData = Calendar.$$resource.create('createFolder', this.name);
|
||||
angular.extend(this, newCalendarData);
|
||||
}
|
||||
if (this.id) {
|
||||
this.$acl = new Calendar.$$Acl('Calendar/' + this.id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberof Calendar
|
||||
* @desc The factory we'll use to register with Angular
|
||||
* @returns the Calendar constructor
|
||||
*/
|
||||
Calendar.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'Resource', 'Component', 'Acl', function($q, $timeout, $log, Settings, Resource, Component, Acl) {
|
||||
angular.extend(Calendar, {
|
||||
$q: $q,
|
||||
$timeout: $timeout,
|
||||
$log: $log,
|
||||
$$resource: new Resource(Settings.activeUser.folderURL + 'Calendar', Settings.activeUser),
|
||||
$Component: Component,
|
||||
$$Acl: Acl,
|
||||
activeUser: Settings.activeUser
|
||||
});
|
||||
|
||||
return Calendar; // return constructor
|
||||
}];
|
||||
|
||||
/* Factory registration in Angular module */
|
||||
angular.module('SOGo.SchedulerUI')
|
||||
.factory('Calendar', Calendar.$factory);
|
||||
|
||||
/**
|
||||
* @memberof Calendar
|
||||
* @desc Add a new calendar to the static list of calendars
|
||||
* @param {Calendar} calendar - an Calendar object instance
|
||||
*/
|
||||
Calendar.$add = function(calendar) {
|
||||
// Insert new calendar at proper index
|
||||
var list, sibling, i;
|
||||
|
||||
if (calendar.isWebCalendar)
|
||||
list = this.$webcalendars;
|
||||
else if (calendar.isSubscription)
|
||||
list = this.$subscriptions;
|
||||
else
|
||||
list = this.$calendars;
|
||||
|
||||
sibling = _.find(list, function(o) {
|
||||
return (o.id != 'personal'
|
||||
&& o.name.localeCompare(calendar.name) === 1);
|
||||
});
|
||||
i = sibling ? _.indexOf(_.pluck(list, 'id'), sibling.id) : 1;
|
||||
list.splice(i, 0, calendar);
|
||||
};
|
||||
|
||||
/**
|
||||
* @memberof Calendar
|
||||
* @desc Set or get the list of calendars. Will instanciate a new Calendar object for each item.
|
||||
* @param {object[]} [data] - the metadata of the calendars
|
||||
* @returns the list of calendars
|
||||
*/
|
||||
Calendar.$findAll = function(data) {
|
||||
var _this = this;
|
||||
if (data) {
|
||||
this.$calendars = [];
|
||||
this.$subscriptions = [];
|
||||
this.$webcalendars = [];
|
||||
// Instanciate Calendar objects
|
||||
angular.forEach(data, function(o, i) {
|
||||
var calendar = new Calendar(o);
|
||||
if (calendar.isWebCalendar)
|
||||
_this.$webcalendars.push(calendar);
|
||||
else if (calendar.isSubscription)
|
||||
_this.$subscriptions.push(calendar);
|
||||
else
|
||||
_this.$calendars.push(calendar);
|
||||
});
|
||||
}
|
||||
return this.$calendars;
|
||||
};
|
||||
|
||||
/**
|
||||
* @memberof Calendar
|
||||
* @desc Find a calendar among local instances (personal calendars and subscriptions).
|
||||
* @param {string} id - the calendar ID
|
||||
* @returns an object literal of the matching Calendar instance
|
||||
*/
|
||||
Calendar.$get = function(id) {
|
||||
var calendar;
|
||||
|
||||
calendar = _.find(Calendar.$calendars, function(o) { return o.id == id });
|
||||
if (!calendar)
|
||||
calendar = _.find(Calendar.$subscriptions, function(o) { return o.id == id });
|
||||
if (!calendar)
|
||||
calendar = _.find(Calendar.$webcalendars, function(o) { return o.id == id });
|
||||
|
||||
return calendar;
|
||||
};
|
||||
|
||||
/**
|
||||
* @memberOf Calendar
|
||||
* @desc Subscribe to another user's calendar and add it to the list of calendars.
|
||||
* @param {string} uid - user id
|
||||
* @param {string} path - path of folder for specified user
|
||||
* @returns a promise of the HTTP query result
|
||||
*/
|
||||
Calendar.$subscribe = function(uid, path) {
|
||||
var _this = this;
|
||||
return Calendar.$$resource.userResource(uid).fetch(path, 'subscribe').then(function(calendarData) {
|
||||
var calendar = new Calendar(calendarData);
|
||||
if (!_.find(_this.$subscriptions, function(o) {
|
||||
return o.id == calendarData.id;
|
||||
})) {
|
||||
Calendar.$add(calendar);
|
||||
}
|
||||
return calendar;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @memberOf Calendar
|
||||
* @desc Subscribe to a remote Web calendar
|
||||
* @param {string} url - URL of .ics file
|
||||
* @returns a promise of the HTTP query result
|
||||
*/
|
||||
Calendar.$addWebCalendar = function(url) {
|
||||
var _this = this,
|
||||
d = Calendar.$q.defer();
|
||||
|
||||
if (_.find(_this.$webcalendars, function(o) {
|
||||
return o.urls.webCalendarURL == url;
|
||||
})) {
|
||||
// Already subscribed
|
||||
d.reject();
|
||||
}
|
||||
else {
|
||||
Calendar.$$resource.post(null, 'addWebCalendar', { url: url }).then(function(calendarData) {
|
||||
angular.extend(calendarData, {
|
||||
isWebCalendar: true,
|
||||
isEditable: true,
|
||||
isRemote: false,
|
||||
owner: Calendar.activeUser.login,
|
||||
urls: { webCalendarURL: url }
|
||||
});
|
||||
var calendar = new Calendar(calendarData);
|
||||
Calendar.$add(calendar);
|
||||
Calendar.$$resource.fetch(calendar.id, 'reload').then(function(data) {
|
||||
// TODO: show a toast of the reload status
|
||||
Calendar.$log.debug(JSON.stringify(data, undefined, 2));
|
||||
});
|
||||
d.resolve();
|
||||
}, function() {
|
||||
d.reject();
|
||||
});
|
||||
}
|
||||
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* @function init
|
||||
* @memberof Calendar.prototype
|
||||
* @desc Extend instance with new data and compute additional attributes.
|
||||
* @param {object} data - attributes of calendar
|
||||
*/
|
||||
Calendar.prototype.init = function(data) {
|
||||
angular.extend(this, data);
|
||||
// Add 'isOwned' and 'isSubscription' attributes based on active user (TODO: add it server-side?)
|
||||
this.isOwned = Calendar.activeUser.isSuperUser || this.owner == Calendar.activeUser.login;
|
||||
this.isSubscription = !this.isRemote && this.owner != Calendar.activeUser.login;
|
||||
};
|
||||
|
||||
/**
|
||||
* @function getClassName
|
||||
* @memberof Calendar.prototype
|
||||
* @desc Return the calendar CSS class name based on its ID.
|
||||
* @returns a string representing the foreground CSS class name
|
||||
*/
|
||||
Calendar.prototype.getClassName = function(base) {
|
||||
if (angular.isUndefined(base))
|
||||
base = 'fg';
|
||||
return base + '-folder' + this.id;
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $rename
|
||||
* @memberof Calendar.prototype
|
||||
* @desc Rename the calendar and keep the list sorted
|
||||
* @param {string} name - the new name
|
||||
* @returns a promise of the HTTP operation
|
||||
*/
|
||||
Calendar.prototype.$rename = function(name) {
|
||||
var i = _.indexOf(_.pluck(Calendar.$calendars, 'id'), this.id);
|
||||
this.name = name;
|
||||
Calendar.$calendars.splice(i, 1);
|
||||
Calendar.$add(this);
|
||||
return this.$save();
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $delete
|
||||
* @memberof Calendar.prototype
|
||||
* @desc Delete the calendar from the server and the static list of calendars.
|
||||
* @returns a promise of the HTTP operation
|
||||
*/
|
||||
Calendar.prototype.$delete = function() {
|
||||
var _this = this,
|
||||
d = Calendar.$q.defer(),
|
||||
list,
|
||||
promise;
|
||||
|
||||
if (this.isSubscription) {
|
||||
promise = Calendar.$$resource.fetch(this.id, 'unsubscribe');
|
||||
list = Calendar.$subscriptions;
|
||||
}
|
||||
else {
|
||||
promise = Calendar.$$resource.remove(this.id);
|
||||
if (this.isWebCalendar)
|
||||
list = Calendar.$webcalendars;
|
||||
else
|
||||
list = Calendar.$calendars;
|
||||
}
|
||||
|
||||
promise.then(function() {
|
||||
var i = _.indexOf(_.pluck(list, 'id'), _this.id);
|
||||
list.splice(i, 1);
|
||||
d.resolve();
|
||||
}, function(data, status) {
|
||||
d.reject(data);
|
||||
});
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $save
|
||||
* @memberof Calendar.prototype
|
||||
* @desc Save the calendar properties to the server.
|
||||
* @returns a promise of the HTTP operation
|
||||
*/
|
||||
Calendar.prototype.$save = function() {
|
||||
return Calendar.$$resource.save(this.id, this.$omit()).then(function(data) {
|
||||
return data;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $setActivation
|
||||
* @memberof Calendar.prototype
|
||||
* @desc Either activate or deactivate the calendar.
|
||||
* @returns a promise of the HTTP operation
|
||||
*/
|
||||
Calendar.prototype.$setActivation = function() {
|
||||
return Calendar.$$resource.fetch(this.id, (this.active?'':'de') + 'activateFolder');
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $getComponent
|
||||
* @memberof Calendar.prototype
|
||||
* @desc Fetch the card attributes from the server.
|
||||
* @returns a promise of the HTTP operation
|
||||
*/
|
||||
Calendar.prototype.$getComponent = function(componentId) {
|
||||
return Calendar.$Component.$find(this.id, componentId);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $omit
|
||||
* @memberof Calendar.prototype
|
||||
* @desc Return a sanitized object used to send to the server.
|
||||
* @return an object literal copy of the Calendar instance
|
||||
*/
|
||||
Calendar.prototype.$omit = function() {
|
||||
var calendar = {};
|
||||
angular.forEach(this, function(value, key) {
|
||||
if (key != 'constructor' &&
|
||||
key[0] != '$') {
|
||||
calendar[key] = value;
|
||||
}
|
||||
});
|
||||
return calendar;
|
||||
};
|
||||
})();
|
||||
@@ -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);
|
||||
})();
|
||||
@@ -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);
|
||||
})();
|
||||
@@ -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);
|
||||
})();
|
||||
@@ -0,0 +1,360 @@
|
||||
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @name Component
|
||||
* @constructor
|
||||
* @param {object} futureComponentData - either an object literal or a promise
|
||||
*/
|
||||
function Component(futureComponentData) {
|
||||
// Data is immediately available
|
||||
if (typeof futureComponentData.then !== 'function') {
|
||||
this.init(futureComponentData);
|
||||
if (this.pid && !this.id) {
|
||||
// Prepare for the creation of a new component;
|
||||
// Get UID from the server.
|
||||
var newComponentData = Component.$$resource.newguid(this.pid);
|
||||
this.$unwrap(newComponentData);
|
||||
this.isNew = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The promise will be unwrapped first
|
||||
this.$unwrap(futureComponentData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberof Component
|
||||
* @desc The factory we'll use to register with Angular
|
||||
* @returns the Component constructor
|
||||
*/
|
||||
Component.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'Resource', function($q, $timeout, $log, Settings, Resource) {
|
||||
angular.extend(Component, {
|
||||
$q: $q,
|
||||
$timeout: $timeout,
|
||||
$log: $log,
|
||||
$$resource: new Resource(Settings.baseURL, Settings.activeUser),
|
||||
$categories: window.UserDefaults.SOGoCalendarCategoriesColors
|
||||
});
|
||||
|
||||
return Component; // return constructor
|
||||
}];
|
||||
|
||||
/**
|
||||
* @module SOGo.SchedulerUI
|
||||
* @desc Factory registration of Component in Angular module.
|
||||
*/
|
||||
angular.module('SOGo.SchedulerUI')
|
||||
/* Factory registration in Angular module */
|
||||
.factory('Component', Component.$factory);
|
||||
|
||||
/**
|
||||
* @function $filter
|
||||
* @memberof Component.prototype
|
||||
* @desc Search for components matching some criterias
|
||||
* @param {string} type - Either 'events' or 'tasks'
|
||||
* @param {object} [options] - additional options to the query
|
||||
* @returns a collection of Components instances
|
||||
*/
|
||||
Component.$filter = function(type, options) {
|
||||
var _this = this,
|
||||
now = new Date(),
|
||||
day = now.getDate(),
|
||||
month = now.getMonth() + 1,
|
||||
year = now.getFullYear(),
|
||||
defaultParams = {
|
||||
search: 'title_Category_Location',
|
||||
day: '' + year + (month < 10?'0':'') + month + (day < 10?'0':'') + day,
|
||||
filterpopup: 'view_thismonth'
|
||||
};
|
||||
|
||||
if (angular.isUndefined(this.$filterOptions))
|
||||
this.$filterOptions = defaultParams;
|
||||
if (options)
|
||||
angular.extend(this.$filterOptions, options);
|
||||
|
||||
var futureComponentData = this.$$resource.fetch(null, type + 'list', this.$filterOptions);
|
||||
|
||||
return this.$unwrapCollection(type, futureComponentData);
|
||||
};
|
||||
|
||||
/**
|
||||
* @memberof Card
|
||||
* @desc Fetch a card from a specific addressbook.
|
||||
* @param {string} addressbook_id - the addressbook ID
|
||||
* @param {string} card_id - the card ID
|
||||
* @see {@link AddressBook.$getCard}
|
||||
*/
|
||||
Component.$find = function(calendarId, componentId) {
|
||||
var futureComponentData = this.$$resource.fetch([calendarId, componentId].join('/'), 'view');
|
||||
|
||||
return new Component(futureComponentData);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function filterCategories
|
||||
* @memberof Component.prototype
|
||||
* @desc Search for categories matching some criterias
|
||||
* @param {string} search - the search string to match
|
||||
* @returns a collection of strings
|
||||
*/
|
||||
Component.filterCategories = function(query) {
|
||||
var re = new RegExp(query, 'i');
|
||||
return _.filter(_.keys(Component.$categories), function(category) {
|
||||
return category.search(re) != -1;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $eventsBlocksForView
|
||||
* @memberof Component.prototype
|
||||
* @desc Events blocks for a specific week
|
||||
* @param {string} view - Either 'day' or 'week'
|
||||
* @param {Date} type - Date of any day of the desired period
|
||||
* @returns a promise of a collection of objects describing the events blocks
|
||||
*/
|
||||
Component.$eventsBlocksForView = function(view, date) {
|
||||
var viewAction, startDate, endDate, params;
|
||||
|
||||
if (view == 'day') {
|
||||
viewAction = 'dayView';
|
||||
startDate = endDate = date;
|
||||
}
|
||||
else if (view == 'week') {
|
||||
viewAction = 'weekView';
|
||||
startDate = date.beginOfWeek();
|
||||
endDate = new Date();
|
||||
endDate.setTime(startDate.getTime());
|
||||
endDate.addDays(6);
|
||||
}
|
||||
else if (view == 'month') {
|
||||
viewAction = 'monthView';
|
||||
startDate = date;
|
||||
startDate.setDate(1);
|
||||
startDate = startDate.beginOfWeek();
|
||||
endDate = new Date();
|
||||
endDate.setTime(startDate.getTime());
|
||||
endDate.setMonth(endDate.getMonth() + 1);
|
||||
endDate.addDays(-1);
|
||||
endDate = endDate.endOfWeek();
|
||||
}
|
||||
return this.$eventsBlocks(viewAction, startDate, endDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $eventsBlocks
|
||||
* @memberof Component.prototype
|
||||
* @desc Events blocks for a specific view and period
|
||||
* @param {string} view - Either 'day' or 'week'
|
||||
* @param {Date} startDate - period's start date
|
||||
* @param {Date} endDate - period's end date
|
||||
* @returns a promise of a collection of objects describing the events blocks
|
||||
*/
|
||||
Component.$eventsBlocks = function(view, startDate, endDate) {
|
||||
var params, futureComponentData, i,
|
||||
deferred = Component.$q.defer();
|
||||
|
||||
params = { view: view.toLowerCase(), sd: startDate.getDayString(), ed: endDate.getDayString() };
|
||||
Component.$log.debug('eventsblocks ' + JSON.stringify(params, undefined, 2));
|
||||
futureComponentData = this.$$resource.fetch(null, 'eventsblocks', params);
|
||||
futureComponentData.then(function(data) {
|
||||
Component.$timeout(function() {
|
||||
var components = [], blocks = {};
|
||||
|
||||
// Instantiate Component objects
|
||||
_.reduce(data.events, function(objects, eventData, i) {
|
||||
var componentData = _.object(data.eventsFields, eventData),
|
||||
start = new Date(componentData.c_startdate * 1000);
|
||||
componentData.hour = start.getHourString();
|
||||
objects.push(new Component(componentData));
|
||||
return objects;
|
||||
}, components);
|
||||
|
||||
// Associate Component objects to blocks positions
|
||||
_.each(_.flatten(data.blocks), function(block) {
|
||||
block.component = components[block.nbr];
|
||||
});
|
||||
|
||||
// Convert array of blocks to object with days as keys
|
||||
for (i = 0; i < data.blocks.length; i++) {
|
||||
blocks[startDate.getDayString()] = data.blocks[i];
|
||||
startDate.addDays(1);
|
||||
}
|
||||
|
||||
Component.$log.debug('blocks ready (' + _.keys(blocks).length + ')');
|
||||
|
||||
// Save the blocks to the object model
|
||||
Component.$blocks = blocks;
|
||||
|
||||
deferred.resolve(blocks);
|
||||
});
|
||||
}, deferred.reject);
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $unwrap
|
||||
* @memberof Comonent.prototype
|
||||
* @desc Unwrap a promise and instanciate new Component objects using received data.
|
||||
* @param {promise} futureComponentData - a promise of the components' metadata
|
||||
* @returns a promise of the HTTP operation
|
||||
*/
|
||||
Component.$unwrapCollection = function(type, futureComponentData) {
|
||||
var _this = this,
|
||||
deferred = Component.$q.defer(),
|
||||
components = [];
|
||||
|
||||
futureComponentData.then(function(data) {
|
||||
Component.$timeout(function() {
|
||||
var fields = _.invoke(data.fields, 'toLowerCase');
|
||||
|
||||
// Instanciate Component objects
|
||||
_.reduce(data[type], function(components, componentData, i) {
|
||||
var data = _.object(fields, componentData);
|
||||
components.push(new Component(data));
|
||||
return components;
|
||||
}, components);
|
||||
|
||||
Component.$log.debug('list of ' + type + ' ready (' + components.length + ')');
|
||||
|
||||
// Save the list of components to the object model
|
||||
Component['$' + type] = components;
|
||||
|
||||
deferred.resolve(components);
|
||||
});
|
||||
}, function(data) {
|
||||
deferred.reject();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* @function init
|
||||
* @memberof Component.prototype
|
||||
* @desc Extend instance with required attributes and new data.
|
||||
* @param {object} data - attributes of component
|
||||
*/
|
||||
Component.prototype.init = function(data) {
|
||||
this.categories = [];
|
||||
angular.extend(this, data);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function getClassName
|
||||
* @memberof Component.prototype
|
||||
* @desc Return the component CSS class name based on its container (calendar) ID.
|
||||
* @param {string} [base] - the prefix to add to the class name (defaults to "fg")
|
||||
* @returns a string representing the foreground CSS class name
|
||||
*/
|
||||
Component.prototype.getClassName = function(base) {
|
||||
if (angular.isUndefined(base))
|
||||
base = 'fg';
|
||||
return base + '-folder' + (this.pid || this.c_folder);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $reset
|
||||
* @memberof Card.prototype
|
||||
* @desc Reset the original state the card's data.
|
||||
*/
|
||||
Component.prototype.$reset = function() {
|
||||
var _this = this;
|
||||
angular.forEach(this, function(value, key) {
|
||||
if (key != 'constructor' && key[0] != '$') {
|
||||
delete _this[key];
|
||||
}
|
||||
});
|
||||
angular.extend(this, this.$shadowData);
|
||||
this.$shadowData = this.$omit(true);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $save
|
||||
* @memberof Component.prototype
|
||||
* @desc Save the component to the server.
|
||||
*/
|
||||
Component.prototype.$save = function() {
|
||||
var _this = this, options;
|
||||
|
||||
if (this.isNew)
|
||||
options = { action: 'saveAs' + this.type.capitalize() };
|
||||
|
||||
return Component.$$resource.save([this.pid, this.id].join('/'), this.$omit(), options)
|
||||
.then(function(data) {
|
||||
// Make a copy of the data for an eventual reset
|
||||
_this.$shadowData = _this.$omit(true);
|
||||
return data;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $unwrap
|
||||
* @memberof Component.prototype
|
||||
* @desc Unwrap a promise.
|
||||
* @param {promise} futureComponentData - a promise of some of the Component's data
|
||||
*/
|
||||
Component.prototype.$unwrap = function(futureComponentData) {
|
||||
var _this = this,
|
||||
deferred = Component.$q.defer();
|
||||
|
||||
// Expose the promise
|
||||
this.$futureComponentData = futureComponentData;
|
||||
|
||||
// Resolve the promise
|
||||
this.$futureComponentData.then(function(data) {
|
||||
// Calling $timeout will force Angular to refresh the view
|
||||
Component.$timeout(function() {
|
||||
_this.init(data);
|
||||
// Make a copy of the data for an eventual reset
|
||||
_this.$shadowData = _this.$omit();
|
||||
deferred.resolve(_this);
|
||||
});
|
||||
}, function(data) {
|
||||
angular.extend(_this, data);
|
||||
_this.isError = true;
|
||||
Component.$log.error(_this.error);
|
||||
deferred.reject();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $omit
|
||||
* @memberof Component.prototype
|
||||
* @desc Return a sanitized object used to send to the server.
|
||||
* @return an object literal copy of the Component instance
|
||||
*/
|
||||
Component.prototype.$omit = function() {
|
||||
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;
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -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);
|
||||
})();
|
||||
@@ -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);
|
||||
})();
|
||||
@@ -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);
|
||||
})();
|
||||
@@ -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);
|
||||
})();
|
||||
@@ -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);
|
||||
})();
|
||||
Reference in New Issue
Block a user