mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
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:
parent
91f6c5a433
commit
d06f8bbbcc
@ -214,12 +214,6 @@ var DefaultController =
|
||||
case "cmd_applyFilters":
|
||||
case "cmd_runJunkControls":
|
||||
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 "cmd_file":
|
||||
case "cmd_emptyTrash":
|
||||
@ -346,12 +340,6 @@ var DefaultController =
|
||||
case "button_mark":
|
||||
case "cmd_markAsRead":
|
||||
case "cmd_markThreadAsRead":
|
||||
case "cmd_label0":
|
||||
case "cmd_label1":
|
||||
case "cmd_label2":
|
||||
case "cmd_label3":
|
||||
case "cmd_label4":
|
||||
case "cmd_label5":
|
||||
return GetNumSelectedMessages() > 0;
|
||||
case "button_previous":
|
||||
case "button_next":
|
||||
@ -647,24 +635,6 @@ var DefaultController =
|
||||
case "cmd_deleteJunk":
|
||||
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;
|
||||
case "cmd_emptyTrash":
|
||||
MsgEmptyTrash();
|
||||
return;
|
||||
|
@ -26,7 +26,7 @@
|
||||
# Håkan Waara <hwaara@chello.se>
|
||||
# Jan Varga <varga@nixcorp.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
|
||||
# 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);
|
||||
}
|
||||
|
||||
function InitMessageLabel(menuType)
|
||||
function TagCurMessage(key)
|
||||
{
|
||||
var color;
|
||||
// ###need to do all selected messsages
|
||||
var msgHdr = gDBView.hdrForFirstSelectedMessage;
|
||||
var messages = Components.classes["@mozilla.org/supports-array;1"].createInstance(Components.interfaces.nsISupportsArray);
|
||||
messages.AppendElement(msgHdr);
|
||||
msgHdr.folder.addKeywordToMessages(messages, key);
|
||||
}
|
||||
|
||||
try
|
||||
function UnTagCurMessage(key)
|
||||
{
|
||||
// ###need to do all selected messsages
|
||||
var msgHdr = gDBView.hdrForFirstSelectedMessage;
|
||||
var messages = Components.classes["@mozilla.org/supports-array;1"].createInstance(Components.interfaces.nsISupportsArray);
|
||||
messages.AppendElement(msgHdr);
|
||||
msgHdr.folder.removeKeywordFromMessages(messages, key);
|
||||
}
|
||||
|
||||
|
||||
function AddTag()
|
||||
{
|
||||
var args = {result: "", okCallback: AddTagCallback};
|
||||
var dialog = window.openDialog(
|
||||
"chrome://messenger/content/newTagDialog.xul",
|
||||
"",
|
||||
"chrome,titlebar,modal",
|
||||
args);
|
||||
}
|
||||
|
||||
function AddTagCallback(name, color)
|
||||
{
|
||||
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++ )
|
||||
{
|
||||
var isChecked = true;
|
||||
var checkedLabel = gDBView.hdrForFirstSelectedMessage.label;
|
||||
if (key == curMsgHdrKeyArray[index])
|
||||
{
|
||||
keySet = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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 = prefBranch.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');
|
||||
// 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()
|
||||
|
@ -606,45 +606,8 @@
|
||||
</rule>
|
||||
</template>
|
||||
</menu>
|
||||
<menu id="threadPaneContext-labels" label="&labelMenu.label;" accesskey="&labelMenu.accesskey;">
|
||||
<menupopup onpopupshowing="InitMessageLabel('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"/>
|
||||
<menu id="threadPaneContext-tags" label="&tagMenu.label;" accesskey="&tagMenu.accesskey;">
|
||||
<menupopup id="threadPaneContext-tagpopup" onpopupshowing="InitMessageTags('threadPaneContext')">
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menu id="threadPaneContext-mark" label="&markMenu.label;" accesskey="&markMenu.accesskey;">
|
||||
@ -948,46 +911,9 @@
|
||||
</rule>
|
||||
</template>
|
||||
</menu>
|
||||
<menuseparator id="messagePaneContext-sep-labels-1"/>
|
||||
<menu id="messagePaneContext-labels" label="&labelMenu.label;" accesskey="&labelMenu.accesskey;">
|
||||
<menupopup onpopupshowing="InitMessageLabel('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"/>
|
||||
<menuseparator id="messagePaneContext-sep-tags-1"/>
|
||||
<menu id="messagePaneContext-tags" label="&tagMenu.label;" accesskey="&tagMenu.accesskey;">
|
||||
<menupopup id="messagePaneContext-tagpopup" onpopupshowing="InitMessageTags('messagePaneContext')">
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menu id="messagePaneContext-mark" label="&markMenu.label;" accesskey="&markMenu.accesskey;">
|
||||
@ -1613,85 +1539,48 @@
|
||||
</template>
|
||||
</menu>
|
||||
|
||||
<menu id="labelMenu" label="&labelMenu.label;" accesskey="&labelMenu.accesskey;">
|
||||
<menupopup id="menuPopup-labels" onpopupshowing="InitMessageLabel('menuPopup')">
|
||||
<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>
|
||||
</menu>
|
||||
<menu id="markMenu" label="&markMenu.label;" accesskey="&markMenu.accesskey;">
|
||||
<menupopup onpopupshowing="InitMessageMark()">
|
||||
<menuitem type="checkbox" id="markReadMenuItem" label="&markAsReadCmd.label;" accesskey="&markAsReadCmd.accesskey;" observes="cmd_markAsRead"
|
||||
#ifndef XP_MACOSX
|
||||
<menu id="messagePaneContext-tags" label="&tagMenu.label;" accesskey="&tagMenu.accesskey;">
|
||||
<menupopup id="messagePaneContext-tagpopup" onpopupshowing="InitMessageTags('messagePaneContext')">
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menu id="markMenu" label="&markMenu.label;" accesskey="&markMenu.accesskey;">
|
||||
<menupopup onpopupshowing="InitMessageMark()">
|
||||
<menuitem type="checkbox" id="markReadMenuItem" label="&markAsReadCmd.label;" accesskey="&markAsReadCmd.accesskey;" observes="cmd_markAsRead"
|
||||
ifndef="" XP_MACOSX=""
|
||||
key="key_toggleRead"
|
||||
#endif
|
||||
endif=""
|
||||
/>
|
||||
<menuitem label="&markThreadAsReadCmd.label;" accesskey="&markThreadAsReadCmd.accesskey;" observes="cmd_markThreadAsRead"
|
||||
#ifndef XP_MACOSX
|
||||
<menuitem label="&markThreadAsReadCmd.label;" accesskey="&markThreadAsReadCmd.accesskey;" observes="cmd_markThreadAsRead"
|
||||
ifndef="" XP_MACOSX=""
|
||||
key="key_markThreadAsRead"
|
||||
#endif
|
||||
endif=""
|
||||
/>
|
||||
<menuitem label="&markReadByDateCmd.label;" accesskey="&markReadByDateCmd.accesskey;" command="cmd_markReadByDate"
|
||||
#ifndef XP_MACOSX
|
||||
<menuitem label="&markReadByDateCmd.label;" accesskey="&markReadByDateCmd.accesskey;" command="cmd_markReadByDate"
|
||||
ifndef="" XP_MACOSX=""
|
||||
key="key_markReadByDate"
|
||||
#endif
|
||||
endif=""
|
||||
/>
|
||||
<menuitem label="&markAllReadCmd.label;" key="key_markAllRead" accesskey="&markAllReadCmd.accesskey;" observes="cmd_markAllRead"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="markFlaggedMenuItem"
|
||||
type="checkbox"
|
||||
label="&markFlaggedCmd.label;"
|
||||
accesskey="&markFlaggedCmd.accesskey;"
|
||||
observes="cmd_markAsFlagged"
|
||||
#ifndef XP_MACOSX
|
||||
<menuitem label="&markAllReadCmd.label;" key="key_markAllRead" accesskey="&markAllReadCmd.accesskey;" observes="cmd_markAllRead"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="markFlaggedMenuItem"
|
||||
type="checkbox"
|
||||
label="&markFlaggedCmd.label;"
|
||||
accesskey="&markFlaggedCmd.accesskey;"
|
||||
observes="cmd_markAsFlagged"
|
||||
ifndef="" XP_MACOSX=""
|
||||
key="key_toggleFlagged"
|
||||
#endif
|
||||
endif=""
|
||||
/>
|
||||
<menuseparator/>
|
||||
<menuitem label="&markAsJunkCmd.label;"
|
||||
accesskey="&markAsJunkCmd.accesskey;"
|
||||
observes="cmd_markAsJunk"
|
||||
#ifndef XP_MACOSX
|
||||
<menuseparator/>
|
||||
<menuitem label="&markAsJunkCmd.label;"
|
||||
accesskey="&markAsJunkCmd.accesskey;"
|
||||
observes="cmd_markAsJunk"
|
||||
ifndef="" XP_MACOSX=""
|
||||
key="key_markJunk"
|
||||
#endif
|
||||
endif=""
|
||||
/>
|
||||
<menuitem label="&markAsNotJunkCmd.label;"
|
||||
key="key_markNotJunk"
|
||||
<menuitem label="&markAsNotJunkCmd.label;"
|
||||
key="key_markNotJunk"
|
||||
accesskey="&markAsNotJunkCmd.accesskey;"
|
||||
observes="cmd_markAsNotJunk"/>
|
||||
<menuitem label="&recalculateJunkScoreCmd.label;"
|
||||
|
@ -1053,24 +1053,6 @@ var MessageWindowController =
|
||||
case "cmd_recalculateJunkScore":
|
||||
analyzeMessagesForJunk();
|
||||
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":
|
||||
MsgDownloadFlagged();
|
||||
return;
|
||||
|
@ -112,8 +112,8 @@ searchterm {
|
||||
-moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-folder");
|
||||
}
|
||||
|
||||
.ruleactiontarget[type="labelmessageas"] {
|
||||
-moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-label");
|
||||
.ruleactiontarget[type="addtagtomessage"] {
|
||||
-moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-tag");
|
||||
}
|
||||
|
||||
.ruleactiontarget[type="setpriorityto"] {
|
||||
|
@ -68,6 +68,8 @@ messenger.jar:
|
||||
content/messenger/shareglue.js (/mailnews/base/resources/content/shareglue.js)
|
||||
content/messenger/newFolderDialog.xul (/mailnews/base/resources/content/newFolderDialog.xul)
|
||||
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/msgAccountCentral.xul (/mailnews/base/resources/content/msgAccountCentral.xul)
|
||||
content/messenger/msgAccountCentral.js (/mailnews/base/resources/content/msgAccountCentral.js)
|
||||
|
@ -389,6 +389,7 @@ FeedItem.prototype =
|
||||
openingLine +
|
||||
'X-Mozilla-Status: 0000\n' +
|
||||
'X-Mozilla-Status2: 00000000\n' +
|
||||
'X-Mozilla-Keys: \n' +
|
||||
'Date: ' + this.mDate + '\n' +
|
||||
'Message-Id: <' + this.messageID + '>\n' +
|
||||
'From: ' + this.author + '\n' +
|
||||
|
@ -43,6 +43,7 @@
|
||||
<!ENTITY markMessageFlagged.label "Mark As Flagged">
|
||||
<!ENTITY labelMessage.label "Label Message As">
|
||||
<!ENTITY setPriority.label "Set Priority to">
|
||||
<!ENTITY addTag.label "Tag Message">
|
||||
<!ENTITY setJunkScore.label "Set Junk Status to">
|
||||
<!ENTITY deleteMessage.label "Delete Message">
|
||||
<!ENTITY deleteFromPOP.label "Delete From POP Server">
|
||||
|
@ -330,6 +330,8 @@
|
||||
<!ENTITY fileHereMenu.accesskey "F">
|
||||
<!ENTITY copyHereMenu.label "Copy Here">
|
||||
<!ENTITY copyHereMenu.accesskey "C">
|
||||
<!ENTITY tagMenu.label "Tag">
|
||||
<!ENTITY tagMenu.accesskey "g">
|
||||
<!ENTITY labelMenu.label "Label">
|
||||
<!ENTITY labelMenu.accesskey "L">
|
||||
<!ENTITY labelCmd0.accesskey "0">
|
||||
|
@ -48,6 +48,7 @@ newSubfolderMenuItem=Subfolder...
|
||||
newFolder=New Folder...
|
||||
newSubfolder=New Subfolder...
|
||||
folderProperties=Folder Properties
|
||||
newTag=New Tag...
|
||||
getNextNMessages=Get Next %S News Messages
|
||||
advanceNextPrompt=Advance to next unread message in %S?
|
||||
titleNewsPreHost=on
|
||||
@ -234,6 +235,9 @@ junk=Junk
|
||||
# for the has attachment picker in search and mail views
|
||||
hasAttachments=Has Attachments
|
||||
|
||||
# for the Tag picker in search and mail views.
|
||||
tag=Tags
|
||||
|
||||
# mailnews.js
|
||||
mailnews.send_default_charset=ISO-8859-1
|
||||
mailnews.view_default_charset=ISO-8859-1
|
||||
@ -361,6 +365,9 @@ passwordTitle=Mail Server Password Required
|
||||
openWindowWarningTitle=Confirm
|
||||
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
|
||||
# %S is the name of the saved search folder
|
||||
editVirtualFolderPropertiesTitle=Edit Saved Search Properties for %S
|
||||
|
38
mail/locales/en-US/chrome/messenger/newTagDialog.dtd
Executable file
38
mail/locales/en-US/chrome/messenger/newTagDialog.dtd
Executable file
@ -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
|
||||
13=Size
|
||||
14=AnyText
|
||||
15=Keywords
|
||||
15=Tags
|
||||
# for AB and LDAP
|
||||
16=Any Name
|
||||
17=Display Name
|
||||
@ -50,5 +50,5 @@
|
||||
46=Attachment Status
|
||||
47=Junk Status
|
||||
48=Label
|
||||
49=Customize
|
||||
# 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/folderpane.dtd (%chrome/messenger/folderpane.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/folderProps.dtd (%chrome/messenger/folderProps.dtd)
|
||||
locale/@AB_CD@/messenger/subscribe.dtd (%chrome/messenger/subscribe.dtd)
|
||||
|
@ -70,6 +70,7 @@ XPIDLSRCS = \
|
||||
nsIMsgMailSession.idl \
|
||||
nsIMsgMessageService.idl \
|
||||
nsIMsgSignature.idl \
|
||||
nsIMsgTagService.idl \
|
||||
nsIMsgThread.idl \
|
||||
nsIUrlListener.idl \
|
||||
nsIUrlListenerManager.idl \
|
||||
|
@ -70,7 +70,7 @@ typedef long nsMsgBiffState;
|
||||
// enumerated type for determining if a message has been replied to, forwarded, etc.
|
||||
typedef long nsMsgDispositionState;
|
||||
|
||||
[scriptable, uuid(28424d1c-db6f-4ac5-bc5d-418dd336120b)]
|
||||
[scriptable, uuid(5711cb97-42ad-466d-9e8f-48b84a8b76a2)]
|
||||
interface nsIMsgFolder : nsICollection {
|
||||
|
||||
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 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);
|
||||
|
||||
};
|
||||
|
72
mailnews/base/public/nsIMsgTagService.idl
Executable file
72
mailnews/base/public/nsIMsgTagService.idl
Executable file
@ -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;
|
||||
};
|
||||
|
@ -515,6 +515,16 @@
|
||||
0xce6038ae, 0xe5e0, 0x4372, \
|
||||
{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
|
||||
//
|
||||
|
@ -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...*/
|
||||
#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
|
||||
|
@ -1235,7 +1235,8 @@
|
||||
<!-- searchvalue - a widget which dynamically changes its user interface
|
||||
depending on what type of data it's supposed to be showing
|
||||
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">
|
||||
<content>
|
||||
@ -1277,12 +1278,6 @@
|
||||
</xul:menulist>
|
||||
<xul:menulist flex="1" class="search-value-menulist">
|
||||
<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:menulist>
|
||||
<xul:menulist flex="1" class="search-value-menulist">
|
||||
@ -1375,12 +1370,7 @@
|
||||
// it's a text search, so show the textbox
|
||||
this.setAttribute("selectedIndex", "0");
|
||||
}
|
||||
else if (val == Components.interfaces.nsMsgSearchAttrib.Label)
|
||||
{
|
||||
var children = document.getAnonymousNodes(this);
|
||||
var abs = children[5].getElementsByAttribute("value", "1");
|
||||
if (abs.item(0))
|
||||
children[5].selectedItem = abs[0];
|
||||
else if (val == Components.interfaces.nsMsgSearchAttrib.Keywords) {
|
||||
this.setAttribute("selectedIndex", "5");
|
||||
}
|
||||
else if (val == Components.interfaces.nsMsgSearchAttrib.JunkStatus) {
|
||||
@ -1433,11 +1423,14 @@
|
||||
else
|
||||
children[0].value = val.str;
|
||||
}
|
||||
else if (attrib == nsMsgSearchAttrib.Label)
|
||||
else if (attrib == nsMsgSearchAttrib.Keywords)
|
||||
{
|
||||
var labelVal = children[5].getElementsByAttribute("value", val.label);
|
||||
if (labelVal.item(0))
|
||||
children[5].selectedItem = labelVal[0];
|
||||
var keywordVal = children[5].getElementsByAttribute("value", val.str);
|
||||
if (keywordVal.item(0))
|
||||
{
|
||||
children[5].value = val.str;
|
||||
children[5].selectedItem = keywordVal[0];
|
||||
}
|
||||
}
|
||||
else if (attrib == nsMsgSearchAttrib.JunkStatus) {
|
||||
var junkStatus =
|
||||
@ -1485,8 +1478,10 @@
|
||||
else
|
||||
searchValue.str = children[0].value;
|
||||
}
|
||||
else if (searchAttribute == nsMsgSearchAttrib.Label)
|
||||
searchValue.label = children[5].selectedItem.value;
|
||||
else if (searchAttribute == nsMsgSearchAttrib.Keywords)
|
||||
{
|
||||
searchValue.str = children[5].value;
|
||||
}
|
||||
else if (searchAttribute == nsMsgSearchAttrib.JunkStatus)
|
||||
searchValue.junkStatus = children[6].value;
|
||||
else if (searchAttribute == nsMsgSearchAttrib.Size)
|
||||
@ -1507,6 +1502,33 @@
|
||||
]]>
|
||||
</body>
|
||||
</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">
|
||||
<parameter name="parentNode"/>
|
||||
<parameter name="bundle"/>
|
||||
@ -1565,18 +1587,14 @@
|
||||
// initialize the address book picker
|
||||
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
|
||||
this.initialize(document.getAnonymousNodes(this)[6], bundle);
|
||||
|
||||
// initialize the has attachment status picker
|
||||
this.initialize(document.getAnonymousNodes(this)[7], bundle);
|
||||
|
||||
// initialize the tag list
|
||||
fillInTags();
|
||||
]]>
|
||||
</constructor>
|
||||
</implementation>
|
||||
|
@ -526,61 +526,6 @@ function SetMenuItemLabel(menuItemId, 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()
|
||||
{
|
||||
var areMessagesRead = SelectedMessagesAreRead();
|
||||
|
@ -108,8 +108,8 @@ searchterm {
|
||||
-moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-folder");
|
||||
}
|
||||
|
||||
.ruleactiontarget[type="labelmessageas"] {
|
||||
-moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-label");
|
||||
.ruleactiontarget[type="addtagtomessage"] {
|
||||
-moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontarget-tag");
|
||||
}
|
||||
|
||||
.ruleactiontarget[type="setpriorityto"] {
|
||||
|
90
mailnews/base/resources/content/newTagDialog.js
Executable file
90
mailnews/base/resources/content/newTagDialog.js
Executable file
@ -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;
|
||||
}
|
||||
}
|
||||
|
58
mailnews/base/resources/content/newTagDialog.xul
Executable file
58
mailnews/base/resources/content/newTagDialog.xul
Executable file
@ -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
|
||||
hasAttachments=Has Attachments
|
||||
|
||||
# for the Tag picker in search and mail views.
|
||||
tag=Tag
|
||||
|
||||
# mailnews.js
|
||||
mailnews.send_default_charset=ISO-8859-1
|
||||
mailnews.view_default_charset=ISO-8859-1
|
||||
|
38
mailnews/base/resources/locale/en-US/newTagDialog.dtd
Executable file
38
mailnews/base/resources/locale/en-US/newTagDialog.dtd
Executable file
@ -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">
|
@ -46,7 +46,7 @@ interface nsIMsgSearchScopeTerm;
|
||||
|
||||
native nsCStringRef(nsCString&);
|
||||
|
||||
[scriptable, uuid(cc7795ce-1dd1-11b2-9ad2-dfa3c0b6ee09)]
|
||||
[scriptable, uuid(cde583fe-9add-4adb-9e1a-9cfe050d8a26)]
|
||||
interface nsIMsgSearchTerm : nsISupports {
|
||||
attribute nsMsgSearchAttribValue attrib;
|
||||
attribute nsMsgSearchOpValue op;
|
||||
@ -93,6 +93,7 @@ interface nsIMsgSearchTerm : nsISupports {
|
||||
readonly attribute boolean matchAllBeforeDeciding;
|
||||
|
||||
readonly attribute ACString termAsString;
|
||||
boolean matchKeyword(in string keyword); // used for tag searches
|
||||
attribute boolean matchAll;
|
||||
};
|
||||
|
||||
|
@ -59,7 +59,7 @@ typedef long nsMsgFilterIndex;
|
||||
|
||||
typedef long nsMsgRuleActionType;
|
||||
|
||||
[scriptable, uuid(3099cee1-eb76-4cd3-a40e-cfc8d2ef4437)]
|
||||
[scriptable, uuid(59af7696-1e28-4642-a400-fa327ae0b8d8)]
|
||||
interface nsMsgFilterAction {
|
||||
|
||||
/* these longs are all actually of type nsMsgFilterActionType */
|
||||
@ -80,5 +80,6 @@ interface nsMsgFilterAction {
|
||||
const long JunkScore=14;
|
||||
const long FetchBodyFromPop3Server=15;
|
||||
const long CopyToFolder=16;
|
||||
const long AddTag=17;
|
||||
};
|
||||
|
||||
|
@ -80,13 +80,13 @@ interface nsMsgSearchAttrib {
|
||||
const nsMsgSearchAttribValue CC = 7;
|
||||
const nsMsgSearchAttribValue ToOrCC = 8;
|
||||
|
||||
const nsMsgSearchAttribValue Location = 9; /* result list only */
|
||||
const nsMsgSearchAttribValue Location = 9; /* result list only */
|
||||
const nsMsgSearchAttribValue MessageKey = 10; /* message result elems */
|
||||
const nsMsgSearchAttribValue AgeInDays = 11;
|
||||
const nsMsgSearchAttribValue AgeInDays = 11;
|
||||
const nsMsgSearchAttribValue FolderInfo = 12; /* for "view thread context" from result */
|
||||
const nsMsgSearchAttribValue Size = 13;
|
||||
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 DisplayName = 17;
|
||||
@ -106,12 +106,12 @@ interface nsMsgSearchAttrib {
|
||||
const nsMsgSearchAttribValue Organization = 31;
|
||||
const nsMsgSearchAttribValue Department = 32;
|
||||
|
||||
// 33 - 45, reserved for ab / LDAP;
|
||||
const nsMsgSearchAttribValue HasAttachmentStatus = 46;
|
||||
const nsMsgSearchAttribValue JunkStatus = 47;
|
||||
// 33 - 45, reserved for ab / LDAP;
|
||||
const nsMsgSearchAttribValue HasAttachmentStatus = 46;
|
||||
const nsMsgSearchAttribValue JunkStatus = 47;
|
||||
const nsMsgSearchAttribValue Label = 48; /* mail only...can search by label */
|
||||
//49 is for showing customize... in ui headers start from 50 onwards up until 99.
|
||||
const nsMsgSearchAttribValue OtherHeader = 49; /* for mail and news. MUST ALWAYS BE LAST attribute since we can have an arbitrary # of these... */
|
||||
const nsMsgSearchAttribValue OtherHeader = 49; /* for mail and news. MUST ALWAYS BE LAST attribute since we can have an arbitrary # of these... */
|
||||
const nsMsgSearchAttribValue kNumMsgSearchAttributes = 100; /* must be last attribute */
|
||||
};
|
||||
|
||||
|
@ -57,9 +57,9 @@ var gFilterActionList;
|
||||
|
||||
var gFilterActionStrings = ["none", "movemessage", "setpriorityto", "deletemessage",
|
||||
"markasread", "ignorethread", "watchthread", "markasflagged",
|
||||
"labelmessageas", "replytomessage", "forwardmessage", "stopexecution",
|
||||
"label", "replytomessage", "forwardmessage", "stopexecution",
|
||||
"deletefrompopserver", "leaveonpopserver", "setjunkscore",
|
||||
"fetchfrompopserver", "copymessage"];
|
||||
"fetchfrompopserver", "copymessage", "addtagtomessage"];
|
||||
|
||||
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);
|
||||
gFilterBundle = document.getElementById("bundle_filter");
|
||||
InitMessageLabel();
|
||||
InitMessageMark();
|
||||
if ("arguments" in window && window.arguments[0])
|
||||
{
|
||||
var args = window.arguments[0];
|
||||
|
@ -70,8 +70,8 @@
|
||||
<xul:menuseparator/>
|
||||
<xul:menuitem label="&markMessageRead.label;" value="markasread"/>
|
||||
<xul:menuitem label="&markMessageFlagged.label;" value="markasflagged"/>
|
||||
<xul:menuitem label="&labelMessage.label;" value="labelmessageas"/>
|
||||
<xul:menuitem label="&setPriority.label;" value="setpriorityto"/>
|
||||
<xul:menuitem label="&addTag.label;" value="addtagtomessage"/>
|
||||
<xul:menuitem label="&setJunkScore.label;" value="setjunkscore" enablefornews="false"/>
|
||||
<xul:menuseparator enableforpop3="true"/>
|
||||
<xul:menuitem label="&deleteMessage.label;" value="deletemessage"/>
|
||||
@ -255,6 +255,9 @@
|
||||
case "setjunkscore":
|
||||
document.getAnonymousNodes(actionTarget)[0].value = aFilterAction.junkScore;
|
||||
break;
|
||||
case "addtagtomessage":
|
||||
document.getAnonymousNodes(actionTarget)[0].value = aFilterAction.strValue;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -328,6 +331,7 @@
|
||||
case "setjunkscore":
|
||||
filterAction.junkScore = document.getAnonymousNodes(actionTarget)[0].value;
|
||||
break;
|
||||
case "addtagtomessage":
|
||||
case "replytomessage":
|
||||
case "forwardmessage":
|
||||
filterAction.strValue = document.getAnonymousNodes(actionTarget)[0].value;
|
||||
@ -385,16 +389,10 @@
|
||||
</implementation>
|
||||
</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>
|
||||
<xul:menulist class="ruleactionitem">
|
||||
<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:menulist>
|
||||
</content>
|
||||
@ -402,19 +400,21 @@
|
||||
<implementation>
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
var menuItems = document.getAnonymousNodes(this)[0].menupopup.childNodes;
|
||||
var prefBranch = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch(null);
|
||||
|
||||
for (var index = 0; index < menuItems.length; index++)
|
||||
{
|
||||
var menuEl = menuItems[index];
|
||||
var prefString = prefBranch.getComplexValue("mailnews.labels.description." + menuEl.value,
|
||||
Components.interfaces.nsIPrefLocalizedString);
|
||||
menuEl.setAttribute("label", prefString);
|
||||
}
|
||||
|
||||
// propagating a pre-existing hack to make the label get displayed correctly in the menulist
|
||||
// now that we've changed the labels for each menu list. We need to use the current selectedIndex
|
||||
var menuPopup = document.getAnonymousNodes(this)[0].menupopup;
|
||||
var tagService = Components.classes["@mozilla.org/messenger/tagservice;1"].getService(Components.interfaces.nsIMsgTagService);
|
||||
var allTags = tagService.tagEnumerator;
|
||||
var allKeys = tagService.keyEnumerator;
|
||||
while (allTags.hasMore())
|
||||
{
|
||||
var tag = allTags.getNext();
|
||||
var key = allKeys.getNext();
|
||||
var newMenuItem = document.createElement('menuitem');
|
||||
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
|
||||
// now that we've changed the tags for each menu list. We need to use the current selectedIndex
|
||||
// (if its defined) to handle the case where we were initialized with a filter action already.
|
||||
var currentItem = document.getAnonymousNodes(this)[0].selectedItem;
|
||||
document.getAnonymousNodes(this)[0].selectedItem = null;
|
||||
|
@ -15,7 +15,7 @@
|
||||
12=Folder Info
|
||||
13=Size (KB)
|
||||
14=AnyText
|
||||
15=Keywords
|
||||
15=Tags
|
||||
# for AB and LDAP
|
||||
16=Any Name
|
||||
17=Display Name
|
||||
@ -50,5 +50,5 @@
|
||||
46=Attachment Status
|
||||
47=Junk Status
|
||||
48=Label
|
||||
49=Customize
|
||||
# don't use above 49
|
||||
49=Customize...
|
||||
|
@ -730,6 +730,7 @@ nsresult nsMsgFilter::SaveRule(nsIOFileStream *aStream)
|
||||
err = filterList->WriteIntAttr(nsIMsgFilterList::attribActionValue, junkScore, aStream);
|
||||
}
|
||||
break;
|
||||
case nsMsgFilterAction::AddTag:
|
||||
case nsMsgFilterAction::Reply:
|
||||
case nsMsgFilterAction::Forward:
|
||||
{
|
||||
@ -818,6 +819,7 @@ static struct RuleActionsTableEntry ruleActionsTable[] =
|
||||
{ nsMsgFilterAction::LeaveOnPop3Server, nsMsgFilterType::Inbox, 0, "Leave on Pop3 server"},
|
||||
{ nsMsgFilterAction::JunkScore, nsMsgFilterType::All, 0, "JunkScore"},
|
||||
{ nsMsgFilterAction::FetchBodyFromPop3Server, nsMsgFilterType::Inbox, 0, "Fetch body from Pop3Server"},
|
||||
{ nsMsgFilterAction::AddTag, nsMsgFilterType::All, 0, "AddTag"},
|
||||
};
|
||||
|
||||
const char *nsMsgFilter::GetActionStr(nsMsgRuleActionType action)
|
||||
|
@ -662,10 +662,16 @@ nsresult nsMsgFilterList::LoadTextFilters(nsIOFileStream *aStream)
|
||||
}
|
||||
else if (type == nsMsgFilterAction::Label)
|
||||
{
|
||||
// upgrade label to corresponding tag/keyword
|
||||
PRInt32 res;
|
||||
PRInt32 labelInt = value.ToInteger(&res, 10);
|
||||
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)
|
||||
{
|
||||
@ -674,7 +680,8 @@ nsresult nsMsgFilterList::LoadTextFilters(nsIOFileStream *aStream)
|
||||
if (!res)
|
||||
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());
|
||||
}
|
||||
|
@ -647,6 +647,13 @@ nsresult nsMsgFilterAfterTheFact::ApplyFilter()
|
||||
m_curFolder->SetLabelForMessages(m_searchHitHdrs, filterLabel);
|
||||
}
|
||||
break;
|
||||
case nsMsgFilterAction::AddTag:
|
||||
{
|
||||
nsXPIDLCString keyword;
|
||||
filterAction->GetStrValue(getter_Copies(keyword));
|
||||
m_curFolder->AddKeywordToMessages(m_searchHitHdrs, keyword.get());
|
||||
}
|
||||
break;
|
||||
case nsMsgFilterAction::JunkScore:
|
||||
{
|
||||
nsCAutoString junkScoreStr;
|
||||
|
@ -197,11 +197,6 @@ nsMsgSearchValidityManager::InitOfflineMailTable()
|
||||
m_offlineMailTable->SetAvailable (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->SetEnabled (nsMsgSearchAttrib::To, nsMsgSearchOp::Contains, 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->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;
|
||||
}
|
||||
|
||||
@ -382,6 +385,15 @@ nsMsgSearchValidityManager::InitOnlineMailTable()
|
||||
m_onlineMailTable->SetAvailable (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->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1);
|
||||
m_onlineMailTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1);
|
||||
|
@ -551,7 +551,14 @@ nsresult nsMsgSearchOfflineMail::ProcessSearchTerm(nsIMsgDBHdr *msgToMatch,
|
||||
msgToMatch->GetLabel(&label);
|
||||
err = aTerm->MatchLabel(label, &result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case nsMsgSearchAttrib::Keywords:
|
||||
{
|
||||
nsXPIDLCString keywords;
|
||||
msgToMatch->GetStringProperty("keywords", getter_Copies(keywords));
|
||||
err = aTerm->MatchKeyword(keywords.get(), &result);
|
||||
break;
|
||||
}
|
||||
case nsMsgSearchAttrib::JunkStatus:
|
||||
{
|
||||
nsXPIDLCString junkScoreStr;
|
||||
|
@ -500,7 +500,7 @@ nsresult nsMsgSearchAdapter::EncodeImapTerm (nsIMsgSearchTerm *term, PRBool real
|
||||
whichMnemonic = m_kImapAnyText;
|
||||
break;
|
||||
case nsMsgSearchAttrib::Keywords:
|
||||
whichMnemonic = m_kNntpKeywords;
|
||||
whichMnemonic = m_kImapKeyword;
|
||||
break;
|
||||
case nsMsgSearchAttrib::MsgStatus:
|
||||
useNot = PR_FALSE; // bizarrely, NOT SEEN is wrong, but UNSEEN is right.
|
||||
|
@ -97,6 +97,7 @@ nsMsgSearchAttribEntry SearchAttribEntryTable[] =
|
||||
{nsMsgSearchAttrib::ToOrCC, "to or cc"},
|
||||
{nsMsgSearchAttrib::AgeInDays, "age in days"},
|
||||
{nsMsgSearchAttrib::Label, "label"},
|
||||
{nsMsgSearchAttrib::Keywords, "tag"},
|
||||
{nsMsgSearchAttrib::Size, "size"},
|
||||
// this used to be nsMsgSearchAttrib::SenderInAddressBook
|
||||
// we used to have two Sender menuitems
|
||||
@ -663,8 +664,16 @@ nsresult nsMsgSearchTerm::DeStreamNew (char *inStream, PRInt16 /*length*/)
|
||||
if (commaSep)
|
||||
rv = ParseOperator(commaSep + 1, &m_operator);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// convert label filters and saved searches to keyword equivalents
|
||||
if (secondCommaSep)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1212,38 +1221,38 @@ nsresult nsMsgSearchTerm::MatchAge (PRTime msgDate, PRBool *pResult)
|
||||
|
||||
nsresult nsMsgSearchTerm::MatchSize (PRUint32 sizeToMatch, PRBool *pResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(pResult);
|
||||
NS_ENSURE_ARG_POINTER(pResult);
|
||||
|
||||
PRBool result = PR_FALSE;
|
||||
// We reduce the sizeToMatch rather than supplied size
|
||||
// as then we can do an exact match on the displayed value
|
||||
// which will be less confusing to the user.
|
||||
PRUint32 sizeToMatchKB = sizeToMatch;
|
||||
PRBool result = PR_FALSE;
|
||||
// We reduce the sizeToMatch rather than supplied size
|
||||
// as then we can do an exact match on the displayed value
|
||||
// which will be less confusing to the user.
|
||||
PRUint32 sizeToMatchKB = sizeToMatch;
|
||||
|
||||
if (sizeToMatchKB < 1024)
|
||||
sizeToMatchKB = 1024;
|
||||
if (sizeToMatchKB < 1024)
|
||||
sizeToMatchKB = 1024;
|
||||
|
||||
sizeToMatchKB /= 1024;
|
||||
sizeToMatchKB /= 1024;
|
||||
|
||||
switch (m_operator)
|
||||
{
|
||||
case nsMsgSearchOp::IsGreaterThan:
|
||||
if (sizeToMatchKB > m_value.u.size)
|
||||
result = PR_TRUE;
|
||||
break;
|
||||
case nsMsgSearchOp::IsLessThan:
|
||||
if (sizeToMatchKB < m_value.u.size)
|
||||
result = PR_TRUE;
|
||||
break;
|
||||
case nsMsgSearchOp::Is:
|
||||
if (sizeToMatchKB == m_value.u.size)
|
||||
result = PR_TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*pResult = result;
|
||||
return NS_OK;
|
||||
switch (m_operator)
|
||||
{
|
||||
case nsMsgSearchOp::IsGreaterThan:
|
||||
if (sizeToMatchKB > m_value.u.size)
|
||||
result = PR_TRUE;
|
||||
break;
|
||||
case nsMsgSearchOp::IsLessThan:
|
||||
if (sizeToMatchKB < m_value.u.size)
|
||||
result = PR_TRUE;
|
||||
break;
|
||||
case nsMsgSearchOp::Is:
|
||||
if (sizeToMatchKB == m_value.u.size)
|
||||
result = PR_TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*pResult = result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsMsgSearchTerm::MatchJunkStatus(const char *aJunkScore, PRBool *pResult)
|
||||
@ -1269,22 +1278,22 @@ nsresult nsMsgSearchTerm::MatchJunkStatus(const char *aJunkScore, PRBool *pResul
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
PRBool matches = (junkStatus == m_value.u.junkStatus);
|
||||
PRBool matches = (junkStatus == m_value.u.junkStatus);
|
||||
|
||||
switch (m_operator)
|
||||
{
|
||||
case nsMsgSearchOp::Is:
|
||||
break;
|
||||
case nsMsgSearchOp::Isnt:
|
||||
matches = !matches;
|
||||
break;
|
||||
default:
|
||||
rv = NS_ERROR_FAILURE;
|
||||
NS_ASSERTION(PR_FALSE, "invalid compare op for junk status");
|
||||
}
|
||||
switch (m_operator)
|
||||
{
|
||||
case nsMsgSearchOp::Is:
|
||||
break;
|
||||
case nsMsgSearchOp::Isnt:
|
||||
matches = !matches;
|
||||
break;
|
||||
default:
|
||||
rv = NS_ERROR_FAILURE;
|
||||
NS_ASSERTION(PR_FALSE, "invalid compare op for junk status");
|
||||
}
|
||||
|
||||
*pResult = matches;
|
||||
return rv;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult nsMsgSearchTerm::MatchLabel(nsMsgLabelValue aLabelValue, PRBool *pResult)
|
||||
@ -1309,62 +1318,109 @@ nsresult nsMsgSearchTerm::MatchLabel(nsMsgLabelValue aLabelValue, PRBool *pResul
|
||||
|
||||
nsresult nsMsgSearchTerm::MatchStatus(PRUint32 statusToMatch, PRBool *pResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(pResult);
|
||||
NS_ENSURE_ARG_POINTER(pResult);
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
PRBool matches = (statusToMatch & m_value.u.msgStatus);
|
||||
nsresult rv = NS_OK;
|
||||
PRBool matches = (statusToMatch & m_value.u.msgStatus);
|
||||
|
||||
switch (m_operator)
|
||||
{
|
||||
case nsMsgSearchOp::Is:
|
||||
break;
|
||||
case nsMsgSearchOp::Isnt:
|
||||
matches = !matches;
|
||||
break;
|
||||
default:
|
||||
rv = NS_ERROR_FAILURE;
|
||||
switch (m_operator)
|
||||
{
|
||||
case nsMsgSearchOp::Is:
|
||||
break;
|
||||
case nsMsgSearchOp::Isnt:
|
||||
matches = !matches;
|
||||
break;
|
||||
default:
|
||||
rv = NS_ERROR_FAILURE;
|
||||
NS_ERROR("invalid compare op for msg status");
|
||||
}
|
||||
}
|
||||
|
||||
*pResult = matches;
|
||||
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
|
||||
nsMsgSearchTerm::MatchPriority (nsMsgPriorityValue priorityToMatch,
|
||||
PRBool *pResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(pResult);
|
||||
NS_ENSURE_ARG_POINTER(pResult);
|
||||
|
||||
nsresult err = NS_OK;
|
||||
PRBool result=NS_OK;
|
||||
nsresult err = NS_OK;
|
||||
PRBool result=NS_OK;
|
||||
|
||||
// Use this ugly little hack to get around the fact that enums don't have
|
||||
// integer compare operators
|
||||
int p1 = (priorityToMatch == nsMsgPriority::none) ? (int) nsMsgPriority::normal : (int) priorityToMatch;
|
||||
int p2 = (int) m_value.u.priority;
|
||||
// Use this ugly little hack to get around the fact that enums don't have
|
||||
// integer compare operators
|
||||
int p1 = (priorityToMatch == nsMsgPriority::none) ? (int) nsMsgPriority::normal : (int) priorityToMatch;
|
||||
int p2 = (int) m_value.u.priority;
|
||||
|
||||
switch (m_operator)
|
||||
{
|
||||
case nsMsgSearchOp::IsHigherThan:
|
||||
if (p1 > p2)
|
||||
result = PR_TRUE;
|
||||
break;
|
||||
case nsMsgSearchOp::IsLowerThan:
|
||||
if (p1 < p2)
|
||||
result = PR_TRUE;
|
||||
break;
|
||||
case nsMsgSearchOp::Is:
|
||||
if (p1 == p2)
|
||||
result = PR_TRUE;
|
||||
break;
|
||||
default:
|
||||
result = PR_FALSE;
|
||||
err = NS_ERROR_FAILURE;
|
||||
NS_ASSERTION(PR_FALSE, "invalid match operator");
|
||||
}
|
||||
*pResult = result;
|
||||
return err;
|
||||
switch (m_operator)
|
||||
{
|
||||
case nsMsgSearchOp::IsHigherThan:
|
||||
if (p1 > p2)
|
||||
result = PR_TRUE;
|
||||
break;
|
||||
case nsMsgSearchOp::IsLowerThan:
|
||||
if (p1 < p2)
|
||||
result = PR_TRUE;
|
||||
break;
|
||||
case nsMsgSearchOp::Is:
|
||||
if (p1 == p2)
|
||||
result = PR_TRUE;
|
||||
break;
|
||||
default:
|
||||
result = PR_FALSE;
|
||||
err = NS_ERROR_FAILURE;
|
||||
NS_ASSERTION(PR_FALSE, "invalid match operator");
|
||||
}
|
||||
*pResult = result;
|
||||
return err;
|
||||
}
|
||||
|
||||
// Lazily initialize the rfc822 header parser we're going to use to do
|
||||
|
@ -99,6 +99,8 @@ nsMsgSearchValueImpl::GetStr(PRUnichar** aResult)
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchValueImpl::SetStr(const PRUnichar* aValue)
|
||||
{
|
||||
if (!aValue)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
NS_ENSURE_TRUE(IS_STRING_ATTRIBUTE(mValue.attribute), NS_ERROR_ILLEGAL_VALUE);
|
||||
if (mValue.string)
|
||||
nsCRT::free(mValue.string);
|
||||
|
@ -127,6 +127,7 @@ CPPSRCS = \
|
||||
nsSpamSettings.cpp \
|
||||
nsCidProtocolHandler.cpp \
|
||||
nsMsgContentPolicy.cpp \
|
||||
nsMsgTagService.cpp\
|
||||
$(NULL)
|
||||
|
||||
# MacOSX requires the MoreFiles module
|
||||
|
@ -769,14 +769,40 @@ nsresult nsMsgDBView::FetchPriority(nsIMsgDBHdr *aHdr, PRUnichar ** aPriorityStr
|
||||
break;
|
||||
}
|
||||
|
||||
if (priorityString)
|
||||
*aPriorityString = nsCRT::strdup(priorityString);
|
||||
else
|
||||
*aPriorityString = nsnull;
|
||||
*aPriorityString = (priorityString) ? nsCRT::strdup(priorityString) : nsnull;
|
||||
|
||||
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 rv = NS_OK;
|
||||
@ -1601,8 +1627,8 @@ NS_IMETHODIMP nsMsgDBView::GetCellText(PRInt32 aRow, nsITreeColumn* aCol, nsAStr
|
||||
rv = FetchPriority(msgHdr, getter_Copies(valueText));
|
||||
aValue.Assign(valueText);
|
||||
break;
|
||||
case 'l': // label
|
||||
rv = FetchLabel(msgHdr, getter_Copies(valueText));
|
||||
case 'l': // label - labels are now tags...
|
||||
rv = FetchTags(msgHdr, getter_Copies(valueText));
|
||||
aValue.Assign(valueText);
|
||||
break;
|
||||
case 'a': // account
|
||||
|
@ -64,6 +64,7 @@
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIMsgFilterPlugin.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsMsgTagService.h"
|
||||
|
||||
#define MESSENGER_STRING_URL "chrome://messenger/locale/messenger.properties"
|
||||
|
||||
@ -171,6 +172,7 @@ protected:
|
||||
nsresult FetchSize(nsIMsgDBHdr * aHdr, PRUnichar ** aSizeString);
|
||||
nsresult FetchPriority(nsIMsgDBHdr *aHdr, PRUnichar ** aPriorityString);
|
||||
nsresult FetchLabel(nsIMsgDBHdr *aHdr, PRUnichar ** aLabelString);
|
||||
nsresult FetchTags(nsIMsgDBHdr *aHdr, PRUnichar ** aTagString);
|
||||
nsresult FetchAccount(nsIMsgDBHdr * aHdr, PRUnichar ** aAccount);
|
||||
nsresult CycleThreadedColumn(nsIDOMElement * aElement);
|
||||
|
||||
@ -358,6 +360,7 @@ protected:
|
||||
// I18N date formater service which we'll want to cache locally.
|
||||
nsCOMPtr<nsIDateTimeFormat> mDateFormater;
|
||||
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
|
||||
// to phase it out eventually....for now we need it though.
|
||||
nsCOMPtr<nsIMessenger> mMessengerInstance;
|
||||
|
@ -55,7 +55,7 @@
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIMsgLocalMailFolder.h"
|
||||
#include "nsIMsgImapMailFolder.h"
|
||||
|
||||
#include "nsMailHeaders.h"
|
||||
#include "nsMsgI18N.h"
|
||||
#include "prprf.h"
|
||||
#include "nsMsgLocalFolderHdrs.h"
|
||||
@ -584,6 +584,20 @@ done:
|
||||
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
|
||||
nsFolderCompactState::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
|
||||
nsIInputStream *inStr,
|
||||
@ -594,9 +608,16 @@ nsFolderCompactState::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
PRUint32 msgFlags;
|
||||
PRBool checkForKeyword = m_startOfMsg;
|
||||
PRBool addKeywordHdr = PR_FALSE;
|
||||
PRUint32 needToGrowKeywords = 0;
|
||||
PRUint32 statusOffset;
|
||||
nsXPIDLCString msgHdrKeywords;
|
||||
|
||||
if (m_startOfMsg)
|
||||
{
|
||||
m_statusOffset = 0;
|
||||
m_addedHeaderSize = 0;
|
||||
m_messageUri.SetLength(0); // clear the previous message uri
|
||||
if (NS_SUCCEEDED(BuildMessageURI(m_baseMessageUri.get(), m_keyArray[m_curIndex],
|
||||
m_messageUri)))
|
||||
@ -606,7 +627,6 @@ nsFolderCompactState::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
|
||||
if (m_curSrcHdr)
|
||||
{
|
||||
(void) m_curSrcHdr->GetFlags(&msgFlags);
|
||||
PRUint32 statusOffset;
|
||||
(void) m_curSrcHdr->GetStatusOffset(&statusOffset);
|
||||
if (statusOffset == 0)
|
||||
m_needStatusLine = PR_TRUE;
|
||||
@ -618,9 +638,21 @@ nsFolderCompactState::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
|
||||
while (NS_SUCCEEDED(rv) && (PRInt32) count > 0)
|
||||
{
|
||||
maxReadCount = count > 4096 ? 4096 : count;
|
||||
writeCount = 0;
|
||||
rv = inStr->Read(m_dataBuffer, maxReadCount, &readCount);
|
||||
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)
|
||||
{
|
||||
m_needStatusLine = PR_FALSE;
|
||||
@ -630,26 +662,16 @@ nsFolderCompactState::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
|
||||
// in OnEndCopy).
|
||||
if (!strncmp(m_dataBuffer, "From ", 5))
|
||||
{
|
||||
PRInt32 charIndex;
|
||||
for (charIndex = 5; charIndex < readCount; charIndex++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
blockOffset = 5;
|
||||
// skip from line
|
||||
AdvanceToNextLine(m_dataBuffer, blockOffset, readCount);
|
||||
char statusLine[50];
|
||||
writeCount = m_fileStream->write(m_dataBuffer, charIndex);
|
||||
m_statusOffset = charIndex;
|
||||
writeCount = m_fileStream->write(m_dataBuffer, blockOffset);
|
||||
m_statusOffset = blockOffset;
|
||||
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);
|
||||
m_statusLineSize += m_fileStream->write(statusLine, strlen(statusLine));
|
||||
writeCount += m_fileStream->write(m_dataBuffer + charIndex, readCount - charIndex);
|
||||
|
||||
m_addedHeaderSize += m_fileStream->write(statusLine, strlen(statusLine));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -665,10 +687,99 @@ nsFolderCompactState::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
#define EXTRA_KEYWORD_HDR " "MSG_LINEBREAK
|
||||
|
||||
if (addKeywordHdr)
|
||||
{
|
||||
writeCount = m_fileStream->write(m_dataBuffer, readCount);
|
||||
// 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
|
||||
{
|
||||
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;
|
||||
if (writeCount != readCount)
|
||||
{
|
||||
@ -860,12 +971,16 @@ nsFolderCompactState::EndCopy(nsISupports *url, nsresult aStatus)
|
||||
m_db->CopyHdrFromExistingHdr(m_startOfNewMsg, m_curSrcHdr, PR_TRUE,
|
||||
getter_AddRefs(newMsgHdr));
|
||||
m_curSrcHdr = nsnull;
|
||||
if (newMsgHdr && m_statusOffset != 0)
|
||||
if (newMsgHdr)
|
||||
{
|
||||
PRUint32 oldMsgSize;
|
||||
newMsgHdr->SetStatusOffset(m_statusOffset);
|
||||
(void) newMsgHdr->GetMessageSize(&oldMsgSize);
|
||||
newMsgHdr->SetMessageSize(oldMsgSize + m_statusLineSize);
|
||||
if ( m_statusOffset != 0)
|
||||
newMsgHdr->SetStatusOffset(m_statusOffset);
|
||||
if (m_addedHeaderSize != 0)
|
||||
{
|
||||
PRUint32 oldMsgSize;
|
||||
(void) newMsgHdr->GetMessageSize(&oldMsgSize);
|
||||
newMsgHdr->SetMessageSize(oldMsgSize + m_addedHeaderSize);
|
||||
}
|
||||
}
|
||||
|
||||
// m_db->Commit(nsMsgDBCommitType::kLargeCommit); // no sense commiting until the end
|
||||
|
@ -79,6 +79,7 @@ protected:
|
||||
void ShowCompactingStatusMsg();
|
||||
void ShowDoneStatus();
|
||||
nsresult CompactNextFolder();
|
||||
void AdvanceToNextLine(const char *buffer, PRUint32 &bufferOffset, PRUint32 maxBufferOffset);
|
||||
|
||||
|
||||
nsCString m_baseMessageUri; // base message uri
|
||||
@ -106,7 +107,7 @@ protected:
|
||||
PRBool m_needStatusLine;
|
||||
PRBool m_startOfMsg;
|
||||
PRInt32 m_statusOffset;
|
||||
PRInt32 m_statusLineSize;
|
||||
PRInt32 m_addedHeaderSize;
|
||||
nsCOMPtr <nsISupportsArray> m_offlineFolderArray;
|
||||
|
||||
};
|
||||
|
329
mailnews/base/src/nsMsgTagService.cpp
Executable file
329
mailnews/base/src/nsMsgTagService.cpp
Executable file
@ -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;
|
||||
|
||||
}
|
76
mailnews/base/src/nsMsgTagService.h
Executable file
76
mailnews/base/src/nsMsgTagService.h
Executable file
@ -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());
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -73,10 +73,10 @@ class NS_MSG_BASE nsMsgLineBuffer : public nsMsgLineBufferHandler
|
||||
public:
|
||||
nsMsgLineBuffer(nsMsgLineBufferHandler *handler, PRBool convertNewlinesP);
|
||||
|
||||
virtual ~nsMsgLineBuffer();
|
||||
PRInt32 BufferInput(const char *net_buffer, PRInt32 net_buffer_size);
|
||||
virtual ~nsMsgLineBuffer();
|
||||
PRInt32 BufferInput(const char *net_buffer, PRInt32 net_buffer_size);
|
||||
// Not sure why anyone cares, by NNTPHost seems to want to know the buf pos.
|
||||
PRUint32 GetBufferPos() {return m_bufferPos;}
|
||||
PRUint32 GetBufferPos() {return m_bufferPos;}
|
||||
|
||||
virtual PRInt32 HandleLine(char *line, PRUint32 line_length);
|
||||
// flush last line, though it won't be CRLF terminated.
|
||||
@ -88,7 +88,7 @@ protected:
|
||||
void SetLookingForCRLF(PRBool b);
|
||||
|
||||
nsMsgLineBufferHandler *m_handler;
|
||||
PRBool m_convertNewlinesP;
|
||||
PRBool m_convertNewlinesP;
|
||||
PRBool m_lookingForCRLF;
|
||||
};
|
||||
|
||||
|
@ -1221,3 +1221,36 @@ void GetSummaryFileLocation(nsFileSpec& fileLocation, nsFileSpec* summaryLocatio
|
||||
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.
|
||||
NS_MSG_BASE void GetSummaryFileLocation(nsFileSpec& fileLocation,
|
||||
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
|
||||
|
||||
|
@ -103,6 +103,8 @@
|
||||
#include "nsCidProtocolHandler.h"
|
||||
#include "nsRssIncomingServer.h"
|
||||
#include "nsRssService.h"
|
||||
#include "nsMsgTagService.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "nsMessengerWinIntegration.h"
|
||||
#endif
|
||||
@ -330,6 +332,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgGroupView)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgOfflineManager)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgProgress)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSpamSettings)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgTagService)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsCidProtocolHandler)
|
||||
#ifdef XP_WIN
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMessengerWinIntegration, Init)
|
||||
@ -826,6 +829,10 @@ static const nsModuleComponentInfo gComponents[] = {
|
||||
NS_SPAMSETTINGS_CONTRACTID,
|
||||
nsSpamSettingsConstructor,
|
||||
},
|
||||
{ "Tag Service", NS_MSGTAGSERVICE_CID,
|
||||
NS_MSGTAGSERVICE_CONTRACTID,
|
||||
nsMsgTagServiceConstructor,
|
||||
},
|
||||
{ "cid protocol", NS_CIDPROTOCOL_CID,
|
||||
NS_CIDPROTOCOLHANDLER_CONTRACTID,
|
||||
nsCidProtocolHandlerConstructor,
|
||||
|
@ -47,8 +47,7 @@ nsMsgRecipientArray::nsMsgRecipientArray()
|
||||
|
||||
nsMsgRecipientArray::~nsMsgRecipientArray()
|
||||
{
|
||||
if (m_array)
|
||||
delete m_array;
|
||||
delete m_array;
|
||||
}
|
||||
|
||||
/* the following macro actually implement addref, release and query interface for our class. */
|
||||
@ -56,13 +55,13 @@ NS_IMPL_ISUPPORTS1(nsMsgRecipientArray, nsIMsgRecipientArray)
|
||||
|
||||
nsresult nsMsgRecipientArray::StringAt(PRInt32 idx, PRUnichar **_retval)
|
||||
{
|
||||
if (!_retval || !m_array)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
nsString aStr;
|
||||
m_array->StringAt(idx, aStr);
|
||||
*_retval = ToNewUnicode(aStr);
|
||||
return NS_OK;
|
||||
if (!_retval || !m_array)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
nsString aStr;
|
||||
m_array->StringAt(idx, aStr);
|
||||
*_retval = ToNewUnicode(aStr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsMsgRecipientArray::AppendString(const PRUnichar *aString, PRBool *_retval)
|
||||
|
@ -876,6 +876,8 @@ nsMsgSendLater::BuildHeaders()
|
||||
prune_p = do_flags_p = PR_TRUE;
|
||||
else if (!PL_strncasecmp(HEADER_X_MOZILLA_DRAFT_INFO, buf, end - buf))
|
||||
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))
|
||||
{
|
||||
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.
|
||||
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.
|
||||
|
@ -43,21 +43,23 @@ typedef unsigned short imapMessageFlagsType;
|
||||
|
||||
typedef long nsOfflineImapOperationType;
|
||||
|
||||
[scriptable, uuid(7cc7dec6-ea50-11d4-a5b7-0060b0fc04b7)]
|
||||
[scriptable, uuid(2728cb2b-4716-4b5e-98a7-ce22569378e5)]
|
||||
|
||||
interface nsIMsgOfflineImapOperation : nsISupports
|
||||
{
|
||||
// type of stored imap operations
|
||||
const long kFlagsChanged = 0x1;
|
||||
const long kMsgMoved = 0x2;
|
||||
const long kMsgCopy = 0x4;
|
||||
const long kMoveResult = 0x8;
|
||||
const long kAppendDraft = 0x10;
|
||||
const long kAddedHeader = 0x20;
|
||||
const long kDeletedMsg = 0x40;
|
||||
const long kFlagsChanged = 0x1;
|
||||
const long kMsgMoved = 0x2;
|
||||
const long kMsgCopy = 0x4;
|
||||
const long kMoveResult = 0x8;
|
||||
const long kAppendDraft = 0x10;
|
||||
const long kAddedHeader = 0x20;
|
||||
const long kDeletedMsg = 0x40;
|
||||
const long kMsgMarkedDeleted = 0x80;
|
||||
const long kAppendTemplate = 0x100;
|
||||
const long kDeleteAllMsgs = 0x200;
|
||||
const long kAppendTemplate = 0x100;
|
||||
const long kDeleteAllMsgs = 0x200;
|
||||
const long kAddKeywords = 0x400;
|
||||
const long kRemoveKeywords = 0x800;
|
||||
|
||||
attribute nsOfflineImapOperationType operation;
|
||||
void clearOperation(in nsOfflineImapOperationType operation);
|
||||
@ -66,6 +68,10 @@ interface nsIMsgOfflineImapOperation : nsISupports
|
||||
attribute imapMessageFlagsType newFlags; // for kFlagsChanged
|
||||
attribute string destinationFolderURI; // for move or copy
|
||||
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;
|
||||
void addMessageCopyOperation(in string destinationBox);
|
||||
string getCopyDestination(in long copyIndex);
|
||||
|
@ -57,6 +57,7 @@ public:
|
||||
|
||||
NS_IMETHOD ForceClosed();
|
||||
NS_IMETHOD SetFolderStream(nsIOFileStream *aFileStream);
|
||||
NS_IMETHOD GetFolderStream(nsIOFileStream **aFileStream);
|
||||
NS_IMETHOD AddNewHdrToDB(nsIMsgDBHdr *newHdr, PRBool notify);
|
||||
NS_IMETHOD SetAttributesOnPendingHdr(nsIMsgDBHdr *pendingHdr, const char *property,
|
||||
const char *propertyVal, PRInt32 flags);
|
||||
|
@ -76,6 +76,7 @@ public:
|
||||
NS_IMETHOD ListAllOfflineDeletes(nsMsgKeyArray *offlineDeletes);
|
||||
|
||||
NS_IMETHOD SetFolderStream(nsIOFileStream *aFileStream);
|
||||
NS_IMETHOD GetFolderStream(nsIOFileStream **aFileStream);
|
||||
|
||||
friend class nsMsgOfflineOpEnumerator;
|
||||
protected:
|
||||
|
@ -188,7 +188,7 @@ protected:
|
||||
static void AddToCache(nsMsgDatabase* pMessageDB)
|
||||
{
|
||||
#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
|
||||
GetDBCache()->AppendElement(pMessageDB);}
|
||||
static void RemoveFromCache(nsMsgDatabase* pMessageDB);
|
||||
|
@ -117,6 +117,11 @@ NS_IMETHODIMP nsImapMailDatabase::ForceClosed()
|
||||
return nsMailDatabase::ForceClosed();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsImapMailDatabase::GetFolderStream(nsIOFileStream **aFileStream)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsImapMailDatabase::SetFolderStream(nsIOFileStream *aFileStream)
|
||||
{
|
||||
NS_ASSERTION(0, "Trying to set folderStream, not implemented");
|
||||
|
@ -79,6 +79,19 @@ NS_IMETHODIMP nsMailDatabase::SetFolderStream(nsIOFileStream *aFileStream)
|
||||
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 PRInt32 gTimeStampLeeway;
|
||||
|
||||
|
@ -4756,6 +4756,11 @@ NS_IMETHODIMP nsMsgDatabase::ResetHdrCacheSize(PRUint32 aSize)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgDatabase::GetFolderStream(nsIOFileStream **aFileStream)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgDatabase::SetFolderStream(nsIOFileStream *aFileStream)
|
||||
{
|
||||
NS_ASSERTION(0, "Trying to set the folderStream, not implemented");
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "msgCore.h"
|
||||
#include "nsMsgOfflineImapOperation.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsMsgUtils.h"
|
||||
|
||||
PRLogModuleInfo *IMAPOffline;
|
||||
|
||||
@ -61,6 +62,8 @@ NS_IMPL_ISUPPORTS1(nsMsgOfflineImapOperation, nsIMsgOfflineImapOperation)
|
||||
#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
|
||||
// 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)
|
||||
{
|
||||
@ -209,6 +212,77 @@ NS_IMETHODIMP nsMsgOfflineImapOperation::SetSourceFolderURI(const char * aSource
|
||||
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)
|
||||
{
|
||||
SetOperation(kMsgCopy);
|
||||
@ -325,5 +399,13 @@ void nsMsgOfflineImapOperation::Log(PRLogModuleInfo *logFile)
|
||||
{
|
||||
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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -45,29 +45,35 @@
|
||||
class nsMsgOfflineImapOperation : public nsIMsgOfflineImapOperation
|
||||
{
|
||||
public:
|
||||
/** Instance Methods **/
|
||||
nsMsgOfflineImapOperation(nsMsgDatabase *db, nsIMdbRow *row);
|
||||
virtual ~nsMsgOfflineImapOperation();
|
||||
/** Instance Methods **/
|
||||
nsMsgOfflineImapOperation(nsMsgDatabase *db, nsIMdbRow *row);
|
||||
virtual ~nsMsgOfflineImapOperation();
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMSGOFFLINEIMAPOPERATION
|
||||
|
||||
|
||||
nsIMdbRow *GetMDBRow() {return m_mdbRow;}
|
||||
nsIMdbRow *GetMDBRow() {return m_mdbRow;}
|
||||
nsresult GetCopiesFromDB();
|
||||
nsresult SetCopiesToDB();
|
||||
void Log(PRLogModuleInfo *logFile);
|
||||
protected:
|
||||
nsresult AddKeyword(const char *aKeyword, nsCString &addList, const char *addProp,
|
||||
nsCString &removeList, const char *removeProp);
|
||||
|
||||
nsOfflineImapOperationType m_operation;
|
||||
nsMsgKey m_messageKey;
|
||||
nsMsgKey m_sourceMessageKey;
|
||||
PRUint32 m_operationFlags; // what to do on sync
|
||||
imapMessageFlagsType m_newFlags; // used for kFlagsChanged
|
||||
nsMsgKey m_messageKey;
|
||||
nsMsgKey m_sourceMessageKey;
|
||||
PRUint32 m_operationFlags; // what to do on sync
|
||||
imapMessageFlagsType m_newFlags; // used for kFlagsChanged
|
||||
|
||||
// these are URI's, and are escaped. Thus, we can use a delimter like ' '
|
||||
// because the real spaces should be escaped.
|
||||
nsXPIDLCString m_sourceFolder;
|
||||
nsXPIDLCString m_moveDestination;
|
||||
nsCStringArray m_copyDestinations;
|
||||
nsXPIDLCString m_sourceFolder;
|
||||
nsXPIDLCString m_moveDestination;
|
||||
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
|
||||
// just a wrapper around the offline operation row in the mdb.
|
||||
|
@ -3462,6 +3462,16 @@ NS_IMETHODIMP nsImapMailFolder::ApplyFilterHit(nsIMsgFilter *filter, nsIMsgWindo
|
||||
keysToFlag.GetSize(), nsnull);
|
||||
}
|
||||
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:
|
||||
{
|
||||
nsCAutoString junkScoreStr;
|
||||
@ -7063,7 +7073,7 @@ nsImapFolderCopyState::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode)
|
||||
PRUint32 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);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
@ -7931,7 +7941,7 @@ NS_IMETHODIMP nsImapMailFolder::RenameClient(nsIMsgWindow *msgWindow, nsIMsgFold
|
||||
nsCOMPtr<nsIMsgFolder> msgParent;
|
||||
msgFolder->GetParentMsgFolder(getter_AddRefs(msgParent));
|
||||
msgFolder->SetParent(nsnull);
|
||||
msgParent->PropagateDelete(msgFolder,PR_FALSE, nsnull);
|
||||
msgParent->PropagateDelete(msgFolder, PR_TRUE, nsnull);
|
||||
|
||||
// Reset online status now that the folder is renamed.
|
||||
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)
|
||||
{
|
||||
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));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCAutoString msgIds;
|
||||
@ -8471,3 +8503,34 @@ NS_IMETHODIMP nsImapMailFolder::FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUi
|
||||
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 *aAsyncResults);
|
||||
|
||||
NS_IMETHOD AddKeywordToMessages(nsISupportsArray *aMessages, const char *aKeyword);
|
||||
NS_IMETHOD RemoveKeywordFromMessages(nsISupportsArray *aMessages, const char *aKeyword);
|
||||
|
||||
// nsIMsgImapMailFolder methods
|
||||
NS_DECL_NSIMSGIMAPMAILFOLDER
|
||||
|
||||
|
@ -294,6 +294,77 @@ void nsImapOfflineSync::ProcessFlagOperation(nsIMsgOfflineImapOperation *op)
|
||||
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
|
||||
nsImapOfflineSync::ProcessAppendMsgOperation(nsIMsgOfflineImapOperation *currentOp, PRInt32 opType)
|
||||
{
|
||||
@ -800,6 +871,20 @@ nsresult nsImapOfflineSync::ProcessNextOperation()
|
||||
{
|
||||
// we are done with the current type
|
||||
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;
|
||||
// recurse to deal with next type of operation
|
||||
@ -844,6 +929,9 @@ nsresult nsImapOfflineSync::ProcessNextOperation()
|
||||
{
|
||||
if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kFlagsChanged)
|
||||
ProcessFlagOperation(currentOp);
|
||||
else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords
|
||||
||mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kRemoveKeywords)
|
||||
ProcessKeywordOperation(currentOp);
|
||||
else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kMsgCopy)
|
||||
ProcessCopyOperation(currentOp);
|
||||
else if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kMsgMoved)
|
||||
|
@ -73,6 +73,7 @@ protected:
|
||||
void DeleteAllOfflineOpsForCurrentDB();
|
||||
|
||||
void ProcessFlagOperation(nsIMsgOfflineImapOperation *currentOp);
|
||||
void ProcessKeywordOperation(nsIMsgOfflineImapOperation *op);
|
||||
void ProcessMoveOperation(nsIMsgOfflineImapOperation *currentOp);
|
||||
void ProcessCopyOperation(nsIMsgOfflineImapOperation *currentOp);
|
||||
void ProcessEmptyTrash(nsIMsgOfflineImapOperation *currentOp);
|
||||
|
@ -86,7 +86,6 @@
|
||||
#include "nsICopyMsgStreamListener.h"
|
||||
#include "nsIFileStream.h"
|
||||
#include "nsIMsgParseMailMsgState.h"
|
||||
#include "nsMsgLineBuffer.h"
|
||||
#include "nsMsgLocalCID.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsIDocShell.h"
|
||||
@ -1589,10 +1588,6 @@ nsImapService::SetMessageFlags(nsIEventTarget * aClientEventTarget,
|
||||
imapMessageFlagsType flags,
|
||||
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,
|
||||
"setmsgflags", flags, messageIdsAreUID);
|
||||
}
|
||||
|
@ -2507,11 +2507,13 @@ keepGoing:
|
||||
|
||||
void nsMsgLocalMailFolder::CopyPropertiesToMsgHdr(nsIMsgDBHdr *destHdr, nsIMsgDBHdr *srcHdr)
|
||||
{
|
||||
nsXPIDLCString sourceJunkScore;
|
||||
srcHdr->GetStringProperty("junkscore", getter_Copies(sourceJunkScore));
|
||||
destHdr->SetStringProperty("junkscore", sourceJunkScore);
|
||||
srcHdr->GetStringProperty("junkscoreorigin", getter_Copies(sourceJunkScore));
|
||||
destHdr->SetStringProperty("junkscoreorigin", sourceJunkScore);
|
||||
nsXPIDLCString sourceString;
|
||||
srcHdr->GetStringProperty("junkscore", getter_Copies(sourceString));
|
||||
destHdr->SetStringProperty("junkscore", sourceString);
|
||||
srcHdr->GetStringProperty("junkscoreorigin", getter_Copies(sourceString));
|
||||
destHdr->SetStringProperty("junkscoreorigin", sourceString);
|
||||
srcHdr->GetStringProperty("keywords", getter_Copies(sourceString));
|
||||
destHdr->SetStringProperty("junkscoreorigin", sourceString);
|
||||
|
||||
nsMsgLabelValue label = 0;
|
||||
srcHdr->GetLabel(&label);
|
||||
@ -3873,3 +3875,141 @@ NS_IMETHODIMP nsMsgLocalMailFolder::FetchMsgPreviewText(nsMsgKey *aKeysToFetch,
|
||||
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_NSIJUNKMAILCLASSIFICATIONLISTENER
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
#if 0
|
||||
static nsresult GetRoot(nsIMsgFolder* *result);
|
||||
#endif
|
||||
// nsIRDFResource methods:
|
||||
NS_IMETHOD Init(const char *aURI);
|
||||
|
||||
@ -199,7 +196,8 @@ public:
|
||||
NS_IMETHOD FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUint32 aNumKeys,
|
||||
PRBool aLocalOnly, nsIUrlListener *aUrlListener,
|
||||
PRBool *aAsyncResults);
|
||||
|
||||
NS_IMETHOD AddKeywordToMessages(nsISupportsArray *aMessages, const char *aKeyword);
|
||||
NS_IMETHOD RemoveKeywordFromMessages(nsISupportsArray *aMessages, const char *aKeyword);
|
||||
|
||||
protected:
|
||||
nsresult CopyFolderAcrossServer(nsIMsgFolder *srcFolder, nsIMsgWindow *msgWindow,nsIMsgCopyServiceListener* listener);
|
||||
@ -233,6 +231,7 @@ protected:
|
||||
virtual nsresult CreateBaseMessageURI(const char *aURI);
|
||||
virtual nsresult SpamFilterClassifyMessage(const char *aURI, 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:
|
||||
nsLocalMailCopyState *mCopyState; //We only allow one of these at a time
|
||||
const char *mType;
|
||||
|
@ -513,6 +513,7 @@ NS_IMETHODIMP nsParseMailMessageState::Clear()
|
||||
m_envelope_from.length = 0;
|
||||
m_envelope_date.length = 0;
|
||||
m_priority.length = 0;
|
||||
m_keywords.length = 0;
|
||||
m_mdn_dnt.length = 0;
|
||||
m_return_path.length = 0;
|
||||
m_account_key.length = 0;
|
||||
@ -945,6 +946,9 @@ int nsParseMailMessageState::ParseHeaders ()
|
||||
else if (!nsCRT::strncasecmp("X-Priority", buf, end - buf)
|
||||
|| !nsCRT::strncasecmp("Priority", buf, end - buf))
|
||||
header = &m_priority;
|
||||
else if (!nsCRT::strncasecmp(HEADER_X_MOZILLA_KEYWORDS, buf, end - buf)
|
||||
&& !m_keywords.length)
|
||||
header = &m_keywords;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1158,6 +1162,7 @@ int nsParseMailMessageState::FinalizeHeaders()
|
||||
struct message_header *mozstatus;
|
||||
struct message_header *mozstatus2;
|
||||
struct message_header *priority;
|
||||
struct message_header *keywords;
|
||||
struct message_header *account_key;
|
||||
struct message_header *ccList;
|
||||
struct message_header *mdn_dnt;
|
||||
@ -1193,11 +1198,12 @@ int nsParseMailMessageState::FinalizeHeaders()
|
||||
references = (m_references.length ? &m_references : 0);
|
||||
statush = (m_status.length ? &m_status : 0);
|
||||
mozstatus = (m_mozstatus.length ? &m_mozstatus : 0);
|
||||
mozstatus2 = (m_mozstatus2.length ? &m_mozstatus2 : 0);
|
||||
mozstatus2 = (m_mozstatus2.length ? &m_mozstatus2 : 0);
|
||||
date = (m_date.length ? &m_date :
|
||||
m_envelope_date.length ? &m_envelope_date :
|
||||
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);
|
||||
inReplyTo = (m_in_reply_to.length ? &m_in_reply_to : 0);
|
||||
replyTo = (m_replyTo.length ? &m_replyTo : 0);
|
||||
@ -1422,6 +1428,8 @@ int nsParseMailMessageState::FinalizeHeaders()
|
||||
m_newMsgHdr->SetPriorityString(priority->value);
|
||||
else if (priorityFlags == nsMsgPriority::notSet)
|
||||
m_newMsgHdr->SetPriority(nsMsgPriority::none);
|
||||
if (keywords)
|
||||
m_newMsgHdr->SetStringProperty("keywords", keywords->value);
|
||||
if (content_type)
|
||||
{
|
||||
char *substring = PL_strstr(content_type->value, "charset");
|
||||
@ -1887,6 +1895,16 @@ NS_IMETHODIMP nsParseNewMailState::ApplyFilterHit(nsIMsgFilter *filter, nsIMsgWi
|
||||
filterAction->GetPriority(&filterPriority);
|
||||
msgHdr->SetPriority(filterPriority);
|
||||
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:
|
||||
nsMsgLabelValue filterLabel;
|
||||
filterAction->GetLabel(&filterLabel);
|
||||
@ -1966,13 +1984,13 @@ NS_IMETHODIMP nsParseNewMailState::ApplyFilterHit(nsIMsgFilter *filter, nsIMsgWi
|
||||
nsCOMPtr<nsISupports> iSupports = do_QueryInterface(msgHdr);
|
||||
messages->AppendElement(iSupports);
|
||||
localFolder->MarkMsgsOnPop3Server(messages, POP3_FETCH_BODY);
|
||||
// Don't add this header to the DB, we're going to replace it
|
||||
// with the full message.
|
||||
// Don't add this header to the DB, we're going to replace it
|
||||
// with the full message.
|
||||
m_msgMovedByFilter = PR_TRUE;
|
||||
msgIsNew = PR_FALSE;
|
||||
// Don't do anything else in this filter, wait until we
|
||||
// have the full message.
|
||||
*applyMore = PR_FALSE;
|
||||
// Don't do anything else in this filter, wait until we
|
||||
// have the full message.
|
||||
*applyMore = PR_FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -141,6 +141,7 @@ public:
|
||||
struct message_header m_envelope_date;
|
||||
struct message_header m_priority;
|
||||
struct message_header m_account_key;
|
||||
struct message_header m_keywords;
|
||||
// Mdn support
|
||||
struct message_header m_mdn_original_recipient;
|
||||
struct message_header m_return_path;
|
||||
|
@ -593,6 +593,8 @@ nsPop3Sink::IncorporateBegin(const char* uidlString,
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = WriteLineToMailbox("X-Mozilla-Status2: 00000000" MSG_LINEBREAK);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
// leave space for 60 bytes worth of keys/tags
|
||||
rv = WriteLineToMailbox(X_MOZILLA_KEYWORDS);
|
||||
PR_smprintf_free(statusLine);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -114,5 +114,5 @@
|
||||
#define HEADER_X_MOZILLA_PART_URL "X-Mozilla-PartURL"
|
||||
#define HEADER_X_MOZILLA_IDENTITY_KEY "X-Identity-Key"
|
||||
#define HEADER_X_MOZILLA_ACCOUNT_KEY "X-Account-Key"
|
||||
|
||||
#define HEADER_X_MOZILLA_KEYWORDS "X-Mozilla-Keys"
|
||||
#endif /* nsMailHeaders_h_ */
|
||||
|
Loading…
Reference in New Issue
Block a user