From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- comm/mailnews/base/content/subscribe.js | 496 ++++++++++++++++++++++++++++++++ 1 file changed, 496 insertions(+) create mode 100644 comm/mailnews/base/content/subscribe.js (limited to 'comm/mailnews/base/content/subscribe.js') diff --git a/comm/mailnews/base/content/subscribe.js b/comm/mailnews/base/content/subscribe.js new file mode 100644 index 0000000000..46eb51ce52 --- /dev/null +++ b/comm/mailnews/base/content/subscribe.js @@ -0,0 +1,496 @@ +/* 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/. */ + +/* globals msgWindow, nsMsgStatusFeedback */ // From mailWindow.js + +var { MailUtils } = ChromeUtils.import("resource:///modules/MailUtils.jsm"); + +var gSubscribeTree = null; +var gSubscribeBody = null; +var okCallback = null; +var gChangeTable = {}; +var gServerURI = null; +var gSubscribableServer = null; +var gNameField = null; +var gNameFieldLabel = null; +var gStatusFeedback; +var gSearchView = null; +var gSearchTree = null; +var gSubscribeBundle; + +window.addEventListener("DOMContentLoaded", SubscribeOnLoad); +window.addEventListener("unload", SubscribeOnUnload); + +document.addEventListener("dialogaccept", subscribeOK); +document.addEventListener("dialogcancel", subscribeCancel); + +function Stop() { + if (gSubscribableServer) { + gSubscribableServer.stopPopulating(msgWindow); + } +} + +function SetServerTypeSpecificTextValues() { + if (!gServerURI) { + return; + } + + let serverType = MailUtils.getExistingFolder(gServerURI).server.type; + + // Set the server specific ui elements. + let subscribeLabelString = gSubscribeBundle.getString( + "subscribeLabel-" + serverType + ); + let currentListTab = "currentListTab-" + serverType; + let currentListTabLabel = gSubscribeBundle.getString( + currentListTab + ".label" + ); + let currentListTabAccesskey = gSubscribeBundle.getString( + currentListTab + ".accesskey" + ); + + document + .getElementById("currentListTab") + .setAttribute("label", currentListTabLabel); + document + .getElementById("currentListTab") + .setAttribute("accesskey", currentListTabAccesskey); + document.getElementById("newGroupsTab").collapsed = serverType != "nntp"; // show newGroupsTab only for nntp servers + document + .getElementById("subscribeLabel") + .setAttribute("value", subscribeLabelString); +} + +function onServerClick(aFolder) { + gServerURI = aFolder.server.serverURI; + let serverMenu = document.getElementById("serverMenu"); + serverMenu.menupopup.selectFolder(aFolder); + + SetServerTypeSpecificTextValues(); + ShowCurrentList(); +} + +var MySubscribeListener = { + OnDonePopulating() { + gStatusFeedback._stopMeteors(); + document.getElementById("stopButton").disabled = true; + document.getElementById("refreshButton").disabled = false; + document.getElementById("currentListTab").disabled = false; + document.getElementById("newGroupsTab").disabled = false; + }, +}; + +function SetUpTree(forceToServer, getOnlyNew) { + if (!gServerURI) { + return; + } + + var server = MailUtils.getExistingFolder(gServerURI).server; + try { + CleanUpSearchView(); + gSubscribableServer = server.QueryInterface(Ci.nsISubscribableServer); + + // Enable (or disable) the search related UI. + EnableSearchUI(); + + // Clear out the text field when switching server. + gNameField.value = ""; + + // Since there is no text, switch to the Subscription view. + toggleSubscriptionView(false); + + gSubscribeTree.view = gSubscribableServer.folderView; + gSubscribableServer.subscribeListener = MySubscribeListener; + + document.getElementById("currentListTab").disabled = true; + document.getElementById("newGroupsTab").disabled = true; + document.getElementById("refreshButton").disabled = true; + + gStatusFeedback._startMeteors(); + gStatusFeedback.setStatusString(""); + gStatusFeedback.showStatusString( + gSubscribeBundle.getString("pleaseWaitString") + ); + document.getElementById("stopButton").disabled = false; + + gSubscribableServer.startPopulating(msgWindow, forceToServer, getOnlyNew); + } catch (e) { + if (e.result == 0x80550014) { + // NS_MSG_ERROR_OFFLINE + gStatusFeedback.setStatusString( + gSubscribeBundle.getString("offlineState") + ); + } else { + console.error("Failed to populate subscribe tree: " + e); + gStatusFeedback.setStatusString( + gSubscribeBundle.getString("errorPopulating") + ); + } + Stop(); + } +} + +function SubscribeOnUnload() { + try { + CleanUpSearchView(); + } catch (ex) { + dump("Failed to remove the subscribe tree: " + ex + "\n"); + } + + msgWindow.closeWindow(); +} + +function EnableSearchUI() { + if (gSubscribableServer.supportsSubscribeSearch) { + gNameField.removeAttribute("disabled"); + gNameFieldLabel.removeAttribute("disabled"); + } else { + gNameField.setAttribute("disabled", true); + gNameFieldLabel.setAttribute("disabled", true); + } +} + +function SubscribeOnLoad() { + gSubscribeBundle = document.getElementById("bundle_subscribe"); + + gSubscribeTree = document.getElementById("subscribeTree"); + gSubscribeBody = document.getElementById("subscribeTreeBody"); + gSearchTree = document.getElementById("searchTree"); + gSearchTree = document.getElementById("searchTree"); + gNameField = document.getElementById("namefield"); + gNameFieldLabel = document.getElementById("namefieldlabel"); + + // eslint-disable-next-line no-global-assign + msgWindow = Cc["@mozilla.org/messenger/msgwindow;1"].createInstance( + Ci.nsIMsgWindow + ); + msgWindow.domWindow = window; + gStatusFeedback = new nsMsgStatusFeedback(); + msgWindow.statusFeedback = gStatusFeedback; + msgWindow.rootDocShell.allowAuth = true; + msgWindow.rootDocShell.appType = Ci.nsIDocShell.APP_TYPE_MAIL; + + // look in arguments[0] for parameters + if (window.arguments && window.arguments[0]) { + if (window.arguments[0].okCallback) { + top.okCallback = window.arguments[0].okCallback; + } + } + + var serverMenu = document.getElementById("serverMenu"); + + gServerURI = null; + let folder = + "folder" in window.arguments[0] ? window.arguments[0].folder : null; + if (folder && folder.server instanceof Ci.nsISubscribableServer) { + serverMenu.menupopup.selectFolder(folder.server.rootMsgFolder); + try { + CleanUpSearchView(); + gSubscribableServer = folder.server.QueryInterface( + Ci.nsISubscribableServer + ); + // Enable (or disable) the search related UI. + EnableSearchUI(); + gServerURI = folder.server.serverURI; + } catch (ex) { + // dump("not a subscribable server\n"); + CleanUpSearchView(); + gSubscribableServer = null; + gServerURI = null; + } + } + + if (!gServerURI) { + // dump("subscribe: no uri\n"); + // dump("xxx todo: use the default news server. right now, I'm just using the first server\n"); + + serverMenu.selectedIndex = 0; + + if (serverMenu.selectedItem) { + gServerURI = serverMenu.selectedItem.getAttribute("id"); + } else { + // dump("xxx todo none of your servers are subscribable\n"); + // dump("xxx todo fix this by disabling subscribe if no subscribable server or, add a CREATE SERVER button, like in 4.x\n"); + return; + } + } + + SetServerTypeSpecificTextValues(); + + ShowCurrentList(); + + gNameField.focus(); +} + +function subscribeOK() { + if (top.okCallback) { + top.okCallback(top.gChangeTable); + } + Stop(); + if (gSubscribableServer) { + gSubscribableServer.subscribeCleanup(); + } +} + +function subscribeCancel() { + Stop(); + if (gSubscribableServer) { + gSubscribableServer.subscribeCleanup(); + } +} + +function SetState(name, state) { + var changed = gSubscribableServer.setState(name, state); + if (changed) { + StateChanged(name, state); + } +} + +function StateChanged(name, state) { + if (gServerURI in gChangeTable) { + if (name in gChangeTable[gServerURI]) { + var oldValue = gChangeTable[gServerURI][name]; + if (oldValue != state) { + delete gChangeTable[gServerURI][name]; + } + } else { + gChangeTable[gServerURI][name] = state; + } + } else { + gChangeTable[gServerURI] = {}; + gChangeTable[gServerURI][name] = state; + } +} + +function InSearchMode() { + return !document.getElementById("searchView").hidden; +} + +function SearchOnClick(event) { + // We only care about button 0 (left click) events. + if (event.button != 0 || event.target.localName != "treechildren") { + return; + } + + let treeCellInfo = gSearchTree.getCellAt(event.clientX, event.clientY); + if (treeCellInfo.row == -1 || treeCellInfo.row > gSearchView.rowCount - 1) { + return; + } + + if (treeCellInfo.col.id == "subscribedColumn2") { + if (event.detail != 2) { + // Single clicked on the check box + // (in the "subscribedColumn2" column) reverse state. + // If double click, do nothing. + ReverseStateFromRow(treeCellInfo.row); + } + } else if (event.detail == 2) { + // Double clicked on a row, reverse state. + ReverseStateFromRow(treeCellInfo.row); + } + + // Invalidate the row. + InvalidateSearchTreeRow(treeCellInfo.row); +} + +function ReverseStateFromRow(aRow) { + // To determine if the row is subscribed or not, + // we get the properties for the "subscribedColumn2" cell in the row + // and look for the "subscribed" property. + // If the "subscribed" string is in the list of properties + // we are subscribed. + let col = gSearchTree.columns.nameColumn2; + let name = gSearchView.getCellValue(aRow, col); + let isSubscribed = gSubscribableServer.isSubscribed(name); + SetStateFromRow(aRow, !isSubscribed); +} + +function SetStateFromRow(row, state) { + var col = gSearchTree.columns.nameColumn2; + var name = gSearchView.getCellValue(row, col); + SetState(name, state); +} + +function SetSubscribeState(state) { + try { + // We need to iterate over the tree selection, and set the state for + // all rows in the selection. + var inSearchMode = InSearchMode(); + var view = inSearchMode ? gSearchView : gSubscribeTree.view; + var colId = inSearchMode ? "nameColumn2" : "nameColumn"; + + var sel = view.selection; + for (var i = 0; i < sel.getRangeCount(); ++i) { + var start = {}, + end = {}; + sel.getRangeAt(i, start, end); + for (var k = start.value; k <= end.value; ++k) { + if (inSearchMode) { + SetStateFromRow(k, state); + } else { + let name = view.getCellValue(k, gSubscribeTree.columns[colId]); + SetState(name, state, k); + } + } + } + + if (inSearchMode) { + // Force a repaint. + InvalidateSearchTree(); + } else { + gSubscribeTree.invalidate(); + } + } catch (ex) { + dump("SetSubscribedState failed: " + ex + "\n"); + } +} + +function ReverseStateFromNode(row) { + let name = gSubscribeTree.view.getCellValue( + row, + gSubscribeTree.columns.nameColumn + ); + SetState(name, !gSubscribableServer.isSubscribed(name), row); +} + +function SubscribeOnClick(event) { + // We only care about button 0 (left click) events. + if (event.button != 0 || event.target.localName != "treechildren") { + return; + } + + let treeCellInfo = gSubscribeTree.getCellAt(event.clientX, event.clientY); + if ( + treeCellInfo.row == -1 || + treeCellInfo.row > gSubscribeTree.view.rowCount - 1 + ) { + return; + } + + if (event.detail == 2) { + // Only toggle subscribed state when double clicking something + // that isn't a container. + if (!gSubscribeTree.view.isContainer(treeCellInfo.row)) { + ReverseStateFromNode(treeCellInfo.row); + } + } else if (event.detail == 1) { + // If the user single clicks on the subscribe check box, we handle it here. + if (treeCellInfo.col.id == "subscribedColumn") { + ReverseStateFromNode(treeCellInfo.row); + } + } +} + +function Refresh() { + // Clear out the textfield's entry. + gNameField.value = ""; + + var newGroupsTab = document.getElementById("newGroupsTab"); + SetUpTree(true, newGroupsTab.selected); +} + +function ShowCurrentList() { + // Clear out the textfield's entry on call of Refresh(). + gNameField.value = ""; + + // Make sure the current list tab is selected. + document.getElementById("subscribeTabs").selectedIndex = 0; + + // Try loading the hostinfo before talk to server. + SetUpTree(false, false); +} + +function ShowNewGroupsList() { + // Clear out the textfield's entry. + gNameField.value = ""; + + // Make sure the new groups tab is selected. + document.getElementById("subscribeTabs").selectedIndex = 1; + + // Force it to talk to the server and get new groups. + SetUpTree(true, true); +} + +function InvalidateSearchTreeRow(row) { + gSearchTree.invalidateRow(row); +} + +function InvalidateSearchTree() { + gSearchTree.invalidate(); +} + +/** + * Toggle the tree panel in the dialog between search view and subscribe view. + * + * @param {boolean} toggle - If true, show the search view else show the + * subscribe view. + */ +function toggleSubscriptionView(toggle) { + document.getElementById("subscribeView").hidden = toggle; + document.getElementById("searchView").hidden = !toggle; +} + +function Search() { + let searchValue = gNameField.value; + if ( + searchValue.length && + gSubscribableServer && + gSubscribableServer.supportsSubscribeSearch + ) { + toggleSubscriptionView(true); + gSubscribableServer.setSearchValue(searchValue); + + if (!gSearchView && gSubscribableServer) { + gSearchView = gSubscribableServer.QueryInterface(Ci.nsITreeView); + gSearchView.selection = null; + gSearchTree.view = gSearchView; + } + return; + } + toggleSubscriptionView(false); +} + +function CleanUpSearchView() { + if (gSearchView) { + gSearchView.selection = null; + gSearchView = null; + } +} + +function onSearchTreeKeyPress(event) { + // For now, only do something on space key. + if (event.charCode != KeyEvent.DOM_VK_SPACE) { + return; + } + + var treeSelection = gSearchView.selection; + for (let i = 0; i < treeSelection.getRangeCount(); i++) { + var start = {}, + end = {}; + treeSelection.getRangeAt(i, start, end); + for (let k = start.value; k <= end.value; k++) { + ReverseStateFromRow(k); + } + + // Force a repaint. + InvalidateSearchTree(); + } +} + +function onSubscribeTreeKeyPress(event) { + // For now, only do something on space key. + if (event.charCode != KeyEvent.DOM_VK_SPACE) { + return; + } + + var treeSelection = gSubscribeTree.view.selection; + for (let i = 0; i < treeSelection.getRangeCount(); i++) { + var start = {}, + end = {}; + treeSelection.getRangeAt(i, start, end); + for (let k = start.value; k <= end.value; k++) { + ReverseStateFromNode(k); + } + } +} -- cgit v1.2.3