summaryrefslogtreecommitdiffstats
path: root/comm/mail/components/im/content/imAccountWizard.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/components/im/content/imAccountWizard.js')
-rw-r--r--comm/mail/components/im/content/imAccountWizard.js526
1 files changed, 526 insertions, 0 deletions
diff --git a/comm/mail/components/im/content/imAccountWizard.js b/comm/mail/components/im/content/imAccountWizard.js
new file mode 100644
index 0000000000..128412aa5b
--- /dev/null
+++ b/comm/mail/components/im/content/imAccountWizard.js
@@ -0,0 +1,526 @@
+/* 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/. */
+
+// chat/content/imAccountOptionsHelper.js
+/* globals accountOptionsHelper */
+
+var { IMServices } = ChromeUtils.importESModule(
+ "resource:///modules/IMServices.sys.mjs"
+);
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+var { ChatIcons } = ChromeUtils.importESModule(
+ "resource:///modules/chatIcons.sys.mjs"
+);
+
+var PREF_EXTENSIONS_GETMOREPROTOCOLSURL = "extensions.getMoreProtocolsURL";
+
+var accountWizard = {
+ onload() {
+ document
+ .querySelector("wizard")
+ .addEventListener("wizardfinish", this.createAccount.bind(this));
+ let accountProtocolPage = document.getElementById("accountprotocol");
+ accountProtocolPage.addEventListener(
+ "pageadvanced",
+ this.selectProtocol.bind(this)
+ );
+ let accountUsernamePage = document.getElementById("accountusername");
+ accountUsernamePage.addEventListener(
+ "pageshow",
+ this.showUsernamePage.bind(this)
+ );
+ accountUsernamePage.addEventListener(
+ "pagehide",
+ this.hideUsernamePage.bind(this)
+ );
+ let accountAdvancedPage = document.getElementById("accountadvanced");
+ accountAdvancedPage.addEventListener(
+ "pageshow",
+ this.showAdvanced.bind(this)
+ );
+ let accountSummaryPage = document.getElementById("accountsummary");
+ accountSummaryPage.addEventListener(
+ "pageshow",
+ this.showSummary.bind(this)
+ );
+
+ // Ensure the im core is initialized before we get a list of protocols.
+ IMServices.core.init();
+
+ accountWizard.setGetMoreProtocols();
+
+ var protoList = document.getElementById("protolist");
+ var protos = IMServices.core.getProtocols();
+ protos.sort((a, b) => {
+ if (a.name < b.name) {
+ return -1;
+ }
+ return a.name > b.name ? 1 : 0;
+ });
+ protos.forEach(function (proto) {
+ let image = document.createElement("img");
+ image.setAttribute("src", ChatIcons.getProtocolIconURI(proto));
+ image.setAttribute("alt", "");
+ image.classList.add("protoIcon");
+
+ let label = document.createXULElement("label");
+ label.setAttribute("value", proto.name);
+
+ let item = document.createXULElement("richlistitem");
+ item.setAttribute("value", proto.id);
+ item.appendChild(image);
+ item.appendChild(label);
+ protoList.appendChild(item);
+ });
+
+ // there is a strange selection bug without this timeout
+ setTimeout(function () {
+ protoList.selectedIndex = 0;
+ }, 0);
+
+ Services.obs.addObserver(this, "prpl-quit");
+ window.addEventListener("unload", this.unload);
+ },
+ unload() {
+ Services.obs.removeObserver(accountWizard, "prpl-quit");
+ },
+ observe(aObject, aTopic, aData) {
+ if (aTopic == "prpl-quit") {
+ // libpurple is being uninitialized. We can't create any new
+ // account so keeping this wizard open would be pointless, close it.
+ window.close();
+ }
+ },
+
+ /**
+ * Builds the full username from the username boxes.
+ *
+ * @returns {string} assembled username
+ */
+ getUsername() {
+ let usernameBoxIndex = 0;
+ if (this.proto.usernamePrefix) {
+ usernameBoxIndex = 1;
+ }
+ // If the first username input is empty, make sure we return an empty
+ // string so that it blocks the 'next' button of the wizard.
+ if (!this.userNameBoxes[usernameBoxIndex].value) {
+ return "";
+ }
+
+ return this.userNameBoxes.reduce((prev, elt) => prev + elt.value, "");
+ },
+
+ /**
+ * Check that the username fields generate a new username, and if it is valid
+ * allow advancing the wizard.
+ */
+ checkUsername() {
+ var wizard = document.querySelector("wizard");
+ var name = accountWizard.getUsername();
+ var duplicateWarning = document.getElementById("duplicateAccount");
+ if (!name) {
+ wizard.canAdvance = false;
+ duplicateWarning.hidden = true;
+ return;
+ }
+
+ var exists = accountWizard.proto.accountExists(name);
+ wizard.canAdvance = !exists;
+ duplicateWarning.hidden = !exists;
+ },
+
+ /**
+ * Takes the value of the primary username field and splits it if the value
+ * matches the split field syntax.
+ */
+ splitUsername() {
+ let usernameBoxIndex = 0;
+ if (this.proto.usernamePrefix) {
+ usernameBoxIndex = 1;
+ }
+ let username = this.userNameBoxes[usernameBoxIndex].value;
+ let splitValues = this.proto.splitUsername(username);
+ if (!splitValues.length) {
+ return;
+ }
+ for (const box of this.userNameBoxes) {
+ if (Element.isInstance(box)) {
+ box.value = splitValues.shift();
+ }
+ }
+ this.checkUsername();
+ },
+
+ selectProtocol() {
+ var protoList = document.getElementById("protolist");
+ var id = protoList.selectedItem.value;
+ this.proto = IMServices.core.getProtocolById(id);
+ },
+
+ /**
+ * Create a new input field for receiving a username.
+ *
+ * @param {string} aName - The id for the input.
+ * @param {string} aLabel - The text for the username label.
+ * @param {Element} grid - A container with a two column grid display to
+ * append the new elements to.
+ * @param {string} [aDefaultValue] - The initial value for the username.
+ *
+ * @returns {HTMLInputElement} - The newly created username input.
+ */
+ insertUsernameField(aName, aLabel, grid, aDefaultValue) {
+ var label = document.createXULElement("label");
+ label.setAttribute("value", aLabel);
+ label.setAttribute("control", aName);
+ label.setAttribute("id", aName + "-label");
+ label.classList.add("label-inline");
+ grid.appendChild(label);
+
+ var input = document.createElementNS(
+ "http://www.w3.org/1999/xhtml",
+ "input"
+ );
+ input.setAttribute("id", aName);
+ input.classList.add("input-inline");
+ if (aDefaultValue) {
+ input.setAttribute("value", aDefaultValue);
+ }
+ input.addEventListener("input", event => {
+ this.checkUsername();
+ });
+ // Only add the split logic to the first input field
+ if (!this.userNameBoxes) {
+ input.addEventListener("blur", event => {
+ this.splitUsername();
+ });
+ }
+ grid.appendChild(input);
+
+ return input;
+ },
+
+ /**
+ * Builds the username input boxes from the username split defined by the
+ * protocol.
+ */
+ showUsernamePage() {
+ var proto = this.proto.id;
+ if ("userNameBoxes" in this && this.userNameProto == proto) {
+ this.checkUsername();
+ return;
+ }
+
+ var bundle = document.getElementById("accountsBundle");
+ var usernameInfo;
+ var emptyText = this.proto.usernameEmptyText;
+ if (emptyText) {
+ usernameInfo = bundle.getFormattedString(
+ "accountUsernameInfoWithDescription",
+ [emptyText, this.proto.name]
+ );
+ } else {
+ usernameInfo = bundle.getFormattedString("accountUsernameInfo", [
+ this.proto.name,
+ ]);
+ }
+ document.getElementById("usernameInfo").textContent = usernameInfo;
+
+ var grid = document.getElementById("userNameBox");
+ // remove anything that may be there for another protocol
+ while (grid.hasChildNodes()) {
+ grid.lastChild.remove();
+ }
+ this.userNameBoxes = undefined;
+
+ var splits = this.proto.getUsernameSplit();
+
+ var label = bundle.getString("accountUsername");
+ this.userNameBoxes = [this.insertUsernameField("name", label, grid)];
+ this.userNameBoxes[0].emptyText = emptyText;
+ let usernameBoxIndex = 0;
+
+ if (this.proto.usernamePrefix) {
+ this.userNameBoxes.unshift({ value: this.proto.usernamePrefix });
+ usernameBoxIndex = 1;
+ }
+
+ for (let i = 0; i < splits.length; ++i) {
+ this.userNameBoxes.push({ value: splits[i].separator });
+ label = bundle.getFormattedString("accountColon", [splits[i].label]);
+ let defaultVal = splits[i].defaultValue;
+ this.userNameBoxes.push(
+ this.insertUsernameField("username-split-" + i, label, grid, defaultVal)
+ );
+ }
+ this.userNameBoxes[usernameBoxIndex].focus();
+ this.userNameProto = proto;
+ this.checkUsername();
+ },
+
+ hideUsernamePage() {
+ document.querySelector("wizard").canAdvance = true;
+ var next = "account" + (this.proto.noPassword ? "advanced" : "password");
+ document.getElementById("accountusername").next = next;
+ },
+
+ showAdvanced() {
+ // ensure we don't destroy user data if it's not necessary
+ var id = this.proto.id;
+ if ("protoSpecOptId" in this && this.protoSpecOptId == id) {
+ return;
+ }
+ this.protoSpecOptId = id;
+
+ this.populateProtoSpecificBox();
+
+ // Make sure the protocol specific options and wizard buttons are visible.
+ let wizard = document.querySelector("wizard");
+ if (wizard.scrollHeight > window.innerHeight) {
+ window.resizeBy(0, wizard.scrollHeight - window.innerHeight);
+ }
+
+ let alias = document.getElementById("alias");
+ alias.focus();
+ },
+
+ populateProtoSpecificBox() {
+ let haveOptions = accountOptionsHelper.addOptions(
+ this.proto.id + "-",
+ this.proto.getOptions()
+ );
+ document.getElementById("protoSpecificGroupbox").hidden = !haveOptions;
+ if (haveOptions) {
+ var bundle = document.getElementById("accountsBundle");
+ document.getElementById("protoSpecificCaption").textContent =
+ bundle.getFormattedString("protoOptions", [this.proto.name]);
+ }
+ },
+
+ /**
+ * Create new summary field and value elements.
+ *
+ * @param {string} aLabel - The name of the field being summarised.
+ * @param {string} aValue - The value of the field being summarised.
+ * @param {Element} grid - A container with a two column grid display to
+ * append the new elements to.
+ */
+ createSummaryRow(aLabel, aValue, grid) {
+ var label = document.createXULElement("label");
+ label.classList.add("header", "label-inline");
+ if (aLabel.length > 20) {
+ aLabel = aLabel.substring(0, 20);
+ aLabel += "…";
+ }
+
+ label.setAttribute("value", aLabel);
+ grid.appendChild(label);
+
+ var input = document.createElementNS(
+ "http://www.w3.org/1999/xhtml",
+ "input"
+ );
+ input.setAttribute("value", aValue);
+ input.classList.add("plain", "input-inline");
+ input.setAttribute("readonly", true);
+ grid.appendChild(input);
+ },
+
+ showSummary() {
+ var rows = document.getElementById("summaryRows");
+ var bundle = document.getElementById("accountsBundle");
+ while (rows.hasChildNodes()) {
+ rows.lastChild.remove();
+ }
+
+ var label = document.getElementById("protoLabel").value;
+ this.createSummaryRow(label, this.proto.name, rows);
+ this.username = this.getUsername();
+ label = bundle.getString("accountUsername");
+ this.createSummaryRow(label, this.username, rows);
+ if (!this.proto.noPassword) {
+ this.password = this.getValue("password");
+ if (this.password) {
+ label = document.getElementById("passwordLabel").value;
+ var pass = "";
+ for (let i = 0; i < this.password.length; ++i) {
+ pass += "*";
+ }
+ this.createSummaryRow(label, pass, rows);
+ }
+ }
+ this.alias = this.getValue("alias");
+ if (this.alias) {
+ label = document.getElementById("aliasLabel").value;
+ this.createSummaryRow(label, this.alias, rows);
+ }
+
+ var id = this.proto.id;
+ this.prefs = [];
+ for (let opt of this.proto.getOptions()) {
+ let name = opt.name;
+ let eltName = id + "-" + name;
+ let val = this.getValue(eltName);
+ // The value will be undefined if the proto specific groupbox has never been opened
+ if (val === undefined) {
+ continue;
+ }
+ switch (opt.type) {
+ case Ci.prplIPref.typeBool:
+ if (val != opt.getBool()) {
+ this.prefs.push({ opt, name, value: !!val });
+ }
+ break;
+ case Ci.prplIPref.typeInt:
+ if (val != opt.getInt()) {
+ this.prefs.push({ opt, name, value: val });
+ }
+ break;
+ case Ci.prplIPref.typeString:
+ if (val != opt.getString()) {
+ this.prefs.push({ opt, name, value: val });
+ }
+ break;
+ case Ci.prplIPref.typeList:
+ if (val != opt.getListDefault()) {
+ this.prefs.push({ opt, name, value: val });
+ }
+ break;
+ default:
+ throw new Error("unknown preference type " + opt.type);
+ }
+ }
+
+ for (let i = 0; i < this.prefs.length; ++i) {
+ let opt = this.prefs[i];
+ let label = bundle.getFormattedString("accountColon", [opt.opt.label]);
+ this.createSummaryRow(label, opt.value, rows);
+ }
+ },
+
+ createAccount() {
+ var acc = IMServices.accounts.createAccount(this.username, this.proto.id);
+ if (!this.proto.noPassword && this.password) {
+ acc.password = this.password;
+ }
+ if (this.alias) {
+ acc.alias = this.alias;
+ }
+
+ for (let i = 0; i < this.prefs.length; ++i) {
+ let option = this.prefs[i];
+ let opt = option.opt;
+ switch (opt.type) {
+ case Ci.prplIPref.typeBool:
+ acc.setBool(option.name, option.value);
+ break;
+ case Ci.prplIPref.typeInt:
+ acc.setInt(option.name, option.value);
+ break;
+ case Ci.prplIPref.typeString:
+ case Ci.prplIPref.typeList:
+ acc.setString(option.name, option.value);
+ break;
+ default:
+ throw new Error("unknown type");
+ }
+ }
+ var autologin = this.getValue("connectNow");
+ acc.autoLogin = autologin;
+
+ acc.save();
+
+ try {
+ if (autologin) {
+ acc.connect();
+ }
+ } catch (e) {
+ // If the connection fails (for example if we are currently in
+ // offline mode), we still want to close the account wizard
+ }
+
+ if (window.opener) {
+ var am = window.opener.gAccountManager;
+ if (am) {
+ am.selectAccount(acc.id);
+ }
+ }
+
+ var inServer = MailServices.accounts.createIncomingServer(
+ this.username,
+ this.proto.id, // hostname
+ "im"
+ );
+ inServer.wrappedJSObject.imAccount = acc;
+
+ var account = MailServices.accounts.createAccount();
+ // Avoid new folder notifications.
+ inServer.valid = false;
+ account.incomingServer = inServer;
+ inServer.valid = true;
+ MailServices.accounts.notifyServerLoaded(inServer);
+
+ return true;
+ },
+
+ getValue(aId) {
+ var elt = document.getElementById(aId);
+ if ("selectedItem" in elt) {
+ return elt.selectedItem.value;
+ }
+ // Strangely various input types also have a "checked" property defined,
+ // so we check for the expected elements explicitly.
+ if (
+ ((elt.localName == "input" && elt.getAttribute("type") == "checkbox") ||
+ elt.localName == "checkbox") &&
+ "checked" in elt
+ ) {
+ return elt.checked;
+ }
+ if ("value" in elt) {
+ return elt.value;
+ }
+ // If the groupbox has never been opened, the binding isn't attached
+ // so the attributes don't exist. The calling code in showSummary
+ // has a special handling of the undefined value for this case.
+ return undefined;
+ },
+
+ *getIter(aEnumerator) {
+ for (let iter of aEnumerator) {
+ yield iter;
+ }
+ },
+
+ /* Check for correctness and set URL for the "Get more protocols..."-link
+ * Stripped down code from preferences/themes.js
+ */
+ setGetMoreProtocols() {
+ let prefURL = PREF_EXTENSIONS_GETMOREPROTOCOLSURL;
+ var getMore = document.getElementById("getMoreProtocols");
+ var showGetMore = false;
+ const nsIPrefBranch = Ci.nsIPrefBranch;
+
+ if (Services.prefs.getPrefType(prefURL) != nsIPrefBranch.PREF_INVALID) {
+ try {
+ var getMoreURL = Services.urlFormatter.formatURLPref(prefURL);
+ getMore.setAttribute("getMoreURL", getMoreURL);
+ showGetMore = getMoreURL != "about:blank";
+ } catch (e) {}
+ }
+ getMore.hidden = !showGetMore;
+ },
+
+ openURL(aURL) {
+ Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService)
+ .loadURI(Services.io.newURI(aURL));
+ },
+};
+
+window.addEventListener("load", event => {
+ accountWizard.onload();
+});