diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/calendar/providers/composite | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/calendar/providers/composite')
-rw-r--r-- | comm/calendar/providers/composite/CalCompositeCalendar.jsm | 426 | ||||
-rw-r--r-- | comm/calendar/providers/composite/components.conf | 14 | ||||
-rw-r--r-- | comm/calendar/providers/composite/moz.build | 12 |
3 files changed, 452 insertions, 0 deletions
diff --git a/comm/calendar/providers/composite/CalCompositeCalendar.jsm b/comm/calendar/providers/composite/CalCompositeCalendar.jsm new file mode 100644 index 0000000000..65c1e3e2f2 --- /dev/null +++ b/comm/calendar/providers/composite/CalCompositeCalendar.jsm @@ -0,0 +1,426 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var EXPORTED_SYMBOLS = ["CalCompositeCalendar"]; + +var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm"); + +var { CalReadableStreamFactory } = ChromeUtils.import( + "resource:///modules/CalReadableStreamFactory.jsm" +); + +/** + * Calendar specific utility functions + */ + +function calCompositeCalendarObserverHelper(compCalendar) { + this.compCalendar = compCalendar; +} + +calCompositeCalendarObserverHelper.prototype = { + QueryInterface: ChromeUtils.generateQI(["calIObserver"]), + + onStartBatch(calendar) { + this.compCalendar.mObservers.notify("onStartBatch", [calendar]); + }, + + onEndBatch(calendar) { + this.compCalendar.mObservers.notify("onEndBatch", [calendar]); + }, + + onLoad(calendar) { + this.compCalendar.mObservers.notify("onLoad", [calendar]); + }, + + onAddItem(aItem) { + this.compCalendar.mObservers.notify("onAddItem", arguments); + }, + + onModifyItem(aNewItem, aOldItem) { + this.compCalendar.mObservers.notify("onModifyItem", arguments); + }, + + onDeleteItem(aDeletedItem) { + this.compCalendar.mObservers.notify("onDeleteItem", arguments); + }, + + onError(aCalendar, aErrNo, aMessage) { + this.compCalendar.mObservers.notify("onError", arguments); + }, + + onPropertyChanged(aCalendar, aName, aValue, aOldValue) { + this.compCalendar.mObservers.notify("onPropertyChanged", arguments); + }, + + onPropertyDeleting(aCalendar, aName) { + this.compCalendar.mObservers.notify("onPropertyDeleting", arguments); + }, +}; + +function CalCompositeCalendar() { + this.mObserverHelper = new calCompositeCalendarObserverHelper(this); + this.wrappedJSObject = this; + + this.mCalendars = []; + this.mCompositeObservers = new cal.data.ObserverSet(Ci.calICompositeObserver); + this.mObservers = new cal.data.ObserverSet(Ci.calIObserver); + this.mDefaultCalendar = null; + this.mStatusObserver = null; +} + +var calCompositeCalendarClassID = Components.ID("{aeff788d-63b0-4996-91fb-40a7654c6224}"); +var calCompositeCalendarInterfaces = ["calICalendar", "calICompositeCalendar"]; +CalCompositeCalendar.prototype = { + classID: calCompositeCalendarClassID, + QueryInterface: ChromeUtils.generateQI(calCompositeCalendarInterfaces), + + // + // calICompositeCalendar interface + // + + mCalendars: null, + mDefaultCalendar: null, + mPrefPrefix: null, + mDefaultPref: null, + mActivePref: null, + + get enabledCalendars() { + return this.mCalendars.filter(e => !e.getProperty("disabled")); + }, + + set prefPrefix(aPrefPrefix) { + if (this.mPrefPrefix) { + for (let calendar of this.mCalendars) { + this.removeCalendar(calendar); + } + } + + this.mPrefPrefix = aPrefPrefix; + this.mActivePref = aPrefPrefix + "-in-composite"; + this.mDefaultPref = aPrefPrefix + "-default"; + let cals = cal.manager.getCalendars(); + + cals.forEach(function (calendar) { + if (calendar.getProperty(this.mActivePref)) { + this.addCalendar(calendar); + } + if (calendar.getProperty(this.mDefaultPref)) { + this.setDefaultCalendar(calendar, false); + } + }, this); + }, + + get prefPrefix() { + return this.mPrefPrefix; + }, + + addCalendar(aCalendar) { + cal.ASSERT(aCalendar.id, "calendar does not have an id!", true); + + // check if the calendar already exists + if (this.getCalendarById(aCalendar.id)) { + return; + } + + // add our observer helper + 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(aCalendar) { + let id = aCalendar.id; + let newCalendars = this.mCalendars.filter(calendar => calendar.id != id); + if (newCalendars.length != this.mCalendars) { + this.mCalendars = newCalendars; + if (this.mPrefPrefix) { + aCalendar.deleteProperty(this.mActivePref); + aCalendar.deleteProperty(this.mDefaultPref); + } + aCalendar.removeObserver(this.mObserverHelper); + this.mCompositeObservers.notify("onCalendarRemoved", [aCalendar]); + } + }, + + getCalendarById(aId) { + for (let calendar of this.mCalendars) { + if (calendar.id == aId) { + return calendar; + } + } + return null; + }, + + getCalendars() { + return this.mCalendars; + }, + + get defaultCalendar() { + return this.mDefaultCalendar; + }, + + setDefaultCalendar(calendar, usePref) { + // Don't do anything if the passed calendar is the default calendar + if (calendar && this.mDefaultCalendar && this.mDefaultCalendar.id == calendar.id) { + return; + } + if (usePref && this.mPrefPrefix) { + if (this.mDefaultCalendar) { + this.mDefaultCalendar.deleteProperty(this.mDefaultPref); + } + // if not null set the new calendar as default in the preferences + if (calendar) { + calendar.setProperty(this.mDefaultPref, true); + } + } + this.mDefaultCalendar = calendar; + this.mCompositeObservers.notify("onDefaultCalendarChanged", [calendar]); + }, + + set defaultCalendar(calendar) { + this.setDefaultCalendar(calendar, true); + }, + + // + // calICalendar interface + // + // Write operations here are forwarded to either the item's + // parent calendar, or to the default calendar if one is set. + // Get operations are sent to each calendar. + // + + get id() { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + set id(id) { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + + get superCalendar() { + // There shouldn't be a superCalendar for the composite + return this; + }, + set superCalendar(val) { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + + // this could, at some point, return some kind of URI identifying + // all the child calendars, thus letting us create nifty calendar + // trees. + get uri() { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + set uri(val) { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + + get readOnly() { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + set readOnly(bool) { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + + get canRefresh() { + return true; + }, + + get name() { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + set name(val) { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + + get type() { + return "composite"; + }, + + getProperty(aName) { + return this.mDefaultCalendar.getProperty(aName); + }, + + get supportsScheduling() { + return false; + }, + + getSchedulingSupport() { + throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); + }, + + setProperty(aName, aValue) { + return this.mDefaultCalendar.setProperty(aName, aValue); + }, + + deleteProperty(aName) { + return this.mDefaultCalendar.deleteProperty(aName); + }, + + // void addObserver( in calIObserver observer ); + mCompositeObservers: null, + mObservers: null, + addObserver(aObserver) { + let wrappedCObserver = cal.wrapInstance(aObserver, Ci.calICompositeObserver); + if (wrappedCObserver) { + this.mCompositeObservers.add(wrappedCObserver); + } + this.mObservers.add(aObserver); + }, + + // void removeObserver( in calIObserver observer ); + removeObserver(aObserver) { + let wrappedCObserver = cal.wrapInstance(aObserver, Ci.calICompositeObserver); + if (wrappedCObserver) { + this.mCompositeObservers.delete(wrappedCObserver); + } + this.mObservers.delete(aObserver); + }, + + refresh() { + if (this.mStatusObserver) { + this.mStatusObserver.startMeteors( + Ci.calIStatusObserver.DETERMINED_PROGRESS, + this.mCalendars.length + ); + } + for (let calendar of this.enabledCalendars) { + try { + if (calendar.canRefresh) { + calendar.refresh(); + } + } catch (e) { + cal.ASSERT(false, e); + } + } + // send out a single onLoad for this composite calendar, + // although e.g. the ics provider will trigger another + // onLoad asynchronously; we cannot rely on every calendar + // sending an onLoad: + this.mObservers.notify("onLoad", [this]); + }, + + // Promise<calIItemBase> modifyItem( in calIItemBase aNewItem, in calIItemBase aOldItem) + async modifyItem(aNewItem, aOldItem) { + cal.ASSERT(aNewItem.calendar, "Composite can't modify item with null calendar", true); + cal.ASSERT(aNewItem.calendar != this, "Composite can't modify item with this calendar", true); + + return aNewItem.calendar.modifyItem(aNewItem, aOldItem); + }, + + // Promise<void> deleteItem(in calIItemBase aItem); + async deleteItem(aItem) { + cal.ASSERT(aItem.calendar, "Composite can't delete item with null calendar", true); + cal.ASSERT(aItem.calendar != this, "Composite can't delete item with this calendar", true); + + return aItem.calendar.deleteItem(aItem); + }, + + // Promise<calIItemBase> addItem(in calIItemBase aItem); + addItem(aItem) { + return this.mDefaultCalendar.addItem(aItem); + }, + + // Promise<calIItemBase|null> getItem(in string aId); + async getItem(aId) { + for (let calendar of this.enabledCalendars) { + let item = await calendar.getItem(aId); + if (item) { + return item; + } + } + return null; + }, + + // ReadableStream<calItemBase> getItems(in unsigned long itemFilter, + // in unsigned long count, + // in calIDateTime rangeStart, + // in calIDateTime rangeEnd) + getItems(itemFilter, count, rangeStart, rangeEnd) { + // If there are no calendars return early. + let enabledCalendars = this.enabledCalendars; + if (enabledCalendars.length == 0) { + return CalReadableStreamFactory.createEmptyReadableStream(); + } + if (this.mStatusObserver) { + if (this.mStatusObserver.spinning == Ci.calIStatusObserver.NO_PROGRESS) { + this.mStatusObserver.startMeteors(Ci.calIStatusObserver.UNDETERMINED_PROGRESS, -1); + } + } + + let compositeCal = this; + return CalReadableStreamFactory.createBoundedReadableStream( + count, + CalReadableStreamFactory.defaultQueueSize, + { + iterators: [], + async start(controller) { + for (let calendar of enabledCalendars) { + let iterator = cal.iterate.streamValues( + calendar.getItems(itemFilter, count, rangeStart, rangeEnd) + ); + this.iterators.push(iterator); + for await (let items of iterator) { + controller.enqueue(items); + } + + if (compositeCal.statusDisplayed) { + compositeCal.mStatusObserver.calendarCompleted(calendar); + } + } + if (compositeCal.statusDisplayed) { + compositeCal.mStatusObserver.stopMeteors(); + } + controller.close(); + }, + + async cancel(reason) { + for (let iterator of this.iterators) { + await iterator.cancel(reason); + } + if (compositeCal.statusDisplayed) { + compositeCal.mStatusObserver.stopMeteors(); + } + }, + } + ); + }, + + // Promise<calItemBase[]> getItemsAsArray(in unsigned long itemFilter, + // in unsigned long count, + // in calIDateTime rangeStart, + // in calIDateTime rangeEnd) + async getItemsAsArray(itemFilter, count, rangeStart, rangeEnd) { + return cal.iterate.streamToArray(this.getItems(itemFilter, count, rangeStart, rangeEnd)); + }, + + startBatch() { + this.mCompositeObservers.notify("onStartBatch", [this]); + }, + endBatch() { + this.mCompositeObservers.notify("onEndBatch", [this]); + }, + + get statusDisplayed() { + if (this.mStatusObserver) { + return this.mStatusObserver.spinning != Ci.calIStatusObserver.NO_PROGRESS; + } + return false; + }, + + setStatusObserver(aStatusObserver, aWindow) { + this.mStatusObserver = aStatusObserver; + if (this.mStatusObserver) { + this.mStatusObserver.initialize(aWindow); + } + }, +}; diff --git a/comm/calendar/providers/composite/components.conf b/comm/calendar/providers/composite/components.conf new file mode 100644 index 0000000000..3f75bf3500 --- /dev/null +++ b/comm/calendar/providers/composite/components.conf @@ -0,0 +1,14 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/ + +Classes = [ + { + 'cid': '{aeff788d-63b0-4996-91fb-40a7654c6224}', + 'contract_ids': ['@mozilla.org/calendar/calendar;1?type=composite'], + 'jsm': 'resource:///modules/CalCompositeCalendar.jsm', + 'constructor': 'CalCompositeCalendar', + }, +]
\ No newline at end of file diff --git a/comm/calendar/providers/composite/moz.build b/comm/calendar/providers/composite/moz.build new file mode 100644 index 0000000000..9009560429 --- /dev/null +++ b/comm/calendar/providers/composite/moz.build @@ -0,0 +1,12 @@ +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXTRA_JS_MODULES += [ + "CalCompositeCalendar.jsm", +] + +XPCOM_MANIFESTS += [ + "components.conf", +] |