!import
1 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is mozilla.org code.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 * Alec Flett <alecf@netscape.com>
24 * HÃ¥kan Waara <hwaara@chello.se>
25 * Seth Spitzer <sspitzer@netscape.com>
26 * Mark Banner <mark@standard8.demon.co.uk>
27 *
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
39 *
40 * ***** END LICENSE BLOCK ***** */
41
42
43 var gPromptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
44 .getService(Components.interfaces.nsIPromptService);
45
46 // the actual filter that we're editing
47 var gFilter;
48 // cache the key elements we need
49 var gFilterList;
50 var gFilterNameElement;
51 var gFilterBundle;
52 var gPreFillName;
53 var nsMsgSearchScope = Components.interfaces.nsMsgSearchScope;
54 var gPrefBranch;
55 var gMailSession = null;
56 var gFilterActionList;
57
58 var gFilterActionStrings = ["none", "movemessage", "setpriorityto", "deletemessage",
59 "markasread", "ignorethread", "watchthread", "markasflagged",
60 "label", "replytomessage", "forwardmessage", "stopexecution",
61 "deletefrompopserver", "leaveonpopserver", "setjunkscore",
62 "fetchfrompopserver", "copymessage", "addtagtomessage"];
63
64 var nsMsgFilterAction = Components.interfaces.nsMsgFilterAction;
65
66 var gFilterEditorMsgWindow = null;
67
filterEditorOnLoad
68 function filterEditorOnLoad()
69 {
70 initializeSearchWidgets();
71 initializeFilterWidgets();
72
73 gPrefBranch = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch(null);
74 gFilterBundle = document.getElementById("bundle_filter");
75 InitMessageMark();
76 if ("arguments" in window && window.arguments[0])
77 {
78 var args = window.arguments[0];
79
80 if ("filterList" in args)
81 gFilterList = args.filterList;
82
83 if ("filter" in args)
84 {
85 // editing a filter
86 gFilter = window.arguments[0].filter;
87 initializeDialog(gFilter);
88 }
89 else
90 {
91 if (gFilterList)
92 setSearchScope(getScopeFromFilterList(gFilterList));
93 // if doing prefill filter create a new filter and populate it.
94 if ("filterName" in args)
95 {
96 gPreFillName = args.filterName;
97 gFilter = gFilterList.createFilter(gPreFillName);
98 var term = gFilter.createTerm();
99
100 term.attrib = Components.interfaces.nsMsgSearchAttrib.Sender;
101 term.op = Components.interfaces.nsMsgSearchOp.Is;
102
103 var termValue = term.value;
104 termValue.attrib = term.attrib;
105 termValue.str = gPreFillName;
106
107 term.value = termValue;
108
109 gFilter.appendTerm(term);
110
111 // the default action for news filters is Delete
112 // for everything else, it's MoveToFolder
113 var filterAction = gFilter.createAction();
114 filterAction.type = (getScopeFromFilterList(gFilterList) == Components.interfaces.nsMsgSearchScope.newsFilter) ? nsMsgFilterAction.Delete : nsMsgFilterAction.MoveToFolder;
115 gFilter.appendAction(filterAction);
116 initializeDialog(gFilter);
117 }
118 else
119 {
120 // fake the first more button press
121 onMore(null);
122 }
123 }
124 }
125
126 // in the case of a new filter, we may not have an action row yet.
127 ensureActionRow();
128
129 if (!gFilter)
130 {
131 var stub = gFilterBundle.getString("untitledFilterName");
132 var count = 1;
133 var name = stub;
134
135 // Set the default filter name to be "Untitled Filter"
136 while (duplicateFilterNameExists(name))
137 {
138 count++;
139 name = stub + " " + count.toString();
140 }
141 gFilterNameElement.value = name;
142 }
143
144 gFilterNameElement.select();
145 // This call is required on mac and linux. It has no effect under win32. See bug 94800.
146 gFilterNameElement.focus();
147 moveToAlertPosition();
148 }
149
filterEditorOnUnload
150 function filterEditorOnUnload()
151 {
152 if (gMailSession)
153 gMailSession.RemoveFolderListener(gFolderListener);
154 }
155
onEnterInSearchTerm
156 function onEnterInSearchTerm()
157 {
158 // do nothing. onOk() will get called since this is a dialog
159 }
160
onAccept
161 function onAccept()
162 {
163 if (!saveFilter())
164 return false;
165
166 // parent should refresh filter list..
167 // this should REALLY only happen when some criteria changes that
168 // are displayed in the filter dialog, like the filter name
169 window.arguments[0].refresh = true;
170 return true;
171 }
172
173 // the folderListener object
174 var gFolderListener = {
OnItemAdded
175 OnItemAdded: function(parentItem, item) {},
176
OnItemRemoved
177 OnItemRemoved: function(parentItem, item){},
178
OnItemPropertyChanged
179 OnItemPropertyChanged: function(item, property, oldValue, newValue) {},
180
OnItemIntPropertyChanged
181 OnItemIntPropertyChanged: function(item, property, oldValue, newValue) {},
182
OnItemBoolPropertyChanged
183 OnItemBoolPropertyChanged: function(item, property, oldValue, newValue) {},
184
OnItemUnicharPropertyChanged
185 OnItemUnicharPropertyChanged: function(item, property, oldValue, newValue){},
OnItemPropertyFlagChanged
186 OnItemPropertyFlagChanged: function(item, property, oldFlag, newFlag) {},
187
OnItemEvent
188 OnItemEvent: function(folder, event)
189 {
190 var eventType = event.toString();
191
192 if (eventType == "FolderCreateCompleted")
193 {
194 SetFolderPicker(folder.URI, gActionTargetElement.id);
195 SetBusyCursor(window, false);
196 }
197 else if (eventType == "FolderCreateFailed")
198 SetBusyCursor(window, false);
199 }
200 }
201
duplicateFilterNameExists
202 function duplicateFilterNameExists(filterName)
203 {
204 if (gFilterList)
205 for (var i = 0; i < gFilterList.filterCount; i++)
206 if (filterName == gFilterList.getFilterAt(i).filterName)
207 return true;
208 return false;
209 }
210
getScopeFromFilterList
211 function getScopeFromFilterList(filterList)
212 {
213 if (!filterList)
214 {
215 dump("yikes, null filterList\n");
216 return nsMsgSearchScope.offlineMail;
217 }
218 return filterList.folder.server.filterScope;
219 }
220
getScope
221 function getScope(filter)
222 {
223 return getScopeFromFilterList(filter.filterList);
224 }
225
initializeFilterWidgets
226 function initializeFilterWidgets()
227 {
228 gFilterNameElement = document.getElementById("filterName");
229 gFilterActionList = document.getElementById("filterActionList");
230 }
231
initializeDialog
232 function initializeDialog(filter)
233 {
234 gFilterNameElement.value = filter.filterName;
235 var actionList = filter.actionList;
236 var numActions = actionList.Count();
237
238 for (var actionIndex=0; actionIndex < numActions; actionIndex++)
239 {
240 var filterAction = actionList.QueryElementAt(actionIndex, Components.interfaces.nsIMsgRuleAction);
241
242 var newActionRow = document.createElement('listitem');
243 newActionRow.setAttribute('initialActionIndex', actionIndex);
244 newActionRow.className = 'ruleaction';
245 gFilterActionList.appendChild(newActionRow);
246 newActionRow.setAttribute('value', gFilterActionStrings[filterAction.type]);
247 }
248
249 var scope = getScope(filter);
250 setSearchScope(scope);
251 initializeSearchRows(scope, filter.searchTerms);
252 }
253
ensureActionRow
254 function ensureActionRow()
255 {
256 // make sure we have at least one action row visible to the user
257 if (!gFilterActionList.getRowCount())
258 {
259 var newActionRow = document.createElement('listitem');
260 newActionRow.className = 'ruleaction';
261 gFilterActionList.appendChild(newActionRow);
262 newActionRow.mRemoveButton.disabled = true;
263 }
264 }
265
266 // move to overlay
saveFilter
267 function saveFilter()
268 {
269 var isNewFilter;
270 var filterAction;
271 var targetUri;
272
273 var filterName= gFilterNameElement.value;
274 if (!filterName || filterName == "")
275 {
276 if (gPromptService)
277 gPromptService.alert(window, null,
278 gFilterBundle.getString("mustEnterName"));
279 gFilterNameElement.focus();
280 return false;
281 }
282
283 // If we think have a duplicate, then we need to check that if we
284 // have an original filter name (i.e. we are editing a filter), then
285 // we must check that the original is not the current as that is what
286 // the duplicateFilterNameExists function will have picked up.
287 if ((!gFilter || gFilter.filterName != filterName) && duplicateFilterNameExists(filterName))
288 {
289 if (gPromptService)
290 gPromptService.alert(window,gFilterBundle.getString("cannotHaveDuplicateFilterTitle"),
291 gFilterBundle.getString("cannotHaveDuplicateFilterMessage"));
292 return false;
293 }
294
295 // before we go any further, validate each specified filter action, abort the save
296 // if any of the actions is invalid...
297 for (var index = 0; index < gFilterActionList.getRowCount(); index++)
298 {
299 var listItem = gFilterActionList.getItemAtIndex(index);
300 if (!listItem.validateAction())
301 return false;
302 }
303
304 // if we made it here, all of the actions are valid, so go ahead and save the filter
305
306 if (!gFilter)
307 {
308 gFilter = gFilterList.createFilter(filterName);
309 isNewFilter = true;
310 gFilter.enabled=true;
311 }
312 else
313 {
314 gFilter.filterName = filterName;
315 //Prefilter is treated as a new filter.
316 if (gPreFillName)
317 {
318 isNewFilter = true;
319 gFilter.enabled=true;
320 }
321 else
322 isNewFilter = false;
323
324 gFilter.clearActionList();
325 }
326
327 // add each filteraction to the filter
328 for (index = 0; index < gFilterActionList.getRowCount(); index++)
329 gFilterActionList.getItemAtIndex(index).saveToFilter(gFilter);
330
331 if (getScope(gFilter) == Components.interfaces.nsMsgSearchScope.newsFilter)
332 gFilter.filterType = Components.interfaces.nsMsgFilterType.NewsRule;
333 else
334 gFilter.filterType = Components.interfaces.nsMsgFilterType.InboxRule;
335
336 saveSearchTerms(gFilter.searchTerms, gFilter);
337
338 if (isNewFilter)
339 {
340 // new filter - insert into gFilterList
341 gFilterList.insertFilterAt(0, gFilter);
342 }
343
344 // success!
345 return true;
346 }
347
GetFirstSelectedMsgFolder
348 function GetFirstSelectedMsgFolder()
349 {
350 var selectedFolder = gActionTargetElement.getAttribute("uri");
351 if (!selectedFolder)
352 return null;
353
354 var msgFolder = GetMsgFolderFromUri(selectedFolder, true);
355 return msgFolder;
356 }
357
SearchNewFolderOkCallback
358 function SearchNewFolderOkCallback(name, uri)
359 {
360 var msgFolder = GetMsgFolderFromUri(uri, true);
361 var imapFolder = null;
362 try
363 {
364 imapFolder = msgFolder.QueryInterface(Components.interfaces.nsIMsgImapMailFolder);
365 }
366 catch(ex) {}
367 var mailSessionContractID = "@mozilla.org/messenger/services/session;1";
368 if (imapFolder) //imapFolder creation is asynchronous.
369 {
370 if (!gMailSession)
371 gMailSession = Components.classes[mailSessionContractID].getService(Components.interfaces.nsIMsgMailSession);
372 try
373 {
374 var nsIFolderListener = Components.interfaces.nsIFolderListener;
375 var notifyFlags = nsIFolderListener.event;
376 gMailSession.AddFolderListener(gFolderListener, notifyFlags);
377 }
378 catch (ex)
379 {
380 dump("Error adding to session: " +ex + "\n");
381 }
382 }
383
384 var msgWindow = GetFilterEditorMsgWindow();
385
386 if (imapFolder)
387 SetBusyCursor(window, true);
388
389 msgFolder.createSubfolder(name, msgWindow);
390
391 if (!imapFolder)
392 {
393 var curFolder = uri+"/"+encodeURIComponent(name);
394 SetFolderPicker(curFolder, gActionTargetElement.id);
395 }
396 }
397
398 function UpdateAfterCustomHeaderChange()
399 {
400 updateSearchAttributes();
401 }
402
403 //if you use msgWindow, please make sure that destructor gets called when you close the "window"
GetFilterEditorMsgWindow
404 function GetFilterEditorMsgWindow()
405 {
406 if (!gFilterEditorMsgWindow)
407 {
408 var msgWindowContractID = "@mozilla.org/messenger/msgwindow;1";
409 var nsIMsgWindow = Components.interfaces.nsIMsgWindow;
410 gFilterEditorMsgWindow = Components.classes[msgWindowContractID].createInstance(nsIMsgWindow);
411 gFilterEditorMsgWindow.domWindow = window;
412 gFilterEditorMsgWindow.rootDocShell.appType = Components.interfaces.nsIDocShell.APP_TYPE_MAIL;
413 }
414 return gFilterEditorMsgWindow;
415 }
416
SetBusyCursor
417 function SetBusyCursor(window, enable)
418 {
419 // setCursor() is only available for chrome windows.
420 // However one of our frames is the start page which
421 // is a non-chrome window, so check if this window has a
422 // setCursor method
423 if ("setCursor" in window)
424 {
425 if (enable)
426 window.setCursor("wait");
427 else
428 window.setCursor("auto");
429 }
430 }
431
doHelpButton
432 function doHelpButton()
433 {
434 openHelp("mail-filters");
435 }