1 /* ***** BEGIN LICENSE BLOCK ***** 2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 * 4 * The contents of this file are subject to the Mozilla Public License Version 5 * 1.1 (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * http://www.mozilla.org/MPL/ 8 * 9 * Software distributed under the License is distributed on an "AS IS" basis, 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 * for the specific language governing rights and limitations under the 12 * License. 13 * 14 * The Original Code is gContactSync. 15 * 16 * The Initial Developer of the Original Code is 17 * Josh Geenen <gcontactsync@pirules.org>. 18 * Portions created by the Initial Developer are Copyright (C) 2008-2009, 2011 19 * the Initial Developer. All Rights Reserved. 20 * 21 * Contributor(s): 22 * 23 * Alternatively, the contents of this file may be used under the terms of 24 * either the GNU General Public License Version 2 or later (the "GPL"), or 25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 26 * in which case the provisions of the GPL or the LGPL are applicable instead 27 * of those above. If you wish to allow use of your version of this file only 28 * under the terms of either the GPL or the LGPL, and not to allow others to 29 * use your version of this file under the terms of the MPL, indicate your 30 * decision by deleting the provisions above and replace them with the notice 31 * and other provisions required by the GPL or the LGPL. If you do not delete 32 * the provisions above, a recipient may use your version of this file under 33 * the terms of any one of the MPL, the GPL or the LGPL. 34 * 35 * ***** END LICENSE BLOCK ***** */ 36 37 if (!com) var com = {}; // A generic wrapper variable 38 // A wrapper for all GCS functions and variables 39 if (!com.gContactSync) com.gContactSync = {}; 40 41 /** 42 * MailList is an abstraction of a mailing list that facilitates getting the 43 * cards contained within the actual list as well as accessing and modifying the 44 * list and its properties. 45 * 46 * @param aList {Components.interfaces.nsIAbDirectory} The actual nsIAbDirectory 47 * representation of a mailing list. 48 * @param aParentDirectory {AddressBook} The parent directory (as an 49 * AddressBook object) containing this 50 * mailing list. 51 * @param aNew {boolean} Set as true for new mailing lists where 52 * no attempt should be made to fetch the 53 * contacts contained in the list. 54 * @constructor 55 * @class 56 */ 57 com.gContactSync.MailList = function gCS_MailList(aList, aParentDirectory, aNew) { 58 if (!aParentDirectory || 59 !(aParentDirectory instanceof com.gContactSync.AddressBook || 60 aParentDirectory instanceof com.gContactSync.GAddressBook)) 61 throw "Error - invalid address book supplied to the MailList Constructor"; 62 this.mParent = aParentDirectory; 63 this.mParent.checkList(aList, "MailList constructor"); 64 this.mList = aList; 65 this.mList.QueryInterface(Components.interfaces.nsIAbMDBDirectory); 66 this.mNew = aNew; 67 this.mIgnoreIfBroken = false; 68 if (!aNew) 69 this.getAllContacts(); 70 }; 71 72 com.gContactSync.MailList.prototype = { 73 /** The contacts in this mailing list (cached) */ 74 mContacts: [], 75 /** This is true whenever the contacts have to be fetched again */ 76 mContactsUpdate: false, 77 /** 78 * Sets the name of this list. The update method must be called in order for 79 * the change to become permanent. 80 * @param aName {string} The new name for the list. 81 */ 82 setName: function MailList_setName(aName) { 83 this.mList.dirName = aName; 84 }, 85 /** 86 * Returns the name of this list. 87 * @returns {string} The name of this list. 88 */ 89 getName: function MailList_getName() { 90 return this.mList.dirName; 91 }, 92 /** 93 * Returns the card in this mail list, if any, with the same (not-null) 94 * value for the GoogleID attribute, or, if the GoogleID is null, if the 95 * display name, primary, and second emails are the same. 96 * @param aContact {TBContact} The contact being searched for. 97 * @param aAttrs {Array} The attributes whose values must be identical in 98 * order for the contact to match. The defaults are 99 * DisplayName, PrimaryEmail, and SecondEmail. 100 * This is only used if the contact doesn't have a 101 * GoogleID 102 * @returns {TBContact} The card in this list, if any, with the same, and 103 * non-null value for its GoogleID attribute, or, if the 104 * GoogleID is null, if the display name, primary, and 105 * second emails are the same. 106 */ 107 hasContact: function MailList_hasContact(aContact, aAttrs) { 108 if (!(aContact instanceof com.gContactSync.TBContact)) { 109 throw "Invalid aContact sent to MailList.hasContact"; 110 } 111 // get all of the cards in this list again, if necessary 112 if (this.mContactsUpdate || this.mContacts.length === 0) { 113 this.getAllContacts(); 114 } 115 // the attributes to check 116 var attrs = aAttrs ? aAttrs : ["DisplayName", "PrimaryEmail", "SecondEmail"]; 117 for (var i = 0, length = this.mContacts.length; i < length; i++) { 118 var contact = this.mContacts[i], 119 aContactID = aContact.getID(); 120 // if it is an old card (has id) compare IDs 121 if (aContactID) { 122 if (aContactID === contact.getID()) { 123 return contact; 124 } 125 } 126 // else check that display name, primary and second email are equal 127 else { 128 for (var j = 0; j < attrs.length; j++) { 129 var aContactVal = aContact.getValue(attrs[j]), 130 contactVal = contact.getValue(attrs[j]); 131 // if a value is non-empty and the two are not equal then return false 132 if ((aContactVal || contactVal) && aContactVal !== contactVal) { 133 return false; 134 } 135 } 136 return contact; 137 } 138 } 139 return null; 140 }, 141 /** 142 * Sets the nick name for this mailing list. The update method must be 143 * called in order for the change to become permanent. 144 * @param aNickName {string} The new nick name for this mailing list. 145 */ 146 setNickName: function MailList_setNickName(aNickName) { 147 this.mList.listNickName = aNickName; 148 }, 149 /** 150 * Returns the nick name of this mailing list. 151 * @returns {string} The nick name of this mailing list. 152 */ 153 getNickName: function MailList_getNickName() { 154 return this.mList.listNickName; 155 }, 156 /** 157 * Sets the description for this mailing list. The update method must be 158 * called in order for the change to become permanent. 159 * @param aDescription {string} The new description for this mailing list. 160 */ 161 setDescription: function MailList_setDescription(aDescription) { 162 this.mList.description = aDescription; 163 }, 164 /** 165 * Returns the description of this mailing list. 166 * @returns {string} The description of this mailing list. 167 */ 168 getDescription: function MailList_getDescription() { 169 return this.mList.description; 170 }, 171 /** 172 * Adds a contact to this mailing list without checking if it already exists. 173 * NOTE: If the contact does not have a primary e-mail address then this 174 * method will add a fake one. 175 * @param aContact {TBContact} The contact to add to this mailing list. 176 * @returns {TBContact} The contact. 177 */ 178 addContact: function MailList_addContact(aContact) { 179 if (!(aContact instanceof com.gContactSync.TBContact)) { 180 throw "Invalid aContact sent to AddressBook.addContact"; 181 } 182 // Add a dummy e-mail address if necessary and ignore the preference 183 // If this was not done then the mailing list would break. 184 if (!(aContact.getValue("PrimaryEmail"))) { 185 aContact.setValue("PrimaryEmail", com.gContactSync.makeDummyEmail(aContact, true)); 186 aContact.update(); // TODO is this necessary 187 } 188 try { 189 var realContact = new com.gContactSync.TBContact(this.mList.addCard(aContact.mContact), 190 this); 191 this.mContacts.push(realContact); 192 return realContact; 193 } 194 catch (e) { 195 com.gContactSync.LOGGER.LOG_ERROR("Unable to add card to the mail list with URI: " + 196 this.getURI(), e); 197 } 198 return null; 199 }, 200 /** 201 * Returns the uniform resource identifier (URI) for this mailing list. 202 * @returns {string} The URI of this list. 203 */ 204 getURI: function MailList_getURI() { 205 if (this.mList.URI) 206 return this.mList.URI; 207 return this.mList.getDirUri(); 208 }, 209 /** 210 * Returns an array of all of the cards in this mailing list. 211 * @returns {array} An array containing all of the cards in this mailing list. 212 */ 213 getAllContacts: function MailList_getAllContacts() { 214 // NOTE: Sometimes hasMoreElements fails if mail lists aren't working 215 this.mContacts = []; 216 var iter = this.mList.childCards, 217 data; 218 if (iter instanceof Components.interfaces.nsISimpleEnumerator) { // Thunderbird 3 219 try { 220 while (iter.hasMoreElements()) { 221 data = iter.getNext(); 222 if (data instanceof Components.interfaces.nsIAbCard) 223 this.mContacts.push(new com.gContactSync.TBContact(data, this)); 224 } 225 } 226 catch (e) { 227 228 // If enumeration fails and the error shouldn't be ignored then offer 229 // to reset this AB for the user. 230 if (!this.mIgnoreIfBroken) { 231 com.gContactSync.LOGGER.LOG_ERROR("A mailing list is not working:", e); 232 if (com.gContactSync.confirm(com.gContactSync.StringBundle.getStr("resetConfirm"))) { 233 if (this.mParent.reset()) { 234 com.gContactSync.alert(com.gContactSync.StringBundle.getStr("pleaseRestart")); 235 } 236 } 237 // Throw an error to stop the sync 238 throw com.gContactSync.StringBundle.getStr("mailListBroken"); 239 240 // If ignoring this broken mailing list (such as when enumerating 241 // through a list immediately after adding a contact to it) then quit. 242 // This is a VERBOSE_LOG instead of LOG_WARNING or ERROR to avoid 243 // unnecessary e-mail/forum posts. 244 } else { 245 com.gContactSync.LOGGER.VERBOSE_LOG("A mailing list is not working:", e); 246 return this.mContacts; 247 } 248 } 249 } 250 else if (iter instanceof Components.interfaces.nsIEnumerator) { // TB 2 251 // use nsIEnumerator... 252 try { 253 iter.first(); 254 do { 255 data = iter.currentItem(); 256 if (data instanceof Components.interfaces.nsIAbCard) 257 this.mContacts.push(new com.gContactSync.TBContact(data, this)); 258 iter.next(); 259 } while (Components.lastResult === 0); 260 } 261 catch (ex) { 262 // TODO find a way to distinguish between the usual errors and the 263 // broken list errors 264 // error is expected when finished 265 com.gContactSync.LOGGER.VERBOSE_LOG("This error is (sometimes) expected:\n" + ex); 266 } 267 } 268 else { 269 com.gContactSync.LOGGER.LOG_ERROR("Could not iterate through an address book's contacts"); 270 throw com.gContactSync.StringBundle.getStr("mailListBroken"); 271 } 272 return this.mContacts; 273 }, 274 /** 275 * Deletes all of the cards in the array of cards from this list. 276 * @param aContacts {array} The array of TBContacts to delete from this mailing list. 277 */ 278 deleteContacts: function MailList_deleteContacts(aContacts) { 279 if (!(aContacts && aContacts.length > 0)) 280 return; 281 var arr, 282 i = 0; 283 if (com.gContactSync.AbManager.mVersion === 3) { // TB 3 284 arr = Components.classes["@mozilla.org/array;1"] 285 .createInstance(Components.interfaces.nsIMutableArray); 286 for (; i < aContacts.length; i++) { 287 if (aContacts[i] instanceof com.gContactSync.TBContact) { 288 arr.appendElement(aContacts[i].mContact, false); 289 } 290 else { 291 com.gContactSync.LOGGER.LOG_WARNING("Found an invalid contact sent " + 292 "MailList.deleteContacts"); 293 } 294 } 295 } 296 else { // TB 2 297 arr = Components.classes["@mozilla.org/supports-array;1"] 298 .createInstance(Components.interfaces.nsISupportsArray); 299 for (; i < aContacts.length; i++) { 300 if (aContacts[i] instanceof com.gContactSync.TBContact) { 301 arr.AppendElement(aContacts[i].mContact, false); 302 } 303 else { 304 com.gContactSync.LOGGER.LOG_WARNING("Found an invalid contact sent " + 305 "MailList.deleteContacts"); 306 } 307 } 308 } 309 try { 310 if (arr) { // make sure arr isn't null (mailnews bug 448165) 311 this.mContactsUpdate = true; // update mContacts when used 312 this.mList.deleteCards(arr); 313 } 314 } 315 catch (e) { 316 com.gContactSync.LOGGER.LOG_WARNING("Error while deleting cards from a mailing list", e); 317 } 318 this.mContacts = this.getAllContacts(); 319 }, 320 /** 321 * Deletes this mailing list from its parent address book. 322 */ 323 remove: function MailList_delete() { 324 this.mParent.mDirectory.deleteDirectory(this.mList); 325 this.mContacts = []; 326 // make sure the functions don't do anything 327 for (var i in this) { 328 if (i instanceof Function) 329 i = function () {}; 330 } 331 }, 332 /** 333 * Updates this mail list (commits changes like renaming or changing the 334 * nickname) 335 */ 336 update: function MailList_update() { 337 try { 338 if (com.gContactSync.AbManager.mVersion === 3) 339 this.mList.editMailListToDatabase(null); 340 else 341 this.mList.editMailListToDatabase(this.getURI(), null); 342 } 343 catch (e) { 344 com.gContactSync.LOGGER.LOG_WARNING("Unable to update mail list", e); 345 } 346 }, 347 /** 348 * Tells this mailing list whether it should avoid asking the user to confirm 349 * a reset if broken. 350 * @param aIgnore {boolean} Set this to true to avoid notifying the user of 351 * a problem if this list is broken. 352 */ 353 setIgnoreIfBroken: function MailList_setIgnoreIfBroken(aIgnore) { 354 this.mIgnoreIfBroken = aIgnore; 355 } 356 }; 357