!import
1 <?xml version="1.0"?>
2
3 <!-- ***** BEGIN LICENSE BLOCK *****
4 - Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 -
6 - The contents of this file are subject to the Mozilla Public License Version
7 - 1.1 (the "License"); you may not use this file except in compliance with
8 - the License. You may obtain a copy of the License at
9 - http://www.mozilla.org/MPL/
10 -
11 - Software distributed under the License is distributed on an "AS IS" basis,
12 - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 - for the specific language governing rights and limitations under the
14 - License.
15 -
16 - The Original Code is Filter Action Rules.
17 -
18 - The Initial Developer of the Original Code is
19 - Scott MacGregor <mscott@mozilla.org>.
20 - Portions created by the Initial Developer are Copyright (C) 2005
21 - the Initial Developer. All Rights Reserved.
22 -
23 - Contributor(s):
24 - Karsten Düsterloh <mnyromyr@tprac.de>
25 -
26 - Alternatively, the contents of this file may be used under the terms of
27 - either the GNU General Public License Version 2 or later (the "GPL"), or
28 - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 - in which case the provisions of the GPL or the LGPL are applicable instead
30 - of those above. If you wish to allow use of your version of this file only
31 - under the terms of either the GPL or the LGPL, and not to allow others to
32 - use your version of this file under the terms of the MPL, indicate your
33 - decision by deleting the provisions above and replace them with the notice
34 - and other provisions required by the LGPL or the GPL. If you do not delete
35 - the provisions above, a recipient may use your version of this file under
36 - the terms of any one of the MPL, the GPL or the LGPL.
37 -
38 - ***** END LICENSE BLOCK ***** -->
39
40 <!--
41 This file has the following external dependencies:
42 -gFilterActionStrings from FilterEditor.js
43 -gFilterList from FilterEditor.js
44 -gPromptService from FilterEditor.js
45 -gMessengerBundle from msgFolderPickerOverlay.js
46 -GetMsgFolderFromUri, SetFolderPickerElement from msgFolderPickerOverlay.js
47 -->
48
49 <!DOCTYPE dialog [
50 <!ENTITY % filterEditorDTD SYSTEM "chrome://messenger/locale/FilterEditor.dtd" >
51 %filterEditorDTD;
52 <!ENTITY % msgFolderPickerOverlayDTD SYSTEM "chrome://messenger/locale/msgFolderPickerOverlay.dtd" >
53 %msgFolderPickerOverlayDTD;
54 ]>
55
56 <bindings id="filterBindings"
57 xmlns="http://www.mozilla.org/xbl"
58 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
59 xmlns:nc="http://home.netscape.com/NC-rdf#"
60 xmlns:xbl="http://www.mozilla.org/xbl">
61
62 <binding id="ruleactiontype-menulist">
63 <content>
64 <xul:menulist class="ruleaction-type">
65 <xul:menupopup>
66 <xul:menuitem label="&moveMessage.label;" value="movemessage" enablefornews="false"/>
67 <xul:menuitem label="©Message.label;" value="copymessage"/>
68 <xul:menuseparator enablefornews="false"/>
69 <xul:menuitem label="&forwardTo.label;" value="forwardmessage" enablefornews="false"/>
70 <xul:menuitem label="&replyWithTemplate.label;" value="replytomessage" enablefornews="false"/>
71 <xul:menuseparator/>
72 <xul:menuitem label="&markMessageRead.label;" value="markasread"/>
73 <xul:menuitem label="&markMessageStarred.label;" value="markasflagged"/>
74 <xul:menuitem label="&setPriority.label;" value="setpriorityto"/>
75 <xul:menuitem label="&addTag.label;" value="addtagtomessage"/>
76 <xul:menuitem label="&setJunkScore.label;" value="setjunkscore" enablefornews="false"/>
77 <xul:menuseparator enableforpop3="true"/>
78 <xul:menuitem label="&deleteMessage.label;" value="deletemessage"/>
79 <xul:menuitem label="&deleteFromPOP.label;" value="deletefrompopserver" enableforpop3="true"/>
80 <xul:menuitem label="&fetchFromPOP.label;" value="fetchfrompopserver" enableforpop3="true"/>
81 <xul:menuseparator enablefornews="true"/>
82 <xul:menuitem label="&ignoreThread.label;" value="ignorethread" enablefornews="true"/>
83 <xul:menuitem label="&watchThread.label;" value="watchthread" enablefornews="true"/>
84 <xul:menuseparator/>
85 <xul:menuitem label="&stopExecution.label;" value="stopexecution"/>
86 </xul:menupopup>
87 </xul:menulist>
88 </content>
89
90 <implementation>
constructor
91 <constructor>
92 <![CDATA[
93 this.hideInvalidActions();
94 // differentiate between creating a new, next available action,
95 // and creating a row which will be initialized with an action
96 if (!this.parentNode.hasAttribute('initialActionIndex'))
97 {
98 var unavailableActions = this.usedActionsList();
99 // select the first one that's not in the list
100 for (var index = 0; index < this.menuitems.length; index++)
101 {
102 var menu = this.menuitems[index];
103 if (!(menu.value in unavailableActions) && !menu.hidden)
104 {
105 this.menulist.value = menu.value;
106 this.parentNode.setAttribute('value', menu.value);
107 break;
108 }
109 }
110 }
111 else
112 {
113 this.parentNode.mActionTypeInitialized = true;
114 this.parentNode.clearInitialActionIndex();
115 }
116 ]]>
117 </constructor>
118
119 <field name="menulist">document.getAnonymousNodes(this)[0]</field>
120 <field name="menuitems">this.menulist.getElementsByTagNameNS(this.menulist.namespaceURI, 'menuitem')</field>
121
122 <method name="hideInvalidActions">
hideInvalidActions
123 <body>
124 <![CDATA[
125 var menupopup = this.menulist.menupopup;
126 var scope = getScopeFromFilterList(gFilterList);
127
128 // walk through the list of filter actions and hide any actions which aren't valid
129 // for our given scope (news, imap, pop, etc)
130 var elements, i;
131
132 // disable / enable all elements in the "filteractionlist"
133 // based on the scope and the "enablefornews" attribute
134 elements = menupopup.getElementsByAttribute("enablefornews", "true");
135 for (i = 0; i < elements.length; i++)
136 elements[i].hidden = scope != Components.interfaces.nsMsgSearchScope.newsFilter;
137
138 elements = menupopup.getElementsByAttribute("enablefornews", "false");
139 for (i = 0; i < elements.length; i++)
140 elements[i].hidden = scope == Components.interfaces.nsMsgSearchScope.newsFilter;
141
142 elements = menupopup.getElementsByAttribute("enableforpop3", "true");
143 for (i = 0; i < elements.length; i++)
144 elements[i].hidden = scope != Components.interfaces.nsMsgSearchScope.offlineMailFilter;
145 ]]>
146 </body>
147 </method>
148
149 <method name="numVisibleActions">
numVisibleActions
150 <body>
151 <![CDATA[
152 var numVisibleActions = 0;
153 // only count the items that are visible
154 for (var index = 0; index < this.menuitems.length; index++)
155 if (!this.menuitems[index].hidden)
156 numVisibleActions++;
157 return numVisibleActions;
158 ]]>
159 </body>
160 </method>
161
162 <!-- returns a hash containing all of the filter actions which are currently being used by other filteractionrows -->
163 <method name="usedActionsList">
usedActionsList
164 <body>
165 <![CDATA[
166 var usedActions = {};
167 var currentFilterActionRow = this.parentNode;
168 var listBox = currentFilterActionRow.mListBox; // need to account for the list item
169 // now iterate over each list item in the list box
170 for (var index = 0; index < listBox.getRowCount(); index++)
171 {
172 var filterActionRow = listBox.getItemAtIndex(index);
173 if (filterActionRow != currentFilterActionRow)
174 {
175 var actionValue = filterActionRow.getAttribute('value');
176
177 // don't add the tag action, several tags can be set
178 if (actionValue != 'addtagtomessage')
179 usedActions[actionValue] = true;
180 }
181 }
182 return usedActions;
183 ]]>
184 </body>
185 </method>
186 </implementation>
187
188 <handlers>
oncommand
189 <handler event="command">
190 <![CDATA[
191 this.parentNode.setAttribute('value', this.menulist.value);
192 ]]>
193 </handler>
194
195 <handler event="popupshowing">
196 <![CDATA[
197 var unavailableActions = this.usedActionsList();
198 for (var index = 0; index < this.menuitems.length; index++)
199 {
200 var menu = this.menuitems[index];
201 menu.setAttribute('disabled', menu.value in unavailableActions);
202 }
203 ]]>
204 </handler>
205 </handlers>
206 </binding>
207
208 <binding id="ruleaction">
209 <content allowevents="true">
210 <xul:listcell class="ruleactiontype"/>
211 <xul:listcell class="ruleactiontarget" xbl:inherits="type=value"
212 orient="vertical" align="start" pack="center"/>
213 <xul:listcell>
214 <xul:button class="small-button" label="+" oncommand="this.parentNode.parentNode.addRow();"/>
215 <xul:button class="small-button" label="-" oncommand="this.parentNode.parentNode.removeRow();" anonid="removeButton"/>
216 </xul:listcell>
217 <xul:listcell/>
218 </content>
219
220 <implementation>
field_mListBox
221 <field name="mListBox">this.parentNode</field>
field_mRemoveButton
222 <field name="mRemoveButton">document.getAnonymousElementByAttribute(this, "anonid", "removeButton")</field>
field_mActionTypeInitialized
223 <field name="mActionTypeInitialized">false</field>
field_mRuleActionTargetInitialized
224 <field name="mRuleActionTargetInitialized">false</field>
225
226 <method name="clearInitialActionIndex">
clearInitialActionIndex
227 <body>
228 <![CDATA[
229 // we should only remove the initialActionIndex after we have been told that
230 // both the rule action type and the rule action target hvae both been built since they both need
231 // this piece of information. This complication arises because both of these child elements are getting
232 // bound asynchronously after the search row has been constructed
233
234 if (this.mActionTypeInitialized && this.mRuleActionTargetInitialized)
235 this.removeAttribute('initialActionIndex');
236 ]]>
237 </body>
238 </method>
239
240 <method name="initWithAction">
241 <parameter name="aFilterAction"/>
initWithAction
242 <body>
243 <![CDATA[
244 var filterActionStr = gFilterActionStrings[aFilterAction.type];
245 document.getAnonymousNodes(document.getAnonymousNodes(this)[0])[0].value = filterActionStr;
246 var actionTarget = document.getAnonymousNodes(this)[1];
247 switch (gFilterActionStrings[aFilterAction.type])
248 {
249 case "movemessage":
250 case "copymessage":
251 SetFolderPickerElement(aFilterAction.targetFolderUri, actionTarget.menulist);
252 break;
253 case "replytomessage":
254 case "forwardmessage":
255 document.getAnonymousNodes(actionTarget)[0].value = aFilterAction.strValue;
256 break;
257 case "labelmessageas":
258 document.getAnonymousNodes(actionTarget)[0].value = aFilterAction.label;
259 break;
260 case "setpriorityto":
261 document.getAnonymousNodes(actionTarget)[0].value = aFilterAction.priority;
262 break;
263 case "setjunkscore":
264 document.getAnonymousNodes(actionTarget)[0].value = aFilterAction.junkScore;
265 break;
266 case "addtagtomessage":
267 document.getAnonymousNodes(actionTarget)[0].value = aFilterAction.strValue;
268 break;
269 default:
270 break;
271 }
272
273 this.mRuleActionTargetInitialized = true;
274 this.clearInitialActionIndex();
275 ]]>
276 </body>
277 </method>
278
279 <method name="validateAction">
validateAction
280 <body>
281 <![CDATA[
282 // returns true if this row represents a valid filter action and false otherwise.
283 // This routine also prompts the user.
284 var filterActionString = this.getAttribute('value');
285 var actionTarget = document.getAnonymousNodes(this)[1];
286 var errorString;
287
288 switch (filterActionString)
289 {
290 case "movemessage":
291 case "copymessage":
292 var msgFolder = actionTarget.uri ? GetMsgFolderFromUri(actionTarget.uri) : null;
293 if (!msgFolder || !msgFolder.canFileMessages)
294 errorString = "mustSelectFolder";
295 break;
296 case "forwardmessage":
297 if (document.getAnonymousNodes(actionTarget)[0].value.length < 3 ||
298 document.getAnonymousNodes(actionTarget)[0].value.indexOf('@') < 1)
299 errorString = "enterValidEmailAddress";
300 break;
301 case "replytomessage":
302 if (!document.getAnonymousNodes(actionTarget)[0].selectedItem)
303 errorString = "pickTemplateToReplyWith";
304 break;
305 default:
306 break;
307 }
308
309 if (errorString && gPromptService)
310 gPromptService.alert(window, null, gFilterBundle.getString(errorString));
311
312 return !errorString;
313 ]]>
314 </body>
315 </method>
316
317 <method name="saveToFilter">
318 <parameter name="aFilter"/>
saveToFilter
319 <body>
320 <![CDATA[
321 // create a new filter action, fill it in, and then append it to the filter
322 var filterAction = aFilter.createAction();
323 var filterActionString = this.getAttribute('value');
324 filterAction.type = gFilterActionStrings.indexOf(filterActionString);
325 var actionTarget = document.getAnonymousNodes(this)[1];
326
327 switch (filterActionString)
328 {
329 case "labelmessageas":
330 filterAction.label = document.getAnonymousNodes(actionTarget)[0].getAttribute("value");
331 break;
332 case "setpriorityto":
333 filterAction.priority = document.getAnonymousNodes(actionTarget)[0].getAttribute("value");
334 break;
335 case "movemessage":
336 case "copymessage":
337 filterAction.targetFolderUri = actionTarget.uri;
338 break;
339 case "setjunkscore":
340 filterAction.junkScore = document.getAnonymousNodes(actionTarget)[0].value;
341 break;
342 case "addtagtomessage":
343 case "replytomessage":
344 case "forwardmessage":
345 filterAction.strValue = document.getAnonymousNodes(actionTarget)[0].value;
346 break;
347 default:
348 break;
349 }
350 aFilter.appendAction(filterAction);
351 ]]>
352 </body>
353 </method>
354
355 <method name="addRow">
addRow
356 <body>
357 <![CDATA[
358 if (this.mListBox.getRowCount() < document.getAnonymousNodes(this)[0].numVisibleActions())
359 {
360 var listItem = document.createElement('listitem');
361 listItem.className = 'ruleaction';
362 this.mListBox.insertBefore(listItem, this.nextSibling);
363 this.mListBox.ensureElementIsVisible(listItem);
364
365 // make sure the first remove button is enabled
366 this.mListBox.getItemAtIndex(0).mRemoveButton.disabled = false;
367 }
368 ]]>
369 </body>
370 </method>
371
372 <method name="removeRow">
removeRow
373 <body>
374 <![CDATA[
375 if (this.mListBox.getRowCount() > 1)
376 this.mListBox.removeChild(this);
377 // if we only have one row left, then disable the remove button for that row
378 this.mListBox.getItemAtIndex(0).mRemoveButton.disabled = this.mListBox.getRowCount() == 1;
379 ]]>
380 </body>
381 </method>
382 </implementation>
383 </binding>
384
385 <binding id="ruleactiontarget-base">
386 <implementation>
constructor
387 <constructor>
388 <![CDATA[
389 if (this.parentNode.hasAttribute('initialActionIndex'))
390 {
391 var actionIndex = this.parentNode.getAttribute('initialActionIndex');
392 var filterAction = gFilter.actionList.QueryElementAt(actionIndex, Components.interfaces.nsIMsgRuleAction);
393 this.parentNode.initWithAction(filterAction);
394 }
395 ]]>
396 </constructor>
397 </implementation>
398 </binding>
399
400 <binding id="ruleactiontarget-tag" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
401 <content>
402 <xul:menulist class="ruleactionitem">
403 <xul:menupopup>
404 </xul:menupopup>
405 </xul:menulist>
406 </content>
407
408 <implementation>
constructor
409 <constructor>
410 <![CDATA[
411 var menuPopup = document.getAnonymousNodes(this)[0].menupopup;
412 var tagService = Components.classes["@mozilla.org/messenger/tagservice;1"]
413 .getService(Components.interfaces.nsIMsgTagService);
414 var tagArray = tagService.getAllTags({});
415 for (var i = 0; i < tagArray.length; ++i)
416 {
417 var taginfo = tagArray[i];
418 var newMenuItem = document.createElement('menuitem');
419 newMenuItem.setAttribute('label', taginfo.tag);
420 newMenuItem.setAttribute('value', taginfo.key);
421 menuPopup.appendChild(newMenuItem);
422 }
423 // propagating a pre-existing hack to make the tag get displayed correctly in the menulist
424 // now that we've changed the tags for each menu list. We need to use the current selectedIndex
425 // (if its defined) to handle the case where we were initialized with a filter action already.
426 var currentItem = document.getAnonymousNodes(this)[0].selectedItem;
427 document.getAnonymousNodes(this)[0].selectedItem = null;
428 document.getAnonymousNodes(this)[0].selectedItem = currentItem;
429 ]]>
430 </constructor>
431 </implementation>
432 </binding>
433
434 <binding id="ruleactiontarget-priority" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
435 <content>
436 <xul:menulist class="ruleactionitem">
437 <xul:menupopup>
438 <xul:menuitem value="6" label="&highestPriorityCmd.label;"/>
439 <xul:menuitem value="5" label="&highPriorityCmd.label;"/>
440 <xul:menuitem value="4" label="&normalPriorityCmd.label;"/>
441 <xul:menuitem value="3" label="&lowPriorityCmd.label;"/>
442 <xul:menuitem value="2" label="&lowestPriorityCmd.label;"/>
443 </xul:menupopup>
444 </xul:menulist>
445 </content>
446 </binding>
447
448 <binding id="ruleactiontarget-junkscore" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
449 <content>
450 <xul:menulist class="ruleactionitem">
451 <xul:menupopup>
452 <xul:menuitem value="100" label="&junk.label;"/>
453 <xul:menuitem value="0" label="¬Junk.label;"/>
454 </xul:menupopup>
455 </xul:menulist>
456 </content>
457 </binding>
458
459 <binding id="ruleactiontarget-replyto" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
460 <content>
461 <xul:menulist class="ruleactionitem">
462 <xul:menupopup>
463 </xul:menupopup>
464 </xul:menulist>
465 </content>
466
467 <implementation>
constructor
468 <constructor>
469 <![CDATA[
470 var accountManager = Components.classes["@mozilla.org/messenger/account-manager;1"].getService(Components.interfaces.nsIMsgAccountManager);
471 var identity = accountManager.getFirstIdentityForServer(gFilterList.folder.server);
472 var rdfService = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService(Components.interfaces.nsIRDFService);
473 var resource = rdfService.GetResource(identity.stationeryFolder);
474 var msgFolder = resource.QueryInterface(Components.interfaces.nsIMsgFolder);
475 var msgWindow = GetFilterEditorMsgWindow();
476 var msgDatabase = msgFolder.getMsgDatabase(msgWindow);
477 var enumerator = msgDatabase.EnumerateMessages();
478 var templateListPopup = document.getAnonymousNodes(this)[0].menupopup;
479
480 if ( enumerator )
481 {
482 while (enumerator.hasMoreElements())
483 {
484 var header = enumerator.getNext();
485 if (header instanceof Components.interfaces.nsIMsgDBHdr)
486 {
487 var msgTemplateUri = msgFolder.URI + "?messageId=" + header.messageId + '&subject=' + header.mime2DecodedSubject;
488 var newItem = document.getAnonymousNodes(this)[0].appendItem(header.mime2DecodedSubject, msgTemplateUri);
489 }
490 }
491 }
492 ]]>
493 </constructor>
494 </implementation>
495 </binding>
496
497 <binding id="ruleactiontarget-forwardto" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
498 <content>
499 <xul:textbox class="ruleactionitem"/>
500 </content>
501 </binding>
502
503 <binding id="ruleactiontarget-folder" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
504 <content>
505 <xul:menulist class="ruleactionitem">
506 <xul:menupopup class="folderTargetPopup" oncommand="SetFolderPickerElement(this.getAttribute('uri'), this.parentNode);"/>
507 </xul:menulist>
508 </content>
509
510 <implementation>
511
constructor
512 <constructor>
513 <![CDATA[
514 if (!this.uri)
515 SetFolderPickerElement(this.menulist.firstChild.tree.builderView.getResourceAtIndex(0).Value, this.menulist);
516 ]]>
517 </constructor>
518
get_uri
519 <property name="uri" readonly="true" onget="return document.getAnonymousNodes(this)[0].getAttribute('uri');"/>
520 <field name="menulist">document.getAnonymousNodes(this)[0]</field>
521 </implementation>
522 </binding>
523 </bindings>