From a19eb7b156a2afb78301112cee80b0687b3c3107 Mon Sep 17 00:00:00 2001 From: jonschz Date: Tue, 28 Mar 2023 15:57:52 +0200 Subject: [PATCH 1/4] Proof-of-concept for colors --- src/api/gdata.js | 12 ++++++++++++ src/legacy/modules/gdataUtils.jsm | 16 ++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/api/gdata.js b/src/api/gdata.js index f130a24f..6dd67e93 100644 --- a/src/api/gdata.js +++ b/src/api/gdata.js @@ -57,6 +57,18 @@ this.gdata = class extends ExtensionAPI { let gdataUI = ChromeUtils.import("resource://gdata-provider/legacy/modules/gdataUI.jsm"); gdataUI.register(); }); + + const google_colors = ["#000", "#7986CB", "#33B679", "#8E24AA", "#E67C73", "#F6BF26", + "#F4511E", "#039BE5", "#616161", "#3F51B5", "#0B8043", "#D50000"]; + let calendar_categories = Services.prefs.getStringPref("calendar.categories.names"); + if (!calendar_categories.includes("Google Color 01")) { + for (let i=1; i<=11; i++) { + let padded_index = i.toString().padStart(2,'0'); + calendar_categories += ",Google Color " + padded_index; + Services.prefs.setStringPref("calendar.category.color.google_color_" + padded_index, google_colors[i]); + } + Services.prefs.setStringPref("calendar.categories.names", calendar_categories); + } } onShutdown(isAppShutdown) { diff --git a/src/legacy/modules/gdataUtils.jsm b/src/legacy/modules/gdataUtils.jsm index cd4b0e7a..9b515510 100644 --- a/src/legacy/modules/gdataUtils.jsm +++ b/src/legacy/modules/gdataUtils.jsm @@ -380,9 +380,17 @@ function EventToJSON(aItem, aOfflineStorage, aIsImport) { } setIf(itemData, "status", status); + let categoryList = aItem.getCategories({}); + // take the first category of the form "Color n" for 1 <= n <= 11 and turn it into a colorId; + // the rest are treated as categories + let colorCat = categoryList.findIndex((category) => /^Google Color (0[1-9]|1[01])$/.test(category)); + if (colorCat > -1) { + itemData.colorId = parseInt(categoryList[colorCat].match(/^Google Color (0[1-9]|1[01])$/)[1]); + categoryList.splice(colorCat, 1); + } // Google does not support categories natively, but allows us to store data // as an "extendedProperty", so we do here - let categories = cal.category.arrayToString(aItem.getCategories({})); + let categories = cal.category.arrayToString(categoryList); addExtendedProperty("X-MOZ-CATEGORIES", categories); // Only parse attendees if they are enabled, due to bug 407961 @@ -893,10 +901,14 @@ function JSONToEvent(aEntry, aCalendar, aDefaultReminders, aReferenceItem, aMeta } } + let categories = cal.category.stringToArray(sharedProps["X-MOZ-CATEGORIES"]); + if (aEntry.colorId) { + // Add color category at the beginning + categories.unshift("Google Color " + aEntry.colorId.toString().padStart(2,'0')); + } // Google does not support categories natively, but allows us to store // data as an "extendedProperty", and here it's going to be retrieved // again - let categories = cal.category.stringToArray(sharedProps["X-MOZ-CATEGORIES"]); item.setCategories(categories); // Conference data From 9ca48ef8cf2bb7b9f6529e297db2507abcfa477e Mon Sep 17 00:00:00 2001 From: jonschz Date: Sat, 28 Oct 2023 08:01:14 +0200 Subject: [PATCH 2/4] Implement basic enabling / disabling color support - clarification on the settings page - still missing: Listener for change of addon preferences --- src/_locales/en/messages.json | 5 +++- src/api/gdata.js | 12 -------- src/legacy/modules/gdataUI.jsm | 17 +++++++++++ src/legacy/modules/gdataUtils.jsm | 48 ++++++++++++++++++++++++++----- src/options/options.html | 20 +++++++------ src/options/options.js | 1 + 6 files changed, 75 insertions(+), 28 deletions(-) diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 5c1c11a8..96722b5d 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -165,7 +165,7 @@ }, "settings.warning": { - "message": "These settings are will allow you to make use of email scheduling via Google Calendar, but there have also been reports about extraneous notification emails being sent. Please test with a small meeting first." + "message": "The settings below allow you to make use of email scheduling via Google Calendar, but there have also been reports about extraneous notification emails being sent. Please test with a small meeting first." }, "settings.enableEmailInvitations": { @@ -180,6 +180,9 @@ "settings.forcePlainText": { "message": "Force use of plain text in event descriptions" }, + "settings.enableColors": { + "message": "Enable support for event colors (experimental; calendars must be unsubscribed, Thunderbird restarted (?), and the calendars resubscribed)" + }, "eventdialog.conferenceLabel": { "message": "Conference:" diff --git a/src/api/gdata.js b/src/api/gdata.js index 6dd67e93..f130a24f 100644 --- a/src/api/gdata.js +++ b/src/api/gdata.js @@ -57,18 +57,6 @@ this.gdata = class extends ExtensionAPI { let gdataUI = ChromeUtils.import("resource://gdata-provider/legacy/modules/gdataUI.jsm"); gdataUI.register(); }); - - const google_colors = ["#000", "#7986CB", "#33B679", "#8E24AA", "#E67C73", "#F6BF26", - "#F4511E", "#039BE5", "#616161", "#3F51B5", "#0B8043", "#D50000"]; - let calendar_categories = Services.prefs.getStringPref("calendar.categories.names"); - if (!calendar_categories.includes("Google Color 01")) { - for (let i=1; i<=11; i++) { - let padded_index = i.toString().padStart(2,'0'); - calendar_categories += ",Google Color " + padded_index; - Services.prefs.setStringPref("calendar.category.color.google_color_" + padded_index, google_colors[i]); - } - Services.prefs.setStringPref("calendar.categories.names", calendar_categories); - } } onShutdown(isAppShutdown) { diff --git a/src/legacy/modules/gdataUI.jsm b/src/legacy/modules/gdataUI.jsm index 379c6ab8..70974303 100644 --- a/src/legacy/modules/gdataUI.jsm +++ b/src/legacy/modules/gdataUI.jsm @@ -7,6 +7,9 @@ var EXPORTED_SYMBOLS = ["register", "unregister", "recordModule", "recordWindow" var { ExtensionSupport } = ChromeUtils.import("resource:///modules/ExtensionSupport.jsm"); +const Services = + globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services; // Thunderbird 103 compat + var unregisterIds = []; var unregisterModules = new Set(); var closeWindows = new Set(); @@ -67,9 +70,23 @@ function register() { checkMigrateCalendars(window); }, }); + + let { doEnableColors, getMessenger } = ChromeUtils.import( + "resource://gdata-provider/legacy/modules/gdataUtils.jsm" + ); + + let enableColors = getMessenger().gdataSyncPrefs.get("settings.enableColors", false); + if (enableColors) { + doEnableColors(); + } } function unregister() { + let { doDisableColors } = ChromeUtils.import( + "resource://gdata-provider/legacy/modules/gdataUtils.jsm" + ); + doDisableColors(); + for (let id of unregisterIds) { ExtensionSupport.unregisterWindowListener(id); Cu.unload(`resource://gdata-provider/legacy/modules/ui/${id}.jsm`); diff --git a/src/legacy/modules/gdataUtils.jsm b/src/legacy/modules/gdataUtils.jsm index 9b515510..a1ffa606 100644 --- a/src/legacy/modules/gdataUtils.jsm +++ b/src/legacy/modules/gdataUtils.jsm @@ -53,6 +53,8 @@ var EXPORTED_SYMBOLS = [ "JSONToDate", "monkeyPatch", "spinEventLoop", + "doEnableColors", + "doDisableColors", "getMessenger", ]; @@ -381,12 +383,16 @@ function EventToJSON(aItem, aOfflineStorage, aIsImport) { setIf(itemData, "status", status); let categoryList = aItem.getCategories({}); - // take the first category of the form "Color n" for 1 <= n <= 11 and turn it into a colorId; - // the rest are treated as categories - let colorCat = categoryList.findIndex((category) => /^Google Color (0[1-9]|1[01])$/.test(category)); - if (colorCat > -1) { - itemData.colorId = parseInt(categoryList[colorCat].match(/^Google Color (0[1-9]|1[01])$/)[1]); - categoryList.splice(colorCat, 1); + let enableColors = getMessenger().gdataSyncPrefs.get("settings.enableColors", false); + if (enableColors) { + // Take the first category of the form "Google Color dd" for 1 <= dd <= 11 and turn it into a colorId. + // Right now, the rest are treated like all other categories. + // TODO would it make sense / be possible to issue some sort of warning / error here or elsewhere? + let colorCat = categoryList.findIndex((category) => /^Google Color (0[1-9]|1[01])$/.test(category)); + if (colorCat > -1) { + itemData.colorId = parseInt(categoryList[colorCat].match(/^Google Color (0[1-9]|1[01])$/)[1]); + categoryList.splice(colorCat, 1); + } } // Google does not support categories natively, but allows us to store data // as an "extendedProperty", so we do here @@ -902,9 +908,12 @@ function JSONToEvent(aEntry, aCalendar, aDefaultReminders, aReferenceItem, aMeta } let categories = cal.category.stringToArray(sharedProps["X-MOZ-CATEGORIES"]); - if (aEntry.colorId) { + let enableColors = getMessenger().gdataSyncPrefs.get("settings.enableColors", false); + if (enableColors) { + if (aEntry.colorId) { // Add color category at the beginning categories.unshift("Google Color " + aEntry.colorId.toString().padStart(2,'0')); + } } // Google does not support categories natively, but allows us to store // data as an "extendedProperty", and here it's going to be retrieved @@ -1491,6 +1500,31 @@ function getWXAPI(extension, name, sync = false) { } } +function doEnableColors() { + const GOOGLE_COLORS = ["#000", "#7986CB", "#33B679", "#8E24AA", "#E67C73", "#F6BF26", + "#F4511E", "#039BE5", "#616161", "#3F51B5", "#0B8043", "#D50000"]; + let calendarCategories = Services.prefs.getStringPref("calendar.categories.names"); + for (let i=1; i<=11; i++) { + let paddedIndex = i.toString().padStart(2,'0'); + let catName = "Google Color " + paddedIndex; + if (!calendarCategories.includes(catName)) { + calendarCategories += "," + catName; + Services.prefs.setStringPref("calendar.category.color.google_color_" + paddedIndex, GOOGLE_COLORS[i]); + } + } + Services.prefs.setStringPref("calendar.categories.names", calendarCategories); +} + +function doDisableColors() { + for (let i=1; i<=11; i++) { + let padded_index = i.toString().padStart(2,'0'); + Services.prefs.clearUserPref("calendar.category.color.google_color_" + padded_index); + } + let calendarCategories = Services.prefs.getStringPref("calendar.categories.names"); + calendarCategories = calendarCategories.replaceAll(/,Google\sColor\s\d\d/g, "") + Services.prefs.setStringPref("calendar.categories.names", calendarCategories); +} + class SyncPrefs { prefs = {}; diff --git a/src/options/options.html b/src/options/options.html index e1c44424..24c48103 100644 --- a/src/options/options.html +++ b/src/options/options.html @@ -4,6 +4,18 @@ +
+ +
+
+ +
+
+ +

@@ -13,14 +25,6 @@
-
- -
-
- -
diff --git a/src/options/options.js b/src/options/options.js index be9901f2..fab37bc4 100644 --- a/src/options/options.js +++ b/src/options/options.js @@ -17,6 +17,7 @@ for (let node of document.querySelectorAll("[data-l10n-id]")) { "settings.sendEventNotifications": false, "settings.enableAttendees": false, "settings.forcePlainText": false, + "settings.enableColors": false, }); for (let [id, value] of Object.entries(prefs)) { From cc72a167f8e0087b24bc0468797114686fef92d0 Mon Sep 17 00:00:00 2001 From: jonschz Date: Mon, 10 Jul 2023 13:03:12 +0200 Subject: [PATCH 3/4] add/remove color categories on settings change --- src/legacy/modules/gdataUI.jsm | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/legacy/modules/gdataUI.jsm b/src/legacy/modules/gdataUI.jsm index 70974303..517c1492 100644 --- a/src/legacy/modules/gdataUI.jsm +++ b/src/legacy/modules/gdataUI.jsm @@ -71,11 +71,21 @@ function register() { }, }); - let { doEnableColors, getMessenger } = ChromeUtils.import( + let { doEnableColors, doDisableColors, getMessenger } = ChromeUtils.import( "resource://gdata-provider/legacy/modules/gdataUtils.jsm" ); - - let enableColors = getMessenger().gdataSyncPrefs.get("settings.enableColors", false); + + let messenger = getMessenger(); + messenger.storage.onChanged.addListener((settings, _target) => { + if ("settings.enableColors" in settings) { + if (settings["settings.enableColors"].newValue) { + doEnableColors(); + } else { + doDisableColors(); + } + } + }); + let enableColors = messenger.gdataSyncPrefs.get("settings.enableColors", false); if (enableColors) { doEnableColors(); } From 7658166c8e6120771dfa81997eabf2667a69b975 Mon Sep 17 00:00:00 2001 From: jonschz Date: Mon, 10 Jul 2023 15:41:59 +0200 Subject: [PATCH 4/4] flush the cache on changes of `enableColors` --- src/_locales/en/messages.json | 2 +- src/legacy/modules/gdataUI.jsm | 12 +++++++++--- src/legacy/modules/gdataUtils.jsm | 13 +++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 96722b5d..124c099a 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -181,7 +181,7 @@ "message": "Force use of plain text in event descriptions" }, "settings.enableColors": { - "message": "Enable support for event colors (experimental; calendars must be unsubscribed, Thunderbird restarted (?), and the calendars resubscribed)" + "message": "Enable support for event colors (experimental; enabling/disabling reloads all calendars)" }, "eventdialog.conferenceLabel": { diff --git a/src/legacy/modules/gdataUI.jsm b/src/legacy/modules/gdataUI.jsm index 517c1492..b1c59005 100644 --- a/src/legacy/modules/gdataUI.jsm +++ b/src/legacy/modules/gdataUI.jsm @@ -71,20 +71,26 @@ function register() { }, }); - let { doEnableColors, doDisableColors, getMessenger } = ChromeUtils.import( + let { doEnableColors, doDisableColors, flushCache, getMessenger } = ChromeUtils.import( "resource://gdata-provider/legacy/modules/gdataUtils.jsm" ); let messenger = getMessenger(); messenger.storage.onChanged.addListener((settings, _target) => { if ("settings.enableColors" in settings) { - if (settings["settings.enableColors"].newValue) { + // check for true and false explicitly, since `undefined` can also happen + if (settings["settings.enableColors"].newValue === true) { doEnableColors(); - } else { + } else if (settings["settings.enableColors"].newValue === false) { doDisableColors(); } + // We must delete the cache and reload all calendars from the source in order + // to add/remove the color categories. Restarting Thunderbird is insufficient + // because the locally cached calendar entries lack the color information. + flushCache(); } }); + let enableColors = messenger.gdataSyncPrefs.get("settings.enableColors", false); if (enableColors) { doEnableColors(); diff --git a/src/legacy/modules/gdataUtils.jsm b/src/legacy/modules/gdataUtils.jsm index a1ffa606..0393b8da 100644 --- a/src/legacy/modules/gdataUtils.jsm +++ b/src/legacy/modules/gdataUtils.jsm @@ -55,6 +55,7 @@ var EXPORTED_SYMBOLS = [ "spinEventLoop", "doEnableColors", "doDisableColors", + "flushCache", "getMessenger", ]; @@ -1525,6 +1526,18 @@ function doDisableColors() { Services.prefs.setStringPref("calendar.categories.names", calendarCategories); } +/** + * Deletes the local Google calendar cache and reloads all Google calendars from the source. + */ +function flushCache() { + let cals = cal.manager.wrappedJSObject.getCalendars(); + for (let calendar of cals) { + if (calendar.type == "gdata") { + calendar.mUncachedCalendar.resetLog(); + } + } +} + class SyncPrefs { prefs = {};