(feat) Handle invitations in appointment viewer

This commit is contained in:
Francis Lachapelle
2015-08-05 16:44:25 -04:00
parent 87aec2fc01
commit 5e19a889c2
15 changed files with 518 additions and 199 deletions

View File

@@ -68,7 +68,7 @@
escapeToClose: true,
templateUrl: templateUrl,
controller: 'ComponentController',
controllerAs: 'viewer',
controllerAs: 'editor',
locals: {
stateComponent: component
}

View File

@@ -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

View File

@@ -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);
})();