Dactyloidae/mailnews/base/search/content/searchWidgets.xml
Matt A. Tobin 7fac86416c Issue #1377 - Fix porting typo in mailnews/base/search/content/searchWidgets.xml
MailNews Core being historically comm tended to avoid defining shorthand for Components.interfaces
2020-02-07 07:52:23 +08:00

740 lines
30 KiB
XML

<?xml version="1.0"?>
<!-- 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/. -->
<!--
This file has the following external dependencies:
-gFilterActionStrings from FilterEditor.js
-gFilterList from FilterEditor.js
-gFilter from FilterEditor.js
-gCustomActions from FilterEditor.js
-gFilterType from FilterEditor.js
-checkActionsReorder from FilterEditor.js
-->
<!DOCTYPE dialog [
<!ENTITY % filterEditorDTD SYSTEM "chrome://messenger/locale/FilterEditor.dtd" >
%filterEditorDTD;
<!ENTITY % messengerDTD SYSTEM "chrome://messenger/locale/messenger.dtd" >
%messengerDTD;
]>
<bindings id="filterBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:nc="http://home.netscape.com/NC-rdf#"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="ruleactiontype-menulist">
<content>
<xul:menulist class="ruleaction-type">
<xul:menupopup>
<xul:menuitem label="&moveMessage.label;" value="movemessage" enablefornews="false"/>
<xul:menuitem label="&copyMessage.label;" value="copymessage"/>
<xul:menuseparator enablefornews="false"/>
<xul:menuitem label="&forwardTo.label;" value="forwardmessage" enablefornews="false"/>
<xul:menuitem label="&replyWithTemplate.label;" value="replytomessage" enablefornews="false"/>
<xul:menuseparator/>
<xul:menuitem label="&markMessageRead.label;" value="markasread"/>
<xul:menuitem label="&markMessageUnread.label;" value="markasunread"/>
<xul:menuitem label="&markMessageStarred.label;" value="markasflagged"/>
<xul:menuitem label="&setPriority.label;" value="setpriorityto"/>
<xul:menuitem label="&addTag.label;" value="addtagtomessage"/>
<xul:menuitem label="&setJunkScore.label;" value="setjunkscore" enablefornews="false"/>
<xul:menuseparator enableforpop3="true"/>
<xul:menuitem label="&deleteMessage.label;" value="deletemessage"/>
<xul:menuitem label="&deleteFromPOP.label;" value="deletefrompopserver" enableforpop3="true"/>
<xul:menuitem label="&fetchFromPOP.label;" value="fetchfrompopserver" enableforpop3="true"/>
<xul:menuseparator/>
<xul:menuitem label="&ignoreThread.label;" value="ignorethread"/>
<xul:menuitem label="&ignoreSubthread.label;" value="ignoresubthread"/>
<xul:menuitem label="&watchThread.label;" value="watchthread"/>
<xul:menuseparator/>
<xul:menuitem label="&stopExecution.label;" value="stopexecution"/>
</xul:menupopup>
</xul:menulist>
</content>
<implementation>
<constructor>
<![CDATA[
this.addCustomActions();
this.hideInvalidActions();
// differentiate between creating a new, next available action,
// and creating a row which will be initialized with an action
if (!this.parentNode.hasAttribute('initialActionIndex'))
{
var unavailableActions = this.usedActionsList();
// select the first one that's not in the list
for (var index = 0; index < this.menuitems.length; index++)
{
var menu = this.menuitems[index];
if (!(menu.value in unavailableActions) && !menu.hidden)
{
this.menulist.value = menu.value;
this.parentNode.setAttribute('value', menu.value);
break;
}
}
}
else
{
this.parentNode.mActionTypeInitialized = true;
this.parentNode.clearInitialActionIndex();
}
]]>
</constructor>
<field name="menulist">document.getAnonymousNodes(this)[0]</field>
<field name="menuitems">this.menulist.getElementsByTagNameNS(this.menulist.namespaceURI, 'menuitem')</field>
<method name="hideInvalidActions">
<body>
<![CDATA[
let menupopup = this.menulist.menupopup;
let scope = getScopeFromFilterList(gFilterList);
// walk through the list of filter actions and hide any actions which aren't valid
// for our given scope (news, imap, pop, etc) and context
let elements, i;
// disable / enable all elements in the "filteractionlist"
// based on the scope and the "enablefornews" attribute
elements = menupopup.getElementsByAttribute("enablefornews", "true");
for (i = 0; i < elements.length; i++)
elements[i].hidden = scope != Components.interfaces.nsMsgSearchScope.newsFilter;
elements = menupopup.getElementsByAttribute("enablefornews", "false");
for (i = 0; i < elements.length; i++)
elements[i].hidden = scope == Components.interfaces.nsMsgSearchScope.newsFilter;
elements = menupopup.getElementsByAttribute("enableforpop3", "true");
for (i = 0; i < elements.length; i++)
elements[i].hidden = !((gFilterList.folder.server.type == "pop3") ||
(gFilterList.folder.server.type == "none"));
elements = menupopup.getElementsByAttribute("isCustom", "true");
// Note there might be an additional element here as a placeholder
// for a missing action, so we iterate over the known actions
// instead of the elements.
for (i = 0; i < gCustomActions.length; i++)
elements[i].hidden = !gCustomActions[i]
.isValidForType(gFilterType, scope);
// Disable "Reply with Template" if there are no templates.
if (!this.getTemplates(false)) {
elements = menupopup.getElementsByAttribute("value", "replytomessage");
if (elements.length == 1)
elements[0].hidden = true;
}
]]>
</body>
</method>
<method name="addCustomActions">
<body>
<![CDATA[
var menupopup = this.menulist.menupopup;
for (var i = 0; i < gCustomActions.length; i++)
{
var customAction = gCustomActions[i];
var menuitem = document.createElementNS(
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
"xul:menuitem");
menuitem.setAttribute("label", customAction.name);
menuitem.setAttribute("value", customAction.id);
menuitem.setAttribute("isCustom", "true");
menupopup.appendChild(menuitem);
}
]]>
</body>
</method>
<!-- returns a hash containing all of the filter actions which are currently being used by other filteractionrows -->
<method name="usedActionsList">
<body>
<![CDATA[
var usedActions = {};
var currentFilterActionRow = this.parentNode;
var listBox = currentFilterActionRow.mListBox; // need to account for the list item
// now iterate over each list item in the list box
for (var index = 0; index < listBox.getRowCount(); index++)
{
var filterActionRow = listBox.getItemAtIndex(index);
if (filterActionRow != currentFilterActionRow)
{
var actionValue = filterActionRow.getAttribute('value');
// let custom actions decide if dups are allowed
var isCustom = false;
for (var i = 0; i < gCustomActions.length; i++)
{
if (gCustomActions[i].id == actionValue)
{
isCustom = true;
if (!gCustomActions[i].allowDuplicates)
usedActions[actionValue] = true;
break;
}
}
if (!isCustom) {
// The following actions can appear more than once in a single filter
// so do not set them as already used.
if (actionValue != 'addtagtomessage' &&
actionValue != 'forwardmessage' &&
actionValue != 'copymessage')
usedActions[actionValue] = true;
// If either Delete message or Move message exists, disable the other one.
// It does not make sense to apply both to the same message.
if (actionValue == 'deletemessage')
usedActions['movemessage'] = true;
else if (actionValue == 'movemessage')
usedActions['deletemessage'] = true;
// The same with Mark as read/Mark as Unread.
else if (actionValue == 'markasread')
usedActions['markasunread'] = true;
else if (actionValue == 'markasunread')
usedActions['markasread'] = true;
}
}
}
return usedActions;
]]>
</body>
</method>
<!--
- Check if there exist any templates in this account.
-
- @param populateTemplateList If true, create menuitems representing
- the found templates.
- @param templateMenuList The menulist element to create items in.
-
- @return True if at least one template was found, otherwise false.
-->
<method name="getTemplates">
<parameter name="populateTemplateList"/>
<parameter name="templateMenuList"/>
<body>
<![CDATA[
Components.utils.import("resource:///modules/iteratorUtils.jsm", this);
let identitiesRaw = MailServices.accounts
.getIdentitiesForServer(gFilterList.folder.server);
let identities = Array.from(this.fixIterator(identitiesRaw,
Components.interfaces.nsIMsgIdentity));
if (identities.length == 0) { // typically if this is Local Folders
if (MailServices.accounts.defaultAccount)
identities.push(MailServices.accounts.defaultAccount.defaultIdentity);
}
let templateFound = false;
let foldersScanned = [];
for (let identity of identities) {
let enumerator = null;
let msgFolder;
try {
msgFolder = Components.classes["@mozilla.org/rdf/rdf-service;1"]
.getService(Components.interfaces.nsIRDFService)
.GetResource(identity.stationeryFolder)
.QueryInterface(Components.interfaces.nsIMsgFolder);
// If we already processed this folder, do not set enumerator
// so that we skip this identity.
if (foldersScanned.indexOf(msgFolder) == -1) {
foldersScanned.push(msgFolder);
enumerator = msgFolder.msgDatabase.EnumerateMessages();
}
} catch (e) {
// The Templates folder may not exist, that is OK.
}
if (!enumerator)
continue;
while (enumerator.hasMoreElements()) {
let header = enumerator.getNext();
if (header instanceof Components.interfaces.nsIMsgDBHdr) {
templateFound = true;
if (!populateTemplateList)
return true;
let msgTemplateUri = msgFolder.URI + "?messageId=" +
header.messageId + '&subject=' + header.mime2DecodedSubject;
let newItem = templateMenuList.appendItem(header.mime2DecodedSubject,
msgTemplateUri);
}
}
}
return templateFound;
]]>
</body>
</method>
</implementation>
<handlers>
<handler event="command">
<![CDATA[
this.parentNode.setAttribute('value', this.menulist.value);
checkActionsReorder();
]]>
</handler>
<handler event="popupshowing">
<![CDATA[
var unavailableActions = this.usedActionsList();
for (var index = 0; index < this.menuitems.length; index++)
{
var menu = this.menuitems[index];
menu.setAttribute('disabled', menu.value in unavailableActions);
}
]]>
</handler>
</handlers>
</binding>
<!-- This binding exists to disable the default binding of a listitem
in the search terms. -->
<binding id="listitem">
<implementation>
<method name="_fireEvent">
<parameter name="aName"/>
<body>
<![CDATA[
/* This provides a dummy _fireEvent function that
the listbox expects to be able to call.
See bug 202036. */
]]>
</body>
</method>
</implementation>
</binding>
<binding id="ruleaction" extends="#listitem">
<content allowevents="true">
<xul:listcell class="ruleactiontype"
orient="vertical" align="stretch" pack="center"/>
<xul:listcell class="ruleactiontarget" xbl:inherits="type=value"
orient="vertical" align="stretch" pack="center"/>
<xul:listcell>
<xul:button class="small-button"
label="+"
tooltiptext="&addAction.tooltip;"
oncommand="this.parentNode.parentNode.addRow();"/>
<xul:button class="small-button"
label="&#x2212;"
tooltiptext="&removeAction.tooltip;"
oncommand="this.parentNode.parentNode.removeRow();"
anonid="removeButton"/>
</xul:listcell>
</content>
<implementation>
<field name="mListBox">this.parentNode</field>
<field name="mRemoveButton">document.getAnonymousElementByAttribute(this, "anonid", "removeButton")</field>
<field name="mActionTypeInitialized">false</field>
<field name="mRuleActionTargetInitialized">false</field>
<field name="mRuleActionType">document.getAnonymousNodes(this)[0]</field>
<method name="clearInitialActionIndex">
<body>
<![CDATA[
// we should only remove the initialActionIndex after we have been told that
// both the rule action type and the rule action target have both been built since they both need
// this piece of information. This complication arises because both of these child elements are getting
// bound asynchronously after the search row has been constructed
if (this.mActionTypeInitialized && this.mRuleActionTargetInitialized)
this.removeAttribute('initialActionIndex');
]]>
</body>
</method>
<method name="initWithAction">
<parameter name="aFilterAction"/>
<body>
<![CDATA[
var filterActionStr;
var actionTarget = document.getAnonymousNodes(this)[1];
var actionItem = document.getAnonymousNodes(actionTarget);
var nsMsgFilterAction = Components.interfaces.nsMsgFilterAction;
switch (aFilterAction.type)
{
case nsMsgFilterAction.Custom:
filterActionStr = aFilterAction.customId;
if (actionItem)
actionItem[0].value = aFilterAction.strValue;
// Make sure the custom action has been added. If not, it
// probably was from an extension that has been removed. We'll
// show a dummy menuitem to warn the user.
var needCustomLabel = true;
for (var i = 0; i < gCustomActions.length; i++)
{
if (gCustomActions[i].id == filterActionStr)
{
needCustomLabel = false;
break;
}
}
if (needCustomLabel)
{
var menuitem = document.createElementNS(
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
"xul:menuitem");
menuitem.setAttribute("label",
gFilterBundle.getString("filterMissingCustomAction"));
menuitem.setAttribute("value", filterActionStr);
menuitem.disabled = true;
this.mRuleActionType.menulist.menupopup.appendChild(menuitem);
var scriptError = Components.classes["@mozilla.org/scripterror;1"]
.createInstance(Components.interfaces.nsIScriptError);
scriptError.init("Missing custom action " + filterActionStr,
null, null, 0, 0,
Components.interfaces.nsIScriptError.errorFlag,
"component javascript");
Services.console.logMessage(scriptError);
}
break;
case nsMsgFilterAction.MoveToFolder:
case nsMsgFilterAction.CopyToFolder:
actionItem[0].value = aFilterAction.targetFolderUri;
break;
case nsMsgFilterAction.Reply:
case nsMsgFilterAction.Forward:
actionItem[0].value = aFilterAction.strValue;
break;
case nsMsgFilterAction.Label:
actionItem[0].value = aFilterAction.label;
break;
case nsMsgFilterAction.ChangePriority:
actionItem[0].value = aFilterAction.priority;
break;
case nsMsgFilterAction.JunkScore:
actionItem[0].value = aFilterAction.junkScore;
break;
case nsMsgFilterAction.AddTag:
actionItem[0].value = aFilterAction.strValue;
break;
default:
break;
}
if (aFilterAction.type != nsMsgFilterAction.Custom)
filterActionStr = gFilterActionStrings[aFilterAction.type];
document.getAnonymousNodes(this.mRuleActionType)[0]
.value = filterActionStr;
this.mRuleActionTargetInitialized = true;
this.clearInitialActionIndex();
checkActionsReorder();
]]>
</body>
</method>
<method name="validateAction">
<body>
<![CDATA[
// returns true if this row represents a valid filter action and false otherwise.
// This routine also prompts the user.
Components.utils.import("resource:///modules/MailUtils.js", this);
var filterActionString = this.getAttribute('value');
var actionTarget = document.getAnonymousNodes(this)[1];
var errorString, customError;
switch (filterActionString)
{
case "movemessage":
case "copymessage":
let msgFolder = document.getAnonymousNodes(actionTarget)[0].value ?
this.MailUtils.getFolderForURI(document.getAnonymousNodes(actionTarget)[0].value) : null;
if (!msgFolder || !msgFolder.canFileMessages)
errorString = "mustSelectFolder";
break;
case "forwardmessage":
if (document.getAnonymousNodes(actionTarget)[0].value.length < 3 ||
document.getAnonymousNodes(actionTarget)[0].value.indexOf('@') < 1)
errorString = "enterValidEmailAddress";
break;
case "replytomessage":
if (!document.getAnonymousNodes(actionTarget)[0].selectedItem)
errorString = "pickTemplateToReplyWith";
break;
default:
// some custom actions have no action value node
if (!document.getAnonymousNodes(actionTarget))
return true;
// locate the correct custom action, and check validity
for (var i = 0; i < gCustomActions.length; i++)
if (gCustomActions[i].id == filterActionString)
{
customError =
gCustomActions[i].validateActionValue(
document.getAnonymousNodes(actionTarget)[0].value,
gFilterList.folder, gFilterType);
break;
}
break;
}
errorString = errorString ?
gFilterBundle.getString(errorString) :
customError;
if (errorString)
Services.prompt.alert(window, null, errorString);
return !errorString;
]]>
</body>
</method>
<method name="saveToFilter">
<parameter name="aFilter"/>
<body>
<![CDATA[
// create a new filter action, fill it in, and then append it to the filter
var filterAction = aFilter.createAction();
var filterActionString = this.getAttribute('value');
filterAction.type = gFilterActionStrings.indexOf(filterActionString);
var actionTarget = document.getAnonymousNodes(this)[1];
var actionItem = document.getAnonymousNodes(actionTarget);
var nsMsgFilterAction = Components.interfaces.nsMsgFilterAction;
switch (filterAction.type)
{
case nsMsgFilterAction.Label:
filterAction.label = actionItem[0].getAttribute("value");
break;
case nsMsgFilterAction.ChangePriority:
filterAction.priority = actionItem[0].getAttribute("value");
break;
case nsMsgFilterAction.MoveToFolder:
case nsMsgFilterAction.CopyToFolder:
filterAction.targetFolderUri = actionItem[0].value;
break;
case nsMsgFilterAction.JunkScore:
filterAction.junkScore = actionItem[0].value;
break;
case nsMsgFilterAction.Custom:
filterAction.customId = filterActionString;
// fall through to set the value
default:
if (actionItem)
filterAction.strValue = actionItem[0].value;
break;
}
aFilter.appendAction(filterAction);
]]>
</body>
</method>
<method name="getActionStrings">
<parameter name="aActionStrings"/>
<body>
<![CDATA[
// Collect the action names and arguments in a plain string form.
let actionTarget = document.getAnonymousNodes(this)[1];
let actionItem = document.getAnonymousNodes(actionTarget);
aActionStrings.push({
label: document.getAnonymousNodes(this.mRuleActionType)[0].label,
argument: actionItem ?
(actionItem[0].label ?
actionItem[0].label : actionItem[0].value) : ""
});
]]>
</body>
</method>
<method name="updateRemoveButton">
<body>
<![CDATA[
// if we only have one row of actions, then disable the remove button for that row
this.mListBox.getItemAtIndex(0).mRemoveButton.disabled = this.mListBox.getRowCount() == 1;
]]>
</body>
</method>
<method name="addRow">
<body>
<![CDATA[
let listItem = document.createElement('listitem');
listItem.className = 'ruleaction';
listItem.setAttribute('onfocus','this.storeFocus();');
this.mListBox.insertBefore(listItem, this.nextSibling);
this.mListBox.ensureElementIsVisible(listItem);
// make sure the first remove button is enabled
this.updateRemoveButton();
checkActionsReorder();
]]>
</body>
</method>
<method name="removeRow">
<body>
<![CDATA[
// this.mListBox will fail after the row is removed, so save it
let listBox = this.mListBox;
if (listBox.getRowCount() > 1)
this.remove();
// can't use 'this' as it is destroyed now
listBox.getItemAtIndex(0).updateRemoveButton();
checkActionsReorder();
]]>
</body>
</method>
<method name="storeFocus">
<body>
<![CDATA[
// When this action row is focused, store its index in the parent listbox.
this.mListBox.setAttribute("focusedAction", this.mListBox.getIndexOfItem(this));
]]>
</body>
</method>
</implementation>
</binding>
<binding id="ruleactiontarget-base">
<implementation>
<constructor>
<![CDATA[
if (this.parentNode.hasAttribute('initialActionIndex'))
{
let actionIndex = this.parentNode.getAttribute('initialActionIndex');
let filterAction = gFilter.getActionAt(actionIndex);
this.parentNode.initWithAction(filterAction);
}
this.parentNode.updateRemoveButton();
]]>
</constructor>
</implementation>
</binding>
<binding id="ruleactiontarget-tag" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
<content>
<xul:menulist class="ruleactionitem">
<xul:menupopup>
</xul:menupopup>
</xul:menulist>
</content>
<implementation>
<constructor>
<![CDATA[
let menuPopup = document.getAnonymousNodes(this)[0].menupopup;
let tagArray = MailServices.tags.getAllTags({});
for (let i = 0; i < tagArray.length; ++i)
{
var taginfo = tagArray[i];
var newMenuItem = document.createElement('menuitem');
newMenuItem.setAttribute('label', taginfo.tag);
newMenuItem.setAttribute('value', taginfo.key);
menuPopup.appendChild(newMenuItem);
}
// propagating a pre-existing hack to make the tag get displayed correctly in the menulist
// now that we've changed the tags for each menu list. We need to use the current selectedIndex
// (if its defined) to handle the case where we were initialized with a filter action already.
var currentItem = document.getAnonymousNodes(this)[0].selectedItem;
document.getAnonymousNodes(this)[0].selectedItem = null;
document.getAnonymousNodes(this)[0].selectedItem = currentItem;
]]>
</constructor>
</implementation>
</binding>
<binding id="ruleactiontarget-priority" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
<content>
<xul:menulist class="ruleactionitem">
<xul:menupopup>
<xul:menuitem value="6" label="&highestPriorityCmd.label;"/>
<xul:menuitem value="5" label="&highPriorityCmd.label;"/>
<xul:menuitem value="4" label="&normalPriorityCmd.label;"/>
<xul:menuitem value="3" label="&lowPriorityCmd.label;"/>
<xul:menuitem value="2" label="&lowestPriorityCmd.label;"/>
</xul:menupopup>
</xul:menulist>
</content>
</binding>
<binding id="ruleactiontarget-junkscore" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
<content>
<xul:menulist class="ruleactionitem">
<xul:menupopup>
<xul:menuitem value="100" label="&junk.label;"/>
<xul:menuitem value="0" label="&notJunk.label;"/>
</xul:menupopup>
</xul:menulist>
</content>
</binding>
<binding id="ruleactiontarget-replyto" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
<content>
<xul:menulist class="ruleactionitem">
<xul:menupopup>
</xul:menupopup>
</xul:menulist>
</content>
<implementation>
<constructor>
<![CDATA[
document.getAnonymousElementByAttribute(
this.parentNode, "class", "ruleactiontype")
.getTemplates(true, document.getAnonymousNodes(this)[0]);
]]>
</constructor>
</implementation>
</binding>
<binding id="ruleactiontarget-forwardto" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
<content>
<xul:textbox class="ruleactionitem" flex="1"/>
</content>
</binding>
<binding id="ruleactiontarget-folder" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
<content>
<xul:menulist class="ruleactionitem folderMenuItem"
displayformat="verbose"
oncommand="this.parentNode.setPicker(event);">
<xul:menupopup type="folder"
mode="filing"
class="menulist-menupopup"
showRecent="true"
recentLabel="&recentFolders.label;"
showFileHereLabel="true"/>
</xul:menulist>
</content>
<implementation>
<constructor>
<![CDATA[
Components.utils.import("resource:///modules/MailUtils.js", this);
let folder = this.menulist.value ?
this.MailUtils.getFolderForURI(this.menulist.value) :
gFilterList.folder;
// An account folder is not a move/copy target; show "Choose Folder".
folder = folder.isServer ? null : folder;
let menupopup = this.menulist.menupopup;
// The menupopup constructor needs to finish first.
setTimeout(function() { menupopup.selectFolder(folder); }, 0);
]]>
</constructor>
<field name="menulist">document.getAnonymousNodes(this)[0]</field>
<method name="setPicker">
<parameter name="aEvent"/>
<body>
<![CDATA[
this.menulist.menupopup.selectFolder(aEvent.target._folder);
]]>
</body>
</method>
</implementation>
</binding>
</bindings>