Fix
bug 463392 - caldav calendars are not visible in the 'select calendar' dialog. r=philipp
--- a/calendar/base/content/dialogs/chooseCalendarDialog.xul
+++ b/calendar/base/content/dialogs/chooseCalendarDialog.xul
@@ -45,17 +45,17 @@
<!ENTITY % dtd1 SYSTEM "chrome://calendar/locale/calendar.dtd" > %dtd1;
]>
<dialog id="chooseCalendar"
title="&calendar.select.dialog.title;"
windowtype="Calendar:CalendarPicker"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
buttons="accept,cancel"
- onload="loadCalendars();"
+ onload="setTimeout('loadCalendars()',0);"
ondialogaccept="return doOK();"
persist="screenX screenY height width">
<script type="application/javascript" src="chrome://calendar/content/calendar-ui-utils.js"/>
<script type="application/javascript"><![CDATA[
function loadCalendars() {
const calendarManager = Components.classes["@mozilla.org/calendar/manager;1"]
.getService(Components.interfaces.calICalendarManager);
--- a/calendar/base/modules/calItipUtils.jsm
+++ b/calendar/base/modules/calItipUtils.jsm
@@ -18,16 +18,17 @@
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Daniel Boelzle <daniel.boelzle@sun.com>
* Philipp Kewisch <mozilla@kewis.ch>
* Clint Talbert <ctalbert.moz@gmail.com>
* Matthew Willis <lilmatt@mozilla.com>
+ * Simon Vaillancourt <simon.at.orcl@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -562,17 +563,17 @@ function sendMessage(aItem, aMethod, aRe
aTransport.sendItems(aRecipientsList.length, aRecipientsList, itipItem);
}
/** local to this module file
* An operation listener that is used on calendar operations which checks and sends further iTIP
* messages based on the calendar action.
*
* @param opListener operation listener to forward
- * @param oldItem the previous item before modification (if any)
+ * @param oldItem the previous item before modification (if any)
*/
function ItipOpListener(opListener, oldItem) {
this.mOpListener = opListener;
this.mOldItem = oldItem;
}
ItipOpListener.prototype = {
onOperationComplete: function ItipOpListener_onOperationComplete(aCalendar,
aStatus,
@@ -596,16 +597,32 @@ ItipOpListener.prototype = {
aItemType,
aDetail,
aCount,
aItems) {
}
};
/** local to this module file
+ * Add a the parameter SCHEDULE-AGENT=CLIENT to the item before it is
+ * created or updated so that the providers knows scheduling will
+ * be handled by the client.
+ *
+ * @param item item about to be added or updated
+ * @param calendar calendar into which the item is about to be added or updated
+ */
+function addScheduleAgentClient(item, calendar) {
+ if (calendar.getProperty("capabilities.autoschedule.supported") === true) {
+ if (item.organizer) {
+ item.organizer.setProperty("SCHEDULE-AGENT","CLIENT");
+ }
+ }
+}
+
+/** local to this module file
* An operation listener triggered by cal.itip.processItipItem() for lookup of the sent iTIP item's UID.
*
* @param itipItem sent iTIP item
* @param optionsFunc options func, see cal.itip.processItipItem()
*/
function ItipFindItemListener(itipItem, optionsFunc) {
this.mItipItem = itipItem;
this.mOptionsFunc = optionsFunc;
@@ -692,16 +709,17 @@ ItipFindItemListener.prototype = {
let att = cal.getInvitedAttendee(newItem);
if (!att) { // fall back to using configured organizer
att = createOrganizer(newItem.calendar);
if (att) {
att.isOrganizer = false;
}
}
if (att) {
+ addScheduleAgentClient(newItem, item.calendar);
newItem.removeAttendee(att);
att = att.clone();
let action = function(opListener, partStat) {
if (!partStat) { // keep PARTSTAT
let att_ = cal.getInvitedAttendee(item);
partStat = (att_ ? att_.participationStatus : "NEEDS-ACTION");
}
att.participationStatus = partStat;
@@ -793,16 +811,17 @@ ItipFindItemListener.prototype = {
switch (method) {
case "REQUEST":
case "PUBLISH": {
let this_ = this;
let action = function(opListener, partStat) {
let newItem = itipItemItem.clone();
setReceivedInfo(newItem, itipItemItem);
newItem.parentItem.calendar = this_.mItipItem.targetCalendar;
+ addScheduleAgentClient(newItem, this_.mItipItem.targetCalendar);
if (partStat) {
if (partStat != "DECLINED") {
cal.alarms.setDefaultValues(newItem);
}
let att = cal.getInvitedAttendee(newItem);
if (!att) { // fall back to using configured organizer
att = createOrganizer(newItem.calendar);
if (att) {
--- a/calendar/base/public/calICalendar.idl
+++ b/calendar/base/public/calICalendar.idl
@@ -183,16 +183,17 @@ interface calICalendar : nsISupports
* capabilities.privacy.supported Supports a privacy state
* capabilities.priority.supported Supports the priority field
* capabilities.events.supported Supports tasks
* capabilities.tasks.supported Supports events
* capabilities.alarms.popup.supported Supports popup alarms
* capabilities.alarms.oninviations.supported Supports alarms on inviations.
* capabilities.timezones.floating.supported Supports local time
* capabilities.timezones.UTC.supported Supports UTC/GMT timezone
+ * capabilities.autoschedule.supported Supports caldav schedule properties in icalendar (SCHEDULE-AGENT, SCHEDULE-STATUS...)
*
* The following capabilities are used to restrict the values for specific
* fields. An array should be specified with the values, the default
* values are specified here. Extensions using this need to take care of
* adding any UI elements needed in an overlay. To make sure the correct
* elements are shown, those elements should additionally specify an attribute
* "provider", with the type of the provider.
*
--- a/calendar/libical/design-data/parameters.csv
+++ b/calendar/libical/design-data/parameters.csv
@@ -29,8 +29,11 @@
"#this parameter should really be called ACTION, but this conflicts with the ACTION property"
"ACTIONPARAM","icalparameter_action","ASK;ABORT"
"ID","const char*",
"ENABLE","icalparameter_enable","TRUE;FALSE"
"LATENCY","const char*",
"LOCAL","icalparameter_local","TRUE;FALSE"
"LOCALIZE","const char*",
"OPTIONS","const char*",
+"SCHEDULE-AGENT","const char*",
+"SCHEDULE-STATUS","const char*",
+"SCHEDULE-FORCE-SEND","const char*",
--- a/calendar/libical/design-data/params-in-prop.txt
+++ b/calendar/libical/design-data/params-in-prop.txt
@@ -1,11 +1,11 @@
ACTION VALUE X
ATTACH FMTTYPE ENCODING VALUE X
-ATTENDEE CN CUTYPE DELEGATED-FROM DELEGATED-TO DIR LANGUAGE MEMBER PARTSTAT ROLE RSVP SENT-BY RECEIVED-SEQUENCE RECEIVED-DTSTAMP X
+ATTENDEE CN CUTYPE DELEGATED-FROM DELEGATED-TO DIR LANGUAGE MEMBER PARTSTAT ROLE RSVP SENT-BY RECEIVED-SEQUENCE RECEIVED-DTSTAMP SCHEDULE-STATUS SCHEDULE-AGENT SCHEDULE-FORCE-SEND X
CALSCALE X
CATEGORIES LANGUAGE X
CLASS X
CMD ACTIONPARAM ID LATENCY LOCALIZE OPTIONS X
COMMENT ALTREP LANGUAGE X
COMPLETED X
CONTACT ALTREP LANGUAGE X
CREATED X
@@ -17,17 +17,17 @@ DUE VALUE TZID X
DURATION X
EXDATE VALUE TZID X
EXRULE X
FREEBUSY FBTYPE X
GEO X
LAST-MODIFIED X
LOCATION ALTREP LANGUAGE X
METHOD X
-ORGANIZER CN DIR LANGUAGE SENT-BY X
+ORGANIZER CN DIR LANGUAGE SENT-BY SCHEDULE-STATUS SCHEDULE-AGENT SCHEDULE-FORCE-SEND X
PERCENT-COMPLETE X
PRIORITY X
PRODID X
RDATE VALUE TZID X
RECURRENCE-ID VALUE RANGE TZID X
RELATED-TO RELTYPE X
REPEAT X
REQUEST-STATUS LANGUAGE X
--- a/calendar/lightning/content/imip-bar.js
+++ b/calendar/lightning/content/imip-bar.js
@@ -18,16 +18,17 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Clint Talbert <ctalbert.moz@gmail.com>
* Matthew Willis <lilmatt@mozilla.com>
* Philipp Kewisch <mozilla@kewis.ch>
* Daniel Boelzle <daniel.boelzle@sun.com>
* Martin Schroeder <mschroeder@mozilla.x-home.org>
+ * Simon Vaillancourt <simon.at.orcl@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -374,32 +375,35 @@ function ltnItipOptions(itipItem, rc, ac
} else {
imipBar.setAttribute("label", ltnGetString("lightning", "imipBarUnsupportedText"));
}
}
function ltnGetTargetCalendar(itipItem) {
let calendarToReturn = null;
let calendars = getCalendarManager().getCalendars({}).filter(ltnIsSchedulingCalendar);
- // XXXNeed an error message if there is no calendar
if (itipItem.receivedMethod == "REQUEST") {
// try to further limit down the list to those calendars that are configured to a matching attendee;
let item = itipItem.getItemList({})[0];
let matchingCals = calendars.filter(
function(calendar) {
return (cal.getInvitedAttendee(item, calendar) != null);
});
// if there's none, we will show the whole list of calendars:
if (matchingCals.length > 0) {
calendars = matchingCals;
}
}
- if (calendars.length == 1) {
+ if (calendars.length == 0) {
+ var stringBundle = getLightningStringBundle();
+ window.alert(stringBundle.GetStringFromName("imipNoCalendarAvailable"));
+ }
+ else if (calendars.length == 1) {
// There's only one calendar, so it's silly to ask what calendar
// the user wants to import into.
calendarToReturn = calendars[0];
} else {
// Ask what calendar to import into
var args = {};
args.calendars = calendars;
args.onOk = function selectCalendar(aCal) { calendarToReturn = aCal; };
--- a/calendar/locales/en-US/chrome/lightning/lightning.properties
+++ b/calendar/locales/en-US/chrome/lightning/lightning.properties
@@ -79,16 +79,17 @@ imipCancelInvitation.label=Delete
imipDeclineInvitation.label=Decline
imipUpdate.label=Update
imipAcceptTentativeInvitation.label=Tentative
imipSend.label=Send
imipSendMail.title=E-Mail Notification
imipSendMail.text=Would you like to send out notification E-Mail now?
imipSendMail.Outlook2000CompatMode.text=Support Outlook 2000 and Outlook 2002/XP
imipNoIdentity=None
+imipNoCalendarAvailable=There are no writable calendars available.
itipReplySubject=Event Invitation Reply: %1$S
itipReplyBodyAccept=%1$S has accepted your event invitation.
itipReplyBodyDecline=%1$S has declined your event invitation.
itipRequestSubject=Event Invitation: %1$S
itipRequestBody=%1$S has invited you to %2$S
itipCancelSubject=Event Canceled: %1$S
itipCancelBody=%1$S has canceled this event: « %2$S »
--- a/calendar/providers/caldav/calDavCalendar.js
+++ b/calendar/providers/caldav/calDavCalendar.js
@@ -380,26 +380,26 @@ calDavCalendar.prototype = {
return this.calendarUserAddress;
} // else use configured email identity
break;
case "organizerCN":
return null; // xxx todo
case "cache.updateTimer":
return getPrefSafe("calendar.autorefresh.timeout");
case "itip.transport":
- if (this.hasAutoScheduling) {
- return null;
- } else if (this.hasScheduling) {
+ if (this.hasAutoScheduling || this.hasScheduling) {
return this.QueryInterface(Components.interfaces.calIItipTransport);
} // else use outbound email-based iTIP (from cal.ProviderBase)
break;
case "capabilities.tasks.supported":
return (this.supportedItemTypes.indexOf("VTODO") > -1);
case "capabilities.events.supported":
return (this.supportedItemTypes.indexOf("VEVENT") > -1);
+ case "capabilities.autoschedule.supported":
+ return this.hasAutoScheduling;
}
return this.__proto__.__proto__.getProperty.apply(this, arguments);
},
promptOverwrite: function caldav_promptOverwrite(aMethod, aItem, aListener, aOldItem) {
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
@@ -714,17 +714,18 @@ calDavCalendar.prototype = {
if (thisCalendar.isCached) {
// the item is deleted in the storage calendar from calCachedCalendar
realListener.onOperationComplete(thisCalendar, status,
Components.interfaces.calIOperationListener.DELETE,
null, null);
} else {
thisCalendar.mTargetCalendar.deleteItem(aItem, aListener);
}
- delete thisCalendar.mHrefIndex[eventUri.path];
+ let decodedHRef = decodeURIComponent(eventUri.path);
+ delete thisCalendar.mHrefIndex[decodedHRef];
delete thisCalendar.mItemInfoCache[aItem.id];
cal.LOG("CalDAV: Item deleted successfully from calendar" +
thisCalendar.name);
}
} else if (status == 412) {
// item has either been modified or deleted by someone else
// check to see which
@@ -2145,17 +2146,23 @@ calDavCalendar.prototype = {
thisCalendar.doDeleteItem(aItem, null, true, true, delUri);
}
};
this.mTargetCalendar.getItem(aItem.id, getItemListener);
},
canNotify: function caldav_canNotify(aMethod, aItem) {
- if (this.hasAutoScheduling) { // auto-sched takes precedence
+ if (this.hasAutoScheduling) {
+ // canNotify should return false if the schedule agent is client
+ // so the itip transport(imip) takes care of notifying participants
+ if (aItem.organizer &&
+ aItem.organizer.getProperty("SCHEDULE-AGENT") == "CLIENT") {
+ return false;
+ }
return true;
}
return false; // use outbound iTIP for all
},
//
// calIItipTransport interface
//
@@ -2169,16 +2176,36 @@ calDavCalendar.prototype = {
return this.mSenderAddress || this.calendarUserAddress;
},
set senderAddress(aString) {
return (this.mSenderAddress = aString);
},
sendItems: function caldav_sendItems(aCount, aRecipients, aItipItem) {
+ if (this.hasAutoScheduling) {
+ // If auto scheduling is supported by the server we still need
+ // to send out REPLIES for meetings where the ORGANIZER has the
+ // parameter SCHEDULE-AGENT set to CLIENT, this property is
+ // checked in in canNotify()
+ if (aItipItem.responseMethod == "REPLY") {
+ let imipTransport = cal.getImipTransport(this);
+ if (imipTransport) {
+ imipTransport.sendItems(aCount, aRecipients, aItipItem);
+ }
+ }
+ // Servers supporting auto schedule should handle all other
+ // scheduling operations for now. Note that eventually the client
+ // could support setting a SCHEDULE-AGENT=CLIENT parameter on
+ // ATTENDEES and/or interpreting the SCHEDULE-STATUS parameter which
+ // could translate in the client sending out IMIP REQUESTS
+ // for specific attendees.
+ return;
+ }
+
if (aItipItem.responseMethod == "REPLY") {
// Get my participation status
var attendee = aItipItem.getItemList({})[0].getAttendeeById(this.calendarUserAddress);
if (!attendee) {
return;
}
// work around BUG 351589, the below just removes RSVP:
aItipItem.setAttendeeStatus(attendee.id, attendee.participationStatus);
--- a/calendar/providers/caldav/calDavRequestHandlers.js
+++ b/calendar/providers/caldav/calDavRequestHandlers.js
@@ -734,17 +734,17 @@ multigetSyncHandler.prototype = {
let D = new Namespace("D", "DAV:");
let queryXml =
<calendar-multiget xmlns:D={D} xmlns={C}>
<D:prop>
<D:getetag/>
<calendar-data/>
</D:prop>
</calendar-multiget>;
-
+
let batchSize = cal.getPrefSafe("calendar.caldav.multigetBatchSize", 100);
while (this.itemsNeedFetching.length && batchSize > 0) {
batchSize --;
let locpath = this.itemsNeedFetching.pop();
queryXml.D::prop += <D:href xmlns:D={D}>{locpath}</D:href>;
}
let multigetQueryString = xmlHeader + queryXml.toXMLString();
@@ -799,17 +799,17 @@ multigetSyncHandler.prototype = {
return;
}
if (this.itemsNeedFetching.length == 0) {
if (this.newSyncToken) {
this.calendar.mWebdavSyncToken = this.newSyncToken;
this.calendar.mTargetCalendar.setMetaData("sync-token", this.newSyncToken);
cal.LOG("CalDAV: New webdav-sync Token: " + this.calendar.mWebdavSyncToken);
}
-
+
this.calendar.finalizeUpdatedItems(this.changelogListener,
this.baseUri);
}
if (!this._reader) {
// No reader means there was a request error
cal.LOG("CalDAV: onStopRequest: no reader");
return;
}
@@ -819,17 +819,17 @@ multigetSyncHandler.prototype = {
this._reader = null;
}
if (this.itemsNeedFetching.length > 0) {
cal.LOG("CalDAV: Still need to fetch " + this.itemsNeedFetching.length + " elements.");
this._reader = Components.classes["@mozilla.org/saxparser/xmlreader;1"]
.createInstance(Components.interfaces.nsISAXXMLReader);
this._reader.contentHandler = this;
this._reader.errorHandler = this;
- this._reader.parseAsync(null);
+ this._reader.parseAsync(null);
let timerCallback = {
requestHandler : this,
notify: function(timer) {
// Call multiget again to get another batch
this.requestHandler.doMultiGet();
}
};
let timer = Components.classes["@mozilla.org/timer;1"]
@@ -872,17 +872,17 @@ multigetSyncHandler.prototype = {
if (this.calendar.isCached) {
this.calendar.superCalendar.startBatch();
}
},
endDocument: function mg_endDocument() {
if (this.calendar.isCached) {
this.calendar.superCalendar.endBatch();
- }
+ }
},
startElement: function mg_startElement(aUri, aLocalName, aQName, aAttributes) {
switch (aLocalName) {
case "response":
this.currentResponse = {};
this.tag = null
this.isInPropStat=false;
@@ -943,17 +943,17 @@ multigetSyncHandler.prototype = {
oldEtag = null;
}
if (!oldEtag || oldEtag != r.getetag) {
this.changeCount++;
this.calendar.addTargetCalendarItem(r.href,
r.calendardata,
this.baseUri,
r.getetag,
- null);
+ this.listener);
}
} else {
cal.WARN("CalDAV: Unexpected response, status: " +
r.status + ", href: " + r.href + " calendar-data:");
this.unhandledErrors++;
}
break;
case "propstat":