Dactyloidae/mailnews/db/gloda/modules/index_ab.js

287 lines
9.3 KiB
JavaScript

/* 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;
}
};