bug 379198 Make Lightning understand event updates r=berend

git-svn-id: svn://10.0.0.236/branches/MOZILLA_1_8_BRANCH@246105 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
ctalbert%mozilla.com
2008-02-20 18:16:59 +00:00
parent bd003734fa
commit fafabf48c5
2 changed files with 273 additions and 81 deletions

View File

@@ -48,6 +48,7 @@ const CAL_ITIP_PROC_DELETE_OP = 3;
*/
function calItipProcessor() {
this.wrappedJSObject = this;
this._handledID = null;
}
calItipProcessor.prototype = {
@@ -115,11 +116,6 @@ calItipProcessor.prototype = {
var autoResponse = respItipItem.autoResponse;
var targetCalendar = respItipItem.targetCalendar;
// XXX Support for transports other than email go here.
// For now we just assume it's email.
var transport = Components.classes["@mozilla.org/calendar/itip-transport;1?type=email"].
createInstance(Components.interfaces.calIItipTransport);
// Sanity checks using the first item
var itemList = respItipItem.getItemList({ });
var calItem = itemList[0];
@@ -141,44 +137,63 @@ calItipProcessor.prototype = {
"response method: " + respMethod);
}
var i = 0;
// Check to see if we have an existing item or not, then continue
// processing appropriately
var i =0;
while (calItem) {
switch (recvMethod) {
case "REQUEST":
// Only add to calendar if we accepted invite
var replyStat = this._getReplyStatus(calItem,
transport.defaultIdentity);
if (replyStat == "DECLINED") {
break;
}
// else fall through
case "PUBLISH":
if (!this._processCalendarAction(calItem,
CAL_ITIP_PROC_ADD_OP,
targetCalendar,
aListener))
{
throw new Error ("processItipItem: " +
"_processCalendarAction failed!");
}
break;
case "REPLY":
case "REFRESH":
case "ADD":
case "CANCEL":
case "COUNTER":
case "DECLINECOUNTER":
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
default:
throw new Error("processItipItem: " +
"Received unknown method: " +
recvMethod);
}
this._isExistingItem(calItem, recvMethod, respMethod, targetCalendar,
aListener);
++i;
calItem = itemList[i];
}
// Send the appropriate response
// figure out a good way to determine when a response is needed!
if (recvMethod != respMethod) {
this._getTransport().simpleSendResponse(respItipItem);
}
},
/* Continue processing the iTip Item now that we have determined whether
* there is an existing item or not.
*/
_continueProcessingItem: function cipCPI(newItem, existingItem, recvMethod, respMethod,
calAction, targetCalendar, aListener) {
var transport = this._getTransport();
switch (recvMethod) {
case "REQUEST":
// Only add to calendar if we accepted invite
var replyStat = this._getReplyStatus(newItem,
transport.defaultIdentity);
if (replyStat == "DECLINED") {
break;
}
// else fall through
case "PUBLISH":
if (!this._processCalendarAction(newItem,
existingItem,
calAction,
targetCalendar,
aListener))
{
throw new Error ("processItipItem: " +
"_processCalendarAction failed!");
}
break;
case "REPLY":
case "REFRESH":
case "ADD":
case "CANCEL":
case "COUNTER":
case "DECLINECOUNTER":
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
default:
throw new Error("processItipItem: " +
"Received unknown method: " +
recvMethod);
}
// When replying, the reply must only contain the ORGANIZER and the
// status of the ATTENDEE that represents ourselves. Therefore we must
// remove all other ATTENDEEs from the itipItem we send back.
@@ -195,32 +210,19 @@ calItipProcessor.prototype = {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
}
for (var j=0; j < itemList.length; ++j) {
var respCalItem = itemList[j];
var attendees = respCalItem.getAttendees({});
var attendees = newItem.getAttendees({});
for each (var attendee in attendees) {
// Leave the ORGANIZER alone.
if (!attendee.isOrganizer) {
// example: mailto:joe@domain.com
var meString = idPrefix + me;
if (attendee.id.toLowerCase() != meString.toLowerCase()) {
respCalItem.removeAttendee(attendee);
}
for each (var attendee in attendees) {
// Leave the ORGANIZER alone.
if (!attendee.isOrganizer) {
// example: mailto:joe@domain.com
var meString = idPrefix + me;
if (attendee.id.toLowerCase() != meString.toLowerCase()) {
newItem.removeAttendee(attendee);
}
}
}
}
// Send the appropriate response
// figure out a good way to determine when a response is needed!
if (recvMethod != respMethod) {
transport.simpleSendResponse(respItipItem);
}
// Yay it worked!
// XXX TODO: Actually tie this to success/failure of the transport
return true;
},
@@ -344,6 +346,7 @@ calItipProcessor.prototype = {
* calendar.
*/
_processCalendarAction: function cipPCA(aCalItem,
aExistingItem,
aOperation,
aTargetCalendar,
aListener) {
@@ -356,6 +359,15 @@ calItipProcessor.prototype = {
return true;
case CAL_ITIP_PROC_UPDATE_OP:
// To udpate, we must require the existing item to be set
if (!aExistingItem)
throw new Error("_processCalendarAction: Item to update not found");
// TODO: Handle generation properly - Bug 418345
aCalItem.generation = aExistingItem.generation;
aTargetCalendar.modifyItem(aCalItem, aExistingItem, aListener);
return true;
case CAL_ITIP_PROC_DELETE_OP:
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
@@ -383,5 +395,68 @@ calItipProcessor.prototype = {
replyStatus = attendee.participationStatus;
}
return replyStatus;
},
// A placeholder to make sure we don't try to add multiple items from the
// onOperationComplete function.
_handledID:null,
/**
* Helper function to determine if this item already exists on this calendar
* or not. It then calls _continueProcessingItem setting calAction and
* existingItem appropirately
*/
_isExistingItem: function cipIEI(aCalItem, aRecvMethod, aRespMethod,
aTargetCal, aListener) {
var foundItemListener = {
itipProcessor: this,
onOperationComplete:
function (aCalendar, aStatus, aOperationType, aId, aDetail) {
// If there is no item, we get this call with a failure error
if (aStatus == Components.results.NS_ERROR_FAILURE &&
!this.itipProcessor._handledID) {
// Cache the id so that we know we've handled this one.
this.itipProcessor._handledID = aCalItem.id;
this.itipProcessor._continueProcessingItem(aCalItem,
null,
aRecvMethod,
aRespMethod,
CAL_ITIP_PROC_ADD_OP,
aTargetCal,
aListener);
}
},
onGetResult:
function onget(aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
// Now, if the old item exists, cache it and return true
if (aCount && aItems[0]) {
this.itipProcessor._continueProcessingItem(aCalItem,
aItems[0],
aRecvMethod,
aRespMethod,
CAL_ITIP_PROC_UPDATE_OP,
aTargetCal,
aListener);
}
}
};
aTargetCal.getItem(aCalItem.id, foundItemListener);
},
/**
* Centralized location for obtaining the proper transport. This way it
* will be easy to support multiple transports in the future
* without changing the existing code too much.
*/
_getTransport: function cipGT() {
// XXX Support for transports other than email go here.
// For now we just assume it's email.
var transport = Components.classes["@mozilla.org/calendar/itip-transport;1?type=email"].
createInstance(Components.interfaces.calIItipTransport);
if (!transport) {
throw new Error("iTipProcessor cannot instantiate transport");
}
return transport;
}
}

View File

@@ -162,29 +162,9 @@ function setupBar(imipMethod)
imipBar.setAttribute("collapsed", "false");
var description = document.getElementById("imip-description");
// Bug 348666: here is where we would check if this event was already
// added to calendar or not and display correct information
if (imipMethod.toUpperCase() == "REQUEST") {
if (description.firstChild.data) {
description.firstChild.data = ltnGetString("lightning",
"imipBarRequestText");
}
var button = document.getElementById("imip-button1");
button.removeAttribute("hidden");
button.setAttribute("label", ltnGetString("lightning",
"imipAcceptInvitation.label"));
button.setAttribute("oncommand",
"setAttendeeResponse('ACCEPTED', 'CONFIRMED');");
// Create a DECLINE button
button = document.getElementById("imip-button2");
button.removeAttribute("hidden");
button.setAttribute("label", ltnGetString("lightning",
"imipDeclineInvitation.label"));
button.setAttribute("oncommand",
"setAttendeeResponse('DECLINED', 'CONFIRMED');");
// Check if this is an update and display things accordingly
isUpdateMsg();
} else if (imipMethod.toUpperCase() == "REPLY") {
// Bug xxxx we currently cannot process REPLY messages so just let
// the user know what this is, and don't give them any options.
@@ -404,3 +384,140 @@ function finishItipAction(aOperationType, aStatus, aDetail)
}
}
}
/**
* Walks through the list of events in the iTipItem and discovers whether or not
* these events already exist on a calendar. Calls determineUpdateType.
*/
function isUpdateMsg()
{
// According to the specification, we have to determine if the event ID
// already exists on the calendar of the user - that means we have to search
// them all. :-(
var isUpdate = 0;
var existingItemSequence = -1;
calendarList = getCalendarManager().getCalendars({});
// Create a composite
compCal = Components.classes["@mozilla.org/calendar/calendar;1?type=composite"]
.createInstance(Components.interfaces.calICompositeCalendar);
for(var i=0; i < calendarList.length; ++i) {
compCal.addCalendar(calendarList[i]);
}
// Per iTIP spec (new Draft 4), multiple items in an iTIP message MUST have
// same ID, this simplifies our searching, we can just look for Item[0].id
var itemList = gItipItem.getItemList({ });
var newSequence = itemList[0].getProperty("SEQUENCE");
var onFindItemListener = {
processedId: null,
onOperationComplete:
function ooc(aCalendar, aStatus, aOperationType, aId, aDetail) {
if (!this.processedId){
// Then the ID doesn't exist, don't call us twice
this.processedId = true;
determineUpdateType(newSequence, -1);
}
},
onGetResult:
function ogr(aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
if (aCount && aItems[0] && !this.processedId) {
this.processedId = true;
determineUpdateType(newSequence, aItems[0].getProperty("SEQUENCE"));
}
}
};
// Search
compCal.getItem(itemList[0].id, onFindItemListener);
}
/**
* Determines what our update status is. It can return three things:
* 0 = the new event does not exist on the calendar (therefore, this is an add)
* 1 = the event does exist and contains a proper update (this is an update)
* 2 = the event clicked on is an old update and should NOT be applied
*/
function determineUpdateType(newItemSequence, existingItemSequence) {
// Three states here:
// Item does not exist yet: existingItemSequence == -1
// Item has been updated: newSequence > existingSequence
// Item is an old message that has already been added/updated: new <= existing
var isUpdate = 0;
if (existingItemSequence == -1)
isUpdate = 0;
else if (newItemSequence > existingItemSequence)
isUpdate = 1;
else
isUpdate = 2;
// We now call our display code to display the proper message for this
// update type
displayRequestMethod(isUpdate);
}
function displayRequestMethod(updateValue) {
var description = document.getElementById("imip-description");
if (updateValue) {
// This is a message updating existing event(s). But updateValue could
// indicate that this update has already been applied, check that first.
if (updateValue == 2) {
// This case, they clicked on an old message that has already been
// added/updated, we want to tell them that.
if (description.firstChild.data) {
description.firstChild.data = ltnGetString("lightning",
"imipBarAlreadyAddedText");
}
var button = document.getElementById("imip-button1");
button.setAttribute("hidden", "true");
button = document.getElementById("imip-button2");
button.setAttribute("hidden", "true");
} else {
// Legitimate update, let's offer the update path
if (description.firstChild.data) {
description.firstChild.data = ltnGetString("lightning",
"imipBarUpdateText");
}
var button = document.getElementById("imip-button1");
button.removeAttribute("hidden");
button.setAttribute("label", ltnGetString("lightning",
"imipUpdateInvitation.label"));
button.setAttribute("oncommand",
"setAttendeeResponse('ACCEPTED', 'CONFIRMED');");
// Create a DECLINE button (user chooses not to attend the updated event)
button = document.getElementById("imip-button2");
button.removeAttribute("hidden");
button.setAttribute("label", ltnGetString("lightning",
"imipDeclineInvitation.label"));
button.setAttribute("oncommand",
"setAttendeeResponse('DECLINED', 'CONFIRMED');");
}
} else {
if (description.firstChild.data) {
description.firstChild.data = ltnGetString("lightning",
"imipBarRequestText");
}
var button = document.getElementById("imip-button1");
button.removeAttribute("hidden");
button.setAttribute("label", ltnGetString("lightning",
"imipAcceptInvitation.label"));
button.setAttribute("oncommand",
"setAttendeeResponse('ACCEPTED', 'CONFIRMED');");
// Create a DECLINE button
button = document.getElementById("imip-button2");
button.removeAttribute("hidden");
button.setAttribute("label", ltnGetString("lightning",
"imipDeclineInvitation.label"));
button.setAttribute("oncommand",
"setAttendeeResponse('DECLINED', 'CONFIRMED');");
}
}