From e1629be6ef2151217e35d4eba8615d165ee0a740 Mon Sep 17 00:00:00 2001 From: Teiichiro Fukuda Date: Mon, 7 Apr 2014 03:14:00 +0200 Subject: [PATCH] Bug 909224 - "Want to add the phonetic name of the name to mozContacts". r=reuben --- dom/contacts/ContactManager.js | 1 + dom/contacts/fallback/ContactDB.jsm | 36 ++- dom/contacts/tests/shared.js | 86 +++++ dom/contacts/tests/test_contacts_basics2.html | 297 ++++++++++++++++++ dom/webidl/Contacts.webidl | 4 + 5 files changed, 422 insertions(+), 2 deletions(-) diff --git a/dom/contacts/ContactManager.js b/dom/contacts/ContactManager.js index 19a636ff2e4a..fbaaa0277463 100644 --- a/dom/contacts/ContactManager.js +++ b/dom/contacts/ContactManager.js @@ -29,6 +29,7 @@ const CONTACTS_SENDMORE_MINIMUM = 5; // Keep in sync with the interfaces. const PROPERTIES = [ "name", "honorificPrefix", "givenName", "additionalName", "familyName", + "phoneticGivenName", "phoneticFamilyName", "honorificSuffix", "nickname", "photo", "category", "org", "jobTitle", "bday", "note", "anniversary", "sex", "genderIdentity", "key", "adr", "email", "url", "impp", "tel" diff --git a/dom/contacts/fallback/ContactDB.jsm b/dom/contacts/fallback/ContactDB.jsm index 54919f5f6ef4..f2ba3ee4499b 100644 --- a/dom/contacts/fallback/ContactDB.jsm +++ b/dom/contacts/fallback/ContactDB.jsm @@ -22,7 +22,7 @@ Cu.importGlobalProperties(["indexedDB"]); /* all exported symbols need to be bound to this on B2G - Bug 961777 */ this.DB_NAME = "contacts"; -this.DB_VERSION = 19; +this.DB_VERSION = 20; this.STORE_NAME = "contacts"; this.SAVED_GETALL_STORE_NAME = "getallcache"; const CHUNK_SIZE = 20; @@ -169,6 +169,10 @@ ContactDB.prototype = { objectStore.createIndex("category", "properties.category", { multiEntry: true }); objectStore.createIndex("email", "search.email", { multiEntry: true }); objectStore.createIndex("telMatch", "search.parsedTel", {multiEntry: true}); + objectStore.createIndex("phoneticFamilyName", "properties.phoneticFamilyName", { multiEntry: true }); + objectStore.createIndex("phoneticGivenName", "properties.phoneticGivenName", { multiEntry: true }); + objectStore.createIndex("phoneticFamilyNameLowerCase", "search.phoneticFamilyName", { multiEntry: true }); + objectStore.createIndex("phoneticGivenNameLowerCase", "search.phoneticGivenName", { multiEntry: true }); aDb.createObjectStore(SAVED_GETALL_STORE_NAME); aDb.createObjectStore(REVISION_STORE).put(0, REVISION_KEY); } @@ -705,6 +709,17 @@ ContactDB.prototype = { next(); }, + function upgrade19to20() { + if (DEBUG) debug("upgrade19to20 create schema(phonetic)"); + if (!objectStore) { + objectStore = aTransaction.objectStore(STORE_NAME); + } + objectStore.createIndex("phoneticFamilyName", "properties.phoneticFamilyName", { multiEntry: true }); + objectStore.createIndex("phoneticGivenName", "properties.phoneticGivenName", { multiEntry: true }); + objectStore.createIndex("phoneticFamilyNameLowerCase", "search.phoneticFamilyName", { multiEntry: true }); + objectStore.createIndex("phoneticGivenNameLowerCase", "search.phoneticGivenName", { multiEntry: true }); + next(); + }, ]; let index = aOldVersion; @@ -824,6 +839,8 @@ ContactDB.prototype = { tel: [], exactTel: [], parsedTel: [], + phoneticFamilyName: [], + phoneticGivenName: [], }; for (let field in aContact.properties) { @@ -1115,6 +1132,21 @@ ContactDB.prototype = { }, null, aErrorCb); }, + getSortByParam: function CDB_getSortByParam(aFindOptions) { + switch (aFindOptions.sortBy) { + case "familyName": + return [ "familyName", "givenName" ]; + case "givenName": + return [ "givenName" , "familyName" ]; + case "phoneticFamilyName": + return [ "phoneticFamilyName" , "phoneticGivenName" ]; + case "phoneticGivenName": + return [ "phoneticGivenName" , "phoneticFamilyName" ]; + default: + return [ "givenName" , "familyName" ]; + } + }, + /* * Sorting the contacts by sortBy field. aSortBy can either be familyName or givenName. * If 2 entries have the same sortyBy field or no sortBy field is present, we continue @@ -1125,7 +1157,7 @@ ContactDB.prototype = { return; if (aFindOptions.sortBy != "undefined") { const sortOrder = aFindOptions.sortOrder; - const sortBy = aFindOptions.sortBy == "familyName" ? [ "familyName", "givenName" ] : [ "givenName" , "familyName" ]; + const sortBy = this.getSortByParam(aFindOptions); aResults.sort(function (a, b) { let x, y; diff --git a/dom/contacts/tests/shared.js b/dom/contacts/tests/shared.js index 864dc34a6b7e..893469ecef73 100644 --- a/dom/contacts/tests/shared.js +++ b/dom/contacts/tests/shared.js @@ -90,6 +90,8 @@ var properties1 = { name: ["Test1 TestFamilyName", "Test2 Wagner"], familyName: ["TestFamilyName","Wagner"], givenName: ["Test1","Test2"], + phoneticFamilyName: ["TestphoneticFamilyName1","TestphoneticFamilyName2"], + phoneticGivenName: ["TestphoneticGivenName1","TestphoneticGivenName2"], nickname: ["nicktest"], tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+55 (31) 9876-3456"}, {type: ["home"], value: "+49 451 491934"}], adr: [adr1], @@ -100,6 +102,8 @@ var properties2 = { name: ["dummyHonorificPrefix dummyGivenName dummyFamilyName dummyHonorificSuffix", "dummyHonorificPrefix2"], familyName: ["dummyFamilyName"], givenName: ["dummyGivenName"], + phoneticFamilyName: ["dummyphoneticFamilyName"], + phoneticGivenName: ["dummyphoneticGivenName"], honorificPrefix: ["dummyHonorificPrefix","dummyHonorificPrefix2"], honorificSuffix: ["dummyHonorificSuffix"], additionalName: ["dummyadditionalName"], @@ -120,6 +124,86 @@ var properties2 = { key: ["ERPJ394GJJWEVJ0349GJ09W3H4FG0WFW80VHW3408GH30WGH348G3H"] }; +// To test sorting(CJK) +var c9 = { + phoneticFamilyName: ["a"], + phoneticGivenName: ["a"], +}; + +var c10 = { + phoneticFamilyName: ["b"], + phoneticGivenName: ["b"], +}; + +var c11 = { + phoneticFamilyName: ["c","a","b"], + phoneticGivenName: ["c","a","b"], +}; + +var c12 = { + phoneticFamilyName: ["c","a","c"], + phoneticGivenName: ["c","a","c"], +}; + +var c13 = { + phoneticFamilyName: [], + phoneticGivenName: [], +}; + +var c14 = { + phoneticFamilyName: ["e","e","e"], + phoneticGivenName: ["e","e","e"], +}; + +var c15 = { + phoneticFamilyName: ["e","e","e"], + phoneticGivenName: ["e","e","e"], +}; + +var c16 = { + phoneticFamilyName: ["e","e","e"], + phoneticGivenName: ["e","e","e"], +}; + +var properties3 = { + // please keep capital letters at the start of these names + name: ["Taro Yamada", "Ichiro Suzuki"], + familyName: ["Yamada","Suzuki"], + givenName: ["Taro","Ichiro"], + phoneticFamilyName: ["TestPhoneticFamilyYamada","TestPhoneticFamilySuzuki"], + phoneticGivenName: ["TestPhoneticGivenTaro","TestPhoneticGivenIchiro"], + nickname: ["phoneticNicktest"], + tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+55 (31) 9876-3456"}, {type: ["home"], value: "+49 451 491934"}], + adr: [adr1], + email: [{type: ["work"], value: "x@y.com"}], +}; + +var properties4 = { + name: ["dummyHonorificPrefix dummyTaro dummyYamada dummyHonorificSuffix", "dummyHonorificPrefix2"], + familyName: ["dummyYamada"], + givenName: ["dummyTaro"], + phoneticFamilyName: ["dummyTestPhoneticFamilyYamada"], + phoneticGivenName: ["dummyTestPhoneticGivenTaro"], + honorificPrefix: ["dummyPhoneticHonorificPrefix","dummyPhoneticHonorificPrefix2"], + honorificSuffix: ["dummyPhoneticHonorificSuffix"], + additionalName: ["dummyPhoneticAdditionalName"], + nickname: ["dummyPhoneticNickname"], + tel: [{type: ["test"], value: "7932012345", carrier: "myCarrier", pref: 1},{type: ["home", "custom"], value: "7932012346", pref: 0}], + email: [{type: ["test"], value: "a@b.c"}, {value: "b@c.d", pref: 1}], + adr: [adr1, adr2], + impp: [{type: ["aim"], value:"im1", pref: 1}, {value: "im2"}], + org: ["org1", "org2"], + jobTitle: ["boss", "superboss"], + note: ["test note"], + category: ["cat1", "cat2"], + url: [{type: ["work", "work2"], value: "www.1.com", pref: 1}, {value:"www2.com"}], + bday: new Date("1980, 12, 01"), + anniversary: new Date("2000, 12, 01"), + sex: "male", + genderIdentity: "test", + key: ["ERPJ394GJJWEVJ0349GJ09W3H4FG0WFW80VHW3408GH30WGH348G3H"] +}; + var sample_id1; var sample_id2; @@ -265,6 +349,8 @@ function checkContacts(contact1, contact2) { checkStrArray(contact1.givenName, contact2.givenName, "Same givenName"); checkStrArray(contact1.additionalName, contact2.additionalName, "Same additionalName"); checkStrArray(contact1.familyName, contact2.familyName, "Same familyName"); + checkStrArray(contact1.phoneticFamilyName, contact2.phoneticFamilyName, "Same phoneticFamilyName"); + checkStrArray(contact1.phoneticGivenName, contact2.phoneticGivenName, "Same phoneticGivenName"); checkStrArray(contact1.honorificSuffix, contact2.honorificSuffix, "Same honorificSuffix"); checkStrArray(contact1.nickname, contact2.nickname, "Same nickname"); checkCategory(contact1.category, contact2.category); diff --git a/dom/contacts/tests/test_contacts_basics2.html b/dom/contacts/tests/test_contacts_basics2.html index bf8d7b5bf11e..7999cc30f9a4 100644 --- a/dom/contacts/tests/test_contacts_basics2.html +++ b/dom/contacts/tests/test_contacts_basics2.html @@ -745,6 +745,8 @@ var steps = [ name: [], familyName: [], givenName: [], + phoneticFamilyName: [], + phoneticGivenName: [], nickname: [], tel: [], adr: [], @@ -825,6 +827,301 @@ var steps = [ }; req.onerror = onFailure; }, + function () { + ok(true, "Adding a new contact"); + createResult1 = new mozContact(properties3); + req = mozContacts.save(createResult1) + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + sample_id1 = createResult1.id; + next(); + } + req.onerror = onFailure; + }, + function () { + ok(true, "Adding a new contact2"); + createResult2 = new mozContact(properties4); + req = mozContacts.save(createResult2); + req.onsuccess = function () { + ok(createResult2.id, "The contact now has an ID."); + sample_id2 = createResult2.id; + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Retrieving all contacts"); + req = mozContacts.find({sortBy: "phoneticFamilyName"}); + req.onsuccess = function () { + is(req.result.length, 2, "Found exactly 2 contact."); + checkContacts(req.result[1], properties3); + next(); + } + req.onerror = onFailure; + }, + function () { + ok(true, "Searching contacts by query1"); + var options = {filterBy: ["phoneticGivenName", "email"], + filterOp: "startsWith", + filterValue: properties3.phoneticGivenName[0].substring(0, 3)} + req = mozContacts.find(options) + req.onsuccess = function () { + is(req.result.length, 1, "Found exactly 1 contact."); + findResult1 = req.result[0]; + ok(findResult1.id == sample_id1, "Same ID"); + checkContacts(findResult1, createResult1); + next(); + } + req.onerror = onFailure; + }, + function () { + ok(true, "Searching contacts by query2"); + var options = {filterBy: ["phoneticGivenName", "email"], + filterOp: "startsWith", + filterValue: properties4.phoneticGivenName[0].substring(0, 3)}; + req = mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 1, "Found exactly 1 contact."); + findResult1 = req.result[0]; + is(findResult1.adr.length, 2, "Adr length 2"); + checkContacts(findResult1, createResult2); + next(); + } + req.onerror = onFailure; + }, + clearDatabase, + function () { + ok(true, "Adding 20 contacts"); + for (var i=0; i<19; i++) { + createResult1 = new mozContact(properties3); + req = mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + }; + req.onerror = onFailure; + }; + createResult1 = new mozContact(properties3); + req = mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkStrArray(createResult1.name, properties3.name, "Same Name"); + checkCount(20, "20 contacts in DB", next); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Retrieving all contacts"); + req = mozContacts.find(defaultOptions); + req.onsuccess = function () { + is(req.result.length, 20, "20 Entries."); + next(); + } + req.onerror = onFailure; + }, + function () { + ok(true, "Retrieving all contacts2"); + var options = {filterBy: ["phoneticGivenName"], + filterOp: "startsWith", + filterValue: properties3.phoneticGivenName[0].substring(0, 3)}; + req = mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 20, "20 Entries."); + checkContacts(createResult1, req.result[19]); + next(); + } + req.onerror = onFailure; + }, + function () { + ok(true, "Retrieving all contacts3"); + var options = {filterBy: ["phoneticGivenName", "tel", "email"], + filterOp: "startsWith", + filterValue: properties3.phoneticGivenName[0].substring(0, 3)}; + req = mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 20, "20 Entries."); + checkContacts(createResult1, req.result[10]); + next(); + } + req.onerror = onFailure; + }, + clearDatabase, + function () { + ok(true, "Testing clone contact"); + createResult1 = new mozContact(properties3); + req = mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkStrArray(createResult1.phoneticFamilyName, properties3.phoneticFamilyName, "Same phoneticFamilyName"); + checkStrArray(createResult1.phoneticGivenName, properties3.phoneticGivenName, "Same phoneticGivenName"); + next(); + } + req.onerror = onFailure; + }, + function () { + ok(true, "Retrieving all contacts"); + req = mozContacts.find({sortBy: "phoneticGivenName"}); + req.onsuccess = function () { + is(req.result.length, 1, "1 Entries."); + next(); + } + req.onerror = onFailure; + }, + clearDatabase, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c11); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c11, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c10); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c10, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c12); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c12, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c9); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c9, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + var options = {sortBy: "phoneticFamilyName", + sortOrder: "ascending"}; + req = navigator.mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 4, "4 results"); + checkContacts(req.result[0], c9); + checkContacts(req.result[1], c10); + checkContacts(req.result[2], c11); + checkContacts(req.result[3], c12); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + var options = {sortBy: "phoneticFamilyName", + sortOrder: "descending"}; + req = navigator.mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 4, "4 results"); + checkContacts(req.result[0], c12); + checkContacts(req.result[1], c11); + checkContacts(req.result[2], c10); + checkContacts(req.result[3], c9); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c13); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c13, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting with empty string"); + var options = {sortBy: "phoneticFamilyName", + sortOrder: "ascending"}; + req = navigator.mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 5, "5 results"); + checkContacts(req.result[0], c13); + checkContacts(req.result[1], c9); + checkContacts(req.result[2], c10); + checkContacts(req.result[3], c11); + checkContacts(req.result[4], c12); + next(); + }; + req.onerror = onFailure; + }, + clearDatabase, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c15); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c15, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c14); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c14, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(c16); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c16, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + // Android does not support published/updated fields. Skip this. + if (isAndroid) { + next(); + return; + } + + ok(true, "Test sorting with published"); + var options = {sortBy: "phoneticFamilyName", + sortOrder: "descending"}; + req = navigator.mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 3, "3 results"); + ok(req.result[0].published < req.result[1].published, "Right sorting order"); + ok(req.result[1].published < req.result[2].published, "Right sorting order"); + next(); + }; + req.onerror = onFailure; + }, + clearDatabase, function () { ok(true, "all done!\n"); SimpleTest.finish(); diff --git a/dom/webidl/Contacts.webidl b/dom/webidl/Contacts.webidl index 2512ecf819bc..91dc264edf35 100644 --- a/dom/webidl/Contacts.webidl +++ b/dom/webidl/Contacts.webidl @@ -44,8 +44,10 @@ dictionary ContactProperties { sequence? name; sequence? honorificPrefix; sequence? givenName; + sequence? phoneticGivenName; sequence? additionalName; sequence? familyName; + sequence? phoneticFamilyName; sequence? honorificSuffix; sequence? nickname; sequence? category; @@ -81,8 +83,10 @@ interface mozContact { [Cached, Pure] attribute sequence? name; [Cached, Pure] attribute sequence? honorificPrefix; [Cached, Pure] attribute sequence? givenName; + [Cached, Pure] attribute sequence? phoneticGivenName; [Cached, Pure] attribute sequence? additionalName; [Cached, Pure] attribute sequence? familyName; + [Cached, Pure] attribute sequence? phoneticFamilyName; [Cached, Pure] attribute sequence? honorificSuffix; [Cached, Pure] attribute sequence? nickname; [Cached, Pure] attribute sequence? category;