diff --git a/UI/WebServerResources/js/Appointments/calendar-model.js b/UI/WebServerResources/js/Appointments/calendar-model.js new file mode 100644 index 000000000..6e3293eef --- /dev/null +++ b/UI/WebServerResources/js/Appointments/calendar-model.js @@ -0,0 +1,191 @@ +/* -*- 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 + angular.extend(this, 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', 'sgResource', 'sgCard', 'sgAcl', function($q, $timeout, $log, Settings, Resource, Card, Acl) { + angular.extend(Calendar, { + $q: $q, + $timeout: $timeout, + $log: $log, + $$resource: new Resource(Settings.activeUser.folderURL + 'Calendar', Settings.activeUser), + $Card: Card, + $$Acl: Acl, + activeUser: Settings.activeUser + }); + + return Calendar; // return constructor + }]; + + /* Factory registration in Angular module */ + angular.module('SOGo.SchedulerUI') + .factory('sgCalendar', 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 sibling, i; + + calendar.isOwned = this.activeUser.isSuperUser || calendar.owner == this.activeUser.login; + calendar.isSubscription = calendar.owner != this.activeUser.login; + sibling = _.find(this.$calendars, function(o) { + return (o.isRemote + || (!calendar.isSubscription && o.isSubscription) + || (o.id != 'personal' + && o.isSubscription === calendar.isSubscription + && o.name.localeCompare(calendar.name) === 1)); + }); + i = sibling ? _.indexOf(_.pluck(this.$calendars, 'id'), sibling.id) : 1; + this.$calendars.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 = data; + // Instanciate Calendar objects + angular.forEach(this.$calendars, function(o, i) { + _this.$calendars[i] = new Calendar(o); + // Add 'isOwned' and 'isSubscription' attributes based on active user (TODO: add it server-side?) + // _this.$calendars[i].isSubscription = _this.$calendars[i].owner != _this.activeUser.login; + // _this.$calendars[i].isOwned = _this.activeUser.isSuperUser + // || _this.$calendars[i].owner == _this.activeUser.login; + }); + } + return this.$calendars; + }; + + /** + * @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.$calendars, function(o) { + return o.id == calendarData.id; + })) { + Calendar.$add(calendar); + } + return calendar; + }); + }; + + /** + * @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(), + promise; + + if (this.isSubscription) + promise = Calendar.$$resource.fetch(this.id, 'unsubscribe'); + else + promise = Calendar.$$resource.remove(this.id); + + promise.then(function() { + var i = _.indexOf(_.pluck(Calendar.$calendars, 'id'), _this.id); + Calendar.$calendars.splice(i, 1); + d.resolve(true); + }, 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 $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; + }; +})(); diff --git a/UI/WebServerResources/js/Appointments/component-model.js b/UI/WebServerResources/js/Appointments/component-model.js new file mode 100644 index 000000000..d7b46661f --- /dev/null +++ b/UI/WebServerResources/js/Appointments/component-model.js @@ -0,0 +1,216 @@ +/* -*- 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') { + angular.extend(this, futureComponentData); + } + 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', 'sgResource', function($q, $timeout, $log, Settings, Resource) { + angular.extend(Component, { + $q: $q, + $timeout: $timeout, + $log: $log, + $$resource: new Resource(Settings.baseURL, Settings.activeUser) + }); + + 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('sgComponent', 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); + }; + + /** + * @function $eventsBlocksForWeek + * @memberof Component.prototype + * @desc Events blocks for a specific week + * @param {Date} type - Date of any day of the week + * @returns a promise of a collection of Components instances + */ + Component.$eventsBlocksForWeek = function(date) { + var startDate, endDate, params, i, + deferred = Component.$q.defer(); + + startDate = date.beginOfWeek(); + endDate = new Date(); + endDate.setTime(startDate.getTime()); + endDate.addDays(6); + + params = { view: 'weekView', sd: startDate.getDayString(), ed: endDate.getDayString() }; + Component.$log.debug('eventsblocks ' + JSON.stringify(params, undefined, 2)); + + var 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 < 7; 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 $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() { + angular.extend(_this, data); + 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 = {}; + angular.forEach(this, function(value, key) { + if (key != 'constructor' && key[0] != '$') { + component[key] = value; + } + }); + + return component; + }; + +})();