diff --git a/chat/components/public/prplIProtocol.idl b/chat/components/public/prplIProtocol.idl index 5f7686878a..9d9eeadbd2 100644 --- a/chat/components/public/prplIProtocol.idl +++ b/chat/components/public/prplIProtocol.idl @@ -51,11 +51,23 @@ interface prplIProtocol: nsISupports { */ Array getOptions(); + /** + * String to put in front of the full account username identifier. Usually + * an empty string. + */ + readonly attribute AUTF8String usernamePrefix; + /** * @returns an array of prplIUsernameSplit */ Array getUsernameSplit(); + /** + * Split a username into its parts without separators (or prefix). + * Returns an empty array if the username can not be split. + */ + Array splitUsername(in AUTF8String aName); + /** * Descriptive text used in the account wizard to describe the username. */ diff --git a/chat/components/src/imAccounts.jsm b/chat/components/src/imAccounts.jsm index ec8ffab6ad..2af240bcb9 100644 --- a/chat/components/src/imAccounts.jsm +++ b/chat/components/src/imAccounts.jsm @@ -108,6 +108,9 @@ UnknownProtocol.prototype = { getOptions() { return []; }, + get usernamePrefix() { + return ""; + }, getUsernameSplit() { return []; }, diff --git a/chat/modules/jsProtoHelper.jsm b/chat/modules/jsProtoHelper.jsm index cb3f19af4d..857403f121 100644 --- a/chat/modules/jsProtoHelper.jsm +++ b/chat/modules/jsProtoHelper.jsm @@ -1169,6 +1169,7 @@ var GenericProtocolPrototype = { } return purplePrefs; }, + usernamePrefix: "", getUsernameSplit() { if (!this.usernameSplits || !this.usernameSplits.length) { return []; @@ -1176,6 +1177,41 @@ var GenericProtocolPrototype = { return this.usernameSplits.map(split => new UsernameSplit(split)); }, + /** + * Protocol agnostic implementation that splits the username by the pattern + * defined with |usernamePrefix| and |usernameSplits| on the protocol. + * Prefers the first occurence of a separator. + * + * @param {string} aName - Username to split. + * @returns {string[]} Parts of the username or empty array if the username + * doesn't match the splitting format. + */ + splitUsername(aName) { + let remainingName = aName; + if (this.usernamePrefix) { + if (!remainingName.startsWith(this.usernamePrefix)) { + return []; + } + remainingName = remainingName.slice(this.usernamePrefix.length); + } + if (!this.usernameSplits || !this.usernameSplits.length) { + return [remainingName]; + } + const parts = []; + for (const split of this.usernameSplits) { + if (!remainingName.includes(split.separator)) { + return []; + } + const separatorIndex = remainingName.indexOf(split.separator); + parts.push(remainingName.slice(0, separatorIndex)); + remainingName = remainingName.slice( + separatorIndex + split.separator.length + ); + } + parts.push(remainingName); + return parts; + }, + registerCommands() { if (!this.commands) { return; diff --git a/chat/protocols/irc/irc.jsm b/chat/protocols/irc/irc.jsm index 3f7507d23f..a0c7ea252a 100644 --- a/chat/protocols/irc/irc.jsm +++ b/chat/protocols/irc/irc.jsm @@ -995,9 +995,9 @@ function ircAccount(aProtocol, aImAccount) { this.conversations = new NormalizedMap(this.normalize.bind(this)); // Split the account name into usable parts. - let splitter = this.name.lastIndexOf("@"); - this._accountNickname = this.name.slice(0, splitter); - this._server = this.name.slice(splitter + 1); + const [accountNickname, server] = this.protocol.splitUsername(this.name); + this._accountNickname = accountNickname; + this._server = server; // To avoid _currentServerName being null, initialize it to the server being // connected to. This will also get overridden during the 001 response from // the server. @@ -2362,6 +2362,11 @@ ircProtocol.prototype = { }, ], + splitUsername(aName) { + let splitter = aName.lastIndexOf("@"); + return [aName.slice(0, splitter), aName.slice(splitter + 1)]; + }, + options: { port: { get label() { diff --git a/mail/components/im/content/imAccountWizard.js b/mail/components/im/content/imAccountWizard.js index 7bf9d9a174..c4edadbc90 100644 --- a/mail/components/im/content/imAccountWizard.js +++ b/mail/components/im/content/imAccountWizard.js @@ -91,16 +91,29 @@ var accountWizard = { } }, + /** + * 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[0].value) { + 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(); @@ -116,6 +129,28 @@ var accountWizard = { 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 (box instanceof Element) { + box.value = splitValues.shift(); + } + } + this.checkUsername(); + }, + selectProtocol() { var protoList = document.getElementById("protolist"); var id = protoList.selectedItem.value; @@ -153,11 +188,21 @@ var accountWizard = { 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) { @@ -191,6 +236,12 @@ var accountWizard = { 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 }); @@ -200,7 +251,7 @@ var accountWizard = { this.insertUsernameField("username-split-" + i, label, grid, defaultVal) ); } - this.userNameBoxes[0].focus(); + this.userNameBoxes[usernameBoxIndex].focus(); this.userNameProto = proto; this.checkUsername(); },