Bug 526541 - Please add a "confirm on delete" option for address book entries...; r=bienvenu ui-r=bwinton
--- a/mail/components/addrbook/content/abCommon.js
+++ b/mail/components/addrbook/content/abCommon.js
@@ -297,30 +297,33 @@ function InitCommonJS()
function AbDelete()
{
var types = GetSelectedCardTypes();
if (types == kNothingSelected)
return;
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
// If at least one mailing list is selected then prompt users for deletion.
- if (types != kCardsOnly)
- {
- var confirmDeleteMessage;
- if (types == kListsAndCards)
- confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteListsAndContacts");
- else if (types == kMultipleListsOnly)
- confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteMailingLists");
+
+ var confirmDeleteMessage;
+ if (types == kListsAndCards)
+ confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteListsAndContacts");
+ else if (types == kMultipleListsOnly)
+ confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteMailingLists");
+ else if (types == kSingleListOnly)
+ confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteMailingList");
+ else if (types == kCardsOnly && gAbView && gAbView.selection) {
+ if (gAbView.selection.count < 2)
+ confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteContact");
else
- confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteMailingList");
- if (!promptService.confirm(window, null, confirmDeleteMessage))
- return;
+ confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteContacts");
}
- gAbView.deleteSelectedCards();
+ if (confirmDeleteMessage && promptService.confirm(window, null, confirmDeleteMessage))
+ gAbView.deleteSelectedCards();
}
function AbNewCard()
{
goNewCardDialog(GetSelectedDirectory());
}
function AbEditCard(card)
--- a/mail/locales/en-US/chrome/messenger/addressbook/addressBook.properties
+++ b/mail/locales/en-US/chrome/messenger/addressbook/addressBook.properties
@@ -63,16 +63,18 @@ incorrectEmailAddressFormatTitle=Incorre
viewListTitle=Mailing List: %S
mailListNameExistsTitle=Mailing List Already Exists
mailListNameExistsMessage=A Mailing List with that name already exists. Please choose a different name.
# used in the addressbook
confirmDeleteMailingListTitle=Delete Mailing List
confirmDeleteAddressbookTitle=Delete Address Book
+confirmDeleteContact=Are you sure you want to delete the selected contact?
+confirmDeleteContacts=Are you sure you want to delete the selected contacts?
confirmDeleteAddressbook=Are you sure you want to delete the selected address book?
confirmDeleteCollectionAddressbook=If this address book is deleted, %S will no longer collect addresses. Are you sure you want to delete the selected address book?
confirmDeleteMailingList=Are you sure you want to delete the selected mailing list?
confirmDeleteListsAndContacts=Are you sure you want to delete the selected contacts and mailing lists?
confirmDeleteMailingLists=Are you sure you want to delete the selected mailing lists?
propertyPrimaryEmail=Email
propertyListName=List Name
--- a/mail/test/mozmill/addrbook/test-address-book.js
+++ b/mail/test/mozmill/addrbook/test-address-book.js
@@ -39,21 +39,60 @@
* Tests for the address book.
*/
var MODULE_NAME = 'test-address-book';
var RELATIVE_ROOT = '../shared-modules';
var MODULE_REQUIRES = ['address-book-helpers', 'folder-display-helpers'];
+const kPromptServiceUUID = "{6cc9c9fe-bc0b-432b-a410-253ef8bcc699}";
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource:///modules/Services.jsm");
+
let abController = null;
-
var addrBook1, addrBook2, addrBook3, addrBook4;
var mListA, mListB, mListC, mListD, mListE;
+var gMockPromptService = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptService]),
+ _will_return: null,
+ _did_confirm: false,
+ _confirm_msg: null,
+
+ confirm: function(aParent, aDialogTitle, aText) {
+ this._did_confirm = true;
+ this._confirm_msg = aText;
+ return this._will_return;
+ },
+
+ _return: function(aReturn) {
+ this._will_return = aReturn;
+ },
+
+ _reset: function() {
+ this._will_return = null;
+ this._did_confirm = false;
+ this._confirm_msg = null;
+ },
+};
+
+var gMockPromptServiceFactory = {
+ createInstance: function(aOuter, aIID) {
+ if (aOuter != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+
+ if (!aIID.equals(Ci.nsIPromptService))
+ throw Cr.NS_ERROR_NO_INTERFACE;
+
+ return gMockPromptService;
+ }
+};
+
function setupModule(module)
{
let fdh = collector.getModule('folder-display-helpers');
fdh.installInto(module);
let abh = collector.getModule('address-book-helpers');
abh.installInto(module);
@@ -149,8 +188,135 @@ function test_persist_collapsed_and_expa
abController.window.close();
abController = open_address_book_window();
assert_true(!is_address_book_collapsed(addrBook2));
assert_true(is_address_book_collapsed(addrBook1));
assert_true(is_address_book_collapsed(addrBook3));
}
+/* Test that if we try to delete a contact, that we are given
+ * a confirm prompt.
+ */
+function test_deleting_contact_causes_confirm_prompt()
+{
+ // Register the Mock Prompt Service
+
+ Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
+ .registerFactory(Components.ID(kPromptServiceUUID),
+ "Mock Prompt Service",
+ "@mozilla.org/embedcomp/prompt-service;1",
+ gMockPromptServiceFactory);
+
+ // Create a contact that we'll try to delete
+ let contact1 = create_contact("test@nobody.com", "Sammy Jenkis", true);
+ let toDelete = [contact1];
+
+ let bundle = Services.strings
+ .createBundle("chrome://messenger/locale/addressbook/addressBook.properties")
+ let confirmSingle = bundle.GetStringFromName("confirmDeleteContact");
+ // Add some contacts to the address book
+ load_contacts_into_address_book(addrBook1, toDelete);
+ select_address_book(addrBook1);
+
+ let totalEntries = abController.window.gAbView.rowCount;
+
+ // Set the mock prompt to return false, so that the
+ // contact should not be deleted.
+ gMockPromptService._return(false);
+
+ // Now attempt to delete the contact
+ select_contact(toDelete);
+ abController.keypress(null, "VK_DELETE", {});
+
+ // Was a confirm displayed?
+ assert_true(gMockPromptService._did_confirm);
+ // Was the right message displayed?
+ assert_equals(gMockPromptService._confirm_msg, confirmSingle);
+ // The contact should not have been deleted.
+ assert_equals(abController.window.gAbView.rowCount, totalEntries);
+
+ gMockPromptService._reset();
+
+ // Now we'll return true on confirm so that
+ // the contact is deleted.
+ gMockPromptService._return(true);
+ select_contact(toDelete);
+ abController.keypress(null, "VK_DELETE", {});
+
+ // Was a confirm displayed?
+ assert_true(gMockPromptService._did_confirm);
+ // Was the right message displayed?
+ assert_equals(gMockPromptService._confirm_msg, confirmSingle);
+ // The contact should have been deleted.
+ assert_equals(abController.window.gAbView.rowCount,
+ totalEntries - toDelete.length);
+
+ Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
+ .unregisterFactory(Components.ID(kPromptServiceUUID),
+ gMockPromptServiceFactory);
+}
+
+/* Test that if we try to delete multiple contacts, that we are give
+ * a confirm prompt.
+ */
+function test_deleting_contacts_causes_confirm_prompt()
+{
+ // Register the Mock Prompt Service
+
+ Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
+ .registerFactory(Components.ID(kPromptServiceUUID),
+ "Mock Prompt Service",
+ "@mozilla.org/embedcomp/prompt-service;1",
+ gMockPromptServiceFactory);
+
+ // Create some contacts that we'll try to delete.
+ let contact2 = create_contact("test2@nobody.com", "Leonard Shelby", true);
+ let contact3 = create_contact("test3@nobody.com", "John Edward Gammell", true);
+ let contact4 = create_contact("test4@nobody.com", "Natalie", true);
+
+ let toDelete = [contact2, contact3, contact4];
+
+ let bundle = Services.strings
+ .createBundle("chrome://messenger/locale/addressbook/addressBook.properties")
+ let confirmMultiple = bundle.GetStringFromName("confirmDeleteContacts");
+
+ // Add some contacts to the address book
+ load_contacts_into_address_book(addrBook1, toDelete);
+ select_address_book(addrBook1);
+
+ let totalEntries = abController.window.gAbView.rowCount;
+
+ // Set the mock prompt to return false, so that the
+ // contact should not be deleted.
+ gMockPromptService._return(false);
+
+ // Now attempt to delete the contact
+ select_contacts(toDelete);
+ abController.keypress(null, "VK_DELETE", {});
+
+ // Was a confirm displayed?
+ assert_true(gMockPromptService._did_confirm);
+ // Was the right message displayed?
+ assert_equals(gMockPromptService._confirm_msg, confirmMultiple);
+ // The contact should not have been deleted.
+ assert_equals(abController.window.gAbView.rowCount, totalEntries);
+
+ gMockPromptService._reset();
+
+ // Now we'll return true on confirm so that
+ // the contact is deleted.
+ gMockPromptService._return(true);
+ select_contacts(toDelete);
+ abController.keypress(null, "VK_DELETE", {});
+
+ // Was a confirm displayed?
+ assert_true(gMockPromptService._did_confirm);
+ // Was the right message displayed?
+ assert_equals(gMockPromptService._confirm_msg, confirmMultiple);
+ // The contact should have been deleted.
+ assert_equals(abController.window.gAbView.rowCount,
+ totalEntries - toDelete.length);
+
+ Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
+ .unregisterFactory(Components.ID(kPromptServiceUUID),
+ gMockPromptServiceFactory);
+}
--- a/mail/test/mozmill/shared-modules/test-address-book-helpers.js
+++ b/mail/test/mozmill/shared-modules/test-address-book-helpers.js
@@ -84,16 +84,21 @@ function installInto(module) {
// the same code as set_address_books_expanded/collapsed, so I just
// alias them here.
module.set_address_book_collapsed = set_address_books_collapsed;
module.set_address_book_expanded = set_address_books_expanded;
module.is_address_book_collapsed = is_address_book_collapsed;
module.is_address_book_collapsible = is_address_book_collapsible;
module.get_name_of_address_book_element_at = get_name_of_address_book_element_at;
module.select_address_book = select_address_book;
+ module.get_contact_ab_view_index = get_contact_ab_view_index;
+ // select_contact is aliased for select_contacts, since they
+ // share the same code.
+ module.select_contact = select_contacts;
+ module.select_contacts = select_contacts;
}
/**
* Make sure that there is a card for this email address
* @param emailAddress the address that should have a card
* @param displayName the display name the card should have
* @param preferDisplayName |true| if the card display name should override the
* header display name
@@ -231,27 +236,34 @@ function get_mailing_list_from_address_b
return list;
}
throw Error("Could not find a mailing list with dirName " + aDirName);
}
/* Given some address book, adds a collection of contacts to that
* address book.
* @param aAddressBook an address book to add the contacts to
- * @param aContacts a collection of contacts, where each contact has
- * members "email" and "displayName"
+ * @param aContacts a collection of nsIAbCards, or contacts,
+ * where each contact has members "email"
+ * and "displayName"
*
* Example:
* [{email: 'test@test.com', displayName: 'Sammy Jenkis'}]
*/
function load_contacts_into_address_book(aAddressBook, aContacts)
{
for each (contact_info in aContacts) {
- let contact = create_contact(contact_info.email,
+ let contact;
+
+ if (contact_info instanceof Ci.nsIAbCard)
+ contact = contact_info.QueryInterface(Ci.nsIAbCard);
+ else
+ contact = create_contact(contact_info.email,
contact_info.displayName, true);
+
aAddressBook.addCard(contact);
}
}
/* Given some mailing list, adds a collection of contacts to that
* mailing list.
* @param aMailingList a mailing list to add the contacts to
* @param aContacts a collection of contacts, where each contact has
@@ -264,31 +276,51 @@ function load_contacts_into_mailing_list
{
for each (contact_info in aContacts) {
let contact = create_contact(contact_info.email,
contact_info.displayName, true);
aMailingList.addressLists.appendElement(contact, false);
}
}
-/* Given some address book, return the row index for that address book
- * in the tree view. Throws an error if it cannot find the address book.
- * @param aAddrBook an address book to search for
+/* given some address book, return the row index for that address book
+ * in the tree view. throws an error if it cannot find the address book.
+ * @param aaddrbook an address book to search for
* @return the row index for that address book
*/
-function get_address_book_tree_view_index(aAddrBook)
+function get_address_book_tree_view_index(aaddrbook)
{
- let addrBooks = abController.window.gDirectoryTreeView._rowMap;
- for (let i = 0; i < addrBooks.length; i++) {
- if (addrBooks[i]._directory == aAddrBook) {
+ let addrbooks = abController.window.gDirectoryTreeView._rowMap;
+ for (let i = 0; i < addrbooks.length; i++) {
+ if (addrbooks[i]._directory == aaddrbook) {
return i;
}
}
- throw Error("Could not find the index for the address book named "
- + aAddrbook.dirName);
+ throw error("could not find the index for the address book named "
+ + aaddrbook.dirname);
+}
+
+/* Given some contact, return the row index for that contact in the
+ * address book view. Assumes that the address book that the contact
+ * belongs to is currently selected. Throws an error if it cannot
+ * find the contact.
+ * @param aContact a contact to search for
+ * @return the row index for that contact
+ */
+function get_contact_ab_view_index(aContact)
+{
+ let contacts = abController.window.gAbView;
+ for (let i = 0; i < contacts.rowCount; i++) {
+ let contact = contacts.getCardFromRow(i);
+ if (contact.localId == aContact.localId &&
+ !contact.isMailList)
+ return i;
+ }
+ throw Error("Could not find the index for the contact named "
+ + aContact.displayName);
}
/* Determines whether or not an address book is collapsed in
* the tree view.
* @param aAddrBook the address book to check
* @return true if the address book is collapsed, otherwise false
*/
function is_address_book_collapsed(aAddrbook)
@@ -369,8 +401,26 @@ function get_name_of_address_book_elemen
/* Selects a given address book in the tree view.
* @param aAddrBook an address book to select
*/
function select_address_book(aAddrBook)
{
let aIndex = get_address_book_tree_view_index(aAddrBook);
abController.window.gDirectoryTreeView.selection.select(aIndex);
}
+
+/* Selects one or more contacts in an address book, assuming that
+ * the address book is already selected. Pass a single nsIAbCard
+ * to select one contact, or an array of nsIAbCards to select
+ * multiple.
+ */
+function select_contacts(aContacts)
+{
+ if (!Array.isArray(aContacts))
+ aContacts = [aContacts];
+
+ abController.window.gAbView.selection.clearSelection();
+ for (let i = 0; i < aContacts.length; i++) {
+ let aIndex = get_contact_ab_view_index(aContacts[i]);
+ abController.window.gAbView.selection.toggleSelect(aIndex);
+ }
+}
+