Bug 1636533 - Improve address book deletion prompts. r=frg
authorIan Neal <iann_cvs@blueyonder.co.uk>
Fri, 08 May 2020 16:01:02 +0100
changeset 39225 94a426143805198f756b6fc772aced706f8d94d4
parent 39224 4bf4b3ce8d53355b8d02452c9d1b99da0fac95d0
child 39226 8502d904e2732914ccec7a9c441112603fc10b77
push id402
push userclokep@gmail.com
push dateMon, 29 Jun 2020 20:48:04 +0000
reviewersfrg
bugs1636533, 1319493
Bug 1636533 - Improve address book deletion prompts. r=frg Port the following bug to SeaMonkey: * Bug 1319493 - Smart and context-sensitive Delete confirmation prompts in Address Book
suite/locales/en-US/chrome/mailnews/addressbook/addressBook.properties
suite/mailnews/components/addrbook/content/abCommon.js
--- a/suite/locales/en-US/chrome/mailnews/addressbook/addressBook.properties
+++ b/suite/locales/en-US/chrome/mailnews/addressbook/addressBook.properties
@@ -1,17 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-#
-# The following are used by the Mailing list dialog
-#
-
-## LOCALIZATION NOTE (mailingListTitleEdit): %S will be replaced by the Mailing List's display name
+# The following are used by the Mailing list dialog.
+# LOCALIZATION NOTE (mailingListTitleEdit): %S will be replaced by the Mailing List's display name.
 mailingListTitleEdit=Edit %S
 emptyListName=You must enter a list name.
 lastFirstFormat=%S, %S
 firstLastFormat=%S %S
 
 allAddressBooks=All Address Books
 
 newContactTitle=New Contact
@@ -30,26 +27,101 @@ cardRequiredDataMissingMessage=You must 
 cardRequiredDataMissingTitle=Required Information Missing
 incorrectEmailAddressFormatMessage=The primary e-mail address must be of the form user@host.
 incorrectEmailAddressFormatTitle=Incorrect Email Address Format
 
 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
-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?
-confirmDeleteContact=Are you sure you want to delete the selected contact?
-confirmDeleteContacts=Are you sure you want to delete the selected contacts?
-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?
+confirmDeleteThisContactTitle=Delete Contact
+# LOCALIZATION NOTE (confirmDeleteThisContact):
+# #1 The name of the selected contact
+# Don't localize "\n• #1" unless your local layout comes out wrong.
+# Example: Are you sure you want to delete this contact?
+#          • John Doe
+confirmDeleteThisContact=Are you sure you want to delete this contact?\n• #1
+
+confirmDelete2orMoreContactsTitle=Delete Multiple Contacts
+# LOCALIZATION NOTE (confirmDelete2orMoreContacts):
+# Semicolon list of plural forms.
+# See: http://developer.mozilla.org/docs/Localization_and_Plurals
+# #1 The number of selected contacts, always more than 1.
+# Example: Are you sure you want to delete these 3 contacts?
+confirmDelete2orMoreContacts=Are you sure you want to delete this #1 contact?;Are you sure you want to delete these #1 contacts?
+
+confirmRemoveThisContactTitle=Remove Contact
+# LOCALIZATION NOTE (confirmRemoveThisContact):
+# #1 The name of the selected contact
+# #2 The name of the containing mailing list
+# This title is about a contact in a mailing list, so it will not be deleted,
+# but only removed from the list.
+# Don't localize "\n• #1" unless your local layout comes out wrong.
+# Example: Are you sure you want to remove this contact from the mailing list 'Customers List'?
+#          • John Doe
+confirmRemoveThisContact=Are you sure you want to remove this contact from the mailing list '#2'?\n• #1
+
+confirmRemove2orMoreContactsTitle=Remove Multiple Contacts
+# LOCALIZATION NOTE (confirmRemove2orMoreContacts):
+# Semicolon list of singular and plural forms.
+# See: http://developer.mozilla.org/docs/Localization_and_Plurals
+# #1 The number of selected contacts, always more than 1.
+# #2 The name of the containing mailing list
+# Example: Are you sure you want to remove these 3 contacts from the mailing list 'Customers List'?
+confirmRemove2orMoreContacts=Are you sure you want to remove this #1 contact from the mailing list '#2'?;Are you sure you want to remove these #1 contacts from the mailing list '#2'?
+
+confirmDeleteThisMailingListTitle=Delete Mailing List
+# LOCALIZATION NOTE (confirmDeleteThisMailingList):
+# #1 The name of the selected mailing list
+# Don't localize "\n• #1" unless your local layout comes out wrong.
+# Example: Are you sure you want to delete this mailing list?
+#          • Customers List
+confirmDeleteThisMailingList=Are you sure you want to delete this mailing list?\n• #1
+
+confirmDelete2orMoreMailingListsTitle=Delete Multiple Mailing Lists
+# LOCALIZATION NOTE (confirmDelete2orMoreMailingLists):
+# Semicolon list of plural forms.
+# See: http://developer.mozilla.org/docs/Localization_and_Plurals
+# #1 The number of selected mailing lists, always more than 1
+# Example: Are you sure you want to delete these 3 mailing lists?
+confirmDelete2orMoreMailingLists=Are you sure you want to delete this #1 mailing list?;Are you sure you want to delete these #1 mailing lists?
+
+confirmDelete2orMoreContactsAndListsTitle=Delete Contacts and Mailing Lists
+# LOCALIZATION NOTE (confirmDelete2orMoreContactsAndLists):
+# Semicolon list of and plural forms.
+# See: http://developer.mozilla.org/docs/Localization_and_Plurals
+# #1 The number of selected contacts and mailing lists, always more than 1
+# Example: Are you sure you want to delete these 3 contacts and mailing lists?
+confirmDelete2orMoreContactsAndLists=Are you sure you want to delete these #1 contacts and mailing lists?;Are you sure you want to delete these #1 contacts and mailing lists?
+
+confirmDeleteThisAddressbookTitle=Delete Address Book
+# LOCALIZATION NOTE (confirmDeleteThisAddressbookTitle):
+# #1 The name of the selected address book
+# Don't localize "\n• #1" unless your local layout comes out wrong.
+# Example: Are you sure you want to delete this address book and all of its contacts?
+#          • Friends and Family Address Book
+confirmDeleteThisAddressbook=Are you sure you want to delete this address book and all of its contacts?\n• #1
+
+confirmDeleteThisLDAPDirTitle=Delete Local LDAP Directory
+# LOCALIZATION NOTE (confirmDeleteThisLDAPDir):
+# #1 The name of the selected LDAP directory
+# Don't localize "\n• #1" unless your local layout comes out wrong.
+# Example: Are you sure you want to delete the local copy of this LDAP directory and all of its offline contacts?
+#          • Mozilla LDAP Directory
+confirmDeleteThisLDAPDir=Are you sure you want to delete the local copy of this LDAP directory and all of its offline contacts?\n• #1
+
+confirmDeleteThisCollectionAddressbookTitle=Delete Collection Address Book
+# LOCALIZATION NOTE (confirmDeleteThisCollectionAddressbook):
+# #1 The name of the selected collection address book
+# #2 The name of the application (Thunderbird)
+# Don't localize "\n• #1" unless your local layout comes out wrong.
+# Example: If this address book is deleted, Thunderbird will no longer collect addresses.
+#          Are you sure you want to delete this address book and all of its contacts?
+#          • My Collecting Addressbook
+confirmDeleteThisCollectionAddressbook=If this address book is deleted, #2 will no longer collect addresses.\nAre you sure you want to delete this address book and all of its contacts?\n• #1
 
 propertyPrimaryEmail=Email
 propertyListName=List Name
 propertySecondaryEmail=Additional Email
 propertyNickname=Nickname
 propertyDisplayName=Display Name
 propertyWork=Work
 propertyHome=Home
--- a/suite/mailnews/components/addrbook/content/abCommon.js
+++ b/suite/mailnews/components/addrbook/content/abCommon.js
@@ -194,50 +194,79 @@ function AbDeleteSelectedDirectory()
   if (!selectedDirURI)
     return;
 
   AbDeleteDirectory(selectedDirURI);
 }
 
 function AbDeleteDirectory(aURI)
 {
-  var directory = GetDirectoryFromURI(aURI);
-  var confirmDeleteMessage;
-  var clearPrefsRequired = false;
+  // Determine strings for smart and context-sensitive user prompts
+  // for confirming deletion.
+  let directory = GetDirectoryFromURI(aURI);
+  let confirmDeleteTitleID;
+  let confirmDeleteTitle;
+  let confirmDeleteMessageID;
+  let confirmDeleteMessage;
+  let brandShortName;
+  let clearCollectionPrefs = false;
 
-  if (directory.isMailList)
-    confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteMailingList");
-  else {
-    // Check if this address book is being used for collection
+  if (directory.isMailList) {
+    // It's a mailing list.
+    confirmDeleteMessageID = "confirmDeleteThisMailingList";
+    confirmDeleteTitleID = "confirmDeleteThisMailingListTitle";
+  } else {
+    // It's an address book: check which type.
     if (Services.prefs.getCharPref("mail.collect_addressbook") == aURI &&
         (Services.prefs.getBoolPref("mail.collect_email_address_outgoing") ||
          Services.prefs.getBoolPref("mail.collect_email_address_incoming") ||
          Services.prefs.getBoolPref("mail.collect_email_address_newsgroup"))) {
-      let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
-      confirmDeleteMessage = gAddressBookBundle.getFormattedString("confirmDeleteCollectionAddressbook", [brandShortName]);
-      clearPrefsRequired = true;
+      // It's a collection address book: let's be clear about the consequences.
+      brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
+      confirmDeleteMessageID = "confirmDeleteThisCollectionAddressbook";
+      confirmDeleteTitleID = "confirmDeleteThisCollectionAddressbookTitle";
+      clearCollectionPrefs = true;
+    } else if (directory.URI.startsWith(kLdapUrlPrefix)) {
+      // It's an LDAP directory, so we only delete our offline copy.
+      confirmDeleteMessageID = "confirmDeleteThisLDAPDir";
+      confirmDeleteTitleID = "confirmDeleteThisLDAPDirTitle";
     } else {
-      confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteAddressbook");
+      // It's a normal personal address book: we'll delete its contacts, too.
+      confirmDeleteMessageID = "confirmDeleteThisAddressbook";
+      confirmDeleteTitleID = "confirmDeleteThisAddressbookTitle";
     }
   }
 
-  let title = gAddressBookBundle.getString(directory.isMailList ?
-                                             "confirmDeleteMailingListTitle" :
-                                             "confirmDeleteAddressbookTitle");
-  if (!Services.prompt.confirm(window, title, confirmDeleteMessage))
+  // Get the raw strings with placeholders.
+  confirmDeleteTitle   = gAddressBookBundle.getString(confirmDeleteTitleID);
+  confirmDeleteMessage = gAddressBookBundle.getString(confirmDeleteMessageID);
+
+  // Substitute placeholders as required.
+  // Replace #1 with the name of the selected address book or mailing list.
+  confirmDeleteMessage = confirmDeleteMessage.replace("#1", directory.dirName);
+  if (brandShortName) {
+    // For a collection address book, replace #2 with the brandShortName.
+    confirmDeleteMessage = confirmDeleteMessage.replace("#2", brandShortName);
+  }
+
+  // Ask for confirmation before deleting
+  if (!Services.prompt.confirm(window, confirmDeleteTitle,
+                                       confirmDeleteMessage)) {
+    // Deletion cancelled by user.
     return;
+  }
 
-  // First clear all the prefs if required
-  if (clearPrefsRequired) {
+  // If we're about to delete the collection AB, update the respective prefs.
+  if (clearCollectionPrefs) {
     Services.prefs.setBoolPref("mail.collect_email_address_outgoing", false);
     Services.prefs.setBoolPref("mail.collect_email_address_incoming", false);
     Services.prefs.setBoolPref("mail.collect_email_address_newsgroup", false);
 
-    // Also reset the displayed value so that we don't get a blank item in the
-    // prefs dialog if it gets enabled.
+    // Change the collection AB pref to "Personal Address Book" so that we
+    // don't get a blank item in prefs dialog when collection is re-enabled.
     Services.prefs.setCharPref("mail.collect_addressbook",
                                kPersonalAddressbookURI);
   }
 
   MailServices.ab.deleteAddressBook(aURI);
 }
 
 function InitCommonJS()
@@ -256,47 +285,107 @@ function InitCommonJS()
 function UpgradeAddressBookResultsPaneUI(prefName)
 {
   // placeholder in case any new columns get added to the address book
   // var resultsPaneUIVersion = Services.prefs.getIntPref(prefName);
 }
 
 function AbDelete()
 {
-  var types = GetSelectedCardTypes();
-
+  let types = GetSelectedCardTypes();
   if (types == kNothingSelected)
     return;
 
-  var confirmDeleteMessage;
+  // Determine strings for smart and context-sensitive user prompts
+  // for confirming deletion.
+  let confirmDeleteTitleID;
+  let confirmDeleteTitle;
+  let confirmDeleteMessageID;
+  let confirmDeleteMessage;
+  let itemName;
+  let containingListName;
+  let selectedDir = getSelectedDirectory();
+  let numSelectedItems = gAbView.selection.count;
 
-  if (types == kCardsOnly)
-  {
-    if (gAbView && gAbView.selection.count < 2)
-      confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteContact");
-    else
-      confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteContacts");
-  }
-  // If at least one mailing list is selected then prompt users for deletion.
-  else
-  {
-    if (types == kListsAndCards)
-      confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteListsAndContacts");
-    else if (types == kMultipleListsOnly)
-      confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteMailingLists");
-    else if (types == kSingleListOnly)
-      confirmDeleteMessage = gAddressBookBundle.getString("confirmDeleteMailingList");
+  switch(types) {
+    case kListsAndCards:
+      confirmDeleteMessageID = "confirmDelete2orMoreContactsAndLists";
+      confirmDeleteTitleID   = "confirmDelete2orMoreContactsAndListsTitle";
+      break;
+    case kSingleListOnly:
+      // Set item name for single mailing list.
+      let theCard = GetSelectedAbCards()[0];
+      itemName = theCard.displayName;
+      confirmDeleteMessageID = "confirmDeleteThisMailingList";
+      confirmDeleteTitleID   = "confirmDeleteThisMailingListTitle";
+      break;
+    case kMultipleListsOnly:
+      confirmDeleteMessageID = "confirmDelete2orMoreMailingLists";
+      confirmDeleteTitleID   = "confirmDelete2orMoreMailingListsTitle";
+      break;
+    case kCardsOnly:
+      if (selectedDir.isMailList) {
+        // Contact(s) in mailing lists will be removed from the list, not deleted.
+        if (numSelectedItems == 1) {
+          confirmDeleteMessageID = "confirmRemoveThisContact";
+          confirmDeleteTitleID = "confirmRemoveThisContactTitle";
+        } else {
+          confirmDeleteMessageID = "confirmRemove2orMoreContacts";
+          confirmDeleteTitleID   = "confirmRemove2orMoreContactsTitle";
+        }
+        // For removing contacts from mailing list, set placeholder value
+        containingListName = selectedDir.dirName;
+      } else {
+        // Contact(s) in address books will be deleted.
+        if (numSelectedItems == 1) {
+          confirmDeleteMessageID = "confirmDeleteThisContact";
+          confirmDeleteTitleID   = "confirmDeleteThisContactTitle";
+        } else {
+          confirmDeleteMessageID = "confirmDelete2orMoreContacts";
+          confirmDeleteTitleID   = "confirmDelete2orMoreContactsTitle";
+        }
+      }
+      if (numSelectedItems == 1) {
+        // Set item name for single contact.
+        let theCard = GetSelectedAbCards()[0];
+        let nameFormatFromPref = Services.prefs.getIntPref("mail.addr_book.lastnamefirst");
+        itemName = theCard.generateName(nameFormatFromPref);
+      }
+      break;
   }
 
-  if (!confirmDeleteMessage ||
-      !Services.prompt.confirm(window, null, confirmDeleteMessage)) {
+  // Get the raw model strings.
+  // For numSelectedItems == 1, it's simple strings.
+  // For messages with numSelectedItems > 1, it's multi-pluralform string sets.
+  // confirmDeleteMessage has placeholders for some forms.
+  confirmDeleteTitle   = gAddressBookBundle.getString(confirmDeleteTitleID);
+  confirmDeleteMessage = gAddressBookBundle.getString(confirmDeleteMessageID);
+
+  // Get plural form where applicable; substitute placeholders as required.
+  if (numSelectedItems == 1) {
+    // If single selected item, substitute itemName.
+    confirmDeleteMessage = confirmDeleteMessage.replace("#1", itemName);
+  } else {
+    // If multiple selected items, get the right plural string from the
+    // localized set, then substitute numSelectedItems.
+    confirmDeleteMessage = PluralForm.get(numSelectedItems, confirmDeleteMessage);
+    confirmDeleteMessage = confirmDeleteMessage.replace("#1", numSelectedItems);
+  }
+  // If contact(s) in a mailing list, substitute containingListName.
+  if (containingListName)
+    confirmDeleteMessage = confirmDeleteMessage.replace("#2", containingListName);
+
+  // Finally, show our smart confirmation message, and act upon it!
+  if (!Services.prompt.confirm(window, confirmDeleteTitle,
+                                       confirmDeleteMessage)) {
+    // Deletion cancelled by user.
     return;
   }
 
-  if (getSelectedDirectoryURI() == (kAllDirectoryRoot + "?")) {
+  if (selectedDir.URI == (kAllDirectoryRoot + "?")) {
     // Delete cards from "All Address Books" view.
     let cards = GetSelectedAbCards();
     for (let i = 0; i < cards.length; i++) {
       let dirId = cards[i].directoryId
                           .substring(0, cards[i].directoryId.indexOf("&"));
       let directory = MailServices.ab.getDirectoryFromId(dirId);
 
       let cardArray =