--- a/mail/components/addrbook/content/abCardOverlay.js
+++ b/mail/components/addrbook/content/abCardOverlay.js
@@ -86,19 +86,20 @@ const kVcardFields =
["Custom3", "Custom3"],
["Custom4", "Custom4"],
// Other > Notes
["Notes", "Notes"]];
const kDefaultYear = 2000;
var gEditCard;
var gOnSaveListeners = new Array();
+var gOnLoadListeners = new Array();
var gOkCallback = null;
var gHideABPicker = false;
-var gOriginalPhotoURI = "";
+var gPhotoHandlers = {};
function OnLoadNewCard()
{
InitEditCard();
gEditCard.card =
(("arguments" in window) && (window.arguments.length > 0) &&
(window.arguments[0] instanceof Components.interfaces.nsIAbCard))
@@ -223,17 +224,17 @@ function EditCardOKButton()
directory.modifyCard(gEditCard.card);
for (i=0; i < foundDirectoriesCount; i++) {
// Update the addressLists item for this card
foundDirectories[i].directory.addressLists
.replaceElementAt(gEditCard.card, foundDirectories[i].index, false);
}
-
+
NotifySaveListeners(directory);
// callback to allow caller to update
if (gOkCallback)
gOkCallback();
return true; // close the window
}
@@ -312,32 +313,68 @@ function OnLoadEditCard()
// hide remote content in HTML field for remote directories
if (directory.isRemote)
document.getElementById('allowRemoteContent').hidden = true;
}
}
}
-// this is used by people who extend the ab card dialog
-// like Netscape does for screenname
+/* Registers functions that are called when loading the card
+ * values into the contact editor dialog. This is useful if
+ * extensions have added extra fields to the nsIAbCard, and
+ * need to display them in the contact editor.
+ */
+function RegisterLoadListener(aFunc)
+{
+ gOnLoadListeners[gOnLoadListeners.length] = aFunc;
+}
+
+function UnregisterLoadListener(aFunc)
+{
+ var fIndex = gOnLoadListeners.indexOf(aFunc);
+ if (fIndex != -1)
+ gOnLoadListeners.splice(fIndex, 1);
+}
+
+// Notifies load listeners that an nsIAbCard is being loaded.
+function NotifyLoadListeners(aCard, aDoc)
+{
+ if (!gOnLoadListeners.length)
+ return;
+
+ for (var i = 0; i < gOnLoadListeners.length; i++)
+ gOnLoadListeners[i](aCard, aDoc);
+}
+
+/* Registers functions that are called when saving the card
+ * values. This is useful if extensions have added extra
+ * fields to the user interface, and need to set those values
+ * in their nsIAbCard.
+ */
function RegisterSaveListener(func)
{
gOnSaveListeners[gOnSaveListeners.length] = func;
}
-// this is used by people who extend the ab card dialog
-// like Netscape does for screenname
+function UnregisterSaveListener(aFunc)
+{
+ var fIndex = gOnSaveListeners.indexOf(aFunc);
+ if (fIndex != -1)
+ gOnSaveListeners.splice(fIndex, 1);
+}
+
+// Notifies save listeners that an nsIAbCard is being saved.
function NotifySaveListeners(directory)
{
if (!gOnSaveListeners.length)
return;
- for ( var i = 0; i < gOnSaveListeners.length; i++ )
- gOnSaveListeners[i]();
+ for (var i = 0; i < gOnSaveListeners.length; i++)
+ gOnSaveListeners[i](gEditCard.card, document);
// the save listeners might have tweaked the card
// in which case we need to commit it.
directory.modifyCard(gEditCard.card);
}
function InitPhoneticFields()
{
@@ -404,18 +441,19 @@ function NewCardOKButton()
if (gEditCard.card)
{
if (!CheckAndSetCardValues(gEditCard.card, document, true))
return false; // don't close window
// replace gEditCard.card with the card we added
// so that save listeners can get / set attributes on
// the card that got created.
- gEditCard.card = GetDirectoryFromURI(uri).addCard(gEditCard.card);
- NotifySaveListeners();
+ var directory = GetDirectoryFromURI(uri);
+ gEditCard.card = directory.addCard(gEditCard.card);
+ NotifySaveListeners(directory);
if ("arguments" in window && window.arguments[0] &&
"allowRemoteContent" in window.arguments[0]) {
// getProperty may return a "1" or "0" string, we want a boolean
window.arguments[0].allowRemoteContent =
gEditCard.card.getProperty("AllowRemoteContent", false) != false;
}
}
}
@@ -424,16 +462,20 @@ function NewCardOKButton()
}
// Move the data from the cardproperty to the dialog
function GetCardValues(cardproperty, doc)
{
if (!cardproperty)
return;
+ // Pass the nsIAbCard and the Document through the listeners
+ // to give extensions a chance to populate custom fields.
+ NotifyLoadListeners(cardproperty, doc);
+
for (var i = kVcardFields.length; i-- > 0; ) {
doc.getElementById(kVcardFields[i][0]).value =
cardproperty.getProperty(kVcardFields[i][1], "");
}
var birthday = doc.getElementById("Birthday");
modifyDatepicker(birthday);
@@ -488,42 +530,20 @@ function GetCardValues(cardproperty, doc
doc.getElementById("PhoneticFirstName").value = cardproperty.getProperty("PhoneticFirstName", "");
doc.getElementById("PhoneticLastName").value = cardproperty.getProperty("PhoneticLastName", "");
}
catch (ex) {}
// Store the original photo URI and update the photo
// Select the type if there is a valid value stored for that type, otherwise
// select the generic photo
- gOriginalPhotoURI = cardproperty.getProperty("PhotoURI", "");
- switch (cardproperty.getProperty("PhotoType", "")) {
- case "file":
- try {
- var file = Components.classes["@mozilla.org/network/io-service;1"]
- .getService(Components.interfaces.nsIIOService)
- .newURI(gOriginalPhotoURI, null, null)
- .QueryInterface(Components.interfaces.nsIFileURL)
- .file;
- } catch (e) {}
- if (file) {
- document.getElementById("PhotoFile").file = file;
- updatePhoto("file");
- }
- else
- updatePhoto("generic");
- break;
- case "web":
- document.getElementById("PhotoURI").value = gOriginalPhotoURI;
- updatePhoto("web");
- break;
- default:
- if (gOriginalPhotoURI)
- document.getElementById("GenericPhotoList").value = gOriginalPhotoURI;
- updatePhoto("generic");
- }
+ var photoType = cardproperty.getProperty("PhotoType", "");
+ document.getElementById("PhotoType").value = photoType;
+ loadPhoto(cardproperty);
+ setCardEditorPhoto(photoType, cardproperty);
}
// when the ab card dialog is being loaded to show a vCard,
// hide the fields which aren't supported
// by vCard so the user does not try to edit them.
function HideNonVcardFields()
{
document.getElementById("homeTabButton").hidden = true;
@@ -576,47 +596,18 @@ function CheckAndSetCardValues(cardprope
// set phonetic fields if exist
try {
cardproperty.setProperty("PhoneticFirstName", doc.getElementById("PhoneticFirstName").value);
cardproperty.setProperty("PhoneticLastName", doc.getElementById("PhoneticLastName").value);
}
catch (ex) {}
- var type = document.getElementById("PhotoType").value;
- var photoURI = gOriginalPhotoURI;
- if (type == "file" && document.getElementById("PhotoFile").file)
- photoURI = Components.classes["@mozilla.org/network/io-service;1"]
- .getService(Components.interfaces.nsIIOService)
- .newFileURI(document.getElementById("PhotoFile").file)
- .spec;
- else if (type == "web" && document.getElementById("PhotoURI").value)
- photoURI = document.getElementById("PhotoURI").value;
- else {
- type = "generic";
- photoURI = document.getElementById("GenericPhotoList").value;
- }
- cardproperty.setProperty("PhotoType", type);
- if (photoURI != gOriginalPhotoURI) {
- // Store the original URI
- cardproperty.setProperty("PhotoURI", photoURI);
- // Save the photo if it isn't one of the generic photos
- if (type == "generic") {
- // Remove the original, if any
- removePhoto(cardproperty.getProperty("PhotoName", null));
- } else {
- // Save the new file and store its URI as PhotoName
- var file = savePhoto(photoURI);
- if (file) {
- // Remove the original, if any
- removePhoto(cardproperty.getProperty("PhotoName", null));
- cardproperty.setProperty("PhotoName", file.leafName);
- }
- }
- }
+ savePhoto(cardproperty);
+
return true;
}
function CleanUpWebPage(webPage)
{
// no :// yet so we should add something
if ( webPage.length && webPage.search("://") == -1 )
{
@@ -906,46 +897,81 @@ function modifyDatepicker(aDatepicker) {
}
// make the field's value null if aValue is null and the field's value isn't
if (aValue == null && aField.value != null)
aField.value = null;
}
}
/**
- * Updates the photo by setting the src attribute of the photo element.
+ * Updates the photo displayed in the contact editor based on the
+ * type of photo selected. If the type is not recognized, the
+ * photo will automatically switch to the generic photo.
*
- * @param aType Optional. The type of photo (web, file, or generic).
- * If supplied the corresponding radio button will be selected.
- * If not supplied the type will be determined by the currently
- * selected type.
+ * @param aType The type of photo (web, file, or generic available
+ * by default).
+ * @param aCard The nsIAbCard being edited
+ *
*/
-function updatePhoto(aType) {
- if (aType)
- // Select the type's radio button
- document.getElementById("PhotoType").value = aType;
- else
- aType = document.getElementById("PhotoType").value;
+function setCardEditorPhoto(aType, aCard)
+{
+ if (!gPhotoHandlers[aType] ||
+ !gPhotoHandlers[aType].onShow(aCard, document, "photo"))
+ gPhotoHandlers["generic"].onShow(aCard, document, "photo");
+}
+
+/**
+ * Extract the photo information from an nsIAbCard, and populate
+ * the appropriate input fields in the contact editor. If the
+ * nsIAbCard returns an unrecognized PhotoType, the generic
+ * display photo is switched to.
+ *
+ * @param aCard The nsIAbCard to extract the information from.
+ *
+ */
+function loadPhoto(aCard)
+{
+ var type = aCard.getProperty("PhotoType", "")
+ if (!gPhotoHandlers[type] ||
+ !gPhotoHandlers[type].onLoad(aCard, document))
+ gPhotoHandlers["generic"].onLoad(aCard, document);
+}
- var value;
- switch (aType) {
- case "file":
- var file = document.getElementById("PhotoFile").file;
- value = file ? Components.classes["@mozilla.org/network/io-service;1"]
- .getService(Components.interfaces.nsIIOService)
- .newFileURI(file)
- .spec : "";
- break;
- case "web":
- value = document.getElementById("PhotoURI").value;
- break;
- default:
- value = document.getElementById("GenericPhotoList").value;
- }
- document.getElementById("photo").setAttribute("src", value || defaultPhotoURI);
+/**
+ * Given the fields in the current contact editor, commit
+ * the photo to an nsIAbCard. If the photo cannot be saved,
+ * the generic contact photo is saved instead.
+ *
+ * @param aType
+ *
+ */
+function savePhoto(aCard)
+{
+ var type = document.getElementById("PhotoType").value;
+ if (!gPhotoHandlers[type] ||
+ !gPhotoHandlers[type].onSave(aCard, document))
+ gPhotoHandlers["generic"].onSave(aCard, document);
+}
+
+/**
+ * Event handler for when the user switches the type of
+ * photo for the nsIAbCard being edited. Called from
+ * abCardOverlay.xul.
+ */
+function onSwitchPhotoType(photoType)
+{
+ if (!gEditCard)
+ return;
+
+ if (photoType)
+ document.getElementById("PhotoType").value = photoType;
+ else
+ photoType = document.getElementById("PhotoType").value;
+
+ setCardEditorPhoto(photoType, gEditCard.card);
}
/**
* Removes the photo file at the given path, if present.
*
* @param aName The name of the photo to remove from the Photos directory.
*
* @return true if the file was deleted.
@@ -979,13 +1005,178 @@ function browsePhoto() {
fp.init(window, gAddressBookBundle.getString("browsePhoto"), nsIFilePicker.modeOpen);
// Add All Files & Image Files filters and select the latter
fp.appendFilters(nsIFilePicker.filterImages);
fp.appendFilters(nsIFilePicker.filterAll);
if (fp.show() == nsIFilePicker.returnOK) {
document.getElementById("PhotoFile").file = fp.file;
- updatePhoto("file");
+ let photoType = document.getElementById("FilePhotoType").value;
+ onSwitchPhotoType(photoType);
return true;
}
return false;
}
+
+/* A photo handler defines the behaviour of the contact editor
+ * for a particular photo type. Each photo handler must implement
+ * the following interface:
+ *
+ * onLoad: function(aCard, aDocument):
+ * Called when the editor wants to populate the contact editor
+ * input fields with information about aCard's photo. Note that
+ * this does NOT make aCard's photo appear in the contact editor -
+ * this is left to the onShow function. Returns true on success.
+ * If the function returns false, the generic photo handler onLoad
+ * function will be called.
+ *
+ * onShow: function(aCard, aDocument, aTargetID):
+ * Called when the editor wants to show this photo type.
+ * The onShow method should take the input fields in the document,
+ * and render the requested photo in the IMG tag with id
+ * aTargetID. Note that onShow does NOT save the photo for aCard -
+ * this job is left to the onSave function. Returns true on success.
+ * If the function returns false, the generic photo handler onShow
+ * function will be called.
+ *
+ * onSave: function(aCard, aDocument)
+ * Called when the editor wants to save this photo type. The
+ * onSave method is responsible for analyzing the photo of this
+ * type requested by the user, and storing it, as well as the
+ * other fields required by onLoad/onShow to retrieve and display
+ * the photo again. Returns true on success. If the function
+ * returns false, the generic photo handler onSave function will
+ * be called.
+ */
+
+var genericPhotoHandler = {
+
+ onLoad: function(aCard, aDocument) {
+ return true;
+ },
+
+ onShow: function(aCard, aDocument, aTargetID) {
+ aDocument.getElementById(aTargetID)
+ .setAttribute("src", defaultPhotoURI);
+ return true;
+ },
+
+ onSave: function(aCard, aDocument) {
+ // If we had the photo saved locally, clear it.
+ removePhoto(aCard.getProperty("PhotoName", null));
+ aCard.setProperty("PhotoName", null);
+ aCard.setProperty("PhotoType", "generic");
+ return true;
+ }
+}
+
+var filePhotoHandler = {
+
+ onLoad: function(aCard, aDocument) {
+ var photoURI = aCard.getProperty("PhotoURI", "");
+ try {
+ var file = Components.classes["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService)
+ .newURI(photoURI, null, null)
+ .QueryInterface(Components.interfaces.nsIFileURL)
+ .file;
+ } catch (e) {}
+
+ if (!file)
+ return false;
+
+ aDocument.getElementById("PhotoFile").file = file;
+ return true;
+ },
+
+ onShow: function(aCard, aDocument, aTargetID) {
+ var file = aDocument.getElementById("PhotoFile").file;
+ try {
+ var value = Components.classes["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService)
+ .newFileURI(file)
+ .spec;
+ } catch (e) {}
+
+ if (!value)
+ return false;
+
+ aDocument.getElementById(aTargetID).setAttribute("src", value);
+ return true;
+ },
+
+ onSave: function(aCard, aDocument) {
+ var file = aDocument.getElementById("PhotoFile").file;
+ if (!file)
+ return false;
+
+ var photoURI = Components.classes["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService)
+ .newFileURI(file)
+ .spec;
+
+ var file = storePhoto(photoURI);
+
+ if (!file)
+ return false;
+
+ // Remove the original, if any
+ removePhoto(aCard.getProperty("PhotoName", null));
+ aCard.setProperty("PhotoName", file.leafName);
+ aCard.setProperty("PhotoType", "file");
+ aCard.setProperty("PhotoURI", photoURI);
+ return true;
+ }
+}
+
+var webPhotoHandler = {
+
+ onLoad: function(aCard, aDocument) {
+ var photoURI = aCard.getProperty("PhotoURI", null);
+
+ if (!photoURI)
+ return false;
+
+ aDocument.getElementById("PhotoURI").value = photoURI;
+ return true;
+ },
+
+ onShow: function(aCard, aDocument, aTargetID) {
+ var photoURI = aDocument.getElementById("PhotoURI").value;
+
+ if (!photoURI)
+ return false;
+
+ aDocument.getElementById(aTargetID).setAttribute("src", photoURI);
+ return true;
+ },
+
+ onSave: function(aCard, aDocument) {
+ var photoURI = aDocument.getElementById("PhotoURI").value;
+
+ var file = storePhoto(photoURI);
+ if (!file)
+ return false;
+
+ // Remove the original, if any
+ removePhoto(aCard.getProperty("PhotoName", null));
+ aCard.setProperty("PhotoName", file.leafName);
+ aCard.setProperty("PhotoURI", photoURI);
+ aCard.setProperty("PhotoType", "web");
+ return true;
+ }
+}
+
+/* In order for other photo handlers to be recognized for
+ * a particular type, they must be registered through this
+ * function.
+ * @param aType the type of photo to handle
+ * @param aPhotoHandler the photo handler to register
+ */
+function registerPhotoHandler(aType, aPhotoHandler)
+{
+ gPhotoHandlers[aType] = aPhotoHandler;
+}
+
+registerPhotoHandler("generic", genericPhotoHandler);
+registerPhotoHandler("web", webPhotoHandler);
+registerPhotoHandler("file", filePhotoHandler);
--- a/mail/components/addrbook/content/abCardOverlay.xul
+++ b/mail/components/addrbook/content/abCardOverlay.xul
@@ -66,25 +66,25 @@
<tabpanels id="abTabPanels" flex="1">
<!-- ** Name Tab ** -->
<!-- The following vbox contains two hboxes
top: Name/Email/Phonenumber bottom: Email prefs. -->
<vbox id="abNameTab" >
<!-- This hbox contains two vboxes
left: Name/Email, right: Phonenumbers -->
<hbox>
- <vbox> <!-- This box contains the Names and Emailnames -->
+ <vbox id="namesAndEmailAddresses"> <!-- This box contains the Names and Emailnames -->
<!-- LOCALIZATION NOTE:
NameField1, NameField2, PhoneticField1, PhoneticField2
those fields are either LN or FN depends on the target country.
They are configurable in the .dtd file.
-->
- <hbox align="center">
+ <hbox id="NameField1Container" align="center">
<spacer flex="1"/>
<label control="&NameField1.id;" value="&NameField1.label;"
accesskey="&NameField1.accesskey;"/>
<hbox class="CardEditWidth" align="center">
<textbox id="&NameField1.id;" flex="1"
oninput="GenerateDisplayName()"/>
<!-- LOCALIZATION NOTE:
@@ -93,17 +93,17 @@
-->
<spacer id="PhoneticSpacer1" flex="1" hidden="true"/>
<label id="PhoneticLabel1" control="&PhoneticField1.id;"
value="&PhoneticField1.label;" hidden="true"/>
<textbox id="&PhoneticField1.id;" flex="1" hidden="true"/>
</hbox>
</hbox>
- <hbox align="center">
+ <hbox id="NameField2Container" align="center">
<spacer flex="1"/>
<label control="&NameField2.id;" value="&NameField2.label;"
accesskey="&NameField2.accesskey;"/>
<hbox class="CardEditWidth" align="center">
<textbox id="&NameField2.id;" flex="1"
oninput="GenerateDisplayName()"/>
<!-- LOCALIZATION NOTE:
@@ -112,94 +112,94 @@
-->
<spacer id="PhoneticSpacer2" flex="1" hidden="true"/>
<label id="PhoneticLabel2" control="&PhoneticField2.id;"
value="&PhoneticField2.label;" hidden="true"/>
<textbox id="&PhoneticField2.id;" flex="1" hidden="true"/>
</hbox>
</hbox>
- <hbox align="center">
+ <hbox id="DisplayNameContainer" align="center">
<spacer flex="1"/>
<label control="DisplayName" value="&DisplayName.label;"
accesskey="&DisplayName.accesskey;" />
<hbox class="CardEditWidth">
<textbox id="DisplayName" flex="1"
oninput="DisplayNameChanged()"/>
</hbox>
</hbox>
- <hbox align="center">
+ <hbox id="PreferDisplayNameContainer" align="center">
<spacer flex="1"/>
<hbox class="CardEditWidth">
<checkbox id="preferDisplayName"
label="&preferDisplayName.label;"
accesskey="&preferDisplayName.accesskey;"/>
</hbox>
</hbox>
- <hbox id="nickNameContainer" align="center">
+ <hbox id="NickNameContainer" align="center">
<spacer flex="1"/>
<label control="NickName" value="&NickName.label;"
accesskey="&NickName.accesskey;"/>
<hbox class="CardEditWidth">
<textbox id="NickName" flex="1"/>
</hbox>
</hbox>
- <hbox align="center">
+ <hbox id="PrimaryEmailContainer" align="center">
<spacer flex="1"/>
<label control="PrimaryEmail" value="&PrimaryEmail.label;"
accesskey="&PrimaryEmail.accesskey;"/>
<hbox class="CardEditWidth">
<textbox id="PrimaryEmail" flex="1" class="uri-element"/>
</hbox>
</hbox>
- <hbox id="secondaryEmailContainer" align="center">
+ <hbox id="SecondaryEmailContainer" align="center">
<spacer flex="1"/>
<label control="SecondEmail" value="&SecondEmail.label;"
accesskey="&SecondEmail.accesskey;"/>
<hbox class="CardEditWidth">
<textbox id="SecondEmail" flex="1" class="uri-element"/>
</hbox>
</hbox>
- <hbox id="screenNameContainer" align="center">
+ <hbox id="ScreenNameContainer" align="center">
<spacer flex="1"/>
<label control="ScreenName" value="&ScreenName.label;"
accesskey="&ScreenName.accesskey;"/>
<hbox class="CardEditWidth">
<textbox id="ScreenName" flex="1"/>
</hbox>
</hbox>
</vbox> <!-- End of Name/Email -->
<!-- Phone Number section -->
- <vbox>
- <hbox align="center">
+ <vbox id="PhoneNumbers">
+ <hbox id="WorkPhoneContainer" align="center">
<spacer flex="1"/>
<label control="WorkPhone" value="&WorkPhone.label;"
accesskey="&WorkPhone.accesskey;" />
<textbox id="WorkPhone" class="PhoneEditWidth"/>
</hbox>
- <hbox align="center">
+ <hbox id="HomePhoneContainer" align="center">
<spacer flex="1"/>
<label control="HomePhone" value="&HomePhone.label;"
accesskey="&HomePhone.accesskey;"/>
<textbox id="HomePhone" class="PhoneEditWidth"/>
</hbox>
- <hbox align="center">
+ <hbox id="FaxNumberContainer" align="center">
<spacer flex="1"/>
<label control="FaxNumber" value="&FaxNumber.label;"
accesskey="&FaxNumber.accesskey;"/>
<textbox id="FaxNumber" class="PhoneEditWidth"/>
</hbox>
- <hbox align="center">
+ <hbox id="PagerNumberContainer" align="center">
<spacer flex="1"/>
<label control="PagerNumber" value="&PagerNumber.label;"
accesskey="&PagerNumber.accesskey;"/>
<textbox id="PagerNumber" class="PhoneEditWidth"/>
</hbox>
- <hbox align="center">
+ <hbox id="CellularNumberContainer" align="center">
<spacer flex="1"/>
<label control="CellularNumber" value="&CellularNumber.label;"
accesskey="&CellularNumber.accesskey;"/>
<textbox id="CellularNumber" class="PhoneEditWidth"/>
</hbox>
</vbox> <!-- End of Phonenumbers -->
</hbox> <!-- End of Name/Email/Phonenumbers -->
<!-- Email Preferences -->
@@ -236,21 +236,21 @@
<hbox align="center">
<spacer flex="1"/>
<label control="HomeAddress2" value="&HomeAddress2.label;"
accesskey="&HomeAddress2.accesskey;"/>
<hbox class="AddressCardEditWidth">
<textbox id="HomeAddress2" flex="1"/>
</hbox>
</hbox>
- <hbox align="center">
+ <hbox id="HomeCityContainer" align="center">
<spacer flex="1"/>
<label control="HomeCity" value="&HomeCity.label;"
accesskey="&HomeCity.accesskey;"/>
- <hbox class="AddressCardEditWidth">
+ <hbox id="HomeCityFieldContainer" align="center" class="AddressCardEditWidth">
<textbox id="HomeCity" flex="1"/>
</hbox>
</hbox>
<hbox align="center">
<spacer flex="1"/>
<label control="HomeState" value="&HomeState.label;"
accesskey="&HomeState.accesskey;"/>
<hbox align="center" class="AddressCardEditWidth">
@@ -264,17 +264,17 @@
<hbox align="center">
<spacer flex="1"/>
<label control="HomeCountry" value="&HomeCountry.label;"
accesskey="&HomeCountry.accesskey;"/>
<hbox class="AddressCardEditWidth">
<textbox id="HomeCountry" flex="1"/>
</hbox>
</hbox>
- <hbox align="center">
+ <hbox id="WebPage2Container" align="center">
<spacer flex="1"/>
<label control="WebPage2" value="&HomeWebPage.label;"
accesskey="&HomeWebPage.accesskey;"/>
<hbox class="AddressCardEditWidth">
<textbox id="WebPage2" flex="1" class="uri-element"/>
</hbox>
</hbox>
<hbox id="birthdayField" align="center">
@@ -294,80 +294,80 @@
<label value="&YearsOld.label;"/>
<spacer flex="1"/>
</hbox>
</hbox>
</vbox>
<!-- ** Business Address Tab ** -->
<vbox id="abBusinessTab" >
- <hbox align="center">
+ <hbox id="JobTitleDepartmentContainer" align="center">
<spacer flex="1"/>
<label control="JobTitle" value="&JobTitle.label;"
accesskey="&JobTitle.accesskey;"/>
<hbox class="AddressCardEditWidth" align="center">
<textbox id="JobTitle" flex="1"/>
<label control="Department" value="&Department.label;"
accesskey="&Department.accesskey;"/>
<textbox id="Department" flex="1"/>
</hbox>
</hbox>
- <hbox align="center">
+ <hbox id="CompanyContainer" align="center">
<spacer flex="1"/>
<label control="Company" value="&Company.label;"
accesskey="&Company.accesskey;"/>
<hbox class="AddressCardEditWidth">
<textbox id="Company" flex="1"/>
</hbox>
</hbox>
- <hbox align="center">
+ <hbox id="WorkAddressContainer" align="center">
<spacer flex="1"/>
<label control="WorkAddress" value="&WorkAddress.label;"
accesskey="&WorkAddress.accesskey;"/>
<hbox class="AddressCardEditWidth">
<textbox id="WorkAddress" flex="1"/>
</hbox>
</hbox>
- <hbox align="center">
+ <hbox id="WorkAddress2Container" align="center">
<spacer flex="1"/>
<label control="WorkAddress2" value="&WorkAddress2.label;"
accesskey="&WorkAddress2.accesskey;"/>
<hbox class="AddressCardEditWidth">
<textbox id="WorkAddress2" flex="1"/>
</hbox>
</hbox>
- <hbox align="center">
+ <hbox id="WorkCityContainer" align="center">
<spacer flex="1"/>
<label control="WorkCity" value="&WorkCity.label;"
accesskey="&WorkCity.accesskey;"/>
- <hbox class="AddressCardEditWidth">
+ <hbox id="WorkCityFieldContainer" class="AddressCardEditWidth" align="center">
<textbox id="WorkCity" flex="1"/>
</hbox>
</hbox>
- <hbox align="center">
+ <hbox id="WorkStateZipContainer" align="center">
<spacer flex="1"/>
<label control="WorkState" value="&WorkState.label;"
accesskey="&WorkState.accesskey;"/>
<hbox class="AddressCardEditWidth" align="center">
<textbox id="WorkState" flex="1"/>
<spacer class="stateZipSpacer"/>
<label control="WorkZipCode" value="&WorkZipCode.label;"
accesskey="&WorkZipCode.accesskey;"/>
<textbox id="WorkZipCode" class="ZipWidth"/>
</hbox>
</hbox>
- <hbox align="center">
+ <hbox id="WorkCountryContainer" align="center">
<spacer flex="1"/>
<label control="WorkCountry" value="&WorkCountry.label;"
accesskey="&WorkCountry.accesskey;"/>
<hbox class="AddressCardEditWidth">
<textbox id="WorkCountry" flex="1"/>
</hbox>
</hbox>
- <hbox align="center">
+ <hbox id="WebPage1Container" align="center">
<spacer flex="1"/>
<label control="WebPage1" value="&WorkWebPage.label;"
accesskey="&WorkWebPage.accesskey;"/>
<hbox class="AddressCardEditWidth">
<textbox id="WebPage1" flex="1" class="uri-element"/>
</hbox>
</hbox>
</vbox>
@@ -403,43 +403,51 @@
<!-- ** Photo Tab ** -->
<hbox id="abPhotoTab" align="center">
<description style="min-width: 25ch; max-width: 25ch; text-align: center">
<html:img id="photo" style="max-width: 25ch; max-height: 25ch;"/>
</description>
<groupbox flex="1">
<caption label="&PhotoDesc.label;"/>
- <radiogroup id="PhotoType" onselect="updatePhoto();">
- <radio value="generic" label="&GenericPhoto.label;"
- accesskey="&GenericPhoto.accesskey;"/>
- <menulist id="GenericPhotoList" class="indent" flex="1"
- oncommand="updatePhoto('generic');">
- <menupopup>
- <menuitem label="&DefaultPhoto.label;" selected="true"
- value="chrome://messenger/skin/addressbook/icons/contact-generic.png"
- image="chrome://messenger/skin/addressbook/icons/contact-generic-tiny.png"/>
- </menupopup>
- </menulist>
- <radio value="file" label="&PhotoFile.label;"
- accesskey="&PhotoFile.accesskey;"/>
- <hbox class="indent">
- <filefield id="PhotoFile" maxlength="255" flex="1"/>
- <button oncommand="browsePhoto();" id="BrowsePhoto"
- label="&BrowsePhoto.label;"
- accesskey="&BrowsePhoto.accesskey;"/>
- </hbox>
- <radio value="web" label="&PhotoURL.label;"
- accesskey="&PhotoURL.accesskey;"/>
- <hbox class="indent">
- <textbox id="PhotoURI" maxlength="255" flex="1"
- placeholder="&PhotoURL.placeholder;"/>
- <button oncommand="updatePhoto('web');" id="UpdatePhoto"
- label="&UpdatePhoto.label;"
- accesskey="&UpdatePhoto.accesskey;"/>
- </hbox>
+ <radiogroup id="PhotoType" onselect="onSwitchPhotoType();">
+ <vbox id="GenericPhotoContainer">
+ <radio id="GenericPhotoType" value="generic" label="&GenericPhoto.label;"
+ accesskey="&GenericPhoto.accesskey;"/>
+ <menulist id="GenericPhotoList" class="indent" flex="1"
+ oncommand="onSwitchPhotoType('generic');">
+ <menupopup>
+ <menuitem label="&DefaultPhoto.label;" selected="true"
+ value="chrome://messenger/skin/addressbook/icons/contact-generic.png"
+ image="chrome://messenger/skin/addressbook/icons/contact-generic-tiny.png"/>
+ </menupopup>
+ </menulist>
+ </vbox>
+
+ <vbox id="FilePhotoContainer">
+ <radio id="FilePhotoType" value="file" label="&PhotoFile.label;"
+ accesskey="&PhotoFile.accesskey;"/>
+ <hbox class="indent">
+ <filefield id="PhotoFile" maxlength="255" flex="1"/>
+ <button oncommand="browsePhoto();" id="BrowsePhoto"
+ label="&BrowsePhoto.label;"
+ accesskey="&BrowsePhoto.accesskey;"/>
+ </hbox>
+ </vbox>
+
+ <vbox id="WebPhotoContainer">
+ <radio id="WebPhotoType" value="web" label="&PhotoURL.label;"
+ accesskey="&PhotoURL.accesskey;"/>
+ <hbox class="indent">
+ <textbox id="PhotoURI" maxlength="255" flex="1"
+ placeholder="&PhotoURL.placeholder;"/>
+ <button oncommand="onSwitchPhotoType('web');" id="UpdatePhoto"
+ label="&UpdatePhoto.label;"
+ accesskey="&UpdatePhoto.accesskey;"/>
+ </hbox>
+ </vbox>
</radiogroup>
</groupbox>
</hbox>
</tabpanels>
</tabbox>
</vbox>
</overlay>
--- a/mail/components/addrbook/content/abCardViewOverlay.js
+++ b/mail/components/addrbook/content/abCardViewOverlay.js
@@ -47,16 +47,17 @@ gPrefs = gPrefs.QueryInterface(Component
var gProfileDirURL;
var gMapItURLFormat = gPrefs.getComplexValue("mail.addr_book.mapit_url.format",
Components.interfaces.nsIPrefLocalizedString).data;
var gIOService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
var gFileHandler = gIOService.getProtocolHandler("file").QueryInterface(Components.interfaces.nsIFileProtocolHandler);
+var gPhotoDisplayHandlers = {};
var zListName;
var zPrimaryEmail;
var zSecondaryEmail;
var zScreenName;
var zNickname;
var zDisplayName;
var zWork;
@@ -204,17 +205,17 @@ function DisplayCardViewPane(realCard)
isMailList : realCard.isMailList,
mailListURI : realCard.mailListURI
};
var data = top.cvData;
var visible;
// Contact photo
- cvData.cvPhoto.setAttribute("src", getPhotoURI(card.getProperty("PhotoName")));
+ displayPhoto(card, cvData.cvPhoto);
var titleString;
if (generatedName == "")
titleString = card.primaryEmail; // if no generatedName, use email
else
titleString = generatedName;
// set fields in card view pane
@@ -589,8 +590,64 @@ function openLink(id)
messenger = messenger.QueryInterface(Components.interfaces.nsIMessenger);
messenger.launchExternalURL(document.getElementById(id).getAttribute("href"));
} catch (ex) {}
// return false, so we don't load the href in the addressbook window
return false;
}
+/* Display the contact photo from the nsIAbCard in the IMG element.
+ * If the photo cannot be displayed, show the generic contact
+ * photo.
+ */
+function displayPhoto(aCard, aImg)
+{
+ var type = aCard.getProperty("PhotoType", "");
+ if (!gPhotoDisplayHandlers[type] ||
+ !gPhotoDisplayHandlers[type](aCard, aImg))
+ gPhotoDisplayHandlers["generic"](aCard, aImg);
+}
+
+/* In order to display the contact photos in the card view, there
+ * must be a registered photo display handler for the card photo
+ * type. The generic, file, and web photo types are handled
+ * by default.
+ *
+ * A photo display handler is a function that behaves as follows:
+ *
+ * function(aCard, aImg):
+ * The function is responsible for determining how to retrieve
+ * the photo from nsIAbCard aCard, and for displaying it in img
+ * img element aImg. Returns true if successful. If it returns
+ * false, the generic photo display handler will be called.
+ *
+ * The following display handlers are for the generic, file and
+ * web photo types.
+ */
+
+var genericPhotoDisplayHandler = function(aCard, aImg)
+{
+ aImg.setAttribute("src", defaultPhotoURI);
+ return true;
+}
+
+var photoNameDisplayHandler = function(aCard, aImg)
+{
+ var photoSrc = getPhotoURI(aCard.getProperty("PhotoName"));
+ aImg.setAttribute("src", photoSrc);
+ return true;
+}
+
+/* In order for a photo display handler to be registered for
+ * a particular photo type, it must be registered here.
+ */
+function registerPhotoDisplayHandler(aType, aPhotoDisplayHandler)
+{
+ if (!gPhotoDisplayHandlers[aType])
+ gPhotoDisplayHandlers[aType] = aPhotoDisplayHandler;
+}
+
+registerPhotoDisplayHandler("generic", genericPhotoDisplayHandler);
+// File and Web are treated the same, and therefore use the
+// same handler.
+registerPhotoDisplayHandler("file", photoNameDisplayHandler);
+registerPhotoDisplayHandler("web", photoNameDisplayHandler);
--- a/mail/components/addrbook/content/abCommon.js
+++ b/mail/components/addrbook/content/abCommon.js
@@ -352,19 +352,31 @@ function AbNewMessage()
if (params)
{
params.type = msgComposeType.New;
params.format = msgComposFormat.Default;
var composeFields = Components.classes["@mozilla.org/messengercompose/composefields;1"].createInstance(Components.interfaces.nsIMsgCompFields);
if (composeFields)
{
if (DirPaneHasFocus())
- composeFields.to = GetSelectedAddressesFromDirTree();
+ {
+ var directory = gDirectoryTreeView.getDirectoryAtIndex(gDirTree.currentIndex);
+ if (directory && directory.isMailList &&
+ directory.getBoolValue("HidesRecipients", false))
+ // Bug 669301 (https://bugzilla.mozilla.org/show_bug.cgi?id=669301)
+ // We're using BCC right now to hide recipients from one another.
+ // We should probably use group syntax, but that's broken
+ // right now, so this will have to do.
+ composeFields.bcc = GetSelectedAddressesFromDirTree();
+ else
+ composeFields.to = GetSelectedAddressesFromDirTree();
+ }
else
composeFields.to = GetSelectedAddresses();
+
params.composeFields = composeFields;
msgComposeService.OpenComposeWindowWithParams(null, params);
}
}
}
// XXX todo
// could this be moved into utilityOverlay.js?
@@ -968,17 +980,18 @@ function saveStreamToFile(aIStream, aFil
* The URI is used to obtain a channel which is then opened synchronously and
* this stream is written to the new file to store an offline, local copy of the
* photo.
*
* @param aUri The URI of the photo.
*
* @return An nsIFile representation of the photo.
*/
-function savePhoto(aUri) {
+function storePhoto(aUri)
+{
if (!aUri)
return false;
// Get the photos directory and check that it exists
var file = getPhotosDir();
// Create a channel from the URI and open it as an input stream
var ios = Components.classes["@mozilla.org/network/io-service;1"]
@@ -1030,8 +1043,10 @@ function makePhotoFile(aDir, aExtension)
// Find a random filename for the photo that doesn't exist yet
do {
filename = new String(Math.random()).replace("0.", "") + "." + aExtension;
newFile = aDir.clone();
newFile.append(filename);
} while (newFile.exists());
return newFile;
}
+
+
--- a/mail/components/addrbook/content/abEditListDialog.xul
+++ b/mail/components/addrbook/content/abEditListDialog.xul
@@ -54,31 +54,31 @@
</stringbundleset>
<!-- move needed functions into a single js file -->
<script type="application/javascript" src="chrome://messenger/content/messengercompose/addressingWidgetOverlay.js"/>
<script type="application/javascript" src="chrome://messenger/content/addressbook/abCommon.js"/>
<script type="application/javascript" src="chrome://messenger/content/addressbook/abMailListDialog.js"/>
<vbox id="editlist">
- <hbox>
+ <hbox id="ListNameContainer" align="center">
<spacer flex="1"/>
<label control="ListName" value="&ListName.label;" accesskey="&ListName.accesskey;" class="CardEditLabel"/>
<hbox class="CardEditWidth">
<textbox id="ListName" flex="1"/>
</hbox>
</hbox>
- <hbox>
+ <hbox id="ListNickNameContainer" align="center">
<spacer flex="1"/>
<label control="ListNickName" value="&ListNickName.label;" accesskey="&ListNickName.accesskey;" class="CardEditLabel"/>
<hbox class="CardEditWidth">
<textbox id="ListNickName" flex="1"/>
</hbox>
</hbox>
- <hbox>
+ <hbox id="ListDescriptionContainer" align="center">
<spacer flex="1"/>
<label control="ListDescription" value="&ListDescription.label;" accesskey="&ListDescription.accesskey;" class="CardEditLabel"/>
<hbox class="CardEditWidth">
<textbox id="ListDescription" flex="1"/>
</hbox>
</hbox>
<spacer style="height:1em"/>
--- a/mail/components/addrbook/content/abMailListDialog.xul
+++ b/mail/components/addrbook/content/abMailListDialog.xul
@@ -64,31 +64,31 @@
<menupopup id="abPopup-menupopup" class="addrbooksPopup" writable="true"
supportsmaillists="true"/>
</menulist>
</hbox>
<spacer style="height:1em"/>
<vbox id="editlist">
- <hbox>
+ <hbox id="ListNameContainer" align="center">
<spacer flex="1"/>
<label control="ListName" value="&ListName.label;" accesskey="&ListName.accesskey;" class="CardEditLabel"/>
<hbox class="CardEditWidth">
<textbox id="ListName" flex="1"/>
</hbox>
</hbox>
- <hbox>
+ <hbox id="ListNickNameContainer" align="center">
<spacer flex="1"/>
<label control="ListNickName" value="&ListNickName.label;" accesskey="&ListNickName.accesskey;" class="CardEditLabel"/>
<hbox class="CardEditWidth">
<textbox id="ListNickName" flex="1"/>
</hbox>
</hbox>
- <hbox>
+ <hbox id="ListDescriptionContainer" align="center">
<spacer flex="1"/>
<label control="ListDescription" value="&ListDescription.label;" accesskey="&ListDescription.accesskey;" class="CardEditLabel"/>
<hbox class="CardEditWidth">
<textbox id="ListDescription" flex="1"/>
</hbox>
</hbox>
<spacer style="height:1em"/>
--- a/mailnews/addrbook/content/abMailListDialog.js
+++ b/mailnews/addrbook/content/abMailListDialog.js
@@ -38,16 +38,18 @@
top.MAX_RECIPIENTS = 1;
var inputElementType = "";
var gListCard;
var gEditList;
var oldListName = "";
var gPromptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
var gHeaderParser = Components.classes["@mozilla.org/messenger/headerparser;1"].getService(Components.interfaces.nsIMsgHeaderParser);
+var gLoadListeners = [];
+var gSaveListeners = [];
try
{
var gDragService = Components.classes["@mozilla.org/widget/dragservice;1"]
.getService(Components.interfaces.nsIDragService);
}
catch (e)
{
@@ -186,19 +188,21 @@ function MailListOKButton()
if ( !uri )
return false; // don't close window
// -----
//Add mailing list to database
var mailList = Components.classes["@mozilla.org/addressbook/directoryproperty;1"].createInstance();
mailList = mailList.QueryInterface(Components.interfaces.nsIAbDirectory);
- if (GetListValue(mailList, true)) {
- var parentDirectory = GetDirectoryFromURI(uri);
- parentDirectory.addMailList(mailList);
+ if (GetListValue(mailList, true))
+ {
+ var parentDirectory = GetDirectoryFromURI(uri);
+ mailList = parentDirectory.addMailList(mailList);
+ NotifySaveListeners(mailList);
}
else
return false;
}
return true; // close the window
}
@@ -239,31 +243,34 @@ function OnLoadNewMailList()
awFitDummyRows(1);
document.addEventListener("keypress", awDocumentKeyPress, true);
// focus on first name
var listName = document.getElementById('ListName');
if ( listName )
setTimeout( function(firstTextBox) { firstTextBox.focus(); }, 0, listName );
+
+ NotifyLoadListeners(directory);
}
function EditListOKButton()
{
//edit mailing list in database
if (GetListValue(gEditList, false))
{
if (gListCard) {
// modify the list card (for the results pane) from the mailing list
gListCard.displayName = gEditList.dirName;
gListCard.lastName = gEditList.dirName;
gListCard.setProperty("NickName", gEditList.listNickName);
gListCard.setProperty("Notes", gEditList.description);
}
+ NotifySaveListeners(gEditList);
gEditList.editMailListToDatabase(gListCard);
return true; // close the window
}
return false;
}
@@ -323,16 +330,17 @@ function OnLoadEditList()
document.addEventListener("keypress", awDocumentKeyPress, true);
// workaround for bug 118337 - for mailing lists that have more rows than fits inside
// the display, the value of the textbox inside the new row isn't inherited into the input -
// the first row then appears to be duplicated at the end although it is actually empty.
// see awAppendNewRow which copies first row and clears it
setTimeout(AppendLastRow, 0);
+ NotifyLoadListeners(gEditList);
}
function AppendLastRow()
{
AppendNewRowAndSetFocus();
awFitDummyRows(1);
// focus on first name
@@ -561,8 +569,65 @@ function DropListAddress(target, address
{
awClickEmptySpace(target, true); //that will automatically set the focus on a new available row, and make sure is visible
if (top.MAX_RECIPIENTS == 0)
top.MAX_RECIPIENTS = 1;
var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
lastInput.value = address;
awAppendNewRow(true);
}
+
+/* Allows extensions to register a listener function for
+ * when a mailing list is loaded. The listener function
+ * should take two parameters - the first being the
+ * mailing list being loaded, the second one being the
+ * current window document.
+ */
+function RegisterLoadListener(aListener)
+{
+ gLoadListeners.push(aListener);
+}
+
+/* Allows extensions to unload a load listener function.
+ */
+function UnregisterLoadListener(aListener)
+{
+ var fIndex = gLoadListeners.indexOf(aListener);
+ if (fIndex != -1)
+ gLoadListeners.splice(fIndex, 1);
+}
+
+/* Allows extensions to register a listener function for
+ * when a mailing list is saved. Like a load listener,
+ * the save listener should take two parameters: the first
+ * being a copy of the mailing list that is being saved,
+ * and the second being the current window document.
+ */
+function RegisterSaveListener(aListener)
+{
+ gSaveListeners.push(aListener);
+}
+
+/* Allows extensions to unload a save listener function.
+ */
+function UnregisterSaveListener(aListener)
+{
+ var fIndex = gSaveListeners.indexOf(aListener);
+ if (fIndex != -1)
+ gSaveListeners.splice(fIndex, 1);
+}
+
+/* Notifies all load listeners.
+ */
+function NotifyLoadListeners(aMailingList)
+{
+ for (let i = 0; i < gLoadListeners.length; i++)
+ gLoadListeners[i](aMailingList, document);
+}
+
+/* Notifies all save listeners.
+ */
+function NotifySaveListeners(aMailingList)
+{
+ for (let i = 0; i < gSaveListeners.length; i++)
+ gSaveListeners[i](aMailingList, document);
+}
+
--- a/mailnews/addrbook/public/nsIAbDirectory.idl
+++ b/mailnews/addrbook/public/nsIAbDirectory.idl
@@ -74,17 +74,17 @@ interface nsIMutableArray;
* address book, the scheme is "moz-abmdbdirectory", so the contract ID for
* the Mork-based address book type is:
*
* @mozilla.org/addressbook/directory;1?type=moz-abmdbdirectory
*
* The UUID of an nsIAbDirectory is its preference ID and its name, concatenated
* together.
*/
-[scriptable, uuid(81927b85-11a2-4967-8c90-19ca600cedf0)]
+[scriptable, uuid(72dc868b-db5b-4daa-b6c6-071be4a05d02)]
interface nsIAbDirectory : nsIAbCollection {
/**
* The chrome URI to use for bringing up a dialog to edit this directory.
* When opening the dialog, use a JS argument of
* {selectedDirectory: thisdir} where thisdir is this directory that you just
* got the chrome URI from.
*/
@@ -213,18 +213,19 @@ interface nsIAbDirectory : nsIAbCollecti
// Specific to a directory which stores mail lists
/**
* Creates a new mailing list in the directory. Currently only supported
* for top-level directories.
*
* @param list The new mailing list to add.
+ * @return The mailing list directory added, which may have been modified.
*/
- void addMailList(in nsIAbDirectory list);
+ nsIAbDirectory addMailList(in nsIAbDirectory list);
/**
* Nick Name of the mailing list. This attribute is only really used when
* the nsIAbDirectory represents a mailing list.
*/
attribute AString listNickName;
/**
@@ -291,16 +292,25 @@ interface nsIAbDirectory : nsIAbCollecti
//@{
long getIntValue(in string aName, in long aDefaultValue);
boolean getBoolValue(in string aName, in boolean aDefaultValue);
ACString getStringValue(in string aName, in ACString aDefaultValue);
AUTF8String getLocalizedStringValue(in string aName, in AUTF8String aDefaultValue);
//@}
/**
+ * The following attributes are read from an nsIAbDirectory via the above methods:
+ *
+ * HidesRecipients (Boolean)
+ * If true, and this nsIAbDirectory is a mailing list, then when sending mail to
+ * this list, recipients addresses will be hidden from one another by sending
+ * via BCC.
+ */
+
+ /**
* @name setXXXValue
*
* Helper functions to set different types of pref values.
*
* @param aName The name of the pref within the branch dirPrefId to
* get a value from.
*
* @param aValue The value to set the pref to.
@@ -309,9 +319,10 @@ interface nsIAbDirectory : nsIAbCollecti
* be obtained (e.g. dirPrefId isn't set).
*/
//@{
void setIntValue(in string aName, in long aValue);
void setBoolValue(in string aName, in boolean aValue);
void setStringValue(in string aName, in ACString aValue);
void setLocalizedStringValue(in string aName, in AUTF8String aValue);
//@}
+
};
--- a/mailnews/addrbook/public/nsIAbManager.idl
+++ b/mailnews/addrbook/public/nsIAbManager.idl
@@ -39,25 +39,26 @@
#include "nsIAbListener.idl"
interface nsIDOMWindow;
interface nsIAbDirectory;
interface nsIAbCard;
interface nsIAbDirectoryProperties;
interface nsILocalFile;
interface nsISimpleEnumerator;
+interface nsIAbBooleanExpression;
/**
* nsIAbManager is an interface to the main address book mananger
* via the contract id "@mozilla.org/abmanager;1"
*
* It contains the main functions to create and delete address books as well
* as some helper functions.
*/
-[scriptable, uuid(549bf1f6-ada4-40c5-9f59-4ea9eda4a935)]
+[scriptable, uuid(479919a2-c5f9-4d84-af87-f99c4ecb7f5e)]
interface nsIAbManager : nsISupports
{
/**
* Returns an enumerator containing all the top-level directories
* (non-recursive)
*/
readonly attribute nsISimpleEnumerator directories;
@@ -195,9 +196,19 @@ interface nsIAbManager : nsISupports
* Use of this method is preferred in such cases, since it is designed to work
* with other methods of this interface.
*
* @param directoryId The directory ID.
* @param localId The per-directory ID.
* @return A string to use for the UUID.
*/
AUTF8String generateUUID(in AUTF8String directoryId, in AUTF8String localId);
+
+
+ /**
+ * A utility function that converts an nsIAbDirectory query string to an
+ * nsIAbBooleanExpression.
+ *
+ * @param aQueryString The nsIAbDirectory query string
+ * @return an nsIAbBooleanExpression for the query string
+ */
+ nsIAbBooleanExpression convertQueryStringToExpression(in string aQueryString);
};
--- a/mailnews/addrbook/src/nsAbDirProperty.cpp
+++ b/mailnews/addrbook/src/nsAbDirProperty.cpp
@@ -337,17 +337,17 @@ nsAbDirProperty::CreateNewDirectory(cons
nsACString &aResult)
{ return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHODIMP
nsAbDirProperty::CreateDirectoryByURI(const nsAString &aDisplayName,
const nsACString &aURI)
{ return NS_ERROR_NOT_IMPLEMENTED; }
-NS_IMETHODIMP nsAbDirProperty::AddMailList(nsIAbDirectory *list)
+NS_IMETHODIMP nsAbDirProperty::AddMailList(nsIAbDirectory *list, nsIAbDirectory **addedList)
{ return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHODIMP nsAbDirProperty::EditMailListToDatabase(nsIAbCard *listCard)
{ return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHODIMP nsAbDirProperty::AddCard(nsIAbCard *childCard, nsIAbCard **addedCard)
{ return NS_ERROR_NOT_IMPLEMENTED; }
--- a/mailnews/addrbook/src/nsAbMDBDirectory.cpp
+++ b/mailnews/addrbook/src/nsAbMDBDirectory.cpp
@@ -621,18 +621,20 @@ NS_IMETHODIMP nsAbMDBDirectory::HasDirec
if (NS_SUCCEEDED(rv))
rv = database->ContainsMailList(dir, hasDir);
}
return rv;
}
-NS_IMETHODIMP nsAbMDBDirectory::AddMailList(nsIAbDirectory *list)
+NS_IMETHODIMP nsAbMDBDirectory::AddMailList(nsIAbDirectory *list, nsIAbDirectory **addedList)
{
+ NS_ENSURE_ARG_POINTER(addedList);
+
if (mIsQueryURI)
return NS_ERROR_NOT_IMPLEMENTED;
nsresult rv = NS_OK;
if (!mDatabase)
rv = GetAbDatabase();
if (NS_FAILED(rv) || !mDatabase)
@@ -672,16 +674,17 @@ NS_IMETHODIMP nsAbMDBDirectory::AddMailL
nsCOMPtr<nsIAbMDBDirectory> dbnewList(do_QueryInterface(newList, &rv));
NS_ENSURE_SUCCESS(rv, rv);
dbnewList->CopyDBMailList(dblist);
AddMailListToDirectory(newList);
NotifyItemAdded(newList);
}
+ NS_IF_ADDREF(*addedList = newList);
return rv;
}
NS_IMETHODIMP nsAbMDBDirectory::AddCard(nsIAbCard* card, nsIAbCard **addedCard)
{
if (mIsQueryURI)
return NS_ERROR_NOT_IMPLEMENTED;
--- a/mailnews/addrbook/src/nsAbMDBDirectory.h
+++ b/mailnews/addrbook/src/nsAbMDBDirectory.h
@@ -87,17 +87,17 @@ public:
// nsIAbDirectory methods:
NS_IMETHOD GetChildNodes(nsISimpleEnumerator* *result);
NS_IMETHOD GetChildCards(nsISimpleEnumerator* *result);
NS_IMETHOD GetIsQuery(PRBool *aResult);
NS_IMETHOD DeleteDirectory(nsIAbDirectory *directory);
NS_IMETHOD DeleteCards(nsIArray *cards);
NS_IMETHOD HasCard(nsIAbCard *cards, PRBool *hasCard);
NS_IMETHOD HasDirectory(nsIAbDirectory *dir, PRBool *hasDir);
- NS_IMETHOD AddMailList(nsIAbDirectory *list);
+ NS_IMETHOD AddMailList(nsIAbDirectory *list, nsIAbDirectory **addedList);
NS_IMETHOD AddCard(nsIAbCard *card, nsIAbCard **addedCard);
NS_IMETHOD ModifyCard(nsIAbCard *aModifiedCard);
NS_IMETHOD DropCard(nsIAbCard *card, PRBool needToCopyCard);
NS_IMETHOD EditMailListToDatabase(nsIAbCard *listCard);
NS_IMETHOD CardForEmailAddress(const nsACString &aEmailAddress,
nsIAbCard ** aAbCard);
NS_IMETHOD GetCardFromProperty(const char *aProperty,
const nsACString &aValue,
--- a/mailnews/addrbook/src/nsAbManager.cpp
+++ b/mailnews/addrbook/src/nsAbManager.cpp
@@ -65,16 +65,17 @@
#include "nsArrayUtils.h"
#include "nsDirectoryServiceUtils.h"
#include "nsIObserverService.h"
#include "nsDirPrefs.h"
#include "nsThreadUtils.h"
#include "nsIAbDirFactory.h"
#include "nsComponentManagerUtils.h"
#include "nsIIOService.h"
+#include "nsAbQueryStringToExpression.h"
struct ExportAttributesTableStruct
{
const char* abPropertyName;
PRUint32 plainTextStringID;
};
// our schema is not fixed yet, but we still want some sort of objectclass
@@ -1274,8 +1275,16 @@ NS_IMETHODIMP
nsAbManager::GenerateUUID(const nsACString &aDirectoryId,
const nsACString &aLocalId, nsACString &uuid)
{
uuid.Assign(aDirectoryId);
uuid.Append('#');
uuid.Append(aLocalId);
return NS_OK;
}
+
+NS_IMETHODIMP
+nsAbManager::ConvertQueryStringToExpression(const char *aQueryString,
+ nsIAbBooleanExpression **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ return nsAbQueryStringToExpression::Convert(aQueryString, _retval);
+}
--- a/mailnews/addrbook/src/nsAbOutlookDirectory.cpp
+++ b/mailnews/addrbook/src/nsAbOutlookDirectory.cpp
@@ -422,21 +422,22 @@ NS_IMETHODIMP nsAbOutlookDirectory::AddC
}
NS_IMETHODIMP nsAbOutlookDirectory::DropCard(nsIAbCard *aData, PRBool needToCopyCard)
{
nsCOMPtr <nsIAbCard> addedCard;
return AddCard(aData, getter_AddRefs(addedCard));
}
-NS_IMETHODIMP nsAbOutlookDirectory::AddMailList(nsIAbDirectory *aMailList)
+NS_IMETHODIMP nsAbOutlookDirectory::AddMailList(nsIAbDirectory *aMailList, nsIAbDirectory **addedList)
{
if (mIsQueryURI)
return NS_ERROR_NOT_IMPLEMENTED;
NS_ENSURE_ARG_POINTER(aMailList);
+ NS_ENSURE_ARG_POINTER(addedList);
if (m_IsMailList)
return NS_OK;
nsAbWinHelperGuard mapiAddBook (mAbWinType);
nsCAutoString entryString;
nsMapiEntry newEntry;
PRBool didCopy = PR_FALSE;
if (!mapiAddBook->IsOK())
@@ -481,16 +482,18 @@ NS_IMETHODIMP nsAbOutlookDirectory::AddM
if (!m_AddressList)
{
m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
}
m_AddressList->AppendElement(newList, PR_FALSE);
NotifyItemAddition(newList);
+ NS_IF_ADDREF(*addedList = newList);
+
return rv;
}
NS_IMETHODIMP nsAbOutlookDirectory::EditMailListToDatabase(nsIAbCard *listCard)
{
if (mIsQueryURI)
return NS_ERROR_NOT_IMPLEMENTED;
--- a/mailnews/addrbook/src/nsAbOutlookDirectory.h
+++ b/mailnews/addrbook/src/nsAbOutlookDirectory.h
@@ -72,17 +72,17 @@ public:
NS_IMETHOD HasCard(nsIAbCard *aCard, PRBool *aHasCard);
NS_IMETHOD HasDirectory(nsIAbDirectory *aDirectory, PRBool *aHasDirectory);
NS_IMETHOD DeleteCards(nsIArray *aCardList);
NS_IMETHOD DeleteDirectory(nsIAbDirectory *aDirectory);
NS_IMETHOD UseForAutocomplete(const nsACString &aIdentityKey, PRBool *aResult);
NS_IMETHOD AddCard(nsIAbCard *aData, nsIAbCard **addedCard);
NS_IMETHOD ModifyCard(nsIAbCard *aModifiedCard);
NS_IMETHOD DropCard(nsIAbCard *aData, PRBool needToCopyCard);
- NS_IMETHOD AddMailList(nsIAbDirectory *aMailList);
+ NS_IMETHOD AddMailList(nsIAbDirectory *aMailList, nsIAbDirectory **addedList);
NS_IMETHOD EditMailListToDatabase(nsIAbCard *listCard);
// nsAbDirProperty method
NS_IMETHOD Init(const char *aUri);
// nsIAbDirectoryQuery methods
NS_DECL_NSIABDIRECTORYQUERY
// nsIAbDirectorySearch methods
NS_DECL_NSIABDIRECTORYSEARCH