Bug 466686 - Can't create cached calendars right away (r=philipp)
authorSimon Vaillancourt <simon.at.orcl@gmail.com>
Wed, 16 Jun 2010 08:44:54 -0400
changeset 5829 eaf205fc21fa15cb6a0a0ac06a36694ac9d58e7e
parent 5828 9b001b523daf941ad16718ead1de7c3f031588ca
child 5830 5b527a449da30f7bc75f314f88071760b463158c
push idunknown
push userunknown
push dateunknown
reviewersphilipp
bugs466686
Bug 466686 - Can't create cached calendars right away (r=philipp)
calendar/base/content/widgets/calendar-list-tree.xml
calendar/base/src/calCalendarManager.js
calendar/locales/en-US/chrome/calendar/calendar.dtd
calendar/providers/caldav/calDavCalendar.js
calendar/resources/content/calendarCreation.js
calendar/resources/content/calendarCreation.xul
--- a/calendar/base/content/widgets/calendar-list-tree.xml
+++ b/calendar/base/content/widgets/calendar-list-tree.xml
@@ -15,16 +15,17 @@
    - The Original Code is Sun Microsystems code.
    -
    - The Initial Developer of the Original Code is
    -   Philipp Kewisch <mozilla@kewis.ch>
    - Portions created by the Initial Developer are Copyright (C) 2008
    - the Initial Developer. All Rights Reserved.
    -
    - Contributor(s):
+   - 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
@@ -148,17 +149,17 @@
         onEndBatch: function cO_onEndBatch() { },
         onLoad: function cO_onLoad() { },
 
         onAddItem: function cO_onAddItem(aItem) {
             if (aItem.calendar.type != "caldav") {
                 this.listTree.ensureCalendarVisible(aItem.calendar);
             }
         },
-        onModifyItem: function cO_onModifyItem(aNewItem, aOldItem) { 
+        onModifyItem: function cO_onModifyItem(aNewItem, aOldItem) {
             if (aNewItem.calendar.type != "caldav") {
                 this.listTree.ensureCalendarVisible(aNewItem.calendar);
             }
         },
         onDeleteItem: function cO_onDeleteItem(aDeletedItem) { },
         onError: function cO_onError(aCalendar, aErrNo, aMessage) { },
 
         onPropertyChanged: function cO_onPropertyChanged(aCalendar,
@@ -377,17 +378,17 @@
           this.mCalendarList.forEach(this.addCalendar, this);
           return this.mCalendarList;
         ]]></setter>
       </property>
 
       <property name="compositeCalendar">
         <getter><![CDATA[
           if (!this.mCompositeCalendar) {
-              this.mCompositeCalendar = 
+              this.mCompositeCalendar =
                   Components.classes["@mozilla.org/calendar/calendar;1?type=composite"]
                             .createInstance(Components.interfaces.calICompositeCalendar);
           }
 
           return this.mCompositeCalendar;
         ]]></getter>
         <setter><![CDATA[
           if (this.mCompositeCalendar) {
@@ -479,17 +480,26 @@
         <!--
           - Add a calendar to the calendar list
           -
           - @param aCalendar     The calendar to add.
           -->
         <parameter name="aCalendar"/>
         <body><![CDATA[
           let composite = this.compositeCalendar;
-          this.mCalendarList.push(aCalendar);
+
+          let initialSortOrderPos = aCalendar.getProperty("initialSortOrderPos");
+          if (initialSortOrderPos != null && initialSortOrderPos < this.mCalendarList.length) {
+              // Insert the calendar at the requested sort order position
+              // and then discard the property
+              this.mCalendarList.splice(initialSortOrderPos, 0, aCalendar);
+              aCalendar.deleteProperty("initialSortOrderPos");
+          } else {
+              this.mCalendarList.push(aCalendar);
+          }
           this.treebox.rowCountChanged(this.mCalendarList.length - 1, 1);
 
           if (!composite.defaultCalendar ||
               aCalendar.id == composite.defaultCalendar.id) {
               this.tree.view.selection.select(this.mCalendarList.length - 1);
           }
 
           this.updateCalendarColor(aCalendar);
@@ -509,17 +519,17 @@
           // Adding a calendar causes the sortorder to be changed.
           this.sortOrderChanged();
         ]]></body>
       </method>
 
       <method name="removeCalendar">
         <!--
           - Remove a calendar from the calendar list
-          - 
+          -
           - @param aCalendar     The calendar to remove.
           -->
         <parameter name="aCalendar"/>
         <body><![CDATA[
           let index = this.findIndexById(aCalendar.id);
           if (index < 0) {
               return;
           }
@@ -540,49 +550,49 @@
           // Remove the css style rule from the sheet.
           let sheet = this.sheet;
           for (let i = 0; i < sheet.cssRules.length; i++) {
               if (sheet.cssRules[i] == this.ruleCache[aCalendar.id]) {
                   sheet.deleteRule(i);
                   delete this.ruleCache[aCalendar.id];
                   break;
               }
-          } 
+          }
 
           this.sortOrderChanged();
         ]]></body>
       </method>
 
       <method name="updateCalendar">
         <!--
           - Update a calendar's tree row (to refresh the color and such)
-          - 
+          -
           - @param aCalendar     The calendar to update.
           -->
         <parameter name="aCalendar"/>
         <body><![CDATA[
           this.treebox.invalidateRow(this.findIndexById(aCalendar.id));
         ]]></body>
       </method>
 
       <method name="updateCalendarColor">
         <!--
           - Update a calendar's color rules.
-          - 
+          -
           - @param aCalendar     The calendar to update.
           -->
         <parameter name="aCalendar"/>
         <body><![CDATA[
           let color = aCalendar.getProperty("color") || "#a8c2e1";
           let sheet = this.sheet;
           if (!(aCalendar.id in this.ruleCache)) {
               let ruleString = "calendar-list-tree > tree > treechildren" +
                                "::-moz-tree-cell(color-treecol, id-"  +
                                aCalendar.id + ") {}";
-                             
+
               let ruleIndex = sheet.insertRule(ruleString, sheet.cssRules.length);
               this.ruleCache[aCalendar.id] = sheet.cssRules[ruleIndex];
           }
           this.ruleCache[aCalendar.id].style.backgroundColor = color;
         ]]></body>
       </method>
 
       <method name="getCalendarFromEvent">
@@ -616,17 +626,17 @@
           }
           return aRow && aRow.value > -1 && this.mCalendarList[aRow.value];
         ]]></body>
       </method>
 
       <method name="getCalendar">
         <!--
           - Get the calendar from a certain index.
-          - 
+          -
           - @param aIndex     The index to get the calendar for.
           -->
         <parameter name="aIndex"/>
         <body><![CDATA[
           let index = Math.max(0, Math.min(this.mCalendarList.length - 1, aIndex));
           return this.mCalendarList[index];
         ]]></body>
       </method>
@@ -636,17 +646,17 @@
                 readonly="true"
                 onget="return this.mCalendarList.length"/>
 
       <method name="getCellProperties">
         <parameter name="aRow"/>
         <parameter name="aCol"/>
         <parameter name="aProps"/>
         <body><![CDATA[
-          try { 
+          try {
               this.getRowProperties(aRow, aProps);
               this.getColumnProperties(aCol, aProps);
           } catch (e) {
               // It seems errors in these functions are not shown, do this
               // explicitly.
               cal.ERROR("Error getting cell props: " + e);
           }
         ]]></body>
@@ -677,17 +687,17 @@
           aProps.AppendElement(cal.getAtomFromService(bgColorProp));
 
           // Set a property to get the contrasting text color (foreground)
           let fgColorProp = cal.getContrastingTextColor(color || "a8c2e1");
           aProps.AppendElement(cal.getAtomFromService(fgColorProp));
 
           let currentStatus = calendar.getProperty("currentStatus");
           if (!Components.isSuccessCode(currentStatus)) {
-              // 'readfailed' is supposed to "win" over 'readonly', meaning that 
+              // 'readfailed' is supposed to "win" over 'readonly', meaning that
               // if reading from a calendar fails there is no further need to also display
               // information about 'readonly' status
               aProps.AppendElement(cal.getAtomFromService("readfailed"));
           } else if (calendar.readOnly) {
               aProps.AppendElement(cal.getAtomFromService("readonly"));
           }
 
           // Set up the disabled state
@@ -765,17 +775,17 @@
 
       <method name="canDrop">
         <parameter name="aRow"/>
         <parameter name="aOrientation"/>
         <body><![CDATA[
           let dragSession = cal.getDragService().getCurrentSession();
           let dataTransfer = dragSession && dragSession.dataTransfer;
           if (!this.allowDrag || !dataTransfer) {
-              // If dragging is not allowed or there is no data transfer then 
+              // If dragging is not allowed or there is no data transfer then
               // we can't drop (i.e dropping a file on the calendar list).
               return false;
           }
 
           let dragCalId = dataTransfer.getData("application/x-moz-calendarID");
 
           return (aOrientation != Components.interfaces.nsITreeView.DROP_ON &&
                   dragCalId != null);
@@ -922,31 +932,31 @@
                   return composite.getCalendarById(calendar.id) ? "true" : "false";
               case "status-treecol":
                   // The value of this cell shows the calendar readonly state
                   return (calendar.readOnly ? "true" : "false");
           }
           return null;
         ]]></body>
       </method>
-        
+
       <method name="getCellText">
         <parameter name="aRow"/>
         <parameter name="aCol"/>
         <body><![CDATA[
           let calendar = this.getCalendar(aRow);
 
           switch (aCol.element.getAttribute("anonid")) {
               case "calendarname-treecol":
                   return this.getCalendar(aRow).name;
           }
           return "";
         ]]></body>
       </method>
-      
+
       <method name="setTree">
         <parameter name="aTreeBox"/>
         <body><![CDATA[
           this.treebox = aTreeBox;
         ]]></body>
       </method>
 
       <method name="toggleOpenState">
--- a/calendar/base/src/calCalendarManager.js
+++ b/calendar/base/src/calCalendarManager.js
@@ -19,16 +19,17 @@
  *
  * Contributor(s):
  *   Stuart Parmenter <stuart.parmenter@oracle.com>
  *   Matthew Willis <lilmatt@mozilla.com>
  *   Michiel van Leeuwen <mvl@exedo.nl>
  *   Martin Schroeder <mschroeder@mozilla.x-home.org>
  *   Philipp Kewisch <mozilla@kewis.ch>
  *   Daniel Boelzle <daniel.boelzle@sun.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
@@ -112,42 +113,42 @@ calCalendarManager.prototype = {
 
     get readOnlyCalendarCount() {
         return this.mReadonlyCalendarCount;
     },
 
     get calendarCount() {
         return this.mCalendarCount;
     },
-    
+
     setUpStartupObservers: function ccm_setUpStartupObservers() {
         var observerSvc = Components.classes["@mozilla.org/observer-service;1"]
                           .getService(Components.interfaces.nsIObserverService);
 
         observerSvc.addObserver(this, "profile-after-change", false);
         observerSvc.addObserver(this, "profile-before-change", false);
         observerSvc.addObserver(this, "em-action-requested", false);
     },
-    
+
     startup: function ccm_startup() {
         this.checkAndMigrateDB();
         this.mCache = null;
         this.mCalObservers = null;
         this.mRefreshTimer = null;
         this.setUpPrefObservers();
         this.setUpRefreshTimer();
         this.setupOfflineObservers();
         if (cal.isSunbird()) {
             this.loginMasterPassword();
         }
         this.mNetworkCalendarCount = 0;
         this.mReadonlyCalendarCount = 0;
         this.mCalendarCount = 0;
     },
-    
+
     shutdown: function ccm_shutdown() {
         for each (var cal in this.mCache) {
             cal.removeObserver(this.mCalObservers[cal.id]);
         }
 
         this.cleanupPrefObservers();
         this.cleanupOfflineObservers();
 
@@ -160,17 +161,17 @@ calCalendarManager.prototype = {
     },
 
     setUpPrefObservers: function ccm_setUpPrefObservers() {
         var prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
                                 .getService(Components.interfaces.nsIPrefBranch2);
         prefBranch.addObserver("calendar.autorefresh.enabled", this, false);
         prefBranch.addObserver("calendar.autorefresh.timeout", this, false);
     },
-    
+
     cleanupPrefObservers: function ccm_cleanupPrefObservers() {
         var prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
                                 .getService(Components.interfaces.nsIPrefBranch2);
         prefBranch.removeObserver("calendar.autorefresh.enabled", this);
         prefBranch.removeObserver("calendar.autorefresh.timeout", this);
     },
 
     setUpRefreshTimer: function ccm_setUpRefreshTimer() {
@@ -266,17 +267,17 @@ calCalendarManager.prototype = {
                 let extension = aSubject.QueryInterface(Components.interfaces.nsIUpdateItem);
                 let extMgr = Components.classes["@mozilla.org/extensions/manager;1"]
                                        .getService(Components.interfaces.nsIExtensionManager);
                 try {
                     switch (aData) {
                         case "item-disabled":
                             if (!this.queryUninstallProvider(extension)) {
                                 // If the extension should not be disabled,
-                                // then re-enable it. 
+                                // then re-enable it.
                                 extMgr.enableItem(extension.id);
                             }
                             break;
                         case "item-uninstalled":
                             if (!this.queryUninstallProvider(extension)) {
                                 // If the extension should not be uninstalled,
                                 // then cancel the uninstall
                                 extMgr.cancelUninstallItem(extension.id);
@@ -342,17 +343,17 @@ calCalendarManager.prototype = {
             // Schema changes in v6:
             //
             // - Change all STRING columns to TEXT to avoid SQLite's
             //   "feature" where it will automatically convert strings to
             //   numbers (ex: 10e4 -> 10000). See bug 333688.
 
             // Create the new tables.
 
-            try { 
+            try {
                 db.executeSimpleSQL("DROP TABLE cal_calendars_v6; DROP TABLE cal_calendars_prefs_v6;");
             } catch (e) {
                 // We should get exceptions for trying to drop tables
                 // that don't (shouldn't) exist.
             }
 
             db.executeSimpleSQL("CREATE TABLE cal_calendars_v6 " +
                                 "(id   INTEGER PRIMARY KEY," +
@@ -378,17 +379,17 @@ calCalendarManager.prototype = {
                                 "       FROM cal_calendars_prefs");
 
             // Delete each old table and rename the new ones to use the
             // old tables' names.
             var tableNames = ["cal_calendars", "cal_calendars_prefs"];
 
             for (var i in tableNames) {
                 db.executeSimpleSQL("DROP TABLE " + tableNames[i] + ";" +
-                                    "ALTER TABLE " + tableNames[i] + "_v6 " + 
+                                    "ALTER TABLE " + tableNames[i] + "_v6 " +
                                     "  RENAME TO " + tableNames[i] + ";");
             }
 
             oldVersion = 8;
         }
 
         if (oldVersion < DB_SCHEMA_VERSION) {
             dump ("**** Upgrading calCalendarManager schema to 9/10\n");
@@ -510,17 +511,17 @@ calCalendarManager.prototype = {
                 db.rollbackTransaction();
             }
         } catch (exc) {
             db.rollbackTransaction();
             throw exc;
         }
     },
 
-    /** 
+    /**
      * @return      db schema version
      * @exception   various, depending on error
      */
     getSchemaVersion: function calMgrGetSchemaVersion(db) {
         var stmt;
         var version = null;
 
         var table;
@@ -667,16 +668,21 @@ calCalendarManager.prototype = {
         // bail if this calendar (or one that looks identical to it) is already registered
         cal.ASSERT(calendar.id === null, "[calCalendarManager::registerCalendar] calendar already registered!", true);
         this.assureCache();
 
         calendar.id = cal.getUUID();
         cal.setPref(getPrefBranchFor(calendar.id) + "type", calendar.type);
         cal.setPref(getPrefBranchFor(calendar.id) + "uri", calendar.uri.spec);
 
+        if ((calendar.getProperty("cache.supported") !== false) &&
+            calendar.getProperty("cache.enabled")) {
+            calendar = new calCachedCalendar(calendar);
+        }
+
         this.setupCalendar(calendar);
         flushPrefs();
 
         if (!calendar.getProperty("disabled") && calendar.canRefresh) {
             calendar.refresh();
         }
 
         this.notifyObservers("onCalendarRegistered", [calendar]);
@@ -790,17 +796,17 @@ calCalendarManager.prototype = {
                     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");
                         }
- 
+
                         if ((calendar.getProperty("cache.supported") !== false) &&
                             calendar.getProperty("cache.enabled")) {
                             calendar = new calCachedCalendar(calendar);
                         }
                     } else { // create dummy calendar that stays disabled for this run:
                         calendar = new calDummyCalendar(ctype);
                         calendar.id = id;
                         calendar.uri = uri;
@@ -863,17 +869,17 @@ calCalendarManager.prototype = {
         cal.ASSERT(calendar, "Invalid Calendar!");
         cal.ASSERT(calendar.id !== null, "Calendar id needs to be set!");
         cal.ASSERT(name && name.length > 0, "Pref Name must be non-empty!");
 
         let prefService = Components.classes["@mozilla.org/preferences-service;1"]
                                     .getService(Components.interfaces.nsIPrefBranch);
         prefService.deleteBranch(getPrefBranchFor(calendar.id) + name);
     },
-    
+
     mObservers: null,
     addObserver: function(aObserver) {
         this.mObservers.add(aObserver);
     },
 
     removeObserver: function(aObserver) {
         this.mObservers.remove(aObserver);
     }
@@ -922,20 +928,65 @@ calMgrCalendarObserver.prototype = {
         switch (aName) {
             case "requiresNetwork":
                 this.calMgr.mNetworkCalendarCount += (aValue ? 1 : -1);
                 break;
             case "readOnly":
                 this.calMgr.mReadonlyCalendarCount += (aValue ? 1 : -1);
                 break;
             case "cache.enabled":
-                if (aCalendar.wrappedJSObject instanceof calCachedCalendar) {
-                    // any attempt to switch this flag will reset the cached calendar;
-                    // could be useful for users in case the cache may be corrupted.
-                    aCalendar.wrappedJSObject.setupCachedCalendar();
+                aOldValue = aOldValue || false;
+                aValue = aValue || false;
+
+                if (aOldValue != aValue) {
+
+                    // Try to find the current sort order
+                    let sortOrderPref = cal.getPrefSafe("calendar.list.sortOrder", "").split(" ");
+                    let initialSortOrderPos = null;
+                    for (let i = 0; i < sortOrderPref.length; ++i) {
+                        if (sortOrderPref[i] == aCalendar.id) {
+                            initialSortOrderPos = i;
+                        }
+                    }
+                    // Enabling or disabling cache on a calendar re-creates
+                    // it so the registerCalendar call can wrap/unwrap the
+                    // calCachedCalendar facade saving the user the need to
+                    // restart Thunderbird and making sure a new Id is used.
+                    this.calMgr.unregisterCalendar(aCalendar);
+                    this.calMgr.deleteCalendar(aCalendar);
+                    var newCal = this.calMgr.createCalendar(aCalendar.type,aCalendar.uri);
+                    newCal.name = aCalendar.name;
+
+                    // TODO: if properties get added this list will need to be adjusted,
+                    // ideally we should add a "getProperties" method to calICalendar.idl
+                    // to retrieve all non-transient properties for a calendar.
+                    let propsToCopy = [ "color",
+                                        "disabled",
+                                        "auto-enabled",
+                                        "cache.enabled",
+                                        "suppressAlarms",
+                                        "calendar-main-in-composite",
+                                        "calendar-main-default"];
+                    for each ( prop in propsToCopy ) {
+                      newCal.setProperty(prop,
+                                         aCalendar.getProperty(prop));
+                    }
+
+                    if (initialSortOrderPos != null) {
+                        newCal.setProperty("initialSortOrderPos",
+                                           initialSortOrderPos);
+                    }
+                    this.calMgr.registerCalendar(newCal);
+                }
+                else {
+                    if (aCalendar.wrappedJSObject instanceof calCachedCalendar) {
+                        // any attempt to switch this flag will reset the cached calendar;
+                        // could be useful for users in case the cache may be corrupted.
+                        aCalendar.wrappedJSObject.setupCachedCalendar();
+                    }
                 }
                 break;
             case "disabled":
                 if (!aValue && aCalendar.canRefresh) {
                     aCalendar.refresh();
                 }
                 break;
         }
@@ -988,18 +1039,18 @@ calMgrCalendarObserver.prototype = {
             case calIErrors.ICS_MALFORMEDDATA:
                 message = props.GetStringFromName("icsMalformedError");
                 break;
             case calIErrors.MODIFICATION_FAILED:
                 errMsg = calGetString("calendar", "errorWriting", [aCalendar.name]);
              default:
                 message = aMessage;
          }
- 
-                
+
+
         paramBlock.SetString(0, errMsg);
         paramBlock.SetString(1, errCode);
         paramBlock.SetString(2, message);
 
         this.storedReadOnly = this.calendar.readOnly;
         var errorCode = calGetString("calendar","errorCode", [errCode]);
         var errorDescription = calGetString("calendar","errorDescription", [message]);
         var summary = errMsg + " " + errorCode + ". " + errorDescription;
@@ -1031,17 +1082,17 @@ calMgrCalendarObserver.prototype = {
                                   .getService(Components.interfaces.nsIWindowWatcher);
             var promptWindow =
                 wWatcher.openWindow
                     (null, "chrome://calendar/content/calendar-error-prompt.xul",
                      "_blank", "chrome,dialog=yes,alwaysRaised=yes",
                      paramBlock);
             // Will remove paramBlock from announced messages when
             // promptWindow is closed.  (Closing fires unloaded event, but
-            // promptWindow is also unloaded [to clean it?] before loading, 
+            // promptWindow is also unloaded [to clean it?] before loading,
             // so wait for detected load event before detecting unload event
             // that signifies user closed this prompt window.)
             var observer = this;
             function awaitLoad(event) {
                 // #2 loaded, remove load listener
                 promptWindow.removeEventListener("load", awaitLoad, false);
                 function awaitUnload(event) {
                     // #4 unloaded (user closed prompt window),
--- a/calendar/locales/en-US/chrome/calendar/calendar.dtd
+++ b/calendar/locales/en-US/chrome/calendar/calendar.dtd
@@ -303,17 +303,17 @@
 <!ENTITY calendarproperties.webdav.label                   "iCalendar (ICS)">
 <!ENTITY calendarproperties.caldav.label                   "CalDAV">
 <!ENTITY calendarproperties.wcap.label                     "Sun Java System Calendar Server (WCAP)">
 <!ENTITY calendarproperties.format.label                   "Format:">
 <!ENTITY calendarproperties.location.label                 "Location:">
 <!ENTITY calendarproperties.name.label                     "Name:">
 <!ENTITY calendarproperties.readonly.label                 "Read Only">
 <!ENTITY calendarproperties.firealarms.label               "Show Alarms">
-<!ENTITY calendarproperties.cache.label                    "Cache (EXPERIMENTAL, requires restart)">
+<!ENTITY calendarproperties.cache.label                    "Cache (EXPERIMENTAL)">
 <!ENTITY calendarproperties.enabled.label                  "Switch this calendar on">
 <!ENTITY calendarproperties.forceDisabled.label            "The provider for this calendar could not be found. This often happens if you have disabled or uninstalled certain addons.">
 <!ENTITY calendarproperties.unsubscribe.label              "Unsubscribe">
 <!ENTITY calendarproperties.unsubscribe.accesskey          "U">
 
 <!-- Calendar Publish Dialog -->
 <!ENTITY calendar.publish.dialog.title              "Publish Calendar">
 <!ENTITY calendar.publish.url.label                 "Publishing URL">
--- a/calendar/providers/caldav/calDavCalendar.js
+++ b/calendar/providers/caldav/calDavCalendar.js
@@ -165,16 +165,29 @@ calDavCalendar.prototype = {
     },
 
 
     // calIChangeLog interface
     resetLog: function caldav_resetLog() {
         if (this.isCached && this.mTargetCalendar) {
             this.mTargetCalendar.startBatch();
             try {
+                try {
+                    this.mCtag = null;
+                    this.mTargetCalendar.deleteMetaData("ctag");
+                } catch(e) {
+                    cal.ERROR(e);
+                }
+                try {
+                    this.mWebdavSyncToken = null;
+                    this.mTargetCalendar.deleteMetaData("sync-token");
+                } catch(e) {
+                    cal.ERROR(e);
+                }
+
                 for (var itemId in this.mItemInfoCache) {
                     this.mTargetCalendar.deleteMetaData(itemId);
                     delete this.mItemInfoCache[itemId];
                 }
             } finally {
                 this.mTargetCalendar.endBatch();
             }
         }
--- a/calendar/resources/content/calendarCreation.js
+++ b/calendar/resources/content/calendarCreation.js
@@ -17,16 +17,17 @@
  * The Initial Developer of the Original Code is
  * Michiel van Leeuwen <mvl@exedo.nl>.
  * Portions created by the Initial Developer are Copyright (C) 2005
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Gary van der Merwe <garyvdm@gmail.com>
  *   Philipp Kewisch <mozilla@kewis.ch>
+ *   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
@@ -174,16 +175,17 @@ function prepareCreateCalendar() {
  * The actual process of registering the created calendar.
  */
 function doCreateCalendar() {
     let cal_name = document.getElementById("calendar-name").value;
     let cal_color = document.getElementById('calendar-color').color;
 
     gCalendar.name = cal_name;
     gCalendar.setProperty('color', cal_color);
+    gCalendar.setProperty("cache.enabled", document.getElementById("cache").checked);
 
     if (!document.getElementById("fire-alarms").checked) {
         gCalendar.setProperty('suppressAlarms', true);
     }
 
     cal.getCalendarManager().registerCalendar(gCalendar);
     return true;
 }
--- a/calendar/resources/content/calendarCreation.xul
+++ b/calendar/resources/content/calendarCreation.xul
@@ -21,16 +21,17 @@
    -
    - Contributor(s):
    -   Mike Shaver <shaver@mozilla.org>
    -   Michiel van Leeuwen <mvl@exedo.nl>
    -   Simon Paquet <bugzilla@babylonsounds.com>
    -   Gary van der Merwe <garyvdm@gmail.com>
    -   Philipp Kewisch <mozilla@kewis.ch>
    -   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
@@ -100,16 +101,21 @@
                           which is triggered. Unfortunately, the more logical
                           choice of dragdrop doesn't work here either.-->
                     <textbox id="calendar-uri"
                              required="true"
                              type="search"
                              oncommand="checkRequired();"
                              ondragexit="checkRequired();"/>
                 </row>
+                <row>
+                     <label/>
+                     <checkbox id="cache"
+                               label="&calendarproperties.cache.label;"/>
+                </row>
                 <notificationbox id="location-notifications" flex="1"/>
                 <!--
                 <description>&locationpage.login.description;</description>
                 <row align="center">
                     <label value="&locationpage.username.label;" control="calendar-username"/>
                     <textbox id="calendar-username"/>
                 </row>
                 <row align="center">