summaryrefslogtreecommitdiff
path: root/mailnews/db/gloda/modules/index_ab.js
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/db/gloda/modules/index_ab.js')
-rw-r--r--mailnews/db/gloda/modules/index_ab.js287
1 files changed, 287 insertions, 0 deletions
diff --git a/mailnews/db/gloda/modules/index_ab.js b/mailnews/db/gloda/modules/index_ab.js
new file mode 100644
index 0000000000..299733275f
--- /dev/null
+++ b/mailnews/db/gloda/modules/index_ab.js
@@ -0,0 +1,287 @@
+/* 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/. */
+
+this.EXPORTED_SYMBOLS = ['GlodaABIndexer', 'GlodaABAttrs'];
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cr = Components.results;
+var Cu = Components.utils;
+
+Cu.import("resource:///modules/gloda/collection.js");
+Cu.import("resource:///modules/gloda/datastore.js");
+Cu.import("resource:///modules/gloda/gloda.js");
+Cu.import("resource:///modules/gloda/indexer.js");
+Cu.import("resource:///modules/gloda/log4moz.js");
+Cu.import("resource:///modules/gloda/noun_freetag.js");
+Cu.import("resource:///modules/gloda/utils.js");
+Cu.import("resource:///modules/mailServices.js");
+
+
+var GlodaABIndexer = {
+ _log: null,
+
+ name: "index_ab",
+ enable: function() {
+ if (this._log == null)
+ this._log = Log4Moz.repository.getLogger("gloda.index_ab");
+
+ MailServices.ab.addAddressBookListener(this,
+ Ci.nsIAbListener.itemAdded |
+ Ci.nsIAbListener.itemChanged |
+ Ci.nsIAbListener.directoryItemRemoved);
+ },
+
+ disable: function() {
+ MailServices.ab.removeAddressBookListener(this);
+ },
+
+ // it's a getter so we can reference 'this'
+ get workers() {
+ return [
+ ["ab-card", {
+ worker: this._worker_index_card,
+ }],
+ ];
+ },
+
+ _worker_index_card: function*(aJob, aCallbackHandle) {
+ let card = aJob.id;
+
+ if (card.primaryEmail) {
+ // load the identity
+ let query = Gloda.newQuery(Gloda.NOUN_IDENTITY);
+ query.kind("email");
+ // we currently normalize all e-mail addresses to be lowercase
+ query.value(card.primaryEmail.toLowerCase());
+ let identityCollection = query.getCollection(aCallbackHandle);
+ yield Gloda.kWorkAsync;
+
+ if (identityCollection.items.length) {
+ let identity = identityCollection.items[0];
+ // force the identity to know it has an associated ab card.
+ identity._hasAddressBookCard = true;
+
+ this._log.debug("Found identity, processing card.");
+ yield aCallbackHandle.pushAndGo(
+ Gloda.grokNounItem(identity.contact, {card: card}, false, false,
+ aCallbackHandle));
+ this._log.debug("Done processing card.");
+ }
+ }
+
+ yield GlodaIndexer.kWorkDone;
+ },
+
+ initialSweep: function() {
+ },
+
+ /* ------ nsIAbListener ------ */
+ /**
+ * When an address book card is added, update the cached GlodaIdentity
+ * object's cached idea of whether the identity has an ab card.
+ */
+ onItemAdded: function ab_indexer_onItemAdded(aParentDir, aItem) {
+ if (!(aItem instanceof Ci.nsIAbCard))
+ return;
+
+ this._log.debug("Received Card Add Notification");
+ let identity = GlodaCollectionManager.cacheLookupOneByUniqueValue(
+ Gloda.NOUN_IDENTITY, "email@" + aItem.primaryEmail.toLowerCase());
+ if (identity)
+ identity._hasAddressBookCard = true;
+ },
+ /**
+ * When an address book card is added, update the cached GlodaIdentity
+ * object's cached idea of whether the identity has an ab card.
+ */
+ onItemRemoved: function ab_indexer_onItemRemoved(aParentDir, aItem) {
+ if (!(aItem instanceof Ci.nsIAbCard))
+ return;
+
+ this._log.debug("Received Card Removal Notification");
+ let identity = GlodaCollectionManager.cacheLookupOneByUniqueValue(
+ Gloda.NOUN_IDENTITY, "email@" + aItem.primaryEmail.toLowerCase());
+ if (identity)
+ identity._hasAddressBookCard = false;
+
+ },
+ onItemPropertyChanged: function ab_indexer_onItemPropertyChanged(aItem,
+ aProperty, aOldValue, aNewValue) {
+ if (aProperty == null && aItem instanceof Ci.nsIAbCard) {
+ this._log.debug("Received Card Change Notification");
+
+ let card = aItem; // instanceof already QueryInterface'd for us.
+ let job = new IndexingJob("ab-card", card);
+ GlodaIndexer.indexJob(job);
+ }
+ }
+};
+GlodaIndexer.registerIndexer(GlodaABIndexer);
+
+var GlodaABAttrs = {
+ providerName: "gloda.ab_attr",
+ _log: null,
+
+ init: function() {
+ this._log = Log4Moz.repository.getLogger("gloda.abattrs");
+
+ try {
+ this.defineAttributes();
+ }
+ catch (ex) {
+ this._log.error("Error in init: " + ex);
+ throw ex;
+ }
+ },
+
+ defineAttributes: function() {
+ /* ***** Contacts ***** */
+ this._attrIdentityContact = Gloda.defineAttribute({
+ provider: this,
+ extensionName: Gloda.BUILT_IN,
+ attributeType: Gloda.kAttrDerived,
+ attributeName: "identities",
+ singular: false,
+ special: Gloda.kSpecialColumnChildren,
+ //specialColumnName: "contactID",
+ storageAttributeName: "_identities",
+ subjectNouns: [Gloda.NOUN_CONTACT],
+ objectNoun: Gloda.NOUN_IDENTITY,
+ }); // tested-by: test_attributes_fundamental
+ this._attrContactName = Gloda.defineAttribute({
+ provider: this,
+ extensionName: Gloda.BUILT_IN,
+ attributeType: Gloda.kAttrFundamental,
+ attributeName: "name",
+ singular: true,
+ special: Gloda.kSpecialString,
+ specialColumnName: "name",
+ subjectNouns: [Gloda.NOUN_CONTACT],
+ objectNoun: Gloda.NOUN_STRING,
+ canQuery: true,
+ }); // tested-by: test_attributes_fundamental
+ this._attrContactPopularity = Gloda.defineAttribute({
+ provider: this,
+ extensionName: Gloda.BUILT_IN,
+ attributeType: Gloda.kAttrDerived,
+ attributeName: "popularity",
+ singular: true,
+ special: Gloda.kSpecialColumn,
+ specialColumnName: "popularity",
+ subjectNouns: [Gloda.NOUN_CONTACT],
+ objectNoun: Gloda.NOUN_NUMBER,
+ canQuery: true,
+ }); // not-tested
+ this._attrContactFrecency = Gloda.defineAttribute({
+ provider: this,
+ extensionName: Gloda.BUILT_IN,
+ attributeType: Gloda.kAttrDerived,
+ attributeName: "frecency",
+ singular: true,
+ special: Gloda.kSpecialColumn,
+ specialColumnName: "frecency",
+ subjectNouns: [Gloda.NOUN_CONTACT],
+ objectNoun: Gloda.NOUN_NUMBER,
+ canQuery: true,
+ }); // not-tested
+
+ /* ***** Identities ***** */
+ this._attrIdentityContact = Gloda.defineAttribute({
+ provider: this,
+ extensionName: Gloda.BUILT_IN,
+ attributeType: Gloda.kAttrDerived,
+ attributeName: "contact",
+ singular: true,
+ special: Gloda.kSpecialColumnParent,
+ specialColumnName: "contactID", // the column in the db
+ idStorageAttributeName: "_contactID",
+ valueStorageAttributeName: "_contact",
+ subjectNouns: [Gloda.NOUN_IDENTITY],
+ objectNoun: Gloda.NOUN_CONTACT,
+ canQuery: true,
+ }); // tested-by: test_attributes_fundamental
+ this._attrIdentityKind = Gloda.defineAttribute({
+ provider: this,
+ extensionName: Gloda.BUILT_IN,
+ attributeType: Gloda.kAttrFundamental,
+ attributeName: "kind",
+ singular: true,
+ special: Gloda.kSpecialString,
+ specialColumnName: "kind",
+ subjectNouns: [Gloda.NOUN_IDENTITY],
+ objectNoun: Gloda.NOUN_STRING,
+ canQuery: true,
+ }); // tested-by: test_attributes_fundamental
+ this._attrIdentityValue = Gloda.defineAttribute({
+ provider: this,
+ extensionName: Gloda.BUILT_IN,
+ attributeType: Gloda.kAttrFundamental,
+ attributeName: "value",
+ singular: true,
+ special: Gloda.kSpecialString,
+ specialColumnName: "value",
+ subjectNouns: [Gloda.NOUN_IDENTITY],
+ objectNoun: Gloda.NOUN_STRING,
+ canQuery: true,
+ }); // tested-by: test_attributes_fundamental
+
+ /* ***** Contact Meta ***** */
+ // Freeform tags; not explicit like thunderbird's fundamental tags.
+ // we differentiate for now because of fundamental implementation
+ // differences.
+ this._attrFreeTag = Gloda.defineAttribute({
+ provider: this,
+ extensionName: Gloda.BUILT_IN,
+ attributeType: Gloda.kAttrExplicit,
+ attributeName: "freetag",
+ bind: true,
+ bindName: "freeTags",
+ singular: false,
+ subjectNouns: [Gloda.NOUN_CONTACT],
+ objectNoun: Gloda.lookupNoun("freetag"),
+ parameterNoun: null,
+ canQuery: true,
+ }); // not-tested
+ // we need to find any existing bound freetag attributes, and use them to
+ // populate to FreeTagNoun's understanding
+ if ("parameterBindings" in this._attrFreeTag) {
+ for (let freeTagName in this._attrFreeTag.parameterBindings) {
+ this._log.debug("Telling FreeTagNoun about: " + freeTagName);
+ FreeTagNoun.getFreeTag(freeTagName);
+ }
+ }
+ },
+
+ process: function*(aContact, aRawReps, aIsNew, aCallbackHandle) {
+ let card = aRawReps.card;
+ if (aContact.NOUN_ID != Gloda.NOUN_CONTACT) {
+ this._log.warn("Somehow got a non-contact: " + aContact);
+ return; // this will produce an exception; we like.
+ }
+
+ // update the name
+ if (card.displayName && card.displayName != aContact.name)
+ aContact.name = card.displayName;
+
+ aContact.freeTags = [];
+
+ let tags = null;
+ try {
+ tags = card.getProperty("Categories", null);
+ } catch (ex) {
+ this._log.error("Problem accessing property: " + ex);
+ }
+ if (tags) {
+ for (let tagName of tags.split(",")) {
+ tagName = tagName.trim();
+ if (tagName) {
+ aContact.freeTags.push(FreeTagNoun.getFreeTag(tagName));
+ }
+ }
+ }
+
+ yield Gloda.kWorkDone;
+ }
+};