add support for unlimited number of tags per profile and per message, replace labels with tags, sr=mscott, sspitzer 114656

This commit is contained in:
bienvenu%nventure.com 2006-06-09 14:26:31 +00:00
Родитель a03e79b499
Коммит 44aee4259f
74 изменённых файлов: 1883 добавлений и 552 удалений

Просмотреть файл

@ -214,12 +214,6 @@ var DefaultController =
case "cmd_applyFilters": case "cmd_applyFilters":
case "cmd_runJunkControls": case "cmd_runJunkControls":
case "cmd_deleteJunk": case "cmd_deleteJunk":
case "cmd_label0":
case "cmd_label1":
case "cmd_label2":
case "cmd_label3":
case "cmd_label4":
case "cmd_label5":
case "button_file": case "button_file":
case "cmd_file": case "cmd_file":
case "cmd_emptyTrash": case "cmd_emptyTrash":
@ -346,12 +340,6 @@ var DefaultController =
case "button_mark": case "button_mark":
case "cmd_markAsRead": case "cmd_markAsRead":
case "cmd_markThreadAsRead": case "cmd_markThreadAsRead":
case "cmd_label0":
case "cmd_label1":
case "cmd_label2":
case "cmd_label3":
case "cmd_label4":
case "cmd_label5":
return GetNumSelectedMessages() > 0; return GetNumSelectedMessages() > 0;
case "button_previous": case "button_previous":
case "button_next": case "button_next":
@ -646,24 +634,6 @@ var DefaultController =
return; return;
case "cmd_deleteJunk": case "cmd_deleteJunk":
deleteJunkInFolder(); deleteJunkInFolder();
return;
case "cmd_label0":
gDBView.doCommand(nsMsgViewCommandType.label0);
return;
case "cmd_label1":
gDBView.doCommand(nsMsgViewCommandType.label1);
return;
case "cmd_label2":
gDBView.doCommand(nsMsgViewCommandType.label2);
return;
case "cmd_label3":
gDBView.doCommand(nsMsgViewCommandType.label3);
return;
case "cmd_label4":
gDBView.doCommand(nsMsgViewCommandType.label4);
return;
case "cmd_label5":
gDBView.doCommand(nsMsgViewCommandType.label5);
return; return;
case "cmd_emptyTrash": case "cmd_emptyTrash":
MsgEmptyTrash(); MsgEmptyTrash();

Просмотреть файл

@ -26,7 +26,7 @@
# Håkan Waara <hwaara@chello.se> # Håkan Waara <hwaara@chello.se>
# Jan Varga <varga@nixcorp.com> # Jan Varga <varga@nixcorp.com>
# Seth Spitzer <sspitzer@netscape.com> # Seth Spitzer <sspitzer@netscape.com>
# David Bienvenu <bienvenu@netscape.com> # David Bienvenu <bienvenu@nventure.com>
# #
# Alternatively, the contents of this file may be used under the terms of # Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or # either the GNU General Public License Version 2 or later (the "GPL"), or
@ -554,58 +554,92 @@ function SetMenuItemLabel(menuItemId, customLabel)
menuItem.setAttribute('label', customLabel); menuItem.setAttribute('label', customLabel);
} }
function InitMessageLabel(menuType) function TagCurMessage(key)
{ {
var color; // ###need to do all selected messsages
var msgHdr = gDBView.hdrForFirstSelectedMessage;
try var messages = Components.classes["@mozilla.org/supports-array;1"].createInstance(Components.interfaces.nsISupportsArray);
{ messages.AppendElement(msgHdr);
var isChecked = true; msgHdr.folder.addKeywordToMessages(messages, key);
var checkedLabel = gDBView.hdrForFirstSelectedMessage.label;
}
catch(ex)
{
isChecked = false;
} }
for (var label = 0; label <= 5; label++) function UnTagCurMessage(key)
{ {
try // ###need to do all selected messsages
{ var msgHdr = gDBView.hdrForFirstSelectedMessage;
var prefString = gPrefBranch.getComplexValue("mailnews.labels.description." + label, var messages = Components.classes["@mozilla.org/supports-array;1"].createInstance(Components.interfaces.nsISupportsArray);
Components.interfaces.nsIPrefLocalizedString); messages.AppendElement(msgHdr);
var formattedPrefString = gMessengerBundle.getFormattedString("labelMenuItemFormat" + label, msgHdr.folder.removeKeywordFromMessages(messages, key);
[prefString], 1); }
var menuItemId = menuType + "-labelMenuItem" + label;
var menuItem = document.getElementById(menuItemId);
SetMenuItemLabel(menuItemId, formattedPrefString);
if (isChecked && label == checkedLabel)
menuItem.setAttribute("checked", "true");
else
menuItem.setAttribute("checked", "false");
// commented out for now until UE decides on how to show the Labels menu items. function AddTag()
// This code will color either the text or background for the Labels menu items.
/*****
if (label != 0)
{ {
color = prefBranch.getCharPref("mailnews.labels.color." + label); var args = {result: "", okCallback: AddTagCallback};
// this colors the text of the menuitem only. var dialog = window.openDialog(
//menuItem.setAttribute("style", ("color: " + color)); "chrome://messenger/content/newTagDialog.xul",
"",
"chrome,titlebar,modal",
args);
}
// this colors the background of the menuitem and function AddTagCallback(name, color)
// when selected, text becomes white.
//menuItem.setAttribute("style", ("color: #FFFFFF"));
//menuItem.setAttribute("style", ("background-color: " + color));
}
****/
}
catch(ex)
{ {
var tagService = Components.classes["@mozilla.org/messenger/tagservice;1"].getService(Components.interfaces.nsIMsgTagService);
tagService.addTag(name, color);
TagCurMessage(name);
return true;
}
function InitMessageTags(menuType)
{
var tagService = Components.classes["@mozilla.org/messenger/tagservice;1"].getService(Components.interfaces.nsIMsgTagService);
var allTags = tagService.tagEnumerator;
var allKeys = tagService.keyEnumerator;
// remove any existing entries...
var menuItemId = menuType + "-tagpopup";
var menupopupNode = document.getElementById(menuItemId);
for (var i = menupopupNode.childNodes.length - 1; i >= 0; --i)
menupopupNode.removeChild(menupopupNode.childNodes[i]);
// now rebuild the list
var msgHdr = gDBView.hdrForFirstSelectedMessage;
var curKeys = msgHdr.getStringProperty("keywords");
var newMenuItem;
var curMsgHdrKeyArray = curKeys.split(" ");
while (allTags.hasMore())
{
var tag = allTags.getNext();
var key = allKeys.getNext();
// TODO we want to either remove or "check" the tags that already exist
newMenuItem = document.createElement('menuitem');
newMenuItem.setAttribute('label', tag);
newMenuItem.setAttribute('value', key);
newMenuItem.setAttribute('type', 'checkbox');
var keySet = false;
for ( var index = 0; index < curMsgHdrKeyArray.length; index++ )
{
if (key == curMsgHdrKeyArray[index])
{
keySet = true;
break;
} }
} }
document.commandDispatcher.updateCommands('create-menu-label'); // if we already have this tag, we should change the command to "UnTag"
var command = ((keySet) ? "Un" : "") + "TagCurMessage(" + "'" + key + "');";
newMenuItem.setAttribute('oncommand', command);
newMenuItem.setAttribute('checked', keySet);
menupopupNode.appendChild(newMenuItem);
}
var menuseparator = document.createElement('menuseparator');
menupopupNode.appendChild(menuseparator);
newMenuItem = document.createElement('menuitem');
newMenuItem.setAttribute('label', gMessengerBundle.getString("newTag"));
newMenuItem.setAttribute('oncommand', "AddTag()");
menupopupNode.appendChild(newMenuItem);
} }
function InitMessageMark() function InitMessageMark()

Просмотреть файл

@ -606,45 +606,8 @@
</rule> </rule>
</template> </template>
</menu> </menu>
<menu id="threadPaneContext-labels" label="&labelMenu.label;" accesskey="&labelMenu.accesskey;"> <menu id="threadPaneContext-tags" label="&tagMenu.label;" accesskey="&tagMenu.accesskey;">
<menupopup onpopupshowing="InitMessageLabel('threadPaneContext')"> <menupopup id="threadPaneContext-tagpopup" onpopupshowing="InitMessageTags('threadPaneContext')">
<menuitem
id="threadPaneContext-labelMenuItem0"
type="radio"
checked="false"
accesskey="&labelCmd0.accesskey;"
observes="cmd_label0"/>
<menuseparator/>
<menuitem
id="threadPaneContext-labelMenuItem1"
type="radio"
checked="false"
accesskey="&labelCmd1.accesskey;"
observes="cmd_label1"/>
<menuitem
id="threadPaneContext-labelMenuItem2"
type="radio"
checked="false"
accesskey="&labelCmd2.accesskey;"
observes="cmd_label2"/>
<menuitem
id="threadPaneContext-labelMenuItem3"
type="radio"
checked="false"
accesskey="&labelCmd3.accesskey;"
observes="cmd_label3"/>
<menuitem
id="threadPaneContext-labelMenuItem4"
type="radio"
checked="false"
accesskey="&labelCmd4.accesskey;"
observes="cmd_label4"/>
<menuitem
id="threadPaneContext-labelMenuItem5"
type="radio"
checked="false"
accesskey="&labelCmd5.accesskey;"
observes="cmd_label5"/>
</menupopup> </menupopup>
</menu> </menu>
<menu id="threadPaneContext-mark" label="&markMenu.label;" accesskey="&markMenu.accesskey;"> <menu id="threadPaneContext-mark" label="&markMenu.label;" accesskey="&markMenu.accesskey;">
@ -948,46 +911,9 @@
</rule> </rule>
</template> </template>
</menu> </menu>
<menuseparator id="messagePaneContext-sep-labels-1"/> <menuseparator id="messagePaneContext-sep-tags-1"/>
<menu id="messagePaneContext-labels" label="&labelMenu.label;" accesskey="&labelMenu.accesskey;"> <menu id="messagePaneContext-tags" label="&tagMenu.label;" accesskey="&tagMenu.accesskey;">
<menupopup onpopupshowing="InitMessageLabel('messagePaneContext')"> <menupopup id="messagePaneContext-tagpopup" onpopupshowing="InitMessageTags('messagePaneContext')">
<menuitem
id="messagePaneContext-labelMenuItem0"
type="radio"
checked="false"
accesskey="&labelCmd0.accesskey;"
observes="cmd_label0"/>
<menuseparator/>
<menuitem
id="messagePaneContext-labelMenuItem1"
type="radio"
checked="false"
accesskey="&labelCmd1.accesskey;"
observes="cmd_label1"/>
<menuitem
id="messagePaneContext-labelMenuItem2"
type="radio"
checked="false"
accesskey="&labelCmd2.accesskey;"
observes="cmd_label2"/>
<menuitem
id="messagePaneContext-labelMenuItem3"
type="radio"
checked="false"
accesskey="&labelCmd3.accesskey;"
observes="cmd_label3"/>
<menuitem
id="messagePaneContext-labelMenuItem4"
type="radio"
checked="false"
accesskey="&labelCmd4.accesskey;"
observes="cmd_label4"/>
<menuitem
id="messagePaneContext-labelMenuItem5"
type="radio"
checked="false"
accesskey="&labelCmd5.accesskey;"
observes="cmd_label5"/>
</menupopup> </menupopup>
</menu> </menu>
<menu id="messagePaneContext-mark" label="&markMenu.label;" accesskey="&markMenu.accesskey;"> <menu id="messagePaneContext-mark" label="&markMenu.label;" accesskey="&markMenu.accesskey;">
@ -1613,63 +1539,26 @@
</template> </template>
</menu> </menu>
<menu id="labelMenu" label="&labelMenu.label;" accesskey="&labelMenu.accesskey;"> <menu id="messagePaneContext-tags" label="&tagMenu.label;" accesskey="&tagMenu.accesskey;">
<menupopup id="menuPopup-labels" onpopupshowing="InitMessageLabel('menuPopup')"> <menupopup id="messagePaneContext-tagpopup" onpopupshowing="InitMessageTags('messagePaneContext')">
<menuitem
id="menuPopup-labelMenuItem0"
type="radio"
checked="false"
accesskey="&labelCmd0.accesskey;"
observes="cmd_label0"/>
<menuseparator/>
<menuitem
id="menuPopup-labelMenuItem1"
type="radio"
checked="false"
accesskey="&labelCmd1.accesskey;"
observes="cmd_label1"/>
<menuitem
id="menuPopup-labelMenuItem2"
type="radio"
checked="false"
accesskey="&labelCmd2.accesskey;"
observes="cmd_label2"/>
<menuitem
id="menuPopup-labelMenuItem3"
type="radio"
checked="false"
accesskey="&labelCmd3.accesskey;"
observes="cmd_label3"/>
<menuitem
id="menuPopup-labelMenuItem4"
type="radio"
checked="false"
accesskey="&labelCmd4.accesskey;"
observes="cmd_label4"/>
<menuitem
id="menuPopup-labelMenuItem5"
type="radio"
checked="false"
accesskey="&labelCmd5.accesskey;"
observes="cmd_label5"/>
</menupopup> </menupopup>
</menu> </menu>
<menu id="markMenu" label="&markMenu.label;" accesskey="&markMenu.accesskey;"> <menu id="markMenu" label="&markMenu.label;" accesskey="&markMenu.accesskey;">
<menupopup onpopupshowing="InitMessageMark()"> <menupopup onpopupshowing="InitMessageMark()">
<menuitem type="checkbox" id="markReadMenuItem" label="&markAsReadCmd.label;" accesskey="&markAsReadCmd.accesskey;" observes="cmd_markAsRead" <menuitem type="checkbox" id="markReadMenuItem" label="&markAsReadCmd.label;" accesskey="&markAsReadCmd.accesskey;" observes="cmd_markAsRead"
#ifndef XP_MACOSX ifndef="" XP_MACOSX=""
key="key_toggleRead" key="key_toggleRead"
#endif endif=""
/> />
<menuitem label="&markThreadAsReadCmd.label;" accesskey="&markThreadAsReadCmd.accesskey;" observes="cmd_markThreadAsRead" <menuitem label="&markThreadAsReadCmd.label;" accesskey="&markThreadAsReadCmd.accesskey;" observes="cmd_markThreadAsRead"
#ifndef XP_MACOSX ifndef="" XP_MACOSX=""
key="key_markThreadAsRead" key="key_markThreadAsRead"
#endif endif=""
/> />
<menuitem label="&markReadByDateCmd.label;" accesskey="&markReadByDateCmd.accesskey;" command="cmd_markReadByDate" <menuitem label="&markReadByDateCmd.label;" accesskey="&markReadByDateCmd.accesskey;" command="cmd_markReadByDate"
#ifndef XP_MACOSX ifndef="" XP_MACOSX=""
key="key_markReadByDate" key="key_markReadByDate"
#endif endif=""
/> />
<menuitem label="&markAllReadCmd.label;" key="key_markAllRead" accesskey="&markAllReadCmd.accesskey;" observes="cmd_markAllRead"/> <menuitem label="&markAllReadCmd.label;" key="key_markAllRead" accesskey="&markAllReadCmd.accesskey;" observes="cmd_markAllRead"/>
<menuseparator/> <menuseparator/>
@ -1678,17 +1567,17 @@
label="&markFlaggedCmd.label;" label="&markFlaggedCmd.label;"
accesskey="&markFlaggedCmd.accesskey;" accesskey="&markFlaggedCmd.accesskey;"
observes="cmd_markAsFlagged" observes="cmd_markAsFlagged"
#ifndef XP_MACOSX ifndef="" XP_MACOSX=""
key="key_toggleFlagged" key="key_toggleFlagged"
#endif endif=""
/> />
<menuseparator/> <menuseparator/>
<menuitem label="&markAsJunkCmd.label;" <menuitem label="&markAsJunkCmd.label;"
accesskey="&markAsJunkCmd.accesskey;" accesskey="&markAsJunkCmd.accesskey;"
observes="cmd_markAsJunk" observes="cmd_markAsJunk"
#ifndef XP_MACOSX ifndef="" XP_MACOSX=""
key="key_markJunk" key="key_markJunk"
#endif endif=""
/> />
<menuitem label="&markAsNotJunkCmd.label;" <menuitem label="&markAsNotJunkCmd.label;"
key="key_markNotJunk" key="key_markNotJunk"

Просмотреть файл

@ -1053,24 +1053,6 @@ var MessageWindowController =
case "cmd_recalculateJunkScore": case "cmd_recalculateJunkScore":
analyzeMessagesForJunk(); analyzeMessagesForJunk();
return; return;
case "cmd_label0":
gDBView.doCommand(nsMsgViewCommandType.label0);
return;
case "cmd_label1":
gDBView.doCommand(nsMsgViewCommandType.label1);
return;
case "cmd_label2":
gDBView.doCommand(nsMsgViewCommandType.label2);
return;
case "cmd_label3":
gDBView.doCommand(nsMsgViewCommandType.label3);
return;
case "cmd_label4":
gDBView.doCommand(nsMsgViewCommandType.label4);
return;
case "cmd_label5":
gDBView.doCommand(nsMsgViewCommandType.label5);
return;
case "cmd_downloadFlagged": case "cmd_downloadFlagged":
MsgDownloadFlagged(); MsgDownloadFlagged();
return; return;

Просмотреть файл

@ -112,8 +112,8 @@ searchterm {
-moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-folder"); -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-folder");
} }
.ruleactiontarget[type="labelmessageas"] { .ruleactiontarget[type="addtagtomessage"] {
-moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-label"); -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-tag");
} }
.ruleactiontarget[type="setpriorityto"] { .ruleactiontarget[type="setpriorityto"] {

Просмотреть файл

@ -68,6 +68,8 @@ messenger.jar:
content/messenger/shareglue.js (/mailnews/base/resources/content/shareglue.js) content/messenger/shareglue.js (/mailnews/base/resources/content/shareglue.js)
content/messenger/newFolderDialog.xul (/mailnews/base/resources/content/newFolderDialog.xul) content/messenger/newFolderDialog.xul (/mailnews/base/resources/content/newFolderDialog.xul)
content/messenger/newFolderDialog.js (/mailnews/base/resources/content/newFolderDialog.js) content/messenger/newFolderDialog.js (/mailnews/base/resources/content/newFolderDialog.js)
content/messenger/newTagDialog.xul (/mailnews/base/resources/content/newTagDialog.xul)
content/messenger/newTagDialog.js (/mailnews/base/resources/content/newTagDialog.js)
content/messenger/msgViewNavigation.js (/mailnews/base/resources/content/msgViewNavigation.js) content/messenger/msgViewNavigation.js (/mailnews/base/resources/content/msgViewNavigation.js)
content/messenger/msgAccountCentral.xul (/mailnews/base/resources/content/msgAccountCentral.xul) content/messenger/msgAccountCentral.xul (/mailnews/base/resources/content/msgAccountCentral.xul)
content/messenger/msgAccountCentral.js (/mailnews/base/resources/content/msgAccountCentral.js) content/messenger/msgAccountCentral.js (/mailnews/base/resources/content/msgAccountCentral.js)

Просмотреть файл

@ -389,6 +389,7 @@ FeedItem.prototype =
openingLine + openingLine +
'X-Mozilla-Status: 0000\n' + 'X-Mozilla-Status: 0000\n' +
'X-Mozilla-Status2: 00000000\n' + 'X-Mozilla-Status2: 00000000\n' +
'X-Mozilla-Keys: \n' +
'Date: ' + this.mDate + '\n' + 'Date: ' + this.mDate + '\n' +
'Message-Id: <' + this.messageID + '>\n' + 'Message-Id: <' + this.messageID + '>\n' +
'From: ' + this.author + '\n' + 'From: ' + this.author + '\n' +

Просмотреть файл

@ -43,6 +43,7 @@
<!ENTITY markMessageFlagged.label "Mark As Flagged"> <!ENTITY markMessageFlagged.label "Mark As Flagged">
<!ENTITY labelMessage.label "Label Message As"> <!ENTITY labelMessage.label "Label Message As">
<!ENTITY setPriority.label "Set Priority to"> <!ENTITY setPriority.label "Set Priority to">
<!ENTITY addTag.label "Tag Message">
<!ENTITY setJunkScore.label "Set Junk Status to"> <!ENTITY setJunkScore.label "Set Junk Status to">
<!ENTITY deleteMessage.label "Delete Message"> <!ENTITY deleteMessage.label "Delete Message">
<!ENTITY deleteFromPOP.label "Delete From POP Server"> <!ENTITY deleteFromPOP.label "Delete From POP Server">

Просмотреть файл

@ -330,6 +330,8 @@
<!ENTITY fileHereMenu.accesskey "F"> <!ENTITY fileHereMenu.accesskey "F">
<!ENTITY copyHereMenu.label "Copy Here"> <!ENTITY copyHereMenu.label "Copy Here">
<!ENTITY copyHereMenu.accesskey "C"> <!ENTITY copyHereMenu.accesskey "C">
<!ENTITY tagMenu.label "Tag">
<!ENTITY tagMenu.accesskey "g">
<!ENTITY labelMenu.label "Label"> <!ENTITY labelMenu.label "Label">
<!ENTITY labelMenu.accesskey "L"> <!ENTITY labelMenu.accesskey "L">
<!ENTITY labelCmd0.accesskey "0"> <!ENTITY labelCmd0.accesskey "0">

Просмотреть файл

@ -48,6 +48,7 @@ newSubfolderMenuItem=Subfolder...
newFolder=New Folder... newFolder=New Folder...
newSubfolder=New Subfolder... newSubfolder=New Subfolder...
folderProperties=Folder Properties folderProperties=Folder Properties
newTag=New Tag...
getNextNMessages=Get Next %S News Messages getNextNMessages=Get Next %S News Messages
advanceNextPrompt=Advance to next unread message in %S? advanceNextPrompt=Advance to next unread message in %S?
titleNewsPreHost=on titleNewsPreHost=on
@ -234,6 +235,9 @@ junk=Junk
# for the has attachment picker in search and mail views # for the has attachment picker in search and mail views
hasAttachments=Has Attachments hasAttachments=Has Attachments
# for the Tag picker in search and mail views.
tag=Tags
# mailnews.js # mailnews.js
mailnews.send_default_charset=ISO-8859-1 mailnews.send_default_charset=ISO-8859-1
mailnews.view_default_charset=ISO-8859-1 mailnews.view_default_charset=ISO-8859-1
@ -361,6 +365,9 @@ passwordTitle=Mail Server Password Required
openWindowWarningTitle=Confirm openWindowWarningTitle=Confirm
openWindowWarningText=Opening %S messages may be slow. Continue? openWindowWarningText=Opening %S messages may be slow. Continue?
# for warning the user that a tag they're trying to create already exists
tagExists=A tag with that name already exists.
# for the virtual folder list dialog title # for the virtual folder list dialog title
# %S is the name of the saved search folder # %S is the name of the saved search folder
editVirtualFolderPropertiesTitle=Edit Saved Search Properties for %S editVirtualFolderPropertiesTitle=Edit Saved Search Properties for %S

Просмотреть файл

@ -0,0 +1,38 @@
<!-- ***** BEGIN LICENSE BLOCK *****
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is new tag dialog
-
- The Initial Developer of the Original Code is
- The Mozilla Corporation.
- Portions created by the Initial Developer are Copyright (C) 2006
- the Initial Developer. All Rights Reserved.
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the GPL or the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of either the GPL or the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the LGPL or the GPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of any one of the MPL, the GPL or the LGPL.
-
- ***** END LICENSE BLOCK ***** -->
<!-- Labels -->
<!ENTITY newTagDialog.title "New Tag">
<!ENTITY name.label "Tag:">
<!ENTITY name.accesskey "T">

Просмотреть файл

@ -15,7 +15,7 @@
12=Folder Info 12=Folder Info
13=Size 13=Size
14=AnyText 14=AnyText
15=Keywords 15=Tags
# for AB and LDAP # for AB and LDAP
16=Any Name 16=Any Name
17=Display Name 17=Display Name
@ -50,5 +50,5 @@
46=Attachment Status 46=Attachment Status
47=Junk Status 47=Junk Status
48=Label 48=Label
49=Customize
# don't use above 49 # don't use above 49
49=Customize...

Просмотреть файл

@ -33,6 +33,7 @@
locale/@AB_CD@/messenger/threadpane.dtd (%chrome/messenger/threadpane.dtd) locale/@AB_CD@/messenger/threadpane.dtd (%chrome/messenger/threadpane.dtd)
locale/@AB_CD@/messenger/folderpane.dtd (%chrome/messenger/folderpane.dtd) locale/@AB_CD@/messenger/folderpane.dtd (%chrome/messenger/folderpane.dtd)
locale/@AB_CD@/messenger/newFolderDialog.dtd (%chrome/messenger/newFolderDialog.dtd) locale/@AB_CD@/messenger/newFolderDialog.dtd (%chrome/messenger/newFolderDialog.dtd)
locale/@AB_CD@/messenger/newTagDialog.dtd (%chrome/messenger/newTagDialog.dtd)
locale/@AB_CD@/messenger/renameFolderDialog.dtd (%chrome/messenger/renameFolderDialog.dtd) locale/@AB_CD@/messenger/renameFolderDialog.dtd (%chrome/messenger/renameFolderDialog.dtd)
locale/@AB_CD@/messenger/folderProps.dtd (%chrome/messenger/folderProps.dtd) locale/@AB_CD@/messenger/folderProps.dtd (%chrome/messenger/folderProps.dtd)
locale/@AB_CD@/messenger/subscribe.dtd (%chrome/messenger/subscribe.dtd) locale/@AB_CD@/messenger/subscribe.dtd (%chrome/messenger/subscribe.dtd)

Просмотреть файл

@ -70,6 +70,7 @@ XPIDLSRCS = \
nsIMsgMailSession.idl \ nsIMsgMailSession.idl \
nsIMsgMessageService.idl \ nsIMsgMessageService.idl \
nsIMsgSignature.idl \ nsIMsgSignature.idl \
nsIMsgTagService.idl \
nsIMsgThread.idl \ nsIMsgThread.idl \
nsIUrlListener.idl \ nsIUrlListener.idl \
nsIUrlListenerManager.idl \ nsIUrlListenerManager.idl \

Просмотреть файл

@ -70,7 +70,7 @@ typedef long nsMsgBiffState;
// enumerated type for determining if a message has been replied to, forwarded, etc. // enumerated type for determining if a message has been replied to, forwarded, etc.
typedef long nsMsgDispositionState; typedef long nsMsgDispositionState;
[scriptable, uuid(28424d1c-db6f-4ac5-bc5d-418dd336120b)] [scriptable, uuid(5711cb97-42ad-466d-9e8f-48b84a8b76a2)]
interface nsIMsgFolder : nsICollection { interface nsIMsgFolder : nsICollection {
const nsMsgBiffState nsMsgBiffState_NewMail = 0; // User has new mail waiting. const nsMsgBiffState nsMsgBiffState_NewMail = 0; // User has new mail waiting.
@ -497,4 +497,9 @@ const nsMsgBiffState nsMsgBiffState_Unknown = 2; // We dunno whether there is ne
in unsigned long aNumKeys, in boolean aLocalOnly, in unsigned long aNumKeys, in boolean aLocalOnly,
in nsIUrlListener aUrlListener, out boolean aAsyncResults); in nsIUrlListener aUrlListener, out boolean aAsyncResults);
// used to set/clear tags - we could have a single method to setKeywords which
// would figure out the diffs, but these methods might be more convenient.
void addKeywordToMessages(in nsISupportsArray aMessages, in string aKeyword);
void removeKeywordFromMessages(in nsISupportsArray aMessages, in string aKeyword);
}; };

Просмотреть файл

@ -0,0 +1,72 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* The Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* David Bienvenu <bienvenu@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
#include "nsIStringEnumerator.idl"
/*
* Keys are the internal representation of tags, and use a limited range of
* characters, basically the characters allowed in imap keywords, which are
* alphanumeric characters, but don't include spaces. Keys are stored on
* the imap server, in local mail messages, and in summary files.
*
* Tags are the user visible representation of keys, and are full unicode
* strings. Tags should allow any unicode character.
*
* This service will do the mapping between keys and tags. When a tag
* is added, we'll need to "compute" the corresponding key to use. This
* will probably entail replacing illegal ascii characters (' ', '/', etc)
* with '_' and then converting to imap mod utf7. We'll then need to make
* sure that no other keyword has the same value since that algorithm
* doesn't guarantee a unique mapping.
*
*/
[scriptable, uuid(ad04db53-cfcc-47eb-b409-b24b3a0b6130)]
interface nsIMsgTagService : nsISupports {
AString getTagForKey(in ACString key); // look up the tag for a key.
void setTagForKey(in ACString key, in AString tag); // this can be used to "rename" a tag
ACString getKeyForTag(in AString tag); // get the key representation of a given tag
void addTagForKey(in ACString key, in AString tag, in ACString color);
void addTag(in AString tag, in ACString color);
ACString getColorForKey(in ACString key);
void deleteTag(in AString tag);
// we need some way to enumerate all tags. Or return a list of all tags.
readonly attribute nsIStringEnumerator tagEnumerator;
readonly attribute nsIUTF8StringEnumerator keyEnumerator;
};

Просмотреть файл

@ -516,6 +516,16 @@
{0x9c, 0xff, 0x2a, 0x66, 0x33, 0x33, 0x3b, 0x2b }} {0x9c, 0xff, 0x2a, 0x66, 0x33, 0x33, 0x3b, 0x2b }}
// //
// nsMsgTagService
//
#define NS_MSGTAGSERVICE_CONTRACTID \
"@mozilla.org/messenger/tagservice;1"
#define NS_MSGTAGSERVICE_CID \
{ /* ad04db53-cfcc-47eb-b409-b24b3a0b6130 */ \
0xad04db53, 0xcfcc, 0x47eb, \
{ 0xb4, 0x09, 0xb2, 0x4b, 0x3a, 0x0b, 0x61, 0x30}}
//
// nsMessengerOSIntegration // nsMessengerOSIntegration
// //
#define NS_MESSENGEROSINTEGRATION_CONTRACTID \ #define NS_MESSENGEROSINTEGRATION_CONTRACTID \

Просмотреть файл

@ -64,4 +64,7 @@
/* Provide a common means of detecting empty lines in a message. i.e. to detect the end of headers among other things...*/ /* Provide a common means of detecting empty lines in a message. i.e. to detect the end of headers among other things...*/
#define EMPTY_MESSAGE_LINE(buf) (buf[0] == nsCRT::CR || buf[0] == nsCRT::LF || buf[0] == '\0') #define EMPTY_MESSAGE_LINE(buf) (buf[0] == nsCRT::CR || buf[0] == nsCRT::LF || buf[0] == '\0')
/* blank filled header to store keyword/tags in the mailbox */
#define X_MOZILLA_KEYWORDS HEADER_X_MOZILLA_KEYWORDS ": " MSG_LINEBREAK
#endif #endif

Просмотреть файл

@ -1235,7 +1235,8 @@
<!-- searchvalue - a widget which dynamically changes its user interface <!-- searchvalue - a widget which dynamically changes its user interface
depending on what type of data it's supposed to be showing depending on what type of data it's supposed to be showing
currently handles arbitrary text entry, and menulists for currently handles arbitrary text entry, and menulists for
priority, status, junk status, hasAttachment status, and addressbook priority, status, junk status, tags, hasAttachment status,
and addressbook
--> -->
<binding id="searchvalue" name="searchValue"> <binding id="searchvalue" name="searchValue">
<content> <content>
@ -1277,12 +1278,6 @@
</xul:menulist> </xul:menulist>
<xul:menulist flex="1" class="search-value-menulist"> <xul:menulist flex="1" class="search-value-menulist">
<xul:menupopup class="search-value-popup"> <xul:menupopup class="search-value-popup">
<xul:menuitem value="0" class="search-value-menuitem"/>
<xul:menuitem value="1" class="search-value-menuitem"/>
<xul:menuitem value="2" class="search-value-menuitem"/>
<xul:menuitem value="3" class="search-value-menuitem"/>
<xul:menuitem value="4" class="search-value-menuitem"/>
<xul:menuitem value="5" class="search-value-menuitem"/>
</xul:menupopup> </xul:menupopup>
</xul:menulist> </xul:menulist>
<xul:menulist flex="1" class="search-value-menulist"> <xul:menulist flex="1" class="search-value-menulist">
@ -1375,12 +1370,7 @@
// it's a text search, so show the textbox // it's a text search, so show the textbox
this.setAttribute("selectedIndex", "0"); this.setAttribute("selectedIndex", "0");
} }
else if (val == Components.interfaces.nsMsgSearchAttrib.Label) else if (val == Components.interfaces.nsMsgSearchAttrib.Keywords) {
{
var children = document.getAnonymousNodes(this);
var abs = children[5].getElementsByAttribute("value", "1");
if (abs.item(0))
children[5].selectedItem = abs[0];
this.setAttribute("selectedIndex", "5"); this.setAttribute("selectedIndex", "5");
} }
else if (val == Components.interfaces.nsMsgSearchAttrib.JunkStatus) { else if (val == Components.interfaces.nsMsgSearchAttrib.JunkStatus) {
@ -1433,11 +1423,14 @@
else else
children[0].value = val.str; children[0].value = val.str;
} }
else if (attrib == nsMsgSearchAttrib.Label) else if (attrib == nsMsgSearchAttrib.Keywords)
{ {
var labelVal = children[5].getElementsByAttribute("value", val.label); var keywordVal = children[5].getElementsByAttribute("value", val.str);
if (labelVal.item(0)) if (keywordVal.item(0))
children[5].selectedItem = labelVal[0]; {
children[5].value = val.str;
children[5].selectedItem = keywordVal[0];
}
} }
else if (attrib == nsMsgSearchAttrib.JunkStatus) { else if (attrib == nsMsgSearchAttrib.JunkStatus) {
var junkStatus = var junkStatus =
@ -1485,8 +1478,10 @@
else else
searchValue.str = children[0].value; searchValue.str = children[0].value;
} }
else if (searchAttribute == nsMsgSearchAttrib.Label) else if (searchAttribute == nsMsgSearchAttrib.Keywords)
searchValue.label = children[5].selectedItem.value; {
searchValue.str = children[5].value;
}
else if (searchAttribute == nsMsgSearchAttrib.JunkStatus) else if (searchAttribute == nsMsgSearchAttrib.JunkStatus)
searchValue.junkStatus = children[6].value; searchValue.junkStatus = children[6].value;
else if (searchAttribute == nsMsgSearchAttrib.Size) else if (searchAttribute == nsMsgSearchAttrib.Size)
@ -1507,6 +1502,33 @@
]]> ]]>
</body> </body>
</method> </method>
<method name="fillInTags">
<body>
<![CDATA[
var children = document.getAnonymousNodes(this);
var popupMenu = children[5].firstChild;
var tagService = Components.classes["@mozilla.org/messenger/tagservice;1"].getService(Components.interfaces.nsIMsgTagService);
var allTags = tagService.tagEnumerator;
var allKeys = tagService.keyEnumerator;
var tagNum = 0;
while (allTags.hasMore())
{
var tag = allTags.getNext();
var key = allKeys.getNext();
var newMenuItem = document.createElement('menuitem');
newMenuItem.setAttribute('label', tag);
newMenuItem.setAttribute('value', key);
popupMenu.appendChild(newMenuItem);
if (tagNum == 0)
{
children[5].selectedItem = newMenuItem;
children[5].value = key;
tagNum++;
}
}
]]>
</body>
</method>
<method name="fillStringsForChildren"> <method name="fillStringsForChildren">
<parameter name="parentNode"/> <parameter name="parentNode"/>
<parameter name="bundle"/> <parameter name="bundle"/>
@ -1565,18 +1587,14 @@
// initialize the address book picker // initialize the address book picker
this.initialize(document.getAnonymousNodes(this)[4], bundle); this.initialize(document.getAnonymousNodes(this)[4], bundle);
// initialize the label picker....
var labelStrings = GetLabelStrings();
var children = document.getAnonymousNodes(this)[5].firstChild.childNodes;
// set the label string on each label element...
for (var index = 0; index < 6; index++)
children[index].setAttribute('label', labelStrings[index]);
// initialize the junk status picker // initialize the junk status picker
this.initialize(document.getAnonymousNodes(this)[6], bundle); this.initialize(document.getAnonymousNodes(this)[6], bundle);
// initialize the has attachment status picker // initialize the has attachment status picker
this.initialize(document.getAnonymousNodes(this)[7], bundle); this.initialize(document.getAnonymousNodes(this)[7], bundle);
// initialize the tag list
fillInTags();
]]> ]]>
</constructor> </constructor>
</implementation> </implementation>

Просмотреть файл

@ -526,61 +526,6 @@ function SetMenuItemLabel(menuItemId, customLabel)
menuItem.setAttribute('label', customLabel); menuItem.setAttribute('label', customLabel);
} }
function InitMessageLabel(menuType)
{
/* this code gets the label strings and changes the menu labels */
var color;
try
{
var isChecked = true;
var checkedLabel = gDBView.hdrForFirstSelectedMessage.label;
}
catch(ex)
{
isChecked = false;
}
for (var label = 0; label <= 5; label++)
{
try
{
var prefString = gPrefBranch.getComplexValue("mailnews.labels.description." + label,
Components.interfaces.nsIPrefLocalizedString);
var formattedPrefString = gMessengerBundle.getFormattedString("labelMenuItemFormat" + label,
[prefString], 1);
var menuItemId = menuType + "-labelMenuItem" + label;
var menuItem = document.getElementById(menuItemId);
SetMenuItemLabel(menuItemId, formattedPrefString);
if (isChecked && label == checkedLabel)
menuItem.setAttribute("checked", "true");
else
menuItem.setAttribute("checked", "false");
// commented out for now until UE decides on how to show the Labels menu items.
// This code will color either the text or background for the Labels menu items.
/*****
if (label != 0)
{
color = gPrefBranch.getCharPref("mailnews.labels.color." + label);
// this colors the text of the menuitem only.
//menuItem.setAttribute("style", ("color: " + color));
// this colors the background of the menuitem and
// when selected, text becomes white.
//menuItem.setAttribute("style", ("color: #FFFFFF"));
//menuItem.setAttribute("style", ("background-color: " + color));
}
****/
}
catch(ex)
{
}
}
document.commandDispatcher.updateCommands('create-menu-label');
}
function InitMessageMark() function InitMessageMark()
{ {
var areMessagesRead = SelectedMessagesAreRead(); var areMessagesRead = SelectedMessagesAreRead();

Просмотреть файл

@ -108,8 +108,8 @@ searchterm {
-moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-folder"); -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-folder");
} }
.ruleactiontarget[type="labelmessageas"] { .ruleactiontarget[type="addtagtomessage"] {
-moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-label"); -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-tag");
} }
.ruleactiontarget[type="setpriorityto"] { .ruleactiontarget[type="setpriorityto"] {

Просмотреть файл

@ -0,0 +1,90 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the new tag dialog
*
* The Initial Developer of the Original Code is
* David Bienvenu.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* David Bienvenu <bienvenu@nventure.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var dialog;
function onLoad()
{
var arguments = window.arguments[0];
dialog = {};
dialog.OKButton = document.documentElement.getButton("accept");
dialog.nameField = document.getElementById("name");
dialog.nameField.focus();
// call this when OK is pressed
dialog.okCallback = arguments.okCallback;
moveToAlertPosition();
doEnabling();
}
function onOK()
{
var name = dialog.nameField.value;
var tagService = Components.classes["@mozilla.org/messenger/tagservice;1"].getService(Components.interfaces.nsIMsgTagService);
// do name validity check? Has to be non-empty, and not existing already
try
{
var key = tagService.getKeyForTag(name);
// above will throw an error if tag doesn't exist. So if it doesn't throw an error,
// the tag exists, so alert the user and return false.
}
catch (ex) {return dialog.okCallback(name, "")}
var messengerBundle = document.getElementById("bundle_messenger");
var alertText = messengerBundle.getString("tagExists");
window.alert(alertText);
return false;
}
function doEnabling()
{
if (dialog.nameField.value) {
if (dialog.OKButton.disabled)
dialog.OKButton.disabled = false;
} else {
if (!dialog.OKButton.disabled)
dialog.OKButton.disabled = true;
}
}

Просмотреть файл

@ -0,0 +1,58 @@
<?xml version="1.0"?>
<!-- ***** BEGIN LICENSE BLOCK *****
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is Mozilla Mail Tag Support
-
- The Initial Developer of the Original Code is
- The Mozilla Corporation.
- Portions created by the Initial Developer are Copyright (C) 2006
- the Initial Developer. All Rights Reserved.
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the GPL or the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of either the GPL or the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the LGPL or the GPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of any one of the MPL, the GPL or the LGPL.
-
- ***** END LICENSE BLOCK ***** -->
<?xml-stylesheet href="chrome://messenger/skin/dialogs.css" type="text/css"?>
<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/newTagDialog.dtd">
<dialog xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&newTagDialog.title;"
onload="onLoad();"
ondialogaccept="return onOK();">
<stringbundleset id="stringbundleset">
<stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
</stringbundleset>
<script type="application/x-javascript" src="chrome://messenger/content/newTagDialog.js"/>
<label value="&name.label;" accesskey="&name.accesskey;" control="name"/>
<textbox id="name" oninput="doEnabling();"/>
<separator/>
</dialog>

Просмотреть файл

@ -236,6 +236,9 @@ junk=Junk
# for the has attachment picker in search and mail views # for the has attachment picker in search and mail views
hasAttachments=Has Attachments hasAttachments=Has Attachments
# for the Tag picker in search and mail views.
tag=Tag
# mailnews.js # mailnews.js
mailnews.send_default_charset=ISO-8859-1 mailnews.send_default_charset=ISO-8859-1
mailnews.view_default_charset=ISO-8859-1 mailnews.view_default_charset=ISO-8859-1

Просмотреть файл

Просмотреть файл

@ -46,7 +46,7 @@ interface nsIMsgSearchScopeTerm;
native nsCStringRef(nsCString&); native nsCStringRef(nsCString&);
[scriptable, uuid(cc7795ce-1dd1-11b2-9ad2-dfa3c0b6ee09)] [scriptable, uuid(cde583fe-9add-4adb-9e1a-9cfe050d8a26)]
interface nsIMsgSearchTerm : nsISupports { interface nsIMsgSearchTerm : nsISupports {
attribute nsMsgSearchAttribValue attrib; attribute nsMsgSearchAttribValue attrib;
attribute nsMsgSearchOpValue op; attribute nsMsgSearchOpValue op;
@ -93,6 +93,7 @@ interface nsIMsgSearchTerm : nsISupports {
readonly attribute boolean matchAllBeforeDeciding; readonly attribute boolean matchAllBeforeDeciding;
readonly attribute ACString termAsString; readonly attribute ACString termAsString;
boolean matchKeyword(in string keyword); // used for tag searches
attribute boolean matchAll; attribute boolean matchAll;
}; };

Просмотреть файл

@ -59,7 +59,7 @@ typedef long nsMsgFilterIndex;
typedef long nsMsgRuleActionType; typedef long nsMsgRuleActionType;
[scriptable, uuid(3099cee1-eb76-4cd3-a40e-cfc8d2ef4437)] [scriptable, uuid(59af7696-1e28-4642-a400-fa327ae0b8d8)]
interface nsMsgFilterAction { interface nsMsgFilterAction {
/* these longs are all actually of type nsMsgFilterActionType */ /* these longs are all actually of type nsMsgFilterActionType */
@ -80,5 +80,6 @@ interface nsMsgFilterAction {
const long JunkScore=14; const long JunkScore=14;
const long FetchBodyFromPop3Server=15; const long FetchBodyFromPop3Server=15;
const long CopyToFolder=16; const long CopyToFolder=16;
const long AddTag=17;
}; };

Просмотреть файл

@ -86,7 +86,7 @@ interface nsMsgSearchAttrib {
const nsMsgSearchAttribValue FolderInfo = 12; /* for "view thread context" from result */ const nsMsgSearchAttribValue FolderInfo = 12; /* for "view thread context" from result */
const nsMsgSearchAttribValue Size = 13; const nsMsgSearchAttribValue Size = 13;
const nsMsgSearchAttribValue AnyText = 14; const nsMsgSearchAttribValue AnyText = 14;
const nsMsgSearchAttribValue Keywords = 15; const nsMsgSearchAttribValue Keywords = 15; // keywords are the internal representation of tags.
const nsMsgSearchAttribValue Name = 16; const nsMsgSearchAttribValue Name = 16;
const nsMsgSearchAttribValue DisplayName = 17; const nsMsgSearchAttribValue DisplayName = 17;

Просмотреть файл

@ -57,9 +57,9 @@ var gFilterActionList;
var gFilterActionStrings = ["none", "movemessage", "setpriorityto", "deletemessage", var gFilterActionStrings = ["none", "movemessage", "setpriorityto", "deletemessage",
"markasread", "ignorethread", "watchthread", "markasflagged", "markasread", "ignorethread", "watchthread", "markasflagged",
"labelmessageas", "replytomessage", "forwardmessage", "stopexecution", "label", "replytomessage", "forwardmessage", "stopexecution",
"deletefrompopserver", "leaveonpopserver", "setjunkscore", "deletefrompopserver", "leaveonpopserver", "setjunkscore",
"fetchfrompopserver", "copymessage"]; "fetchfrompopserver", "copymessage", "addtagtomessage"];
var nsMsgFilterAction = Components.interfaces.nsMsgFilterAction; var nsMsgFilterAction = Components.interfaces.nsMsgFilterAction;
@ -72,7 +72,7 @@ function filterEditorOnLoad()
gPrefBranch = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch(null); gPrefBranch = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch(null);
gFilterBundle = document.getElementById("bundle_filter"); gFilterBundle = document.getElementById("bundle_filter");
InitMessageLabel(); InitMessageMark();
if ("arguments" in window && window.arguments[0]) if ("arguments" in window && window.arguments[0])
{ {
var args = window.arguments[0]; var args = window.arguments[0];

Просмотреть файл

@ -70,8 +70,8 @@
<xul:menuseparator/> <xul:menuseparator/>
<xul:menuitem label="&markMessageRead.label;" value="markasread"/> <xul:menuitem label="&markMessageRead.label;" value="markasread"/>
<xul:menuitem label="&markMessageFlagged.label;" value="markasflagged"/> <xul:menuitem label="&markMessageFlagged.label;" value="markasflagged"/>
<xul:menuitem label="&labelMessage.label;" value="labelmessageas"/>
<xul:menuitem label="&setPriority.label;" value="setpriorityto"/> <xul:menuitem label="&setPriority.label;" value="setpriorityto"/>
<xul:menuitem label="&addTag.label;" value="addtagtomessage"/>
<xul:menuitem label="&setJunkScore.label;" value="setjunkscore" enablefornews="false"/> <xul:menuitem label="&setJunkScore.label;" value="setjunkscore" enablefornews="false"/>
<xul:menuseparator enableforpop3="true"/> <xul:menuseparator enableforpop3="true"/>
<xul:menuitem label="&deleteMessage.label;" value="deletemessage"/> <xul:menuitem label="&deleteMessage.label;" value="deletemessage"/>
@ -255,6 +255,9 @@
case "setjunkscore": case "setjunkscore":
document.getAnonymousNodes(actionTarget)[0].value = aFilterAction.junkScore; document.getAnonymousNodes(actionTarget)[0].value = aFilterAction.junkScore;
break; break;
case "addtagtomessage":
document.getAnonymousNodes(actionTarget)[0].value = aFilterAction.strValue;
break;
default: default:
break; break;
} }
@ -328,6 +331,7 @@
case "setjunkscore": case "setjunkscore":
filterAction.junkScore = document.getAnonymousNodes(actionTarget)[0].value; filterAction.junkScore = document.getAnonymousNodes(actionTarget)[0].value;
break; break;
case "addtagtomessage":
case "replytomessage": case "replytomessage":
case "forwardmessage": case "forwardmessage":
filterAction.strValue = document.getAnonymousNodes(actionTarget)[0].value; filterAction.strValue = document.getAnonymousNodes(actionTarget)[0].value;
@ -385,16 +389,10 @@
</implementation> </implementation>
</binding> </binding>
<binding id="ruleactiontarget-label" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base"> <binding id="ruleactiontarget-tag" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
<content> <content>
<xul:menulist class="ruleactionitem"> <xul:menulist class="ruleactionitem">
<xul:menupopup> <xul:menupopup>
<xul:menuitem value="0"/>
<xul:menuitem value="1"/>
<xul:menuitem value="2"/>
<xul:menuitem value="3"/>
<xul:menuitem value="4"/>
<xul:menuitem value="5"/>
</xul:menupopup> </xul:menupopup>
</xul:menulist> </xul:menulist>
</content> </content>
@ -402,19 +400,21 @@
<implementation> <implementation>
<constructor> <constructor>
<![CDATA[ <![CDATA[
var menuItems = document.getAnonymousNodes(this)[0].menupopup.childNodes; var menuPopup = document.getAnonymousNodes(this)[0].menupopup;
var prefBranch = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch(null); var tagService = Components.classes["@mozilla.org/messenger/tagservice;1"].getService(Components.interfaces.nsIMsgTagService);
var allTags = tagService.tagEnumerator;
for (var index = 0; index < menuItems.length; index++) var allKeys = tagService.keyEnumerator;
while (allTags.hasMore())
{ {
var menuEl = menuItems[index]; var tag = allTags.getNext();
var prefString = prefBranch.getComplexValue("mailnews.labels.description." + menuEl.value, var key = allKeys.getNext();
Components.interfaces.nsIPrefLocalizedString); var newMenuItem = document.createElement('menuitem');
menuEl.setAttribute("label", prefString); newMenuItem.setAttribute('label', tag);
newMenuItem.setAttribute('value', key);
menuPopup.appendChild(newMenuItem);
} }
// propagating a pre-existing hack to make the tag get displayed correctly in the menulist
// propagating a pre-existing hack to make the label get displayed correctly in the menulist // now that we've changed the tags for each menu list. We need to use the current selectedIndex
// now that we've changed the labels 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. // (if its defined) to handle the case where we were initialized with a filter action already.
var currentItem = document.getAnonymousNodes(this)[0].selectedItem; var currentItem = document.getAnonymousNodes(this)[0].selectedItem;
document.getAnonymousNodes(this)[0].selectedItem = null; document.getAnonymousNodes(this)[0].selectedItem = null;

Просмотреть файл

@ -15,7 +15,7 @@
12=Folder Info 12=Folder Info
13=Size (KB) 13=Size (KB)
14=AnyText 14=AnyText
15=Keywords 15=Tags
# for AB and LDAP # for AB and LDAP
16=Any Name 16=Any Name
17=Display Name 17=Display Name
@ -50,5 +50,5 @@
46=Attachment Status 46=Attachment Status
47=Junk Status 47=Junk Status
48=Label 48=Label
49=Customize
# don't use above 49 # don't use above 49
49=Customize...

Просмотреть файл

@ -730,6 +730,7 @@ nsresult nsMsgFilter::SaveRule(nsIOFileStream *aStream)
err = filterList->WriteIntAttr(nsIMsgFilterList::attribActionValue, junkScore, aStream); err = filterList->WriteIntAttr(nsIMsgFilterList::attribActionValue, junkScore, aStream);
} }
break; break;
case nsMsgFilterAction::AddTag:
case nsMsgFilterAction::Reply: case nsMsgFilterAction::Reply:
case nsMsgFilterAction::Forward: case nsMsgFilterAction::Forward:
{ {
@ -818,6 +819,7 @@ static struct RuleActionsTableEntry ruleActionsTable[] =
{ nsMsgFilterAction::LeaveOnPop3Server, nsMsgFilterType::Inbox, 0, "Leave on Pop3 server"}, { nsMsgFilterAction::LeaveOnPop3Server, nsMsgFilterType::Inbox, 0, "Leave on Pop3 server"},
{ nsMsgFilterAction::JunkScore, nsMsgFilterType::All, 0, "JunkScore"}, { nsMsgFilterAction::JunkScore, nsMsgFilterType::All, 0, "JunkScore"},
{ nsMsgFilterAction::FetchBodyFromPop3Server, nsMsgFilterType::Inbox, 0, "Fetch body from Pop3Server"}, { nsMsgFilterAction::FetchBodyFromPop3Server, nsMsgFilterType::Inbox, 0, "Fetch body from Pop3Server"},
{ nsMsgFilterAction::AddTag, nsMsgFilterType::All, 0, "AddTag"},
}; };
const char *nsMsgFilter::GetActionStr(nsMsgRuleActionType action) const char *nsMsgFilter::GetActionStr(nsMsgRuleActionType action)

Просмотреть файл

@ -662,10 +662,16 @@ nsresult nsMsgFilterList::LoadTextFilters(nsIOFileStream *aStream)
} }
else if (type == nsMsgFilterAction::Label) else if (type == nsMsgFilterAction::Label)
{ {
// upgrade label to corresponding tag/keyword
PRInt32 res; PRInt32 res;
PRInt32 labelInt = value.ToInteger(&res, 10); PRInt32 labelInt = value.ToInteger(&res, 10);
if (res == 0) if (res == 0)
currentFilterAction->SetLabel((nsMsgLabelValue) labelInt); {
nsCAutoString keyword("$label");
keyword.Append('0' + labelInt);
currentFilterAction->SetType(nsMsgFilterAction::AddTag);
currentFilterAction->SetStrValue(keyword.get());
}
} }
else if (type == nsMsgFilterAction::JunkScore) else if (type == nsMsgFilterAction::JunkScore)
{ {
@ -674,7 +680,8 @@ nsresult nsMsgFilterList::LoadTextFilters(nsIOFileStream *aStream)
if (!res) if (!res)
currentFilterAction->SetJunkScore(junkScore); currentFilterAction->SetJunkScore(junkScore);
} }
else if (type == nsMsgFilterAction::Forward || type == nsMsgFilterAction::Reply) else if (type == nsMsgFilterAction::Forward || type == nsMsgFilterAction::Reply
|| type == nsMsgFilterAction::AddTag)
{ {
currentFilterAction->SetStrValue(value.get()); currentFilterAction->SetStrValue(value.get());
} }

Просмотреть файл

@ -647,6 +647,13 @@ nsresult nsMsgFilterAfterTheFact::ApplyFilter()
m_curFolder->SetLabelForMessages(m_searchHitHdrs, filterLabel); m_curFolder->SetLabelForMessages(m_searchHitHdrs, filterLabel);
} }
break; break;
case nsMsgFilterAction::AddTag:
{
nsXPIDLCString keyword;
filterAction->GetStrValue(getter_Copies(keyword));
m_curFolder->AddKeywordToMessages(m_searchHitHdrs, keyword.get());
}
break;
case nsMsgFilterAction::JunkScore: case nsMsgFilterAction::JunkScore:
{ {
nsCAutoString junkScoreStr; nsCAutoString junkScoreStr;

Просмотреть файл

@ -197,11 +197,6 @@ nsMsgSearchValidityManager::InitOfflineMailTable()
m_offlineMailTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsntInAB, 1); m_offlineMailTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsntInAB, 1);
m_offlineMailTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsntInAB, 1); m_offlineMailTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsntInAB, 1);
m_offlineMailTable->SetAvailable (nsMsgSearchAttrib::Label, nsMsgSearchOp::Is, 1);
m_offlineMailTable->SetEnabled (nsMsgSearchAttrib::Label, nsMsgSearchOp::Is, 1);
m_offlineMailTable->SetAvailable (nsMsgSearchAttrib::Label, nsMsgSearchOp::Isnt, 1);
m_offlineMailTable->SetEnabled (nsMsgSearchAttrib::Label, nsMsgSearchOp::Isnt, 1);
m_offlineMailTable->SetAvailable (nsMsgSearchAttrib::To, nsMsgSearchOp::Contains, 1); m_offlineMailTable->SetAvailable (nsMsgSearchAttrib::To, nsMsgSearchOp::Contains, 1);
m_offlineMailTable->SetEnabled (nsMsgSearchAttrib::To, nsMsgSearchOp::Contains, 1); m_offlineMailTable->SetEnabled (nsMsgSearchAttrib::To, nsMsgSearchOp::Contains, 1);
m_offlineMailTable->SetAvailable (nsMsgSearchAttrib::To, nsMsgSearchOp::DoesntContain, 1); m_offlineMailTable->SetAvailable (nsMsgSearchAttrib::To, nsMsgSearchOp::DoesntContain, 1);
@ -315,6 +310,14 @@ nsMsgSearchValidityManager::InitOfflineMailTable()
m_offlineMailTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1); m_offlineMailTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1);
m_offlineMailTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1); m_offlineMailTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1);
m_offlineMailTable->SetAvailable (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::Contains, 1);
m_offlineMailTable->SetEnabled (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::Contains, 1);
m_offlineMailTable->SetAvailable (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::DoesntContain, 1);
m_offlineMailTable->SetEnabled (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::DoesntContain, 1);
m_offlineMailTable->SetAvailable (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::Is, 1);
m_offlineMailTable->SetEnabled (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::Is, 1);
m_offlineMailTable->SetAvailable (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::Isnt, 1);
m_offlineMailTable->SetEnabled (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::Isnt, 1);
return rv; return rv;
} }
@ -382,6 +385,15 @@ nsMsgSearchValidityManager::InitOnlineMailTable()
m_onlineMailTable->SetAvailable (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsLessThan, 1); m_onlineMailTable->SetAvailable (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsLessThan, 1);
m_onlineMailTable->SetEnabled (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsLessThan, 1); m_onlineMailTable->SetEnabled (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsLessThan, 1);
m_onlineMailTable->SetAvailable (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::Contains, 1);
m_onlineMailTable->SetEnabled (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::Contains, 1);
m_onlineMailTable->SetAvailable (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::DoesntContain, 1);
m_onlineMailTable->SetEnabled (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::DoesntContain, 1);
m_onlineMailTable->SetAvailable (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::Is, 1);
m_onlineMailTable->SetEnabled (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::Is, 1);
m_onlineMailTable->SetAvailable (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::Isnt, 1);
m_onlineMailTable->SetEnabled (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::Isnt, 1);
m_onlineMailTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1); m_onlineMailTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1);
m_onlineMailTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1); m_onlineMailTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1);
m_onlineMailTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1); m_onlineMailTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1);

Просмотреть файл

@ -552,6 +552,13 @@ nsresult nsMsgSearchOfflineMail::ProcessSearchTerm(nsIMsgDBHdr *msgToMatch,
err = aTerm->MatchLabel(label, &result); err = aTerm->MatchLabel(label, &result);
break; break;
} }
case nsMsgSearchAttrib::Keywords:
{
nsXPIDLCString keywords;
msgToMatch->GetStringProperty("keywords", getter_Copies(keywords));
err = aTerm->MatchKeyword(keywords.get(), &result);
break;
}
case nsMsgSearchAttrib::JunkStatus: case nsMsgSearchAttrib::JunkStatus:
{ {
nsXPIDLCString junkScoreStr; nsXPIDLCString junkScoreStr;

Просмотреть файл

@ -500,7 +500,7 @@ nsresult nsMsgSearchAdapter::EncodeImapTerm (nsIMsgSearchTerm *term, PRBool real
whichMnemonic = m_kImapAnyText; whichMnemonic = m_kImapAnyText;
break; break;
case nsMsgSearchAttrib::Keywords: case nsMsgSearchAttrib::Keywords:
whichMnemonic = m_kNntpKeywords; whichMnemonic = m_kImapKeyword;
break; break;
case nsMsgSearchAttrib::MsgStatus: case nsMsgSearchAttrib::MsgStatus:
useNot = PR_FALSE; // bizarrely, NOT SEEN is wrong, but UNSEEN is right. useNot = PR_FALSE; // bizarrely, NOT SEEN is wrong, but UNSEEN is right.

Просмотреть файл

@ -97,6 +97,7 @@ nsMsgSearchAttribEntry SearchAttribEntryTable[] =
{nsMsgSearchAttrib::ToOrCC, "to or cc"}, {nsMsgSearchAttrib::ToOrCC, "to or cc"},
{nsMsgSearchAttrib::AgeInDays, "age in days"}, {nsMsgSearchAttrib::AgeInDays, "age in days"},
{nsMsgSearchAttrib::Label, "label"}, {nsMsgSearchAttrib::Label, "label"},
{nsMsgSearchAttrib::Keywords, "tag"},
{nsMsgSearchAttrib::Size, "size"}, {nsMsgSearchAttrib::Size, "size"},
// this used to be nsMsgSearchAttrib::SenderInAddressBook // this used to be nsMsgSearchAttrib::SenderInAddressBook
// we used to have two Sender menuitems // we used to have two Sender menuitems
@ -663,8 +664,16 @@ nsresult nsMsgSearchTerm::DeStreamNew (char *inStream, PRInt16 /*length*/)
if (commaSep) if (commaSep)
rv = ParseOperator(commaSep + 1, &m_operator); rv = ParseOperator(commaSep + 1, &m_operator);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// convert label filters and saved searches to keyword equivalents
if (secondCommaSep) if (secondCommaSep)
ParseValue(secondCommaSep + 1); ParseValue(secondCommaSep + 1);
if (m_attribute == nsMsgSearchAttrib::Label)
{
nsCAutoString keyword("$label");
m_value.attribute = m_attribute = nsMsgSearchAttrib::Keywords;
keyword.Append('0' + m_value.u.label);
m_value.string = PL_strdup(keyword.get());
}
return NS_OK; return NS_OK;
} }
@ -1330,6 +1339,53 @@ nsresult nsMsgSearchTerm::MatchStatus(PRUint32 statusToMatch, PRBool *pResult)
return rv; return rv;
} }
nsresult nsMsgSearchTerm::MatchKeyword(const char *keyword, PRBool *pResult)
{
NS_ENSURE_ARG_POINTER(pResult);
nsresult rv = NS_OK;
nsCAutoString keys;
PRBool matches = PR_FALSE;
switch (m_operator)
{
case nsMsgSearchOp::Is:
matches = !strcmp(keyword, m_value.string);
break;
case nsMsgSearchOp::Isnt:
matches = strcmp(keyword, m_value.string);
break;
case nsMsgSearchOp::DoesntContain:
case nsMsgSearchOp::Contains:
{
const char *keywordLoc = PL_strstr(keyword, m_value.string);
const char *startOfKeyword = keyword;
PRUint32 keywordLen = strlen(keyword);
while (keywordLoc)
{
// if the keyword is at the beginning of the string, then it's a match if
// it is either the whole string, or is followed by a space, it's a match.
if (keywordLoc == startOfKeyword || (keywordLoc[-1] == ' '))
{
matches = keywordLen == strlen(keywordLoc) || (keywordLoc[keywordLen] == ' ');
if (matches)
break;
}
startOfKeyword = keywordLoc + keywordLen;
keywordLoc = PL_strstr(keyword, keywordLoc + keywordLen + 1);
}
}
break;
default:
rv = NS_ERROR_FAILURE;
NS_ERROR("invalid compare op for msg status");
}
*pResult = (m_operator == nsMsgSearchOp::DoesntContain) ? !matches : matches;
return rv;
}
nsresult nsresult
nsMsgSearchTerm::MatchPriority (nsMsgPriorityValue priorityToMatch, nsMsgSearchTerm::MatchPriority (nsMsgPriorityValue priorityToMatch,
PRBool *pResult) PRBool *pResult)

Просмотреть файл

@ -99,6 +99,8 @@ nsMsgSearchValueImpl::GetStr(PRUnichar** aResult)
NS_IMETHODIMP NS_IMETHODIMP
nsMsgSearchValueImpl::SetStr(const PRUnichar* aValue) nsMsgSearchValueImpl::SetStr(const PRUnichar* aValue)
{ {
if (!aValue)
return NS_ERROR_NULL_POINTER;
NS_ENSURE_TRUE(IS_STRING_ATTRIBUTE(mValue.attribute), NS_ERROR_ILLEGAL_VALUE); NS_ENSURE_TRUE(IS_STRING_ATTRIBUTE(mValue.attribute), NS_ERROR_ILLEGAL_VALUE);
if (mValue.string) if (mValue.string)
nsCRT::free(mValue.string); nsCRT::free(mValue.string);

Просмотреть файл

@ -127,6 +127,7 @@ CPPSRCS = \
nsSpamSettings.cpp \ nsSpamSettings.cpp \
nsCidProtocolHandler.cpp \ nsCidProtocolHandler.cpp \
nsMsgContentPolicy.cpp \ nsMsgContentPolicy.cpp \
nsMsgTagService.cpp\
$(NULL) $(NULL)
# MacOSX requires the MoreFiles module # MacOSX requires the MoreFiles module

Просмотреть файл

@ -769,14 +769,40 @@ nsresult nsMsgDBView::FetchPriority(nsIMsgDBHdr *aHdr, PRUnichar ** aPriorityStr
break; break;
} }
if (priorityString) *aPriorityString = (priorityString) ? nsCRT::strdup(priorityString) : nsnull;
*aPriorityString = nsCRT::strdup(priorityString);
else
*aPriorityString = nsnull;
return NS_OK; return NS_OK;
} }
nsresult nsMsgDBView::FetchTags(nsIMsgDBHdr *aHdr, PRUnichar ** aTagString)
{
nsresult rv = NS_OK;
if (!mTagService)
{
mTagService = do_GetService(NS_MSGTAGSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
}
nsXPIDLCString keywords;
nsXPIDLString label, tags;
FetchLabel(aHdr, getter_Copies(label));
aHdr->GetStringProperty("keywords", getter_Copies(keywords));
nsCStringArray keywordsArray;
keywordsArray.ParseString(keywords.get(), " ");
nsAutoString tag;
for (PRInt32 i = 0; i < keywordsArray.Count(); i++)
{
rv = mTagService->GetTagForKey(*(keywordsArray[i]), tag);
if (NS_SUCCEEDED(rv) && !tag.IsEmpty() && !tag.Equals(label))
{
if (!tags.IsEmpty())
tags.Append((PRUnichar) ' ');
tags.Append(tag);
}
}
tags.Append(label);
*aTagString = ToNewUnicode(tags);
return rv;
}
nsresult nsMsgDBView::FetchLabel(nsIMsgDBHdr *aHdr, PRUnichar ** aLabelString) nsresult nsMsgDBView::FetchLabel(nsIMsgDBHdr *aHdr, PRUnichar ** aLabelString)
{ {
nsresult rv = NS_OK; nsresult rv = NS_OK;
@ -1601,8 +1627,8 @@ NS_IMETHODIMP nsMsgDBView::GetCellText(PRInt32 aRow, nsITreeColumn* aCol, nsAStr
rv = FetchPriority(msgHdr, getter_Copies(valueText)); rv = FetchPriority(msgHdr, getter_Copies(valueText));
aValue.Assign(valueText); aValue.Assign(valueText);
break; break;
case 'l': // label case 'l': // label - labels are now tags...
rv = FetchLabel(msgHdr, getter_Copies(valueText)); rv = FetchTags(msgHdr, getter_Copies(valueText));
aValue.Assign(valueText); aValue.Assign(valueText);
break; break;
case 'a': // account case 'a': // account

Просмотреть файл

@ -64,6 +64,7 @@
#include "nsIObserver.h" #include "nsIObserver.h"
#include "nsIMsgFilterPlugin.h" #include "nsIMsgFilterPlugin.h"
#include "nsIStringBundle.h" #include "nsIStringBundle.h"
#include "nsMsgTagService.h"
#define MESSENGER_STRING_URL "chrome://messenger/locale/messenger.properties" #define MESSENGER_STRING_URL "chrome://messenger/locale/messenger.properties"
@ -171,6 +172,7 @@ protected:
nsresult FetchSize(nsIMsgDBHdr * aHdr, PRUnichar ** aSizeString); nsresult FetchSize(nsIMsgDBHdr * aHdr, PRUnichar ** aSizeString);
nsresult FetchPriority(nsIMsgDBHdr *aHdr, PRUnichar ** aPriorityString); nsresult FetchPriority(nsIMsgDBHdr *aHdr, PRUnichar ** aPriorityString);
nsresult FetchLabel(nsIMsgDBHdr *aHdr, PRUnichar ** aLabelString); nsresult FetchLabel(nsIMsgDBHdr *aHdr, PRUnichar ** aLabelString);
nsresult FetchTags(nsIMsgDBHdr *aHdr, PRUnichar ** aTagString);
nsresult FetchAccount(nsIMsgDBHdr * aHdr, PRUnichar ** aAccount); nsresult FetchAccount(nsIMsgDBHdr * aHdr, PRUnichar ** aAccount);
nsresult CycleThreadedColumn(nsIDOMElement * aElement); nsresult CycleThreadedColumn(nsIDOMElement * aElement);
@ -358,6 +360,7 @@ protected:
// I18N date formater service which we'll want to cache locally. // I18N date formater service which we'll want to cache locally.
nsCOMPtr<nsIDateTimeFormat> mDateFormater; nsCOMPtr<nsIDateTimeFormat> mDateFormater;
nsCOMPtr<nsIMsgHeaderParser> mHeaderParser; nsCOMPtr<nsIMsgHeaderParser> mHeaderParser;
nsCOMPtr<nsIMsgTagService> mTagService;
// i'm not sure if we are going to permamently need a nsIMessenger instance or if we'll be able // i'm not sure if we are going to permamently need a nsIMessenger instance or if we'll be able
// to phase it out eventually....for now we need it though. // to phase it out eventually....for now we need it though.
nsCOMPtr<nsIMessenger> mMessengerInstance; nsCOMPtr<nsIMessenger> mMessengerInstance;

Просмотреть файл

@ -55,7 +55,7 @@
#include "nsIInterfaceRequestorUtils.h" #include "nsIInterfaceRequestorUtils.h"
#include "nsIMsgLocalMailFolder.h" #include "nsIMsgLocalMailFolder.h"
#include "nsIMsgImapMailFolder.h" #include "nsIMsgImapMailFolder.h"
#include "nsMailHeaders.h"
#include "nsMsgI18N.h" #include "nsMsgI18N.h"
#include "prprf.h" #include "prprf.h"
#include "nsMsgLocalFolderHdrs.h" #include "nsMsgLocalFolderHdrs.h"
@ -584,6 +584,20 @@ done:
return rv; return rv;
} }
void nsFolderCompactState::AdvanceToNextLine(const char *buffer, PRUint32 &bufferOffset, PRUint32 maxBufferOffset)
{
for (; bufferOffset < maxBufferOffset; bufferOffset++)
{
if (buffer[bufferOffset] == nsCRT::CR || buffer[bufferOffset] == nsCRT::LF)
{
bufferOffset++;
if (buffer[bufferOffset- 1] == nsCRT::CR && buffer[bufferOffset] == nsCRT::LF)
bufferOffset++;
break;
}
}
}
NS_IMETHODIMP NS_IMETHODIMP
nsFolderCompactState::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsFolderCompactState::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
nsIInputStream *inStr, nsIInputStream *inStr,
@ -594,9 +608,16 @@ nsFolderCompactState::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
nsresult rv = NS_OK; nsresult rv = NS_OK;
PRUint32 msgFlags; PRUint32 msgFlags;
PRBool checkForKeyword = m_startOfMsg;
PRBool addKeywordHdr = PR_FALSE;
PRUint32 needToGrowKeywords = 0;
PRUint32 statusOffset;
nsXPIDLCString msgHdrKeywords;
if (m_startOfMsg) if (m_startOfMsg)
{ {
m_statusOffset = 0; m_statusOffset = 0;
m_addedHeaderSize = 0;
m_messageUri.SetLength(0); // clear the previous message uri m_messageUri.SetLength(0); // clear the previous message uri
if (NS_SUCCEEDED(BuildMessageURI(m_baseMessageUri.get(), m_keyArray[m_curIndex], if (NS_SUCCEEDED(BuildMessageURI(m_baseMessageUri.get(), m_keyArray[m_curIndex],
m_messageUri))) m_messageUri)))
@ -606,7 +627,6 @@ nsFolderCompactState::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
if (m_curSrcHdr) if (m_curSrcHdr)
{ {
(void) m_curSrcHdr->GetFlags(&msgFlags); (void) m_curSrcHdr->GetFlags(&msgFlags);
PRUint32 statusOffset;
(void) m_curSrcHdr->GetStatusOffset(&statusOffset); (void) m_curSrcHdr->GetStatusOffset(&statusOffset);
if (statusOffset == 0) if (statusOffset == 0)
m_needStatusLine = PR_TRUE; m_needStatusLine = PR_TRUE;
@ -618,9 +638,21 @@ nsFolderCompactState::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
while (NS_SUCCEEDED(rv) && (PRInt32) count > 0) while (NS_SUCCEEDED(rv) && (PRInt32) count > 0)
{ {
maxReadCount = count > 4096 ? 4096 : count; maxReadCount = count > 4096 ? 4096 : count;
writeCount = 0;
rv = inStr->Read(m_dataBuffer, maxReadCount, &readCount); rv = inStr->Read(m_dataBuffer, maxReadCount, &readCount);
if (NS_SUCCEEDED(rv)) if (NS_SUCCEEDED(rv))
{ {
if (checkForKeyword)
{
const char *keywordHdr = PL_strnrstr(m_dataBuffer, HEADER_X_MOZILLA_KEYWORDS, readCount);
if (keywordHdr)
m_curSrcHdr->GetUint32Property("growKeywords", &needToGrowKeywords);
else
addKeywordHdr = PR_TRUE;
checkForKeyword = PR_FALSE;
m_curSrcHdr->GetStringProperty("keywords", getter_Copies(msgHdrKeywords));
}
PRUint32 blockOffset = 0;
if (m_needStatusLine) if (m_needStatusLine)
{ {
m_needStatusLine = PR_FALSE; m_needStatusLine = PR_FALSE;
@ -630,26 +662,16 @@ nsFolderCompactState::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
// in OnEndCopy). // in OnEndCopy).
if (!strncmp(m_dataBuffer, "From ", 5)) if (!strncmp(m_dataBuffer, "From ", 5))
{ {
PRInt32 charIndex; blockOffset = 5;
for (charIndex = 5; charIndex < readCount; charIndex++) // skip from line
{ AdvanceToNextLine(m_dataBuffer, blockOffset, readCount);
if (m_dataBuffer[charIndex] == nsCRT::CR || m_dataBuffer[charIndex] == nsCRT::LF)
{
charIndex++;
if (m_dataBuffer[charIndex- 1] == nsCRT::CR && m_dataBuffer[charIndex] == nsCRT::LF)
charIndex++;
break;
}
}
char statusLine[50]; char statusLine[50];
writeCount = m_fileStream->write(m_dataBuffer, charIndex); writeCount = m_fileStream->write(m_dataBuffer, blockOffset);
m_statusOffset = charIndex; m_statusOffset = blockOffset;
PR_snprintf(statusLine, sizeof(statusLine), X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF); PR_snprintf(statusLine, sizeof(statusLine), X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF);
m_statusLineSize = m_fileStream->write(statusLine, strlen(statusLine)); m_addedHeaderSize = m_fileStream->write(statusLine, strlen(statusLine));
PR_snprintf(statusLine, sizeof(statusLine), X_MOZILLA_STATUS2_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF0000); PR_snprintf(statusLine, sizeof(statusLine), X_MOZILLA_STATUS2_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF0000);
m_statusLineSize += m_fileStream->write(statusLine, strlen(statusLine)); m_addedHeaderSize += m_fileStream->write(statusLine, strlen(statusLine));
writeCount += m_fileStream->write(m_dataBuffer + charIndex, readCount - charIndex);
} }
else else
{ {
@ -665,10 +687,99 @@ nsFolderCompactState::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
} }
} }
} }
#define EXTRA_KEYWORD_HDR " "MSG_LINEBREAK
if (addKeywordHdr)
{
// if blockOffset is set, we added x-mozilla-status headers so
// file pointer is already past them.
if (!blockOffset)
{
blockOffset = statusOffset;
// skip x-mozilla-status and status2 lines.
AdvanceToNextLine(m_dataBuffer, blockOffset, readCount);
AdvanceToNextLine(m_dataBuffer, blockOffset, readCount);
// need to rewrite the headers up to and including the x-mozilla-status2 header
writeCount = m_fileStream->write(m_dataBuffer, blockOffset);
}
// we should write out the existing keywords from the msg hdr, if any.
if (msgHdrKeywords.IsEmpty())
{ // no keywords, so write blank header
m_addedHeaderSize += m_fileStream->write(X_MOZILLA_KEYWORDS, sizeof(X_MOZILLA_KEYWORDS) - 1);
}
else else
{ {
writeCount = m_fileStream->write(m_dataBuffer, readCount); if (msgHdrKeywords.Length() < sizeof(X_MOZILLA_KEYWORDS) - sizeof(HEADER_X_MOZILLA_KEYWORDS) + 10 /* allow some slop */)
{ // keywords fit in normal blank header, so replace blanks in keyword hdr with keywords
nsCAutoString keywordsHdr(X_MOZILLA_KEYWORDS);
keywordsHdr.Replace(sizeof(HEADER_X_MOZILLA_KEYWORDS) + 1, msgHdrKeywords.Length(), msgHdrKeywords);
m_addedHeaderSize += m_fileStream->write(keywordsHdr.get(), keywordsHdr.Length());
} }
else
{ // keywords don't fit, so write out keywords on one line and an extra blank line
nsCString newKeywordHeader(HEADER_X_MOZILLA_KEYWORDS ": ");
newKeywordHeader.Append(msgHdrKeywords);
newKeywordHeader.Append(MSG_LINEBREAK EXTRA_KEYWORD_HDR);
m_addedHeaderSize += m_fileStream->write(newKeywordHeader.get(), newKeywordHeader.Length());
}
}
addKeywordHdr = PR_FALSE;
}
else if (needToGrowKeywords)
{
blockOffset = statusOffset;
if (!strncmp(m_dataBuffer + blockOffset, X_MOZILLA_STATUS, X_MOZILLA_STATUS_LEN))
AdvanceToNextLine(m_dataBuffer, blockOffset, readCount); // skip x-mozilla-status hdr
if (!strncmp(m_dataBuffer + blockOffset, X_MOZILLA_STATUS2, X_MOZILLA_STATUS2_LEN))
AdvanceToNextLine(m_dataBuffer, blockOffset, readCount); // skip x-mozilla-status2 hdr
PRUint32 preKeywordBlockOffset = blockOffset;
if (!strncmp(m_dataBuffer + blockOffset, HEADER_X_MOZILLA_KEYWORDS, sizeof(HEADER_X_MOZILLA_KEYWORDS) - 1))
{
do
{
// skip x-mozilla-keywords hdr and any existing continuation headers
AdvanceToNextLine(m_dataBuffer, blockOffset, readCount);
}
while (m_dataBuffer[blockOffset] == ' ');
}
PRInt32 oldKeywordSize = blockOffset - preKeywordBlockOffset;
// rewrite the headers up to and including the x-mozilla-status2 header
writeCount = m_fileStream->write(m_dataBuffer, preKeywordBlockOffset);
// let's just rewrite all the keywords on several lines and add a blank line,
// instead of worrying about which are missing.
PRBool done = PR_FALSE;
nsCAutoString keywordHdr(HEADER_X_MOZILLA_KEYWORDS ": ");
PRInt32 nextBlankOffset = 0;
PRInt32 curHdrLineStart = 0;
PRInt32 newKeywordSize = 0;
while (!done)
{
nextBlankOffset = msgHdrKeywords.FindChar(' ', nextBlankOffset);
if (nextBlankOffset == kNotFound)
{
nextBlankOffset = msgHdrKeywords.Length();
done = PR_TRUE;
}
if (nextBlankOffset - curHdrLineStart > 90 || done)
{
keywordHdr.Append(nsDependentCSubstring(msgHdrKeywords, curHdrLineStart, msgHdrKeywords.Length() - curHdrLineStart));
keywordHdr.Append(MSG_LINEBREAK);
newKeywordSize += m_fileStream->write(keywordHdr.get(), keywordHdr.Length());
curHdrLineStart = nextBlankOffset;
keywordHdr.Assign(' ');
}
nextBlankOffset++;
}
newKeywordSize += m_fileStream->write(EXTRA_KEYWORD_HDR, sizeof(EXTRA_KEYWORD_HDR) - 1);
m_addedHeaderSize += newKeywordSize - oldKeywordSize;
m_curSrcHdr->SetUint32Property("growKeywords", 0);
needToGrowKeywords = PR_FALSE;
writeCount += blockOffset - preKeywordBlockOffset; // fudge writeCount
}
NS_ASSERTION(readCount > blockOffset, "bad block offset");
writeCount += m_fileStream->write(m_dataBuffer + blockOffset, readCount - blockOffset);
count -= readCount; count -= readCount;
if (writeCount != readCount) if (writeCount != readCount)
{ {
@ -860,12 +971,16 @@ nsFolderCompactState::EndCopy(nsISupports *url, nsresult aStatus)
m_db->CopyHdrFromExistingHdr(m_startOfNewMsg, m_curSrcHdr, PR_TRUE, m_db->CopyHdrFromExistingHdr(m_startOfNewMsg, m_curSrcHdr, PR_TRUE,
getter_AddRefs(newMsgHdr)); getter_AddRefs(newMsgHdr));
m_curSrcHdr = nsnull; m_curSrcHdr = nsnull;
if (newMsgHdr && m_statusOffset != 0) if (newMsgHdr)
{
if ( m_statusOffset != 0)
newMsgHdr->SetStatusOffset(m_statusOffset);
if (m_addedHeaderSize != 0)
{ {
PRUint32 oldMsgSize; PRUint32 oldMsgSize;
newMsgHdr->SetStatusOffset(m_statusOffset);
(void) newMsgHdr->GetMessageSize(&oldMsgSize); (void) newMsgHdr->GetMessageSize(&oldMsgSize);
newMsgHdr->SetMessageSize(oldMsgSize + m_statusLineSize); newMsgHdr->SetMessageSize(oldMsgSize + m_addedHeaderSize);
}
} }
// m_db->Commit(nsMsgDBCommitType::kLargeCommit); // no sense commiting until the end // m_db->Commit(nsMsgDBCommitType::kLargeCommit); // no sense commiting until the end

Просмотреть файл

@ -79,6 +79,7 @@ protected:
void ShowCompactingStatusMsg(); void ShowCompactingStatusMsg();
void ShowDoneStatus(); void ShowDoneStatus();
nsresult CompactNextFolder(); nsresult CompactNextFolder();
void AdvanceToNextLine(const char *buffer, PRUint32 &bufferOffset, PRUint32 maxBufferOffset);
nsCString m_baseMessageUri; // base message uri nsCString m_baseMessageUri; // base message uri
@ -106,7 +107,7 @@ protected:
PRBool m_needStatusLine; PRBool m_needStatusLine;
PRBool m_startOfMsg; PRBool m_startOfMsg;
PRInt32 m_statusOffset; PRInt32 m_statusOffset;
PRInt32 m_statusLineSize; PRInt32 m_addedHeaderSize;
nsCOMPtr <nsISupportsArray> m_offlineFolderArray; nsCOMPtr <nsISupportsArray> m_offlineFolderArray;
}; };

Просмотреть файл

@ -0,0 +1,329 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* David Bienvenu <bienvenu@mozilla.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "msgCore.h"
#include "nsMsgTagService.h"
#include "nsIPrefService.h"
#include "nsISupportsPrimitives.h"
#include "nsMsgI18N.h"
#include "nsIPrefLocalizedString.h"
#include "nsMsgDBView.h" // for labels migration
#include "nsStringEnumerator.h"
NS_IMPL_ISUPPORTS1(nsMsgTagService, nsIMsgTagService)
nsMsgTagService::nsMsgTagService()
{
m_prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
// need to figure out how to migrate the tags only once.
MigrateLabelsToTags();
}
nsMsgTagService::~nsMsgTagService()
{
/* destructor code */
}
/* wstring getTagForKey (in string key); */
NS_IMETHODIMP nsMsgTagService::GetTagForKey(const nsACString &key, nsAString &_retval)
{
nsCAutoString prefName("mailnews.tags.");
prefName.Append(key);
prefName.AppendLiteral(".tag");
return GetUnicharPref(prefName.get(), _retval);
}
/* void setTagForKey (in string key); */
NS_IMETHODIMP nsMsgTagService::SetTagForKey(const nsACString &key, const nsAString &tag )
{
nsCAutoString prefName("mailnews.tags.");
prefName.Append(key);
prefName.AppendLiteral(".tag");
return SetUnicharPref(prefName.get(), tag);
}
/* void getKeyForTag (in wstring tag); */
NS_IMETHODIMP nsMsgTagService::GetKeyForTag(const nsAString &aTag, nsACString &aKey)
{
PRUint32 count;
char **prefList;
nsresult rv = m_prefBranch->GetChildList("mailnews.tags.", &count, &prefList);
NS_ENSURE_SUCCESS(rv, rv);
// traverse the list, and look for a pref with the desired tag value.
for (PRUint32 i = count; i--; )
{
// The prefname we passed to GetChildList was of the form
// "mailnews.tags." and we are returned the descendants
// in the form of ""mailnews.tags.<key>.tag"
// But we only want the tags, so check that the string
// ends with tag.
if (StringEndsWith(nsDependentCString(prefList[i]), NS_LITERAL_CSTRING(".tag")))
{
nsAutoString curTag;
GetUnicharPref(prefList[i], curTag);
if (aTag.Equals(curTag))
{
aKey = Substring(nsDependentCString(prefList[i]), 14, strlen(prefList[i]) - 18);
break;
}
}
}
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, prefList);
return aKey.IsEmpty() ? NS_ERROR_FAILURE : NS_OK;
}
/* void addTagForKey (in string key, in wstring tag, in long color); */
NS_IMETHODIMP nsMsgTagService::AddTagForKey(const nsACString &key, const nsAString &tag, const nsACString &color)
{
nsCAutoString prefName("mailnews.tags.");
prefName.Append(key);
prefName.AppendLiteral(".tag");
SetUnicharPref(prefName.get(), tag);
prefName.Replace(prefName.Length() - 3, 3, NS_LITERAL_CSTRING("color"));
m_prefBranch->SetCharPref(prefName.get(), PromiseFlatCString(color).get());
return NS_OK;
}
/* void addTag (in wstring tag, in long color); */
NS_IMETHODIMP nsMsgTagService::AddTag(const nsAString &tag, const nsACString &color)
{
nsCAutoString prefName("mailnews.tags.");
// figure out key from tag. Apply transformation stripping out
// illegal characters like <SP> and then convert to imap mod utf7.
// Then, check if we have a tag with that key yet, and if so,
// make it unique by appending -1, -2, etc.
// Should we use an iterator?
nsAutoString transformedTag(tag);
transformedTag.ReplaceChar(" ()/{%*<>\\\"", '_');
nsCAutoString key;
CopyUTF16toMUTF7(transformedTag, key);
prefName.Append(key);
while (PR_TRUE)
{
nsAutoString tagValue;
GetUnicharPref(prefName.get(), tagValue);
if (tagValue.IsEmpty() || tagValue.Equals(tag))
return AddTagForKey(key, tag, color);
prefName.Append('A');
}
NS_ASSERTION(PR_FALSE, "can't get here");
return NS_ERROR_FAILURE;
}
/* long getColorForKey (in string key); */
NS_IMETHODIMP nsMsgTagService::GetColorForKey(const nsACString &key, nsACString &_retval)
{
nsCAutoString prefName("mailnews.tags.");
prefName.Append(key);
prefName.AppendLiteral(".color");
nsXPIDLCString color;
return m_prefBranch->GetCharPref(prefName.get(), getter_Copies(color));
_retval = color;
}
/* void deleteTag (in wstring tag); */
NS_IMETHODIMP nsMsgTagService::DeleteTag(const nsAString &tag)
{
// do we want to set a .deleted pref, or just set the tag
// property to "", or clear the pref(s)?
return NS_ERROR_NOT_IMPLEMENTED;
}
/* readonly attribute nsIStringEnumerator tagEnumerator; */
NS_IMETHODIMP nsMsgTagService::GetTagEnumerator(nsIStringEnumerator * *aTagEnumerator)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 count;
char **prefList;
rv = prefBranch->GetChildList("mailnews.tags.", &count, &prefList);
NS_ENSURE_SUCCESS(rv, rv);
nsStringArray *stringArray = new nsStringArray(count); // or should it be count / 2?
if (!stringArray)
return NS_ERROR_OUT_OF_MEMORY;
// traverse the list, and truncate all the descendant strings to just
// one branch level below the root branch.
for (PRUint32 i = count; i--; )
{
// The prefname we passed to GetChildList was of the form
// "mailnews.tags." and we are returned the descendants
// in the form of ""mailnews.tags.<key>.tag"
// But we only want the tags, so check that the string
// ends with tag.
if (StringEndsWith(nsDependentCString(prefList[i]), NS_LITERAL_CSTRING(".tag")))
{
nsAutoString tag;
GetUnicharPref(prefList[i], tag);
stringArray->AppendString(tag);
}
}
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, prefList);
return NS_NewAdoptingStringEnumerator(aTagEnumerator, stringArray);;
}
NS_IMETHODIMP nsMsgTagService::GetKeyEnumerator(nsIUTF8StringEnumerator * *aKeyEnumerator)
{
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 count;
char **prefList;
rv = prefBranch->GetChildList("mailnews.tags.", &count, &prefList);
NS_ENSURE_SUCCESS(rv, rv);
nsCStringArray *stringArray = new nsCStringArray; // or should it be count / 2?
if (!stringArray)
return NS_ERROR_OUT_OF_MEMORY;
// traverse the list, and truncate all the descendant strings to just
// one branch level below the root branch.
for (PRUint32 i = count; i--; )
{
// The prefname we passed to GetChildList was of the form
// "mailnews.tags." and we are returned the descendants
// in the form of ""mailnews.tags.<key>.tag"
// But we only want the keys, so check that the string
// ends with tag.
if (StringEndsWith(nsDependentCString(prefList[i]), NS_LITERAL_CSTRING(".tag")))
{
nsDependentCSubstring key(nsDependentCString(prefList[i]), 14, strlen(prefList[i]) - 18);
stringArray->AppendCString(key);
}
}
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, prefList);
return NS_NewAdoptingUTF8StringEnumerator(aKeyEnumerator, stringArray);;
}
/* End of implementation class template. */
nsresult
nsMsgTagService::getPrefService()
{
if (m_prefBranch)
return NS_OK;
nsresult rv;
m_prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv))
m_prefBranch = nsnull;
return rv;
}
nsresult nsMsgTagService::SetUnicharPref(const char *prefName,
const nsAString &val)
{
nsresult rv = getPrefService();
NS_ENSURE_SUCCESS(rv, rv);
if (!val.IsEmpty())
{
nsCOMPtr<nsISupportsString> supportsString =
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
if (supportsString)
{
supportsString->SetData(val);
rv = m_prefBranch->SetComplexValue(prefName,
NS_GET_IID(nsISupportsString),
supportsString);
}
}
else
{
m_prefBranch->ClearUserPref(prefName);
}
return rv;
}
nsresult nsMsgTagService::GetUnicharPref(const char *prefName,
nsAString &prefValue)
{
nsresult rv = getPrefService();
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISupportsString> supportsString =
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
if (supportsString)
{
rv = m_prefBranch->GetComplexValue(prefName,
NS_GET_IID(nsISupportsString),
getter_AddRefs(supportsString));
if (supportsString)
rv = supportsString->GetData(prefValue);
else
prefValue.Truncate();
}
return rv;
}
nsresult nsMsgTagService::MigrateLabelsToTags()
{
nsCString prefString;
PRInt32 prefVersion = 0;
nsresult rv = m_prefBranch->GetIntPref("mailnews.tags.version", &prefVersion);
if (NS_SUCCEEDED(rv) && prefVersion == 1)
return rv;
nsCOMPtr<nsIPrefLocalizedString> pls;
nsXPIDLString ucsval;
nsCAutoString labelKey("$label1");
for(PRInt32 i = 0; i < PREF_LABELS_MAX; )
{
prefString.Assign(PREF_LABELS_DESCRIPTION);
prefString.AppendInt(i + 1);
rv = m_prefBranch->GetComplexValue(prefString.get(), NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(pls));
NS_ENSURE_SUCCESS(rv, rv);
pls->ToString(getter_Copies(ucsval));
prefString.Assign(PREF_LABELS_COLOR);
prefString.AppendInt(i + 1);
nsXPIDLCString csval;
NS_ENSURE_SUCCESS(rv, rv);
rv = m_prefBranch->GetCharPref(prefString.get(), getter_Copies(csval));
NS_ENSURE_SUCCESS(rv, rv);
rv = AddTagForKey(labelKey, ucsval, csval);
labelKey.SetCharAt(++i + '1', 6);
}
m_prefBranch->SetIntPref("mailnews.tags.version", 1);
return rv;
}

Просмотреть файл

@ -0,0 +1,76 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* David Bienvenu <bienvenu@mozilla.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsMsgTagService_h__
#define nsMsgTagService_h__
#include "nsIMsgTagService.h"
#include "nsIPrefBranch.h"
class nsMsgTagEntry
{
public:
nsMsgTagEntry(const char *key, const PRUnichar *tag, PRUint32 color);
nsString m_tag;
nsCString m_key;
PRUint32 m_color;
};
class nsMsgTagService : public nsIMsgTagService
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMSGTAGSERVICE
nsMsgTagService();
private:
~nsMsgTagService();
protected:
nsresult getPrefService() ;
nsresult SetUnicharPref(const char *prefName,
const nsAString &prefValue);
nsresult GetUnicharPref(const char *prefName,
nsAString &prefValue);
nsresult MigrateLabelsToTags();
nsCOMPtr<nsIPrefBranch> m_prefBranch;
};
#endif

Просмотреть файл

@ -5250,3 +5250,79 @@ void nsMsgDBFolder::SetMRUTime()
SetStringProperty(MRU_TIME_PROPERTY, nowStr.get()); SetStringProperty(MRU_TIME_PROPERTY, nowStr.get());
} }
NS_IMETHODIMP nsMsgDBFolder::AddKeywordToMessages(nsISupportsArray *aMessages, const char *aKeyword)
{
nsresult rv = NS_OK;
GetDatabase(nsnull);
if (mDatabase)
{
PRUint32 count;
NS_ENSURE_ARG(aMessages);
nsresult rv = aMessages->Count(&count);
NS_ENSURE_SUCCESS(rv, rv);
nsXPIDLCString keywords;
for(PRUint32 i = 0; i < count; i++)
{
nsMsgKey msgKey;
nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aMessages, i, &rv);
NS_ENSURE_SUCCESS(rv, rv);
(void) message->GetMessageKey(&msgKey);
message->GetStringProperty("keywords", getter_Copies(keywords));
nsACString::const_iterator start, end;
if (!MsgFindKeyword(nsDependentCString(aKeyword), keywords, start, end))
{
if (!keywords.IsEmpty())
keywords.Append(' ');
keywords.Append(aKeyword);
mDatabase->SetStringProperty(msgKey, "keywords", keywords);
}
}
}
return rv;
}
NS_IMETHODIMP nsMsgDBFolder::RemoveKeywordFromMessages(nsISupportsArray *aMessages, const char *aKeyword)
{
nsresult rv = NS_OK;
GetDatabase(nsnull);
if (mDatabase)
{
PRUint32 count;
NS_ENSURE_ARG(aMessages);
nsresult rv = aMessages->Count(&count);
NS_ENSURE_SUCCESS(rv, rv);
nsXPIDLCString keywords;
// If the tag is also a label, we should remove the label too...
PRBool keywordIsLabel = (!strncmp(aKeyword, "$label", 6) && aKeyword[6] >= '1' && aKeyword[6] <= '5');
for(PRUint32 i = 0; i < count; i++)
{
nsMsgKey msgKey;
nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aMessages, i, &rv);
NS_ENSURE_SUCCESS(rv, rv);
(void) message->GetMessageKey(&msgKey);
if (keywordIsLabel)
{
nsMsgLabelValue labelValue;
message->GetLabel(&labelValue);
if (labelValue == aKeyword[6])
message->SetLabel(0);
}
rv = message->GetStringProperty("keywords", getter_Copies(keywords));
nsACString::const_iterator start, end;
nsACString::const_iterator saveStart;
keywords.BeginReading(saveStart);
if (MsgFindKeyword(nsDependentCString(aKeyword), keywords, start, end))
{
keywords.Cut(Distance(saveStart, start), Distance(start, end));
mDatabase->SetStringProperty(msgKey, "keywords", keywords);
}
}
}
return rv;
}

Просмотреть файл

@ -1221,3 +1221,36 @@ void GetSummaryFileLocation(nsFileSpec& fileLocation, nsFileSpec* summaryLocatio
summaryLocation->SetLeafName(fileName); summaryLocation->SetLeafName(fileName);
} }
PRBool MsgFindKeyword(nsACString &keyword, nsACString &keywords, nsACString::const_iterator &start, nsACString::const_iterator &end)
{
keywords.BeginReading(start);
keywords.EndReading(end);
if (*start == ' ')
start++;
nsACString::const_iterator saveStart(start), saveEnd(end);
while (PR_TRUE)
{
if (FindInReadable(keyword, start, end))
{
PRBool beginMatches = start == saveStart;
PRBool endMatches = end == saveEnd;
nsACString::const_iterator beforeStart(start);
beforeStart--;
// start and end point to the beginning and end of the match
if (beginMatches && (end == saveEnd || *end == ' ')
|| (endMatches && *beforeStart == ' ')
|| *beforeStart == ' ' && *end == ' ')
{
if (*end == ' ')
end++;
return PR_TRUE;
}
else
start = end; // advance past bogus match.
}
else
break;
}
return PR_FALSE;
}

Просмотреть файл

@ -158,6 +158,11 @@ NS_MSG_BASE nsresult GetSummaryFileLocation(nsIFileSpec* fileLocation,
// on bug 33451 to remove nsIFileSpec from mailnews. // on bug 33451 to remove nsIFileSpec from mailnews.
NS_MSG_BASE void GetSummaryFileLocation(nsFileSpec& fileLocation, NS_MSG_BASE void GetSummaryFileLocation(nsFileSpec& fileLocation,
nsFileSpec* summaryLocation); nsFileSpec* summaryLocation);
// fills in the position of the passed in keyword in the passed in keyword list
// and returns false if the keyword isn't present
NS_MSG_BASE PRBool MsgFindKeyword(nsACString &keyword, nsACString &keywords,
nsACString::const_iterator &start,
nsACString::const_iterator &end);
#endif #endif

Просмотреть файл

@ -103,6 +103,8 @@
#include "nsCidProtocolHandler.h" #include "nsCidProtocolHandler.h"
#include "nsRssIncomingServer.h" #include "nsRssIncomingServer.h"
#include "nsRssService.h" #include "nsRssService.h"
#include "nsMsgTagService.h"
#ifdef XP_WIN #ifdef XP_WIN
#include "nsMessengerWinIntegration.h" #include "nsMessengerWinIntegration.h"
#endif #endif
@ -330,6 +332,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgGroupView)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgOfflineManager) NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgOfflineManager)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgProgress) NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgProgress)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSpamSettings) NS_GENERIC_FACTORY_CONSTRUCTOR(nsSpamSettings)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgTagService)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsCidProtocolHandler) NS_GENERIC_FACTORY_CONSTRUCTOR(nsCidProtocolHandler)
#ifdef XP_WIN #ifdef XP_WIN
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMessengerWinIntegration, Init) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMessengerWinIntegration, Init)
@ -826,6 +829,10 @@ static const nsModuleComponentInfo gComponents[] = {
NS_SPAMSETTINGS_CONTRACTID, NS_SPAMSETTINGS_CONTRACTID,
nsSpamSettingsConstructor, nsSpamSettingsConstructor,
}, },
{ "Tag Service", NS_MSGTAGSERVICE_CID,
NS_MSGTAGSERVICE_CONTRACTID,
nsMsgTagServiceConstructor,
},
{ "cid protocol", NS_CIDPROTOCOL_CID, { "cid protocol", NS_CIDPROTOCOL_CID,
NS_CIDPROTOCOLHANDLER_CONTRACTID, NS_CIDPROTOCOLHANDLER_CONTRACTID,
nsCidProtocolHandlerConstructor, nsCidProtocolHandlerConstructor,

Просмотреть файл

@ -47,7 +47,6 @@ nsMsgRecipientArray::nsMsgRecipientArray()
nsMsgRecipientArray::~nsMsgRecipientArray() nsMsgRecipientArray::~nsMsgRecipientArray()
{ {
if (m_array)
delete m_array; delete m_array;
} }

Просмотреть файл

@ -876,6 +876,8 @@ nsMsgSendLater::BuildHeaders()
prune_p = do_flags_p = PR_TRUE; prune_p = do_flags_p = PR_TRUE;
else if (!PL_strncasecmp(HEADER_X_MOZILLA_DRAFT_INFO, buf, end - buf)) else if (!PL_strncasecmp(HEADER_X_MOZILLA_DRAFT_INFO, buf, end - buf))
prune_p = do_return_receipt_p = PR_TRUE; prune_p = do_return_receipt_p = PR_TRUE;
else if (!PL_strncasecmp(HEADER_X_MOZILLA_KEYWORDS, buf, end - buf))
prune_p = PR_TRUE;
else if (!PL_strncasecmp(HEADER_X_MOZILLA_NEWSHOST, buf, end - buf)) else if (!PL_strncasecmp(HEADER_X_MOZILLA_NEWSHOST, buf, end - buf))
{ {
prune_p = PR_TRUE; prune_p = PR_TRUE;

Просмотреть файл

@ -290,7 +290,9 @@ interface nsIMsgDatabase : nsIDBChangeAnnouncer {
// for msg hdr hash table allocation. controllable by caller to improve folder loading preformance. // for msg hdr hash table allocation. controllable by caller to improve folder loading preformance.
attribute unsigned long msgHdrCacheSize; attribute unsigned long msgHdrCacheSize;
void setFolderStream(in nsIOFileStream aFileStream); // extremely deprecated - this is going away when we get rid of nsFileSpec and friends.
// and it's [noscript] though I don't know if you can make an attribute noscript.
attribute nsIOFileStream folderStream;
/** /**
* The list of messages currently in the NEW state. * The list of messages currently in the NEW state.

Просмотреть файл

@ -43,7 +43,7 @@ typedef unsigned short imapMessageFlagsType;
typedef long nsOfflineImapOperationType; typedef long nsOfflineImapOperationType;
[scriptable, uuid(7cc7dec6-ea50-11d4-a5b7-0060b0fc04b7)] [scriptable, uuid(2728cb2b-4716-4b5e-98a7-ce22569378e5)]
interface nsIMsgOfflineImapOperation : nsISupports interface nsIMsgOfflineImapOperation : nsISupports
{ {
@ -58,6 +58,8 @@ interface nsIMsgOfflineImapOperation : nsISupports
const long kMsgMarkedDeleted = 0x80; const long kMsgMarkedDeleted = 0x80;
const long kAppendTemplate = 0x100; const long kAppendTemplate = 0x100;
const long kDeleteAllMsgs = 0x200; const long kDeleteAllMsgs = 0x200;
const long kAddKeywords = 0x400;
const long kRemoveKeywords = 0x800;
attribute nsOfflineImapOperationType operation; attribute nsOfflineImapOperationType operation;
void clearOperation(in nsOfflineImapOperationType operation); void clearOperation(in nsOfflineImapOperationType operation);
@ -66,6 +68,10 @@ interface nsIMsgOfflineImapOperation : nsISupports
attribute imapMessageFlagsType newFlags; // for kFlagsChanged attribute imapMessageFlagsType newFlags; // for kFlagsChanged
attribute string destinationFolderURI; // for move or copy attribute string destinationFolderURI; // for move or copy
attribute string sourceFolderURI; attribute string sourceFolderURI;
void addKeywordToAdd(in string aKeyword);
void addKeywordToRemove(in string aKeyword);
readonly attribute string keywordsToAdd;
readonly attribute string keywordsToRemove;
readonly attribute long numberOfCopies; readonly attribute long numberOfCopies;
void addMessageCopyOperation(in string destinationBox); void addMessageCopyOperation(in string destinationBox);
string getCopyDestination(in long copyIndex); string getCopyDestination(in long copyIndex);

Просмотреть файл

@ -57,6 +57,7 @@ public:
NS_IMETHOD ForceClosed(); NS_IMETHOD ForceClosed();
NS_IMETHOD SetFolderStream(nsIOFileStream *aFileStream); NS_IMETHOD SetFolderStream(nsIOFileStream *aFileStream);
NS_IMETHOD GetFolderStream(nsIOFileStream **aFileStream);
NS_IMETHOD AddNewHdrToDB(nsIMsgDBHdr *newHdr, PRBool notify); NS_IMETHOD AddNewHdrToDB(nsIMsgDBHdr *newHdr, PRBool notify);
NS_IMETHOD SetAttributesOnPendingHdr(nsIMsgDBHdr *pendingHdr, const char *property, NS_IMETHOD SetAttributesOnPendingHdr(nsIMsgDBHdr *pendingHdr, const char *property,
const char *propertyVal, PRInt32 flags); const char *propertyVal, PRInt32 flags);

Просмотреть файл

@ -76,6 +76,7 @@ public:
NS_IMETHOD ListAllOfflineDeletes(nsMsgKeyArray *offlineDeletes); NS_IMETHOD ListAllOfflineDeletes(nsMsgKeyArray *offlineDeletes);
NS_IMETHOD SetFolderStream(nsIOFileStream *aFileStream); NS_IMETHOD SetFolderStream(nsIOFileStream *aFileStream);
NS_IMETHOD GetFolderStream(nsIOFileStream **aFileStream);
friend class nsMsgOfflineOpEnumerator; friend class nsMsgOfflineOpEnumerator;
protected: protected:

Просмотреть файл

@ -188,7 +188,7 @@ protected:
static void AddToCache(nsMsgDatabase* pMessageDB) static void AddToCache(nsMsgDatabase* pMessageDB)
{ {
#ifdef DEBUG_David_Bienvenu #ifdef DEBUG_David_Bienvenu
NS_ASSERTION(GetNumInCache() < 28, "28 or more open db's"); NS_ASSERTION(GetNumInCache() < 40, "40 or more open db's");
#endif #endif
GetDBCache()->AppendElement(pMessageDB);} GetDBCache()->AppendElement(pMessageDB);}
static void RemoveFromCache(nsMsgDatabase* pMessageDB); static void RemoveFromCache(nsMsgDatabase* pMessageDB);

Просмотреть файл

@ -117,6 +117,11 @@ NS_IMETHODIMP nsImapMailDatabase::ForceClosed()
return nsMailDatabase::ForceClosed(); return nsMailDatabase::ForceClosed();
} }
NS_IMETHODIMP nsImapMailDatabase::GetFolderStream(nsIOFileStream **aFileStream)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsImapMailDatabase::SetFolderStream(nsIOFileStream *aFileStream) NS_IMETHODIMP nsImapMailDatabase::SetFolderStream(nsIOFileStream *aFileStream)
{ {
NS_ASSERTION(0, "Trying to set folderStream, not implemented"); NS_ASSERTION(0, "Trying to set folderStream, not implemented");

Просмотреть файл

@ -79,6 +79,19 @@ NS_IMETHODIMP nsMailDatabase::SetFolderStream(nsIOFileStream *aFileStream)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP nsMailDatabase::GetFolderStream(nsIOFileStream **aFileStream)
{
NS_ENSURE_ARG_POINTER(aFileStream);
if (!m_folderStream)
{
m_folderStream = new nsIOFileStream(nsFileSpec(*m_folderSpec));
m_ownFolderStream = PR_TRUE;
}
// N.B. - not a ref-counted interface pointer
*aFileStream = m_folderStream;
return NS_OK;
}
static PRBool gGotGlobalPrefs = PR_FALSE; static PRBool gGotGlobalPrefs = PR_FALSE;
static PRInt32 gTimeStampLeeway; static PRInt32 gTimeStampLeeway;

Просмотреть файл

@ -4756,6 +4756,11 @@ NS_IMETHODIMP nsMsgDatabase::ResetHdrCacheSize(PRUint32 aSize)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP nsMsgDatabase::GetFolderStream(nsIOFileStream **aFileStream)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsMsgDatabase::SetFolderStream(nsIOFileStream *aFileStream) NS_IMETHODIMP nsMsgDatabase::SetFolderStream(nsIOFileStream *aFileStream)
{ {
NS_ASSERTION(0, "Trying to set the folderStream, not implemented"); NS_ASSERTION(0, "Trying to set the folderStream, not implemented");

Просмотреть файл

@ -43,6 +43,7 @@
#include "msgCore.h" #include "msgCore.h"
#include "nsMsgOfflineImapOperation.h" #include "nsMsgOfflineImapOperation.h"
#include "nsReadableUtils.h" #include "nsReadableUtils.h"
#include "nsMsgUtils.h"
PRLogModuleInfo *IMAPOffline; PRLogModuleInfo *IMAPOffline;
@ -61,6 +62,8 @@ NS_IMPL_ISUPPORTS1(nsMsgOfflineImapOperation, nsIMsgOfflineImapOperation)
#define PROP_NUM_COPY_DESTS "numCopyDests" #define PROP_NUM_COPY_DESTS "numCopyDests"
#define PROP_COPY_DESTS "copyDests" // how to delimit these? Or should we do the "dest1","dest2" etc trick? But then we'd need to shuffle #define PROP_COPY_DESTS "copyDests" // how to delimit these? Or should we do the "dest1","dest2" etc trick? But then we'd need to shuffle
// them around since we delete off the front first. // them around since we delete off the front first.
#define PROP_KEYWORD_ADD "addedKeywords"
#define PROP_KEYWORD_REMOVE "removedKeywords"
nsMsgOfflineImapOperation::nsMsgOfflineImapOperation(nsMsgDatabase *db, nsIMdbRow *row) nsMsgOfflineImapOperation::nsMsgOfflineImapOperation(nsMsgDatabase *db, nsIMdbRow *row)
{ {
@ -209,6 +212,77 @@ NS_IMETHODIMP nsMsgOfflineImapOperation::SetSourceFolderURI(const char * aSource
return m_mdb->SetProperty(m_mdbRow, PROP_SRC_FOLDER_URI, aSourceFolderURI); return m_mdb->SetProperty(m_mdbRow, PROP_SRC_FOLDER_URI, aSourceFolderURI);
} }
/* attribute string keyword; */
NS_IMETHODIMP nsMsgOfflineImapOperation::GetKeywordsToAdd(char * *aKeywords)
{
NS_ENSURE_ARG(aKeywords);
nsresult rv = m_mdb->GetProperty(m_mdbRow, PROP_KEYWORD_ADD, getter_Copies(m_keywordsToAdd));
*aKeywords = nsCRT::strdup(m_keywordsToAdd);
return rv;
}
NS_IMETHODIMP nsMsgOfflineImapOperation::AddKeywordToAdd(const char * aKeyword)
{
return AddKeyword(aKeyword, m_keywordsToAdd, PROP_KEYWORD_ADD, m_keywordsToRemove, PROP_KEYWORD_REMOVE);
nsACString::const_iterator start, end;
if (!MsgFindKeyword(nsDependentCString(aKeyword), m_keywordsToAdd, start, end))
{
if (!m_keywordsToAdd.IsEmpty())
m_keywordsToAdd.Append(' ');
m_keywordsToAdd.Append(aKeyword);
}
// if the keyword we're adding was in the list of keywords to remove,
// cut it from that list.
nsACString::const_iterator removeStart, removeEnd;
if (MsgFindKeyword(nsDependentCString(aKeyword), m_keywordsToRemove, removeStart, removeEnd))
{
nsACString::const_iterator saveStart;
m_keywordsToRemove.BeginReading(saveStart);
m_keywordsToRemove.Cut(Distance(saveStart, removeStart), Distance(removeStart, removeEnd));
m_mdb->SetProperty(m_mdbRow, PROP_KEYWORD_REMOVE, m_keywordsToRemove.get());
}
SetOperation(kAddKeywords);
return m_mdb->SetProperty(m_mdbRow, PROP_KEYWORD_ADD, m_keywordsToAdd.get());
}
NS_IMETHODIMP nsMsgOfflineImapOperation::GetKeywordsToRemove(char * *aKeywords)
{
NS_ENSURE_ARG(aKeywords);
nsresult rv = m_mdb->GetProperty(m_mdbRow, PROP_KEYWORD_REMOVE, getter_Copies(m_keywordsToRemove));
*aKeywords = nsCRT::strdup(m_keywordsToRemove);
return rv;
}
nsresult nsMsgOfflineImapOperation::AddKeyword(const char *aKeyword, nsCString &addList, const char *addProp,
nsCString &removeList, const char *removeProp)
{
nsACString::const_iterator start, end;
if (!MsgFindKeyword(nsDependentCString(aKeyword), addList, start, end))
{
if (!addList.IsEmpty())
addList.Append(' ');
addList.Append(aKeyword);
}
// if the keyword we're removing was in the list of keywords to add,
// cut it from that list.
nsACString::const_iterator addStart, addEnd;
if (MsgFindKeyword(nsDependentCString(aKeyword), removeList, addStart, addEnd))
{
nsACString::const_iterator saveStart;
removeList.BeginReading(saveStart);
removeList.Cut(Distance(saveStart, addStart), Distance(addStart, addEnd));
m_mdb->SetProperty(m_mdbRow, removeProp, removeList.get());
}
SetOperation(kRemoveKeywords);
return m_mdb->SetProperty(m_mdbRow, addProp, addList.get());
}
NS_IMETHODIMP nsMsgOfflineImapOperation::AddKeywordToRemove(const char * aKeyword)
{
return AddKeyword(aKeyword, m_keywordsToRemove, PROP_KEYWORD_REMOVE, m_keywordsToAdd, PROP_KEYWORD_ADD);
}
NS_IMETHODIMP nsMsgOfflineImapOperation::AddMessageCopyOperation(const char *destinationBox) NS_IMETHODIMP nsMsgOfflineImapOperation::AddMessageCopyOperation(const char *destinationBox)
{ {
SetOperation(kMsgCopy); SetOperation(kMsgCopy);
@ -325,5 +399,13 @@ void nsMsgOfflineImapOperation::Log(PRLogModuleInfo *logFile)
{ {
PR_LOG(IMAPOffline, PR_LOG_ALWAYS, ("msg id %x append draft", m_messageKey)); PR_LOG(IMAPOffline, PR_LOG_ALWAYS, ("msg id %x append draft", m_messageKey));
} }
if (m_operation & nsIMsgOfflineImapOperation::kAddKeywords)
{
PR_LOG(IMAPOffline, PR_LOG_ALWAYS, ("msg id %x add keyword:%s", m_messageKey, m_keywordsToAdd.get()));
}
if (m_operation & nsIMsgOfflineImapOperation::kRemoveKeywords)
{
PR_LOG(IMAPOffline, PR_LOG_ALWAYS, ("msg id %x remove keyword:%s", m_messageKey, m_keywordsToRemove.get()));
}
} }

Просмотреть файл

@ -57,6 +57,9 @@ public:
nsresult SetCopiesToDB(); nsresult SetCopiesToDB();
void Log(PRLogModuleInfo *logFile); void Log(PRLogModuleInfo *logFile);
protected: protected:
nsresult AddKeyword(const char *aKeyword, nsCString &addList, const char *addProp,
nsCString &removeList, const char *removeProp);
nsOfflineImapOperationType m_operation; nsOfflineImapOperationType m_operation;
nsMsgKey m_messageKey; nsMsgKey m_messageKey;
nsMsgKey m_sourceMessageKey; nsMsgKey m_sourceMessageKey;
@ -69,6 +72,9 @@ protected:
nsXPIDLCString m_moveDestination; nsXPIDLCString m_moveDestination;
nsCStringArray m_copyDestinations; nsCStringArray m_copyDestinations;
nsXPIDLCString m_keywordsToAdd;
nsXPIDLCString m_keywordsToRemove;
// nsMsgOfflineImapOperation will have to know what db and row they belong to, since they are really // nsMsgOfflineImapOperation will have to know what db and row they belong to, since they are really
// just a wrapper around the offline operation row in the mdb. // just a wrapper around the offline operation row in the mdb.
// though I hope not. // though I hope not.

Просмотреть файл

@ -3462,6 +3462,16 @@ NS_IMETHODIMP nsImapMailFolder::ApplyFilterHit(nsIMsgFilter *filter, nsIMsgWindo
keysToFlag.GetSize(), nsnull); keysToFlag.GetSize(), nsnull);
} }
break; break;
case nsMsgFilterAction::AddTag:
{
nsXPIDLCString keyword;
filterAction->GetStrValue(getter_Copies(keyword));
nsCOMPtr<nsISupportsArray> messageArray;
NS_NewISupportsArray(getter_AddRefs(messageArray));
messageArray->AppendElement(msgHdr);
AddKeywordToMessages(messageArray, keyword.get());
break;
}
case nsMsgFilterAction::JunkScore: case nsMsgFilterAction::JunkScore:
{ {
nsCAutoString junkScoreStr; nsCAutoString junkScoreStr;
@ -7063,7 +7073,7 @@ nsImapFolderCopyState::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode)
PRUint32 childCount; PRUint32 childCount;
m_srcFolder->Count(&childCount); m_srcFolder->Count(&childCount);
for (PRInt32 childIndex = 0; childIndex < childCount; childIndex++) for (PRUint32 childIndex = 0; childIndex < childCount; childIndex++)
{ {
nsCOMPtr <nsISupports> child = do_QueryElementAt(m_srcFolder, childIndex, &rv); nsCOMPtr <nsISupports> child = do_QueryElementAt(m_srcFolder, childIndex, &rv);
if (NS_SUCCEEDED(rv)) if (NS_SUCCEEDED(rv))
@ -7931,7 +7941,7 @@ NS_IMETHODIMP nsImapMailFolder::RenameClient(nsIMsgWindow *msgWindow, nsIMsgFold
nsCOMPtr<nsIMsgFolder> msgParent; nsCOMPtr<nsIMsgFolder> msgParent;
msgFolder->GetParentMsgFolder(getter_AddRefs(msgParent)); msgFolder->GetParentMsgFolder(getter_AddRefs(msgParent));
msgFolder->SetParent(nsnull); msgFolder->SetParent(nsnull);
msgParent->PropagateDelete(msgFolder,PR_FALSE, nsnull); msgParent->PropagateDelete(msgFolder, PR_TRUE, nsnull);
// Reset online status now that the folder is renamed. // Reset online status now that the folder is renamed.
nsCOMPtr <nsIMsgImapMailFolder> oldImapFolder = do_QueryInterface(msgFolder); nsCOMPtr <nsIMsgImapMailFolder> oldImapFolder = do_QueryInterface(msgFolder);
@ -8149,6 +8159,28 @@ nsImapMailFolder::StoreCustomKeywords(nsIMsgWindow *aMsgWindow, const char *aFla
const char *aFlagsToSubtract, nsMsgKey *aKeysToStore, PRUint32 aNumKeys, nsIURI **_retval) const char *aFlagsToSubtract, nsMsgKey *aKeysToStore, PRUint32 aNumKeys, nsIURI **_retval)
{ {
nsresult rv; nsresult rv;
if (WeAreOffline())
{
GetDatabase(nsnull);
if (mDatabase)
{
for (PRUint32 keyIndex = 0; keyIndex < aNumKeys; keyIndex++)
{
nsCOMPtr <nsIMsgOfflineImapOperation> op;
rv = mDatabase->GetOfflineOpForKey(aKeysToStore[keyIndex], PR_TRUE, getter_AddRefs(op));
SetFlag(MSG_FOLDER_FLAG_OFFLINEEVENTS);
if (NS_SUCCEEDED(rv) && op)
{
if (aFlagsToAdd)
op->AddKeywordToAdd(aFlagsToAdd);
if (aFlagsToSubtract)
op->AddKeywordToRemove(aFlagsToSubtract);
}
}
mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); // flush offline ops
return rv;
}
}
nsCOMPtr<nsIImapService> imapService(do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv)); nsCOMPtr<nsIImapService> imapService(do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString msgIds; nsCAutoString msgIds;
@ -8471,3 +8503,34 @@ NS_IMETHODIMP nsImapMailFolder::FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUi
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP nsImapMailFolder::AddKeywordToMessages(nsISupportsArray *aMessages, const char *aKeyword)
{
nsresult rv = nsMsgDBFolder::AddKeywordToMessages(aMessages, aKeyword);
if (NS_SUCCEEDED(rv))
{
nsCAutoString messageIds;
nsMsgKeyArray keys;
rv = BuildIdsAndKeyArray(aMessages, messageIds, keys);
NS_ENSURE_SUCCESS(rv, rv);
rv = StoreCustomKeywords(nsnull, aKeyword, nsnull, keys.GetArray(), keys.GetSize(), nsnull);
if (mDatabase)
mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
}
return rv;
}
NS_IMETHODIMP nsImapMailFolder::RemoveKeywordFromMessages(nsISupportsArray *aMessages, const char *aKeyword)
{
nsresult rv = nsMsgDBFolder::RemoveKeywordFromMessages(aMessages, aKeyword);
if (NS_SUCCEEDED(rv))
{
nsCAutoString messageIds;
nsMsgKeyArray keys;
nsresult rv = BuildIdsAndKeyArray(aMessages, messageIds, keys);
NS_ENSURE_SUCCESS(rv, rv);
rv = StoreCustomKeywords(nsnull, nsnull, aKeyword, keys.GetArray(), keys.GetSize(), nsnull);
if (mDatabase)
mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
}
return rv;
}

Просмотреть файл

@ -291,6 +291,9 @@ public:
PRBool aLocalOnly, nsIUrlListener *aUrlListener, PRBool aLocalOnly, nsIUrlListener *aUrlListener,
PRBool *aAsyncResults); PRBool *aAsyncResults);
NS_IMETHOD AddKeywordToMessages(nsISupportsArray *aMessages, const char *aKeyword);
NS_IMETHOD RemoveKeywordFromMessages(nsISupportsArray *aMessages, const char *aKeyword);
// nsIMsgImapMailFolder methods // nsIMsgImapMailFolder methods
NS_DECL_NSIMSGIMAPMAILFOLDER NS_DECL_NSIMSGIMAPMAILFOLDER

Просмотреть файл

@ -294,6 +294,77 @@ void nsImapOfflineSync::ProcessFlagOperation(nsIMsgOfflineImapOperation *op)
ProcessNextOperation(); ProcessNextOperation();
} }
void nsImapOfflineSync::ProcessKeywordOperation(nsIMsgOfflineImapOperation *op)
{
nsCOMPtr <nsIMsgOfflineImapOperation> currentOp = op;
nsMsgKeyArray matchingKeywordKeys;
PRUint32 currentKeyIndex = m_KeyIndex;
nsXPIDLCString keywords;
if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords)
currentOp->GetKeywordsToAdd(getter_Copies(keywords));
else
currentOp->GetKeywordsToRemove(getter_Copies(keywords));
PRBool keywordsMatch = PR_TRUE;
do
{ // loop for all messsages with the same keywords
if (keywordsMatch)
{
nsMsgKey curKey;
currentOp->GetMessageKey(&curKey);
matchingKeywordKeys.Add(curKey);
currentOp->ClearOperation(mCurrentPlaybackOpType);
}
currentOp = nsnull;
if (++currentKeyIndex < m_CurrentKeys.GetSize())
m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex], PR_FALSE,
getter_AddRefs(currentOp));
if (currentOp)
{
nsXPIDLCString curOpKeywords;
nsOfflineImapOperationType operation;
currentOp->GetOperation(&operation);
if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords)
currentOp->GetKeywordsToAdd(getter_Copies(curOpKeywords));
else
currentOp->GetKeywordsToRemove(getter_Copies(curOpKeywords));
keywordsMatch = (operation & mCurrentPlaybackOpType)
&& (curOpKeywords.Equals(keywords));
}
} while (currentOp);
if (matchingKeywordKeys.GetSize() > 0)
{
PRUint32 curFolderFlags;
m_currentFolder->GetFlags(&curFolderFlags);
if (curFolderFlags & MSG_FOLDER_FLAG_IMAPBOX)
{
nsresult rv = NS_OK;
nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_currentFolder);
nsCOMPtr <nsIURI> uriToStoreCustomKeywords;
if (imapFolder)
{
rv = imapFolder->StoreCustomKeywords(m_window,
(mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords) ? keywords.get() : nsnull,
(mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kRemoveKeywords) ? keywords.get() : nsnull,
matchingKeywordKeys.GetArray(),
matchingKeywordKeys.GetSize(), getter_AddRefs(uriToStoreCustomKeywords));
if (NS_SUCCEEDED(rv) && uriToStoreCustomKeywords)
{
nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(uriToStoreCustomKeywords);
if (mailnewsUrl)
mailnewsUrl->RegisterListener(this);
}
}
}
}
else
ProcessNextOperation();
}
void void
nsImapOfflineSync::ProcessAppendMsgOperation(nsIMsgOfflineImapOperation *currentOp, PRInt32 opType) nsImapOfflineSync::ProcessAppendMsgOperation(nsIMsgOfflineImapOperation *currentOp, PRInt32 opType)
{ {
@ -800,6 +871,20 @@ nsresult nsImapOfflineSync::ProcessNextOperation()
{ {
// we are done with the current type // we are done with the current type
if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kFlagsChanged) if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kFlagsChanged)
{
mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kAddKeywords;
// recurse to deal with next type of operation
m_KeyIndex = 0;
ProcessNextOperation();
}
else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords)
{
mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kRemoveKeywords;
// recurse to deal with next type of operation
m_KeyIndex = 0;
ProcessNextOperation();
}
else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kRemoveKeywords)
{ {
mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kMsgCopy; mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kMsgCopy;
// recurse to deal with next type of operation // recurse to deal with next type of operation
@ -844,6 +929,9 @@ nsresult nsImapOfflineSync::ProcessNextOperation()
{ {
if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kFlagsChanged) if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kFlagsChanged)
ProcessFlagOperation(currentOp); ProcessFlagOperation(currentOp);
else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords
||mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kRemoveKeywords)
ProcessKeywordOperation(currentOp);
else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kMsgCopy) else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kMsgCopy)
ProcessCopyOperation(currentOp); ProcessCopyOperation(currentOp);
else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kMsgMoved) else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kMsgMoved)

Просмотреть файл

@ -73,6 +73,7 @@ protected:
void DeleteAllOfflineOpsForCurrentDB(); void DeleteAllOfflineOpsForCurrentDB();
void ProcessFlagOperation(nsIMsgOfflineImapOperation *currentOp); void ProcessFlagOperation(nsIMsgOfflineImapOperation *currentOp);
void ProcessKeywordOperation(nsIMsgOfflineImapOperation *op);
void ProcessMoveOperation(nsIMsgOfflineImapOperation *currentOp); void ProcessMoveOperation(nsIMsgOfflineImapOperation *currentOp);
void ProcessCopyOperation(nsIMsgOfflineImapOperation *currentOp); void ProcessCopyOperation(nsIMsgOfflineImapOperation *currentOp);
void ProcessEmptyTrash(nsIMsgOfflineImapOperation *currentOp); void ProcessEmptyTrash(nsIMsgOfflineImapOperation *currentOp);

Просмотреть файл

@ -86,7 +86,6 @@
#include "nsICopyMsgStreamListener.h" #include "nsICopyMsgStreamListener.h"
#include "nsIFileStream.h" #include "nsIFileStream.h"
#include "nsIMsgParseMailMsgState.h" #include "nsIMsgParseMailMsgState.h"
#include "nsMsgLineBuffer.h"
#include "nsMsgLocalCID.h" #include "nsMsgLocalCID.h"
#include "nsIOutputStream.h" #include "nsIOutputStream.h"
#include "nsIDocShell.h" #include "nsIDocShell.h"
@ -1589,10 +1588,6 @@ nsImapService::SetMessageFlags(nsIEventTarget * aClientEventTarget,
imapMessageFlagsType flags, imapMessageFlagsType flags,
PRBool messageIdsAreUID) PRBool messageIdsAreUID)
{ {
// create a protocol instance to handle the request.
// NOTE: once we start working with multiple connections, this step will be much more complicated...but for now
// just create a connection and process the request.
return DiddleFlags(aClientEventTarget, aImapMailFolder, aUrlListener, aURL, messageIdentifierList, return DiddleFlags(aClientEventTarget, aImapMailFolder, aUrlListener, aURL, messageIdentifierList,
"setmsgflags", flags, messageIdsAreUID); "setmsgflags", flags, messageIdsAreUID);
} }

Просмотреть файл

@ -2507,11 +2507,13 @@ keepGoing:
void nsMsgLocalMailFolder::CopyPropertiesToMsgHdr(nsIMsgDBHdr *destHdr, nsIMsgDBHdr *srcHdr) void nsMsgLocalMailFolder::CopyPropertiesToMsgHdr(nsIMsgDBHdr *destHdr, nsIMsgDBHdr *srcHdr)
{ {
nsXPIDLCString sourceJunkScore; nsXPIDLCString sourceString;
srcHdr->GetStringProperty("junkscore", getter_Copies(sourceJunkScore)); srcHdr->GetStringProperty("junkscore", getter_Copies(sourceString));
destHdr->SetStringProperty("junkscore", sourceJunkScore); destHdr->SetStringProperty("junkscore", sourceString);
srcHdr->GetStringProperty("junkscoreorigin", getter_Copies(sourceJunkScore)); srcHdr->GetStringProperty("junkscoreorigin", getter_Copies(sourceString));
destHdr->SetStringProperty("junkscoreorigin", sourceJunkScore); destHdr->SetStringProperty("junkscoreorigin", sourceString);
srcHdr->GetStringProperty("keywords", getter_Copies(sourceString));
destHdr->SetStringProperty("junkscoreorigin", sourceString);
nsMsgLabelValue label = 0; nsMsgLabelValue label = 0;
srcHdr->GetLabel(&label); srcHdr->GetLabel(&label);
@ -3873,3 +3875,141 @@ NS_IMETHODIMP nsMsgLocalMailFolder::FetchMsgPreviewText(nsMsgKey *aKeysToFetch,
return rv; return rv;
} }
NS_IMETHODIMP nsMsgLocalMailFolder::AddKeywordToMessages(nsISupportsArray *aMessages, const char *aKeyword)
{
return ChangeKeywordForMessages(aMessages, aKeyword, PR_TRUE /* add */);
}
nsresult nsMsgLocalMailFolder::ChangeKeywordForMessages(nsISupportsArray *aMessages, const char *aKeyword, PRBool add)
{
nsresult rv = (add) ? nsMsgDBFolder::AddKeywordToMessages(aMessages, aKeyword)
: nsMsgDBFolder::RemoveKeywordFromMessages(aMessages, aKeyword);
if (NS_SUCCEEDED(rv))
{
rv = GetDatabase(nsnull);
NS_ENSURE_SUCCESS(rv, rv);
// this will fail if the folder is locked.
rv = mDatabase->StartBatch();
NS_ENSURE_SUCCESS(rv, rv);
nsIOFileStream *fileStream;
rv = mDatabase->GetFolderStream(&fileStream);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 count;
NS_ENSURE_ARG(aMessages);
nsresult rv = aMessages->Count(&count);
NS_ENSURE_SUCCESS(rv, rv);
nsXPIDLCString keywords;
nsCAutoString keywordToWrite(" ");
keywordToWrite.Append(aKeyword);
// for each message, we seek to the beginning of the x-mozilla-status header, and
// start reading lines, looking for x-mozilla-keys: headers; If we're adding
// the keyword and we find
// a header with the desired keyword already in it, we don't need to
// do anything. Likewise, if removing keyword and we don't find it,
// we don't need to do anything. Otherwise, if adding, we need to
// see if there's an x-mozilla-keys
// header with room for the new keyword. If so, we replace the
// corresponding number of spaces with the keyword. If no room,
// we can't do anything until the folder is compacted and another
// x-mozilla-keys header is added. In that case, we set a property
// on the header, which the compaction code will check.
// don't return out of the for loop - otherwise, we won't call EndBatch();
for(PRUint32 i = 0; i < count; i++) // for each message
{
char lineBuff[500];
nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aMessages, i, &rv);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 messageOffset;
PRUint32 len = 0;
nsCAutoString header;
nsCAutoString keywords;
message->GetMessageOffset(&messageOffset);
PRBool done = PR_FALSE;
PRUint32 statusOffset = 0;
(void)message->GetStatusOffset(&statusOffset);
PRUint32 desiredOffset = messageOffset + statusOffset;
fileStream->seek(PR_SEEK_SET, desiredOffset);
PRBool inKeywordHeader = PR_FALSE;
PRBool foundKeyword = PR_FALSE;
PRUint32 offsetToAddKeyword = 0;
message->GetMessageSize(&len);
// loop through
while (!done)
{
lineBuff[0] = '\0';
PRInt32 lineStartPos = fileStream->tell();
// readLine won't return line termination chars.
if (fileStream->readline(lineBuff, sizeof(lineBuff)))
{
if (EMPTY_MESSAGE_LINE(lineBuff))
break; // passed headers; no x-mozilla-keywords header; give up.
nsCString keywordHeaders;
if (!strncmp(lineBuff, HEADER_X_MOZILLA_KEYWORDS, sizeof(HEADER_X_MOZILLA_KEYWORDS) - 1))
{
inKeywordHeader = PR_TRUE;
keywordHeaders = lineBuff;
}
else if (inKeywordHeader && (lineBuff[0] == ' ' || lineBuff[0] == '\t'))
keywordHeaders = lineBuff;
else if (inKeywordHeader)
break;
else
continue;
PRInt32 keywordHdrLength = keywordHeaders.Length();
nsACString::const_iterator start, end;
nsACString::const_iterator keywordHdrStart;
keywordHeaders.BeginReading(keywordHdrStart);
// check if we have the keyword
if (MsgFindKeyword(nsDependentCString(aKeyword), keywordHeaders, start, end))
{
foundKeyword = PR_TRUE;
if (!add) // if we're removing, remove it, and break;
{
PRInt32 keywordStartOffset = Distance(keywordHdrStart, start);
keywordHeaders.Cut(keywordStartOffset, Distance(start, end));
for (PRInt32 i = Distance(start, end); i > 0; i--)
keywordHeaders.Append(' ');
fileStream->seek(PR_SEEK_SET, lineStartPos);
fileStream->write(keywordHeaders.get(), keywordHeaders.Length());
}
offsetToAddKeyword = 0;
// if adding and we already have the keyword, done
done = PR_TRUE;
break;
}
// argh, we need to check all the lines to see if we already have the
// keyword, but if we don't find it, we want to remember the line and
// position where we have room to add the keyword.
if (add)
{
nsCAutoString curKeywordHdr(lineBuff);
// strip off line ending spaces.
curKeywordHdr.Trim(" ", PR_FALSE, PR_TRUE);
if (!offsetToAddKeyword && curKeywordHdr.Length() + keywordToWrite.Length() < keywordHdrLength)
offsetToAddKeyword = lineStartPos + curKeywordHdr.Length();
}
}
}
if (add && !foundKeyword)
{
if (!offsetToAddKeyword)
message->SetUint32Property("growKeywords", 1);
else
{
fileStream->seek(PR_SEEK_SET, offsetToAddKeyword);
fileStream->write(keywordToWrite.get(), keywordToWrite.Length());
}
}
}
mDatabase->EndBatch();
}
return rv;
}
NS_IMETHODIMP nsMsgLocalMailFolder::RemoveKeywordFromMessages(nsISupportsArray *aMessages, const char *aKeyword)
{
return ChangeKeywordForMessages(aMessages, aKeyword, PR_FALSE /* remove */);
}

Просмотреть файл

@ -123,9 +123,6 @@ public:
NS_DECL_NSIMSGLOCALMAILFOLDER NS_DECL_NSIMSGLOCALMAILFOLDER
NS_DECL_NSIJUNKMAILCLASSIFICATIONLISTENER NS_DECL_NSIJUNKMAILCLASSIFICATIONLISTENER
NS_DECL_ISUPPORTS_INHERITED NS_DECL_ISUPPORTS_INHERITED
#if 0
static nsresult GetRoot(nsIMsgFolder* *result);
#endif
// nsIRDFResource methods: // nsIRDFResource methods:
NS_IMETHOD Init(const char *aURI); NS_IMETHOD Init(const char *aURI);
@ -199,7 +196,8 @@ public:
NS_IMETHOD FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUint32 aNumKeys, NS_IMETHOD FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUint32 aNumKeys,
PRBool aLocalOnly, nsIUrlListener *aUrlListener, PRBool aLocalOnly, nsIUrlListener *aUrlListener,
PRBool *aAsyncResults); PRBool *aAsyncResults);
NS_IMETHOD AddKeywordToMessages(nsISupportsArray *aMessages, const char *aKeyword);
NS_IMETHOD RemoveKeywordFromMessages(nsISupportsArray *aMessages, const char *aKeyword);
protected: protected:
nsresult CopyFolderAcrossServer(nsIMsgFolder *srcFolder, nsIMsgWindow *msgWindow,nsIMsgCopyServiceListener* listener); nsresult CopyFolderAcrossServer(nsIMsgFolder *srcFolder, nsIMsgWindow *msgWindow,nsIMsgCopyServiceListener* listener);
@ -233,6 +231,7 @@ protected:
virtual nsresult CreateBaseMessageURI(const char *aURI); virtual nsresult CreateBaseMessageURI(const char *aURI);
virtual nsresult SpamFilterClassifyMessage(const char *aURI, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin); virtual nsresult SpamFilterClassifyMessage(const char *aURI, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin);
virtual nsresult SpamFilterClassifyMessages(const char **aURIArray, PRUint32 aURICount, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin); virtual nsresult SpamFilterClassifyMessages(const char **aURIArray, PRUint32 aURICount, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin);
nsresult ChangeKeywordForMessages(nsISupportsArray *aMessages, const char *aKeyword, PRBool add);
protected: protected:
nsLocalMailCopyState *mCopyState; //We only allow one of these at a time nsLocalMailCopyState *mCopyState; //We only allow one of these at a time
const char *mType; const char *mType;

Просмотреть файл

@ -513,6 +513,7 @@ NS_IMETHODIMP nsParseMailMessageState::Clear()
m_envelope_from.length = 0; m_envelope_from.length = 0;
m_envelope_date.length = 0; m_envelope_date.length = 0;
m_priority.length = 0; m_priority.length = 0;
m_keywords.length = 0;
m_mdn_dnt.length = 0; m_mdn_dnt.length = 0;
m_return_path.length = 0; m_return_path.length = 0;
m_account_key.length = 0; m_account_key.length = 0;
@ -945,6 +946,9 @@ int nsParseMailMessageState::ParseHeaders ()
else if (!nsCRT::strncasecmp("X-Priority", buf, end - buf) else if (!nsCRT::strncasecmp("X-Priority", buf, end - buf)
|| !nsCRT::strncasecmp("Priority", buf, end - buf)) || !nsCRT::strncasecmp("Priority", buf, end - buf))
header = &m_priority; header = &m_priority;
else if (!nsCRT::strncasecmp(HEADER_X_MOZILLA_KEYWORDS, buf, end - buf)
&& !m_keywords.length)
header = &m_keywords;
break; break;
} }
@ -1158,6 +1162,7 @@ int nsParseMailMessageState::FinalizeHeaders()
struct message_header *mozstatus; struct message_header *mozstatus;
struct message_header *mozstatus2; struct message_header *mozstatus2;
struct message_header *priority; struct message_header *priority;
struct message_header *keywords;
struct message_header *account_key; struct message_header *account_key;
struct message_header *ccList; struct message_header *ccList;
struct message_header *mdn_dnt; struct message_header *mdn_dnt;
@ -1198,6 +1203,7 @@ int nsParseMailMessageState::FinalizeHeaders()
m_envelope_date.length ? &m_envelope_date : m_envelope_date.length ? &m_envelope_date :
0); 0);
priority = (m_priority.length ? &m_priority : 0); priority = (m_priority.length ? &m_priority : 0);
keywords = (m_keywords.length ? &m_keywords : 0);
mdn_dnt = (m_mdn_dnt.length ? &m_mdn_dnt : 0); mdn_dnt = (m_mdn_dnt.length ? &m_mdn_dnt : 0);
inReplyTo = (m_in_reply_to.length ? &m_in_reply_to : 0); inReplyTo = (m_in_reply_to.length ? &m_in_reply_to : 0);
replyTo = (m_replyTo.length ? &m_replyTo : 0); replyTo = (m_replyTo.length ? &m_replyTo : 0);
@ -1422,6 +1428,8 @@ int nsParseMailMessageState::FinalizeHeaders()
m_newMsgHdr->SetPriorityString(priority->value); m_newMsgHdr->SetPriorityString(priority->value);
else if (priorityFlags == nsMsgPriority::notSet) else if (priorityFlags == nsMsgPriority::notSet)
m_newMsgHdr->SetPriority(nsMsgPriority::none); m_newMsgHdr->SetPriority(nsMsgPriority::none);
if (keywords)
m_newMsgHdr->SetStringProperty("keywords", keywords->value);
if (content_type) if (content_type)
{ {
char *substring = PL_strstr(content_type->value, "charset"); char *substring = PL_strstr(content_type->value, "charset");
@ -1887,6 +1895,16 @@ NS_IMETHODIMP nsParseNewMailState::ApplyFilterHit(nsIMsgFilter *filter, nsIMsgWi
filterAction->GetPriority(&filterPriority); filterAction->GetPriority(&filterPriority);
msgHdr->SetPriority(filterPriority); msgHdr->SetPriority(filterPriority);
break; break;
case nsMsgFilterAction::AddTag:
{
nsXPIDLCString keyword;
filterAction->GetStrValue(getter_Copies(keyword));
nsCOMPtr<nsISupportsArray> messageArray;
NS_NewISupportsArray(getter_AddRefs(messageArray));
messageArray->AppendElement(msgHdr);
m_downloadFolder->AddKeywordToMessages(messageArray, keyword.get());
break;
}
case nsMsgFilterAction::Label: case nsMsgFilterAction::Label:
nsMsgLabelValue filterLabel; nsMsgLabelValue filterLabel;
filterAction->GetLabel(&filterLabel); filterAction->GetLabel(&filterLabel);

Просмотреть файл

@ -141,6 +141,7 @@ public:
struct message_header m_envelope_date; struct message_header m_envelope_date;
struct message_header m_priority; struct message_header m_priority;
struct message_header m_account_key; struct message_header m_account_key;
struct message_header m_keywords;
// Mdn support // Mdn support
struct message_header m_mdn_original_recipient; struct message_header m_mdn_original_recipient;
struct message_header m_return_path; struct message_header m_return_path;

Просмотреть файл

@ -593,6 +593,8 @@ nsPop3Sink::IncorporateBegin(const char* uidlString,
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
rv = WriteLineToMailbox("X-Mozilla-Status2: 00000000" MSG_LINEBREAK); rv = WriteLineToMailbox("X-Mozilla-Status2: 00000000" MSG_LINEBREAK);
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
// leave space for 60 bytes worth of keys/tags
rv = WriteLineToMailbox(X_MOZILLA_KEYWORDS);
PR_smprintf_free(statusLine); PR_smprintf_free(statusLine);
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -114,5 +114,5 @@
#define HEADER_X_MOZILLA_PART_URL "X-Mozilla-PartURL" #define HEADER_X_MOZILLA_PART_URL "X-Mozilla-PartURL"
#define HEADER_X_MOZILLA_IDENTITY_KEY "X-Identity-Key" #define HEADER_X_MOZILLA_IDENTITY_KEY "X-Identity-Key"
#define HEADER_X_MOZILLA_ACCOUNT_KEY "X-Account-Key" #define HEADER_X_MOZILLA_ACCOUNT_KEY "X-Account-Key"
#define HEADER_X_MOZILLA_KEYWORDS "X-Mozilla-Keys"
#endif /* nsMailHeaders_h_ */ #endif /* nsMailHeaders_h_ */