mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-04-01 03:22:43 +00:00
(feat) Handle invitations in appointment viewer
This commit is contained in:
@@ -38,6 +38,7 @@
|
||||
* @memberof User
|
||||
* @desc Search for users that match a string.
|
||||
* @param {string} search - a string used to performed the search
|
||||
* @param {object[]} excludedUsers - a list of User objects that must be excluded from the results
|
||||
* @return a promise of an array of matching User objects
|
||||
*/
|
||||
User.$filter = function(search, excludedUsers) {
|
||||
|
||||
@@ -67,19 +67,58 @@
|
||||
* @desc Search for cards among all addressbooks matching some criterias.
|
||||
* @param {string} search - the search string to match
|
||||
* @param {object} [options] - additional options to the query (excludeGroups and excludeLists)
|
||||
* @param {object[]} excludedCards - a list of Card objects that must be excluded from the results
|
||||
* @returns a collection of Cards instances
|
||||
*/
|
||||
AddressBook.$filterAll = function(search, options) {
|
||||
AddressBook.$filterAll = function(search, options, excludedCards) {
|
||||
var params = {search: search};
|
||||
|
||||
if (!search) {
|
||||
// No query specified
|
||||
AddressBook.$cards = [];
|
||||
return AddressBook.$q.when(AddressBook.$cards);
|
||||
}
|
||||
if (angular.isUndefined(AddressBook.$cards)) {
|
||||
// First session query
|
||||
AddressBook.$cards = [];
|
||||
}
|
||||
else if (AddressBook.$query == search) {
|
||||
// Query hasn't changed
|
||||
return AddressBook.$q.when(AddressBook.$cards);
|
||||
}
|
||||
AddressBook.$query = search;
|
||||
|
||||
angular.extend(params, options);
|
||||
|
||||
return AddressBook.$$resource.fetch(null, 'allContactSearch', params).then(function(response) {
|
||||
var results = [];
|
||||
angular.forEach(response.contacts, function(data) {
|
||||
var card = new AddressBook.$Card(data);
|
||||
results.push(card);
|
||||
var results, card, index,
|
||||
compareIds = function(data) {
|
||||
return this.id == data.id;
|
||||
};
|
||||
if (excludedCards) {
|
||||
// Remove excluded cards from results
|
||||
results = _.filter(response.contacts, function(data) {
|
||||
return _.isUndefined(_.find(excludedCards, compareIds, data));
|
||||
});
|
||||
}
|
||||
else {
|
||||
results = response.contacts;
|
||||
}
|
||||
// Remove cards that no longer match the search query
|
||||
for (index = AddressBook.$cards.length - 1; index >= 0; index--) {
|
||||
card = AddressBook.$cards[index];
|
||||
if (_.isUndefined(_.find(results, compareIds, card))) {
|
||||
AddressBook.$cards.splice(index, 1);
|
||||
}
|
||||
}
|
||||
// Add new cards matching the search query
|
||||
_.each(results, function(data, index) {
|
||||
if (_.isUndefined(_.find(AddressBook.$cards, compareIds, data))) {
|
||||
var card = new AddressBook.$Card(data, search);
|
||||
AddressBook.$cards.splice(index, 0, card);
|
||||
}
|
||||
});
|
||||
return results;
|
||||
return AddressBook.$cards;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -133,6 +133,9 @@
|
||||
if (!this.$$image)
|
||||
this.$$image = this.image || Card.$gravatar(this.$preferredEmail(partial), 32);
|
||||
this.selected = false;
|
||||
|
||||
// An empty attribute to trick md-autocomplete when adding attendees from the appointment editor
|
||||
this.empty = ' ';
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
escapeToClose: true,
|
||||
templateUrl: templateUrl,
|
||||
controller: 'ComponentController',
|
||||
controllerAs: 'viewer',
|
||||
controllerAs: 'editor',
|
||||
locals: {
|
||||
stateComponent: component
|
||||
}
|
||||
|
||||
@@ -311,6 +311,7 @@
|
||||
this.due = new Date(this.dueDate.substring(0,10) + ' ' + this.dueDate.substring(11,16));
|
||||
|
||||
// Parse recurrence rule definition and initialize default values
|
||||
this.$isRecurrent = angular.isDefined(data.repeat);
|
||||
if (this.repeat.days) {
|
||||
var byDayMask = _.find(this.repeat.days, function(o) {
|
||||
return angular.isDefined(o.occurrence);
|
||||
@@ -368,6 +369,10 @@
|
||||
// Allow the component to be moved to a different calendar
|
||||
this.destinationCalendar = this.pid;
|
||||
|
||||
if (this.organizer && this.organizer.email) {
|
||||
this.organizer.$image = Component.$gravatar(this.organizer.email, 32);
|
||||
}
|
||||
|
||||
// Load freebusy of attendees
|
||||
this.freebusy = this.updateFreeBusyCoverage();
|
||||
|
||||
@@ -394,6 +399,56 @@
|
||||
return b;
|
||||
};
|
||||
|
||||
/**
|
||||
* @function isEditable
|
||||
* @memberof Component.prototype
|
||||
* @desc Check if the component is editable and not an occurrence of a recurrent component
|
||||
* @returns true or false
|
||||
*/
|
||||
Component.prototype.isEditable = function() {
|
||||
return (!this.occurrenceId && !this.isReadOnly);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function isEditableOccurrence
|
||||
* @memberof Component.prototype
|
||||
* @desc Check if the component is editable and an occurrence of a recurrent component
|
||||
* @returns true or false
|
||||
*/
|
||||
Component.prototype.isEditableOccurrence = function() {
|
||||
return (this.occurrenceId && !this.isReadOnly);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function isInvitation
|
||||
* @memberof Component.prototype
|
||||
* @desc Check if the component an invitation and not an occurrence of a recurrent component
|
||||
* @returns true or false
|
||||
*/
|
||||
Component.prototype.isInvitation = function() {
|
||||
return (!this.occurrenceId && this.userHasRSVP);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function isInvitationOccurrence
|
||||
* @memberof Component.prototype
|
||||
* @desc Check if the component an invitation and an occurrence of a recurrent component
|
||||
* @returns true or false
|
||||
*/
|
||||
Component.prototype.isInvitationOccurrence = function() {
|
||||
return (this.occurrenceId && this.userHasRSVP);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function isReadOnly
|
||||
* @memberof Component.prototype
|
||||
* @desc Check if the component is not editable and not an invitation
|
||||
* @returns true or false
|
||||
*/
|
||||
Component.prototype.isReadOnly = function() {
|
||||
return (this.isReadOnly && !this.userHasRSVP);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function enablePercentComplete
|
||||
* @memberof Component.prototype
|
||||
@@ -581,6 +636,7 @@
|
||||
*/
|
||||
Component.prototype.canRemindAttendeesByEmail = function() {
|
||||
return this.alarm.action == 'email' &&
|
||||
!this.isReadOnly &&
|
||||
this.attendees && this.attendees.length > 0;
|
||||
};
|
||||
|
||||
@@ -635,6 +691,32 @@
|
||||
this.$shadowData = this.$omit(true);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function reply
|
||||
* @memberof Component.prototype
|
||||
* @desc Reply to an invitation.
|
||||
* @returns a promise of the HTTP operation
|
||||
*/
|
||||
Component.prototype.$reply = function() {
|
||||
var _this = this, data, path = [this.pid, this.id];
|
||||
|
||||
if (this.occurrenceId)
|
||||
path.push(this.occurrenceId);
|
||||
|
||||
data = {
|
||||
reply: this.reply,
|
||||
delegatedTo: this.delegatedTo,
|
||||
alarm: this.$hasAlarm? this.alarm : {}
|
||||
};
|
||||
|
||||
return Component.$$resource.save(path.join('/'), data, { action: 'rsvpAppointment' })
|
||||
.then(function(data) {
|
||||
// Make a copy of the data for an eventual reset
|
||||
_this.$shadowData = _this.$omit(true);
|
||||
return data;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $save
|
||||
* @memberof Component.prototype
|
||||
|
||||
@@ -6,20 +6,24 @@
|
||||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
ComponentController.$inject = ['$mdDialog', 'Calendar', 'stateComponent'];
|
||||
function ComponentController($mdDialog, Calendar, stateComponent) {
|
||||
ComponentController.$inject = ['$rootScope', '$mdDialog', 'Calendar', 'AddressBook', 'Alarm', 'stateComponent'];
|
||||
function ComponentController($rootScope, $mdDialog, Calendar, AddressBook, Alarm, stateComponent) {
|
||||
var vm = this, component;
|
||||
|
||||
vm.component = stateComponent;
|
||||
vm.close = close;
|
||||
vm.cardFilter = cardFilter;
|
||||
vm.edit = edit;
|
||||
vm.editAllOccurrences = editAllOccurrences;
|
||||
vm.reply = reply;
|
||||
vm.replyAllOccurrences = replyAllOccurrences;
|
||||
|
||||
// Load all attributes of component
|
||||
if (angular.isUndefined(vm.component.$futureComponentData)) {
|
||||
component = Calendar.$get(vm.component.c_folder).$getComponent(vm.component.c_name, vm.component.c_recurrence_id);
|
||||
component.$futureComponentData.then(function() {
|
||||
vm.component = component;
|
||||
vm.organizer = [vm.component.organizer];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27,12 +31,10 @@
|
||||
$mdDialog.hide();
|
||||
}
|
||||
|
||||
function editAllOccurrences() {
|
||||
component = Calendar.$get(vm.component.pid).$getComponent(vm.component.id);
|
||||
component.$futureComponentData.then(function() {
|
||||
vm.component = component;
|
||||
edit();
|
||||
});
|
||||
// Autocomplete cards for attendees
|
||||
function cardFilter($query) {
|
||||
AddressBook.$filterAll($query);
|
||||
return AddressBook.$cards;
|
||||
}
|
||||
|
||||
function edit() {
|
||||
@@ -54,6 +56,38 @@
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function editAllOccurrences() {
|
||||
component = Calendar.$get(vm.component.pid).$getComponent(vm.component.id);
|
||||
component.$futureComponentData.then(function() {
|
||||
vm.component = component;
|
||||
edit();
|
||||
});
|
||||
}
|
||||
|
||||
function reply(component) {
|
||||
var c = component || vm.component;
|
||||
|
||||
c.$reply().then(function() {
|
||||
$rootScope.$broadcast('calendars:list');
|
||||
$mdDialog.hide();
|
||||
Alarm.getAlarms();
|
||||
});
|
||||
}
|
||||
|
||||
function replyAllOccurrences() {
|
||||
// Retrieve master event
|
||||
component = Calendar.$get(vm.component.pid).$getComponent(vm.component.id);
|
||||
component.$futureComponentData.then(function() {
|
||||
// Propagate the participant status and alarm to the master event
|
||||
component.reply = vm.component.reply;
|
||||
component.delegatedTo = vm.component.delegatedTo;
|
||||
component.$hasAlarm = vm.component.$hasAlarm;
|
||||
component.alarm = vm.component.alarm;
|
||||
// Send reply to the server
|
||||
reply(component);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +105,6 @@
|
||||
vm.showAttendeesEditor = angular.isDefined(vm.component.attendees);
|
||||
vm.toggleAttendeesEditor = toggleAttendeesEditor;
|
||||
vm.cardFilter = cardFilter;
|
||||
vm.cardResults = [];
|
||||
vm.addAttendee = addAttendee;
|
||||
vm.addAttachUrl = addAttachUrl;
|
||||
vm.cancel = cancel;
|
||||
@@ -119,29 +152,8 @@
|
||||
|
||||
// Autocomplete cards for attendees
|
||||
function cardFilter($query) {
|
||||
var index, indexResult, card;
|
||||
if ($query) {
|
||||
AddressBook.$filterAll($query).then(function(results) {
|
||||
var compareIds = function(result) {
|
||||
return this.id == result.id;
|
||||
};
|
||||
// Remove cards that no longer match the search query
|
||||
for (index = vm.cardResults.length - 1; index >= 0; index--) {
|
||||
card = vm.cardResults[index];
|
||||
indexResult = _.findIndex(results, compareIds, card);
|
||||
if (indexResult >= 0)
|
||||
results.splice(indexResult, 1);
|
||||
else
|
||||
vm.cardResults.splice(index, 1);
|
||||
}
|
||||
_.each(results, function(card) {
|
||||
// Add cards matching the search query but not already in the list of attendees
|
||||
if (!vm.component.hasAttendee(card))
|
||||
vm.cardResults.push(card);
|
||||
});
|
||||
});
|
||||
}
|
||||
return vm.cardResults;
|
||||
AddressBook.$filterAll($query);
|
||||
return AddressBook.$cards;
|
||||
}
|
||||
|
||||
function addAttendee(card) {
|
||||
@@ -202,7 +214,7 @@
|
||||
}
|
||||
|
||||
angular
|
||||
.module('SOGo.SchedulerUI')
|
||||
.module('SOGo.SchedulerUI')
|
||||
.controller('ComponentController', ComponentController)
|
||||
.controller('ComponentEditorController', ComponentEditorController);
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user