Fix bug 378754 - Calendar Auto Configuration/Deployment. r=philipp
authorDaniel Boelzle [:dbo] <daniel.boelzle@sun.com>
Tue, 27 Jan 2009 16:08:42 +0100
changeset 1770 b2cb2f44642f705404dc663666fff7e0e39bd62b
parent 1769 4650662557d98885ff0b189101afd7c67cc8b14f
child 1771 daa8b76471bff769faf7b05d3b536753489bdfe7
push idunknown
push userunknown
push dateunknown
reviewersphilipp
bugs378754
Fix bug 378754 - Calendar Auto Configuration/Deployment. r=philipp
calendar/base/content/calendar-management.js
calendar/base/public/calICalendar.idl
calendar/base/src/calCalendarManager.js
calendar/providers/composite/calCompositeCalendar.js
calendar/providers/wcap/calWcapCalendar.js
calendar/providers/wcap/calWcapSession.js
--- a/calendar/base/content/calendar-management.js
+++ b/calendar/base/content/calendar-management.js
@@ -81,20 +81,17 @@ function promptDeleteCalendar(aCalendar)
         calMgr.deleteCalendar(aCalendar);
     }
 }
 
 /**
  * Ensure that the passed calendar is visible to the user in the current window.
  */
 function ensureCalendarVisible(aCalendar) {
-    var composite = getCompositeCalendar();
-    if (!composite.getCalendar(aCalendar.uri)) {
-        composite.addCalendar(aCalendar);
-    }
+    getCompositeCalendar().addCalendar(aCalendar);
 }
 
 /**
  * Called to initialize the calendar manager for a window.
  */
 function loadCalendarManager() {
     var calMgr = getCalendarManager();
     var composite = getCompositeCalendar();
@@ -415,17 +412,17 @@ var calendarListTreeView = {
         this.getColumnProperties(aCol, aProps);
     },
 
     getRowProperties: function cLTV_getRowProperties(aRow, aProps) {
         var calendar = this.getCalendar(aRow);
         var composite = getCompositeCalendar();
 
         // Set up the composite calendar status
-        if (composite.getCalendar(calendar.uri)) {
+        if (composite.getCalendarById(calendar.id)) {
             aProps.AppendElement(getAtomFromService("checked"));
         } else {
             aProps.AppendElement(getAtomFromService("unchecked"));
         }
 
         // Get the calendar color
         var color = calendar.getProperty("color");
         color = color && color.substr(1);
@@ -593,28 +590,25 @@ var calendarListTreeView = {
     getProgressMode: function cLTV_getProgressMode(aRow, aCol) {},
 
     getCellValue: function cLTV_getCellValue(aRow, aCol) {
         var calendar = this.getCalendar(aRow);
         var composite = getCompositeCalendar();
 
         switch (aCol.id) {
             case "calendar-list-tree-checkbox":
-                return composite.getCalendar(calendar.uri) ? "true" : "false";
+                return composite.getCalendarById(calendar.id) ? "true" : "false";
             case "calendar-list-tree-status":
                 // The value of this cell shows the calendar readonly state
                 return (calendar.readOnly ? "true" : "false");
         }
         return null;
     },
 
     getCellText: function cLTV_getCellText(aRow, aCol) {
-        var calendar = this.getCalendar(aRow);
-        var composite = getCompositeCalendar();
-
         switch (aCol.id) {
             case "calendar-list-tree-calendar":
                 return this.getCalendar(aRow).name;
         }
         return "";
     },
 
     setTree: function cLTV_setTree(aTreeBox) {
@@ -626,27 +620,27 @@ var calendarListTreeView = {
     cycleHeader: function cLTV_cycleHeader(aCol) { },
 
     cycleCell: function cLTV_cycleCell(aRow, aCol) {
         var calendar = this.getCalendar(aRow);
         var composite = getCompositeCalendar();
 
         switch (aCol.id) {
             case "calendar-list-tree-checkbox":
-            try {
                 composite.startBatch();
-                if (composite.getCalendar(calendar.uri)) {
-                    composite.removeCalendar(calendar.uri);
-                } else {
-                    composite.addCalendar(calendar);
+                try {
+                    if (composite.getCalendarById(calendar.id)) {
+                        composite.removeCalendar(calendar);
+                    } else {
+                        composite.addCalendar(calendar);
+                    }
+                } finally {
+                    composite.endBatch();
                 }
-            } finally {
-                composite.endBatch();
-            }
-            break;
+                break;
         }
         this.treebox.invalidateRow(aRow);
     },
 
     isEditable: function cLTV_isEditable(aRow, aCol) {
         return false;
     },
 
@@ -989,17 +983,17 @@ var calendarManagerObserver = {
 
     onCalendarUnregistering: function cMO_onCalendarUnregistering(aCalendar) {
         var calendars = getCalendarManager().getCalendars({});
 
         calendarListTreeView.removeCalendar(aCalendar);
         aCalendar.removeObserver(this);
 
         // Make sure the calendar is removed from the composite calendar
-        getCompositeCalendar().removeCalendar(aCalendar.uri);
+        getCompositeCalendar().removeCalendar(aCalendar);
 
         // Update commands to disallow deleting the last calendar and only
         // allowing reload remote calendars when there are remote calendars.
         document.commandDispatcher.updateCommands("calendar_commands");
     },
 
     onCalendarDeleting: function cMO_onCalendarDeleting(aCalendar) {
     },
--- a/calendar/base/public/calICalendar.idl
+++ b/calendar/base/public/calICalendar.idl
@@ -465,41 +465,44 @@ interface calICalendar : nsISupports
   /**
    * Turn off batch mode.
    */
   void endBatch();
 };
 
 /** 
  * Used to allow multiple calendars (eg work and home) to be easily queried
- * and displayed as a single unit.
+ * and displayed as a single unit. All calendars are referenced by ID, i.e.
+ * calendars need to have an ID when being added.
  */
-[scriptable, uuid(8285aa0b-594c-4f93-abe3-523df947e0ad)]
+[scriptable, uuid(6748fa00-79b5-4728-84f3-20dd47e0b031)]
 interface calICompositeCalendar : calICalendar
 {
   /**
-   * Add an already created calendar to the composite
-   * 
+   * Adds a calendar to the composite, if not already part of it.
+   *
    * @param aCalendar the calendar to be added
    */
-  void addCalendar( in calICalendar aCalendar );
+  void addCalendar(in calICalendar aCalendar);
+
   /**
    * Remove a calendar from the composite
    * 
-   * @param aServer  URI of the server to be removed
+   * @param aCalendar the calendar to be removed
    */
-  void removeCalendar( in nsIURI aServer );
+  void removeCalendar(in calICalendar aCalendar);
+
   /**
-   * If a calendar for the given URI exists in the CompositeCalendar,
+   * If a calendar for the given ID exists in the CompositeCalendar,
    * return it; otherwise return null.
    *
-   * @param aServer  URI of the server whose calendar to return
-   * @return calendar for aServer, or null if none
+   * @param aId id of calendar
+   * @return calendar, or null if none
    */
-  calICalendar getCalendar( in nsIURI aServer );
+  calICalendar getCalendarById(in AUTF8String aId);
 
   /* return a list of all calendars currently registered */
   void getCalendars(out PRUint32 count,
                     [array, size_is(count), retval] out calICalendar aCalendars);
 
   /**
    * In order for addItem() to be called on this object, it is first necessary
    * to set this attribute to specify which underlying calendar the item is
@@ -518,17 +521,17 @@ interface calICompositeCalendar : calICa
    * If returns true there is a process running that needs to displayed
    * by the statusObserver
    */
   readonly attribute boolean statusDisplayed;
   
   /**
    * Sets a statusobserver for status notifications like startMeteors() and StopMeteors().
    */
-   void setStatusObserver( in calIStatusObserver aStatusObserver, in nsIDOMChromeWindow aWindow );
+  void setStatusObserver(in calIStatusObserver aStatusObserver, in nsIDOMChromeWindow aWindow);
 };
 
 /**
  * Make a more general nsIObserverService2 and friends to support
  * nsISupports data and use that instead?
  */
 [scriptable, uuid(2953c9b2-2c73-11d9-80b6-00045ace3b8d)]
 interface calIObserver : nsISupports
--- a/calendar/base/src/calCalendarManager.js
+++ b/calendar/base/src/calCalendarManager.js
@@ -668,16 +668,21 @@ calCalendarManager.prototype = {
             }
 
             for (let calBranch in allCals) {
                 let id = calBranch.substring(REGISTRY_BRANCH.length);
                 let ctype = cal.getPrefSafe(calBranch + ".type", null);
                 let curi = cal.getPrefSafe(calBranch + ".uri", null);
 
                 try {
+                    if (!ctype || !curi) { // sanity check
+                        prefService.deleteBranch(calBranch + ".");
+                        continue;
+                    }
+
                     let uri = cal.makeURL(curi);
                     let calendar = this.createCalendar(ctype, uri);
                     if (calendar) {
                         calendar.id = id;
                         if (calendar.getProperty("auto-enabled")) {
                             calendar.deleteProperty("disabled");
                             calendar.deleteProperty("auto-enabled");
                         }
--- a/calendar/providers/composite/calCompositeCalendar.js
+++ b/calendar/providers/composite/calCompositeCalendar.js
@@ -164,17 +164,19 @@ calCompositeCalendar.prototype = {
     get enabledCalendars() {
       return this.mCalendars.filter(
         function(e) { return !e.getProperty("disabled"); }
       );
     },
 
     set prefPrefix (aPrefPrefix) {
         if (this.mPrefPrefix) {
-            this.mCalendars.forEach(this.removeCalendar, this);
+            for each (let calendar in this.mCalendars) {
+                this.removeCalendar(calendar);
+            }
         }
         this.mPrefPrefix = aPrefPrefix;
         this.mActivePref = aPrefPrefix + "-in-composite";
         this.mDefaultPref = aPrefPrefix + "-default";
         var mgr = getCalendarManager();
         var cals = mgr.getCalendars({});
 
         cals.forEach(function (c) {
@@ -184,67 +186,59 @@ calCompositeCalendar.prototype = {
                 this.setDefaultCalendar(c, false);
         }, this);
     },
 
     get prefPrefix () {
         return this.mPrefPrefix;
     },
 
-    addCalendar: function (aCalendar) {
+    addCalendar: function cCC_addCalendar(aCalendar) {
+        cal.ASSERT(aCalendar.id, "calendar does not have an id!", true);
+
         // check if the calendar already exists
-        for each (cal in this.mCalendars) {
-            if (aCalendar.uri.equals(cal.uri)) {
-                // throw exception if calendar already exists?
-                return;
-            }
+        if (this.getCalendarById(aCalendar.id)) {
+            return;
         }
 
         // add our observer helper
-        aCalendar.addObserver(this.mObserverHelper); // XXX Never removed!
+        aCalendar.addObserver(this.mObserverHelper);
 
         this.mCalendars.push(aCalendar);
         if (this.mPrefPrefix) {
             aCalendar.setProperty(this.mActivePref, true);
         }
         this.mCompositeObservers.notify("onCalendarAdded", [aCalendar]);
 
         // if we have no default calendar, we need one here
         if (this.mDefaultCalendar == null && !aCalendar.getProperty("disabled")) {
             this.setDefaultCalendar(aCalendar, false);
         }
     },
 
-    removeCalendar: function (aServer) {
-        var newCalendars = Array();
-        var calToRemove = null;
-        for each (cal in this.mCalendars) {
-            if (!aServer.equals(cal.uri))
-                newCalendars.push(cal);
-            else
-                calToRemove = cal;
-        }
-
-        if (calToRemove) {
+    removeCalendar: function cCC_removeCalendar(aCalendar) {
+        let id = aCalendar.id;
+        let newCalendars = this.mCalendars.filter(function(calendar) { return calendar.id != id; });
+        if (newCalendars.length != this.mCalendars) {
             this.mCalendars = newCalendars;
             if (this.mPrefPrefix) {
-                calToRemove.deleteProperty(this.mActivePref);
-                calToRemove.deleteProperty(this.mDefaultPref);
+                aCalendar.deleteProperty(this.mActivePref);
+                aCalendar.deleteProperty(this.mDefaultPref);
             }
-            calToRemove.removeObserver(this.mObserverHelper);
-            this.mCompositeObservers.notify("onCalendarRemoved", [calToRemove]);
+            aCalendar.removeObserver(this.mObserverHelper);
+            this.mCompositeObservers.notify("onCalendarRemoved", [aCalendar]);
         }
     },
 
-    getCalendar: function (aServer) {
-        for each (cal in this.mCalendars) {
-            if (aServer.equals(cal.uri))
-                return cal;
+    getCalendarById: function cCC_getCalendarById(aId) {
+        for each (let calendar in this.mCalendars) {
+            if (calendar.id == aId) {
+                return calendar;
+            }
         }
-
         return null;
     },
 
     getCalendars: function getCalendars(count) {
         count.value = this.mCalendars.length;
         return this.mCalendars;
     },
 
--- a/calendar/providers/wcap/calWcapCalendar.js
+++ b/calendar/providers/wcap/calWcapCalendar.js
@@ -239,25 +239,17 @@ calWcapCalendar.prototype = {
             });
     },
 
     // calIWcapCalendar:
 
     m_session: null,
     get session calWcapCalendar_sessionGetter() {
         if (!this.m_session) {
-            var uri = this.uri;
-            ASSERT(uri, "no URI set!");
-            var path = uri.path;
-            var qmPos = path.indexOf("?");
-            if (qmPos != -1) {
-                uri = uri.clone();
-                uri.path = path.substring(0, qmPos); // get rid of params
-            }
-            this.m_session = getWcapSessionFor(this, uri);
+            this.m_session = getWcapSessionFor(this);
         }
         return this.m_session;
     },
 
     m_calId: null,
     get calId calWcapCalendar_calIdGetter() {
         return (this.m_calId || this.session.defaultCalId);
     },
@@ -318,17 +310,17 @@ calWcapCalendar.prototype = {
             out_count.value = ret.length;
         }
         return ret;
     },
 
     get defaultTimezone calWcapCalendar_defaultTimezoneGetter() {
         var tzid = this.getCalendarProperties("X-NSCP-CALPROPS-TZID");
         if (tzid.length == 0) {
-            logError("defaultTimezone: cannot get X-NSCP-CALPROPS-TZID!", this);
+            logWarning("defaultTimezone: cannot get X-NSCP-CALPROPS-TZID!", this);
             return "UTC"; // fallback
         }
         return tzid[0];
     },
 
     getAlignedTzid: function calWcapCalendar_getAlignedTzid(tz) {
         var tzid = tz.tzid;
         // check whether it is one cs supports:
--- a/calendar/providers/wcap/calWcapSession.js
+++ b/calendar/providers/wcap/calWcapSession.js
@@ -57,70 +57,96 @@ calWcapTimezone.prototype = {
         }
         return this.mDisplayName;
     },
     toString: function() {
         return this.icalComponent.toString();
     }
 };
 
+function splitUriParams(uri) {
+    let spec = uri.spec;
+    let qmPos = spec.indexOf("?");
+    return ((qmPos != -1)
+            ? [spec.substring(0, qmPos), spec.substring(qmPos)]
+            : [spec, ""]);
+}
+
 function getWcapSessionFor(calendar, uri) {
     let contextId = calendar.getProperty("shared_context");
     if (!contextId) {
         contextId = getUUID();
         calendar.setProperty("shared_context", contextId);
     }
+
     if (!getWcapSessionFor.m_sessions) {
         getWcapSessionFor.m_sessions = {};
     }
     let session = getWcapSessionFor.m_sessions[contextId];
+
     if (!session) {
-        session = new calWcapSession(contextId, uri);
+        session = new calWcapSession(contextId);
         getWcapSessionFor.m_sessions[contextId] = session;
-        // install a mandatory default calendar:
-        let defaultCal = calendar;
-        for each (let regCal in session.getRegisteredCalendars()) {
+
+        let defaultCal = null;
+        let registeredCalendars = session.getRegisteredCalendars();
+        for each (let regCal in registeredCalendars) {
             if (regCal.isDefaultCalendar) {
                 defaultCal = regCal;
-                session.credentials.userId = defaultCal.getProperty("user_id");
                 break;
             }
         }
-        if (!defaultCal) {
-            logError("no default calendar!", session);
+
+        if (defaultCal) {
+            session.defaultCalendar = defaultCal;
+            let [defaultSpec,] = splitUriParams(defaultCal.uri);
+            session.uri = cal.makeURL(defaultSpec);
+            session.credentials.userId = defaultCal.getProperty("user_id");
+            log("default calendar found.", defaultCal);
+
+            // check and fix changing urls (autoconf) of subscribed calendars here:
+            for each (let regCal in registeredCalendars) {
+                if (!regCal.isDefaultCalendar) {
+                    let [spec, params] = splitUriParams(regCal.uri);
+                    if (spec != defaultSpec) {
+                        log("fixing url of subscribed calendar: " + regCal.calId, session);
+                        let uri = regCal.uri.clone();
+                        uri.spec = (defaultSpec + params);
+                        regCal.uri = uri;
+                        regCal.setProperty("uri", uri.spec);
+                    }
+                }
+            }
+        } else { // no default calendar found, dump all subscribed calendars:
+            registeredCalendars.forEach(cal.getCalendarManager().unregisterCalendar,
+                                        cal.getCalendarManager());
         }
-        session.defaultCalendar = defaultCal;
     }
     return session;
 }
 
-function calWcapSession(contextId, thatUri) {
+function calWcapSession(contextId) {
     this.wrappedJSObject = this;
     this.m_contextId = contextId;
     this.m_loginQueue = [];
 
-    this.m_uri = thatUri.clone();
-    this.m_sessionUri = thatUri.clone();
-    this.m_sessionUri.userPass = "";
-    log("new session", this);
-
     // listen for shutdown, being logged out:
     var observerService = Components.classes["@mozilla.org/observer-service;1"]
                                     .getService(Components.interfaces.nsIObserverService);
     observerService.addObserver(this, "quit-application", false /* don't hold weakly */);
     getCalendarManager().addObserver(this);
 }
 calWcapSession.prototype = {
     // nsISupports:
     QueryInterface: function calWcapSession_QueryInterface(iid) {
         return doQueryInterface(this, calWcapSession.prototype, iid, null, g_classInfo.wcapSession);
     },
 
     toString: function calWcapSession_toString(msg) {
-        var str = ("context-id: " + this.m_contextId + ", uri: " + this.uri.spec);
+        let str = ("context-id: " + this.m_contextId + ", uri: " + (this.uri ? this.uri.spec : "unknown"));
         if (this.credentials.userId) {
             str += (", userId=" + this.credentials.userId);
         }
         if (!this.m_sessionId) {
             str += (getIOService().offline ? ", offline" : ", not logged in");
         }
         return str;
     },
@@ -772,16 +798,21 @@ calWcapSession.prototype = {
     m_uri: null,
     m_sessionUri: null,
     get uri calWcapSession_uriGetter() {
         return this.m_uri;
     },
     get sessionUri calWcapSession_sessionUriGetter() {
         return this.m_sessionUri;
     },
+    set uri calWcapSession_uriSetter(thatUri) {
+        this.m_uri = thatUri.clone();
+        this.m_sessionUri = thatUri.clone();
+        this.m_sessionUri.userPass = "";
+    },
 
     get userId calWcapSession_userIdGetter() {
         return this.credentials.userId;
     },
 
     get defaultCalId calWcapSession_defaultCalIdGetter() {
         var list = this.getUserPreferences("X-NSCP-WCAP-PREF-icsCalendar");
         var id = null;