summaryrefslogtreecommitdiffstats
path: root/content/provider.js
diff options
context:
space:
mode:
Diffstat (limited to 'content/provider.js')
-rw-r--r--content/provider.js729
1 files changed, 729 insertions, 0 deletions
diff --git a/content/provider.js b/content/provider.js
new file mode 100644
index 0000000..de5af36
--- /dev/null
+++ b/content/provider.js
@@ -0,0 +1,729 @@
+/*
+ * This file is part of EAS-4-TbSync.
+ *
+ * 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/.
+ */
+
+"use strict";
+
+// Every object in here will be loaded into TbSync.providers.<providername>.
+const eas = TbSync.providers.eas;
+
+eas.prefs = Services.prefs.getBranch("extensions.eas4tbsync.");
+
+//use flags instead of strings to avoid errors due to spelling errors
+eas.flags = Object.freeze({
+ allowEmptyResponse: true,
+});
+
+eas.windowsToIanaTimezoneMap = {};
+eas.ianaToWindowsTimezoneMap = {};
+eas.cachedTimezoneData = null;
+eas.defaultTimezoneInfo = null;
+eas.defaultTimezone = null;
+eas.utcTimezone = null;
+
+
+/**
+ * Implementing the TbSync interface for external provider extensions.
+ */
+var Base = class {
+ /**
+ * Called during load of external provider extension to init provider.
+ */
+ static async load() {
+ // Set default prefs
+ let branch = Services.prefs.getDefaultBranch("extensions.eas4tbsync.");
+ branch.setIntPref("timeout", 90000);
+ branch.setIntPref("maxitems", 50);
+ branch.setBoolPref("msTodoCompat", false);
+ branch.setCharPref("clientID.type", "TbSync");
+ branch.setCharPref("clientID.useragent", "Thunderbird ActiveSync");
+ branch.setCharPref("oauth.clientID", "");
+
+ eas.defaultTimezone = null;
+ eas.utcTimezone = null;
+ eas.defaultTimezoneInfo = null;
+ eas.windowsToIanaTimezoneMap = {};
+ eas.openWindows = {};
+
+ try {
+ // Create a basic error info (no accountname or foldername, just the provider)
+ let eventLogInfo = new TbSync.EventLogInfo("eas");
+
+ //get timezone info of default timezone (old cal. without dtz are depricated)
+ eas.defaultTimezone = (TbSync.lightning.cal.dtz && TbSync.lightning.cal.dtz.defaultTimezone) ? TbSync.lightning.cal.dtz.defaultTimezone : TbSync.lightning.cal.calendarDefaultTimezone();
+ eas.utcTimezone = (TbSync.lightning.cal.dtz && TbSync.lightning.cal.dtz.UTC) ? TbSync.lightning.cal.dtz.UTC : TbSync.lightning.cal.UTC();
+ if (eas.defaultTimezone && eas.defaultTimezone.icalComponent) {
+ TbSync.eventlog.add("info", eventLogInfo, "Default timezone has been found.");
+ } else {
+ TbSync.eventlog.add("info", eventLogInfo, "Default timezone is not defined, using UTC!");
+ eas.defaultTimezone = eas.utcTimezone;
+ }
+
+ eas.defaultTimezoneInfo = eas.tools.getTimezoneInfo(eas.defaultTimezone);
+ if (!eas.defaultTimezoneInfo) {
+ TbSync.eventlog.add("info", eventLogInfo, "Could not create defaultTimezoneInfo");
+ }
+
+ //get windows timezone data from CSV
+ let aliasData = await eas.tools.fetchFile("chrome://eas4tbsync/content/timezonedata/Aliases.csv");
+ let aliasNames = {};
+ for (let i = 0; i<aliasData.length; i++) {
+ let lData = aliasData[i].split(",");
+ if (lData.length<2) continue;
+ aliasNames[lData[0].toString().trim()] = lData[1].toString().trim().split(" ");
+ }
+
+ let csvData = await eas.tools.fetchFile("chrome://eas4tbsync/content/timezonedata/WindowsTimezone.csv");
+ for (let i = 0; i<csvData.length; i++) {
+ let lData = csvData[i].split(",");
+ if (lData.length<3) continue;
+
+ let windowsZoneName = lData[0].toString().trim();
+ let zoneType = lData[1].toString().trim();
+ let ianaZoneName = lData[2].toString().trim();
+
+ if (zoneType == "001") eas.windowsToIanaTimezoneMap[windowsZoneName] = ianaZoneName;
+ if (ianaZoneName == eas.defaultTimezoneInfo.std.id) eas.defaultTimezoneInfo.std.windowsZoneName = windowsZoneName;
+
+ // build the revers map as well, which is many-to-one, grap iana aliases from the csvData and from the aliasData
+ // 1. multiple iana zones map to the same windows zone
+ let ianaZones = ianaZoneName.split(" ");
+ for (let ianaZone of ianaZones) {
+ eas.ianaToWindowsTimezoneMap[ianaZone] = windowsZoneName;
+ if (aliasNames.hasOwnProperty(ianaZone)) {
+ for (let aliasName of aliasNames[ianaZone]) {
+ // 2. multiple iana zonescan be an alias to a main iana zone
+ eas.ianaToWindowsTimezoneMap[aliasName] = windowsZoneName;
+ }
+ }
+ }
+ }
+
+ let tzService = TbSync.lightning.cal.timezoneService;
+ for (let timezoneId of tzService.timezoneIds) {
+ if (!eas.ianaToWindowsTimezoneMap[timezoneId]) {
+ TbSync.eventlog.add("info", eventLogInfo, "The IANA timezone <"+timezoneId+"> cannot be mapped to any Exchange timezone.");
+ }
+ }
+
+ //If an EAS calendar is currently NOT associated with an email identity, try to associate,
+ //but do not change any explicitly set association
+ // - A) find email identity and associate (which sets organizer to that user identity)
+ // - B) overwrite default organizer with current best guess
+ //TODO: Do this after email accounts changed, not only on restart?
+ let providerData = new TbSync.ProviderData("eas");
+ let folders = providerData.getFolders({"selected": true, "type": ["8","13"]});
+ for (let folder of folders) {
+ let manager = TbSync.lightning.cal.manager;
+ let calendar = manager.getCalendarById(folder.getFolderProperty("target"));
+ if (calendar && calendar.getProperty("imip.identity.key") == "") {
+ //is there an email identity for this eas account?
+ let authData = eas.network.getAuthData(folder.accountData);
+
+ let key = eas.tools.getIdentityKey(authData.user);
+ if (key === "") { //TODO: Do this even after manually switching to NONE, not only on restart?
+ //set transient calendar organizer settings based on current best guess and
+ calendar.setProperty("organizerId", TbSync.lightning.cal.email.prependMailTo(authData.user));
+ calendar.setProperty("organizerCN", calendar.getProperty("fallbackOrganizerName"));
+ } else {
+ //force switch to found identity
+ calendar.setProperty("imip.identity.key", key);
+ }
+ }
+ }
+ } catch(e) {
+ Components.utils.reportError(e);
+ }
+ }
+
+
+ /**
+ * Called during unload of external provider extension to unload provider.
+ */
+ static async unload() {
+ // Close all open windows of this provider.
+ for (let id in eas.openWindows) {
+ if (eas.openWindows.hasOwnProperty(id)) {
+ try {
+ eas.openWindows[id].close();
+ } catch(e) {
+ //NOOP
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Returns string for the name of provider for the add account menu.
+ */
+ static getProviderName() {
+ return "Exchange ActiveSync";
+ }
+
+
+ /**
+ * Returns version of the TbSync API this provider is using
+ */
+ static getApiVersion() { return "2.5"; }
+
+
+ /**
+ * Returns location of a provider icon.
+ */
+ static getProviderIcon(size, accountData = null) {
+ let base = (accountData && accountData.getAccountProperty("servertype") == "office365") ? "365_" : "eas";
+
+ switch (size) {
+ case 16:
+ return "chrome://eas4tbsync/content/skin/" + base + "16.png";
+ case 32:
+ return "chrome://eas4tbsync/content/skin/" + base + "32.png";
+ default :
+ return "chrome://eas4tbsync/content/skin/" + base + "64.png";
+ }
+ }
+
+
+ /**
+ * Returns a list of sponsors, they will be sorted by the index
+ */
+ static getSponsors() {
+ return {
+ "Schiessl, Michael 1" : {name: "Michael Schiessl", description: "Tine 2.0", icon: "", link: "" },
+ "Schiessl, Michael 2" : {name: "Michael Schiessl", description: " Exchange 2007", icon: "", link: "" },
+ "netcup GmbH" : {name: "netcup GmbH", description : "SOGo", icon: "chrome://eas4tbsync/content/skin/sponsors/netcup.png", link: "http://www.netcup.de/" },
+ "nethinks GmbH" : {name: "nethinks GmbH", description : "Zarafa", icon: "chrome://eas4tbsync/content/skin/sponsors/nethinks.png", link: "http://www.nethinks.com/" },
+ "Jau, Stephan" : {name: "Stephan Jau", description: "Horde", icon: "", link: "" },
+ "Zavar " : {name: "Zavar", description: "Zoho", icon: "", link: "" },
+ };
+ }
+
+
+ /**
+ * Returns the url of a page with details about contributors (used in the manager UI)
+ */
+ static getContributorsUrl() {
+ return "https://github.com/jobisoft/EAS-4-TbSync/blob/master/CONTRIBUTORS.md";
+ }
+
+
+ /**
+ * Returns the email address of the maintainer (used for bug reports).
+ */
+ static getMaintainerEmail() {
+ return "john.bieling@gmx.de";
+ }
+
+
+ /**
+ * Returns URL of the new account window.
+ *
+ * The URL will be opened via openDialog(), when the user wants to create a
+ * new account of this provider.
+ */
+ static getCreateAccountWindowUrl() {
+ return "chrome://eas4tbsync/content/manager/createAccount.xhtml";
+ }
+
+
+ /**
+ * Returns overlay XUL URL of the edit account dialog
+ * (chrome://tbsync/content/manager/editAccount.xhtml)
+ */
+ static getEditAccountOverlayUrl() {
+ return "chrome://eas4tbsync/content/manager/editAccountOverlay.xhtml";
+ }
+
+
+ /**
+ * Return object which contains all possible fields of a row in the
+ * accounts database with the default value if not yet stored in the
+ * database.
+ */
+ static getDefaultAccountEntries() {
+ let row = {
+ "policykey" : "0",
+ "foldersynckey" : "0",
+ "deviceId" : eas.tools.getNewDeviceId(),
+ "asversionselected" : "auto",
+ "asversion" : "",
+ "host" : "",
+ "user" : "",
+ "servertype" : "",
+ "seperator" : "10",
+ "https" : true,
+ "provision" : false,
+ "displayoverride" : false,
+ "lastEasOptionsUpdate":"0",
+ "allowedEasVersions": "",
+ "allowedEasCommands": "",
+ "useragent": eas.prefs.getCharPref("clientID.useragent"),
+ "devicetype": eas.prefs.getCharPref("clientID.type"),
+ "synclimit" : "7",
+ };
+ return row;
+ }
+
+
+ /**
+ * Return object which contains all possible fields of a row in the folder
+ * database with the default value if not yet stored in the database.
+ */
+ static getDefaultFolderEntries() {
+ let folder = {
+ "type" : "",
+ "synckey" : "",
+ "target" : "",
+ "targetColor" : "",
+ "targetName" : "",
+ "parentID" : "0",
+ "serverID" : "",
+ };
+ return folder;
+ }
+
+
+ /**
+ * Is called everytime an account of this provider is enabled in the
+ * manager UI.
+ */
+ static onEnableAccount(accountData) {
+ accountData.resetAccountProperty("policykey");
+ accountData.resetAccountProperty("foldersynckey");
+ accountData.resetAccountProperty("lastEasOptionsUpdate");
+ accountData.resetAccountProperty("lastsynctime");
+ }
+
+
+ /**
+ * Is called everytime an account of this provider is disabled in the
+ * manager UI.
+ *
+ * @param accountData [in] AccountData
+ */
+ static onDisableAccount(accountData) {
+ }
+
+
+ /**
+ * Is called everytime an account of this provider is deleted in the
+ * manager UI.
+ */
+ static onDeleteAccount(accountData) {
+ eas.network.getAuthData(accountData).removeLoginData();
+ }
+
+
+ /**
+ * Returns all folders of the account, sorted in the desired order.
+ * The most simple implementation is to return accountData.getAllFolders();
+ */
+ static getSortedFolders(accountData) {
+ let allowedTypesOrder = ["9","14","8","13","7","15"];
+
+ function getIdChain (aServerID) {
+ let serverID = aServerID;
+ let chain = [];
+ let folder;
+ let rootType = "";
+
+ // create sort string so that child folders are directly below their parent folders
+ do {
+ folder = accountData.getFolder("serverID", serverID);
+ if (folder) {
+ chain.unshift(folder.getFolderProperty("foldername"));
+ serverID = folder.getFolderProperty("parentID");
+ rootType = folder.getFolderProperty("type");
+ }
+ } while (folder && serverID != "0")
+
+ // different folder types are grouped and trashed folders at the end
+ let pos = allowedTypesOrder.indexOf(rootType);
+ chain.unshift(pos == -1 ? "ZZZ" : pos.toString().padStart(3,"0"));
+
+ return chain.join(".");
+ };
+
+ let toBeSorted = [];
+ let folders = accountData.getAllFolders();
+ for (let f of folders) {
+ if (!allowedTypesOrder.includes(f.getFolderProperty("type"))) {
+ continue;
+ }
+ toBeSorted.push({"key": getIdChain(f.getFolderProperty("serverID")), "folder": f});
+ }
+
+ //sort
+ toBeSorted.sort(function(a,b) {
+ return a.key > b.key;
+ });
+
+ let sortedFolders = [];
+ for (let sortObj of toBeSorted) {
+ sortedFolders.push(sortObj.folder);
+ }
+ return sortedFolders;
+ }
+
+
+ /**
+ * Return the connection timeout for an active sync, so TbSync can append
+ * a countdown to the connection timeout, while waiting for an answer from
+ * the server. Only syncstates which start with "send." will trigger this.
+ */
+ static getConnectionTimeout(accountData) {
+ return eas.prefs.getIntPref("timeout");
+ }
+
+
+ /**
+ * Is called if TbSync needs to synchronize the folder list.
+ */
+ static async syncFolderList(syncData, syncJob, syncRunNr) {
+ // Recommendation: Put the actual function call inside a try catch, to
+ // ensure returning a proper StatusData object, regardless of what
+ // happens inside that function. You may also throw custom errors
+ // in that function, which have the StatusData obj attached, which
+ // should be returned.
+
+ try {
+ await eas.sync.folderList(syncData);
+ } catch (e) {
+ if (e.name == "eas4tbsync") {
+ return e.statusData;
+ } else {
+ Components.utils.reportError(e);
+ // re-throw any other error and let TbSync handle it
+ throw (e);
+ }
+ }
+
+ // Fall through, if there was no error.
+ return new TbSync.StatusData();
+ }
+
+
+ /**
+ * Is called if TbSync needs to synchronize a folder.
+ */
+ static async syncFolder(syncData, syncJob, syncRunNr) {
+ // Recommendation: Put the actual function call inside a try catch, to
+ // ensure returning a proper StatusData object, regardless of what
+ // happens inside that function. You may also throw custom errors
+ // in that function, which have the StatusData obj attached, which
+ // should be returned.
+
+ try {
+ switch (syncJob) {
+ case "deletefolder":
+ await eas.sync.deleteFolder(syncData);
+ break;
+ default:
+ await eas.sync.singleFolder(syncData);
+ }
+ } catch (e) {
+ if (e.name == "eas4tbsync") {
+ return e.statusData;
+ } else {
+ Components.utils.reportError(e);
+ // re-throw any other error and let TbSync handle it
+ throw (e);
+ }
+ }
+
+ // Fall through, if there was no error.
+ return new TbSync.StatusData();
+ }
+
+
+ /**
+ * Return the custom OAuth2 ClientID.
+ */
+ static getCustomeOauthClientID() {
+ return eas.prefs.getCharPref("oauth.clientID");
+ }
+}
+
+
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// * TargetData implementation
+// * Using TbSyncs advanced address book TargetData
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+var TargetData_addressbook = class extends TbSync.addressbook.AdvancedTargetData {
+ constructor(folderData) {
+ super(folderData);
+ }
+
+ get primaryKeyField() {
+ return "X-EAS-SERVERID";
+ }
+
+ generatePrimaryKey() {
+ return TbSync.generateUUID();
+ }
+
+ // enable or disable changelog
+ get logUserChanges() {
+ return true;
+ }
+
+ directoryObserver(aTopic) {
+ switch (aTopic) {
+ case "addrbook-removed":
+ case "addrbook-updated":
+ //Services.console.logStringMessage("["+ aTopic + "] " + this.folderData.getFolderProperty("foldername"));
+ break;
+ }
+ }
+
+ cardObserver(aTopic, abCardItem) {
+ switch (aTopic) {
+ case "addrbook-contact-updated":
+ case "addrbook-contact-removed":
+ //Services.console.logStringMessage("["+ aTopic + "] " + abCardItem.getProperty("DisplayName"));
+ break;
+
+ case "addrbook-contact-created":
+ {
+ //Services.console.logStringMessage("["+ aTopic + "] "+ abCardItem.getProperty("DisplayName")+">");
+ break;
+ }
+ }
+ }
+
+ listObserver(aTopic, abListItem, abListMember) {
+ switch (aTopic) {
+ case "addrbook-list-member-added":
+ case "addrbook-list-member-removed":
+ //Services.console.logStringMessage("["+ aTopic + "] MemberName: " + abListMember.getProperty("DisplayName"));
+ break;
+
+ case "addrbook-list-removed":
+ case "addrbook-list-updated":
+ //Services.console.logStringMessage("["+ aTopic + "] ListName: " + abListItem.getProperty("ListName"));
+ break;
+
+ case "addrbook-list-created":
+ //Services.console.logStringMessage("["+ aTopic + "] ListName: "+abListItem.getProperty("ListName")+">");
+ break;
+ }
+ }
+
+ async createAddressbook(newname) {
+ // https://searchfox.org/comm-central/source/mailnews/addrbook/src/nsDirPrefs.h
+ let dirPrefId = MailServices.ab.newAddressBook(newname, "", 101);
+ let directory = MailServices.ab.getDirectoryFromId(dirPrefId);
+
+ eas.sync.resetFolderSyncInfo(this.folderData);
+
+ if (directory && directory instanceof Components.interfaces.nsIAbDirectory && directory.dirPrefId == dirPrefId) {
+ directory.setStringValue("tbSyncIcon", "eas" + (this.folderData.accountData.getAccountProperty("servertype") == "office365" ? "_365" : ""));
+ return directory;
+ }
+ return null;
+ }
+}
+
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// * TargetData implementation
+// * Using TbSyncs advanced calendar TargetData
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+var TargetData_calendar = class extends TbSync.lightning.AdvancedTargetData {
+ constructor(folderData) {
+ super(folderData);
+ }
+
+ // The calendar target does not support a custom primaryKeyField, because
+ // the lightning implementation only allows to search for items via UID.
+ // Like the addressbook target, the calendar target item element has a
+ // primaryKey getter/setter which - however - only works on the UID.
+
+ // enable or disable changelog
+ get logUserChanges() {
+ return true;
+ }
+
+ calendarObserver(aTopic, tbCalendar, aPropertyName, aPropertyValue, aOldPropertyValue) {
+ switch (aTopic) {
+ case "onCalendarPropertyChanged":
+ //Services.console.logStringMessage("["+ aTopic + "] " + tbCalendar.calendar.name + " : " + aPropertyName);
+ break;
+
+ case "onCalendarDeleted":
+ case "onCalendarPropertyDeleted":
+ //Services.console.logStringMessage("["+ aTopic + "] " +tbCalendar.calendar.name);
+ break;
+ }
+ }
+
+ itemObserver(aTopic, tbItem, tbOldItem) {
+ switch (aTopic) {
+ case "onAddItem":
+ case "onModifyItem":
+ case "onDeleteItem":
+ //Services.console.logStringMessage("["+ aTopic + "] " + tbItem.nativeItem.title);
+ break;
+ }
+ }
+
+ async createCalendar(newname) {
+ let calManager = TbSync.lightning.cal.manager;
+
+ //Create the new standard calendar with a unique name
+ let newCalendar = calManager.createCalendar("storage", Services.io.newURI("moz-storage-calendar://"));
+ newCalendar.id = TbSync.lightning.cal.getUUID();
+ newCalendar.name = newname;
+
+ eas.sync.resetFolderSyncInfo(this.folderData);
+
+ newCalendar.setProperty("color", this.folderData.getFolderProperty("targetColor"));
+ newCalendar.setProperty("relaxedMode", true); //sometimes we get "generation too old for modifyItem", check can be disabled with relaxedMode
+ // removed in TB78, as it seems to not fully enable the calendar, if present before registering
+ // https://searchfox.org/comm-central/source/calendar/base/content/calendar-management.js#385
+ //newCalendar.setProperty("calendar-main-in-composite",true);
+ newCalendar.setProperty("readOnly", this.folderData.getFolderProperty("downloadonly"));
+
+ switch (this.folderData.getFolderProperty("type")) {
+ case "8": //event
+ case "13":
+ newCalendar.setProperty("capabilities.tasks.supported", false);
+ newCalendar.setProperty("capabilities.events.supported", true);
+ break;
+ case "7": //todo
+ case "15":
+ newCalendar.setProperty("capabilities.tasks.supported", true);
+ newCalendar.setProperty("capabilities.events.supported", false);
+ break;
+ default:
+ newCalendar.setProperty("capabilities.tasks.supported", false);
+ newCalendar.setProperty("capabilities.events.supported", false);
+ }
+
+ calManager.registerCalendar(newCalendar);
+
+ let authData = eas.network.getAuthData(this.folderData.accountData);
+
+ //is there an email identity we can associate this calendar to?
+ //getIdentityKey returns "" if none found, which removes any association
+ let key = eas.tools.getIdentityKey(authData.user);
+ newCalendar.setProperty("fallbackOrganizerName", newCalendar.getProperty("organizerCN"));
+ newCalendar.setProperty("imip.identity.key", key);
+ if (key === "") {
+ //there is no matching email identity - use current default value as best guess and remove association
+ //use current best guess
+ newCalendar.setProperty("organizerCN", newCalendar.getProperty("fallbackOrganizerName"));
+ newCalendar.setProperty("organizerId", TbSync.lightning.cal.email.prependMailTo(authData.user));
+ }
+
+ return newCalendar;
+ }
+}
+
+
+
+
+
+/**
+ * This provider is implementing the StandardFolderList class instead of
+ * the FolderList class.
+ */
+var StandardFolderList = class {
+ /**
+ * Is called before the context menu of the folderlist is shown, allows to
+ * show/hide custom menu options based on selected folder. During an active
+ * sync, folderData will be null.
+ */
+ static onContextMenuShowing(window, folderData) {
+ let hideContextMenuDelete = true;
+ if (folderData !== null) {
+ //if a folder in trash is selected, also show ContextMenuDelete (but only if FolderDelete is allowed)
+ if (eas.tools.parentIsTrash(folderData) && folderData.accountData.getAccountProperty("allowedEasCommands").split(",").includes("FolderDelete")) {
+ hideContextMenuDelete = false;
+ window.document.getElementById("TbSync.eas.FolderListContextMenuDelete").label = TbSync.getString("deletefolder.menuentry::" + folderData.getFolderProperty("foldername"), "eas");
+ }
+ }
+ window.document.getElementById("TbSync.eas.FolderListContextMenuDelete").hidden = hideContextMenuDelete;
+ }
+
+
+ /**
+ * Return the icon used in the folderlist to represent the different folder
+ * types.
+ */
+ static getTypeImage(folderData) {
+ let src = "";
+ switch (folderData.getFolderProperty("type")) {
+ case "9":
+ case "14":
+ src = "contacts16.png";
+ break;
+ case "8":
+ case "13":
+ src = "calendar16.png";
+ break;
+ case "7":
+ case "15":
+ src = "todo16.png";
+ break;
+ }
+ return "chrome://tbsync/content/skin/" + src;
+ }
+
+
+ /**
+ * Return the name of the folder shown in the folderlist.
+ */
+ static getFolderDisplayName(folderData) {
+ let folderName = folderData.getFolderProperty("foldername");
+ if (eas.tools.parentIsTrash(folderData)) folderName = TbSync.getString("recyclebin", "eas") + " | " + folderName;
+ return folderName;
+ }
+
+
+ /**
+ * Return the attributes for the ACL RO (readonly menu element per folder.
+ * (label, disabled, hidden, style, ...)
+ *
+ * Return a list of attributes and their values If both (RO+RW) do
+ * not return any attributes, the ACL menu is not displayed at all.
+ */
+ static getAttributesRoAcl(folderData) {
+ return {
+ label: TbSync.getString("acl.readonly", "eas"),
+ };
+ }
+
+
+ /**
+ * Return the attributes for the ACL RW (readwrite) menu element per folder.
+ * (label, disabled, hidden, style, ...)
+ *
+ * Return a list of attributes and their values. If both (RO+RW) do
+ * not return any attributes, the ACL menu is not displayed at all.
+ */
+ static getAttributesRwAcl(folderData) {
+ return {
+ label: TbSync.getString("acl.readwrite", "eas"),
+ }
+ }
+}
+
+Services.scriptloader.loadSubScript("chrome://eas4tbsync/content/includes/network.js", this, "UTF-8");
+Services.scriptloader.loadSubScript("chrome://eas4tbsync/content/includes/wbxmltools.js", this, "UTF-8");
+Services.scriptloader.loadSubScript("chrome://eas4tbsync/content/includes/xmltools.js", this, "UTF-8");
+Services.scriptloader.loadSubScript("chrome://eas4tbsync/content/includes/tools.js", this, "UTF-8");
+Services.scriptloader.loadSubScript("chrome://eas4tbsync/content/includes/sync.js", this, "UTF-8");
+Services.scriptloader.loadSubScript("chrome://eas4tbsync/content/includes/contactsync.js", this.sync, "UTF-8");
+Services.scriptloader.loadSubScript("chrome://eas4tbsync/content/includes/calendarsync.js", this.sync, "UTF-8");
+Services.scriptloader.loadSubScript("chrome://eas4tbsync/content/includes/tasksync.js", this.sync, "UTF-8");