Bug 1776129 - Add Custom1-4 fields to Address Book UI. r=aleca
This converts nsIAbCard style Custom[1-4] properties to X-Custom[1-4] vCard properties when a card is edited. Since the properties could exist in either form a number of hacks have been deployed. Differential Revision: https://phabricator.services.mozilla.com/D153550 --HG-- extra : rebase_source : a793101ae36434240acca28e3892fe083520b170
This commit is contained in:
Родитель
f7a2b7878b
Коммит
2c2be3af27
|
@ -67,6 +67,11 @@ XPCOMUtils.defineLazyGetter(this, "SubDialog", function() {
|
|||
},
|
||||
});
|
||||
});
|
||||
XPCOMUtils.defineLazyGetter(this, "bundle", function() {
|
||||
return Services.strings.createBundle(
|
||||
"chrome://messenger/locale/addressbook/addressBook.properties"
|
||||
);
|
||||
});
|
||||
|
||||
UIDensity.registerWindow(window);
|
||||
UIFontSize.registerWindow(window);
|
||||
|
@ -2398,10 +2403,7 @@ var detailsPane = {
|
|||
|
||||
let photoButton = document.getElementById("photoButton");
|
||||
// FIXME: Remove this once we get new strings after 102.
|
||||
let stringBundle = Services.strings.createBundle(
|
||||
"chrome://messenger/locale/addressbook/addressBook.properties"
|
||||
);
|
||||
photoButton.title = stringBundle.GetStringFromName("browsePhoto");
|
||||
photoButton.title = bundle.GetStringFromName("browsePhoto");
|
||||
photoButton.addEventListener("click", () => {
|
||||
if (this._photoDetails.sourceURL) {
|
||||
photoDialog.showWithURL(
|
||||
|
@ -2954,6 +2956,28 @@ var detailsPane = {
|
|||
time.setAttribute("tz", tz);
|
||||
li.querySelector(".entry-value").appendChild(time);
|
||||
}
|
||||
|
||||
for (let key of ["Custom1", "Custom2", "Custom3", "Custom4"]) {
|
||||
// Custom properties can be nsIAbCard properties or vCard properties.
|
||||
// If there's both, the vCard property has precedence.
|
||||
let value = card.getProperty(key, "");
|
||||
if (card.supportsVCard) {
|
||||
value =
|
||||
card.vCardProperties.getFirstValue(`x-${key.toLowerCase()}`) ?? value;
|
||||
}
|
||||
if (value) {
|
||||
let li = list.appendChild(createEntryItem());
|
||||
li.querySelector(".entry-type").textContent = bundle.GetStringFromName(
|
||||
`property${key}`
|
||||
);
|
||||
li.querySelector(".entry-type").style.setProperty(
|
||||
"white-space",
|
||||
"nowrap"
|
||||
);
|
||||
li.querySelector(".entry-value").textContent = value;
|
||||
}
|
||||
}
|
||||
|
||||
section.hidden = list.childElementCount == 0;
|
||||
|
||||
this.isEditing = false;
|
||||
|
@ -3012,6 +3036,20 @@ var detailsPane = {
|
|||
let card = this.currentCard;
|
||||
|
||||
if (card && card.supportsVCard) {
|
||||
for (let key of ["Custom1", "Custom2", "Custom3", "Custom4"]) {
|
||||
// Custom properties could still exist as nsIAbCard properties.
|
||||
// If they do, and no vCard equivalent exists, add them to the vCard
|
||||
// so that they get displayed.
|
||||
let value = card.getProperty(key, "");
|
||||
if (
|
||||
value &&
|
||||
card.vCardProperties.getFirstEntry(`x-${key.toLowerCase()}`) === null
|
||||
) {
|
||||
card.vCardProperties.addEntry(
|
||||
new VCardPropertyEntry(`x-${key.toLowerCase()}`, {}, "text", value)
|
||||
);
|
||||
}
|
||||
}
|
||||
this.vCardEdit.vCardProperties = card.vCardProperties;
|
||||
// getProperty may return a "1" or "0" string, we want a boolean.
|
||||
this.vCardEdit.preferDisplayName.checked =
|
||||
|
@ -3059,13 +3097,10 @@ var detailsPane = {
|
|||
*/
|
||||
handleInvalidForm() {
|
||||
// FIXME: Drop this in favor of an inline notification with fluent strings.
|
||||
let stringBundle = Services.strings.createBundle(
|
||||
"chrome://messenger/locale/addressbook/addressBook.properties"
|
||||
);
|
||||
Services.prompt.alert(
|
||||
window,
|
||||
stringBundle.GetStringFromName("cardRequiredDataMissingTitle"),
|
||||
stringBundle.GetStringFromName("cardRequiredDataMissingMessage")
|
||||
bundle.GetStringFromName("cardRequiredDataMissingTitle"),
|
||||
bundle.GetStringFromName("cardRequiredDataMissingMessage")
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -3126,6 +3161,12 @@ var detailsPane = {
|
|||
this.vCardEdit.preferDisplayName.checked
|
||||
);
|
||||
|
||||
// By now, nsIAbCard custom properties should be on the vCard. Delete them.
|
||||
card.deleteProperty("Custom1");
|
||||
card.deleteProperty("Custom2");
|
||||
card.deleteProperty("Custom3");
|
||||
card.deleteProperty("Custom4");
|
||||
|
||||
// No photo or a new photo. Delete the old one.
|
||||
if (this._photoChanged) {
|
||||
let oldLeafName = card.getProperty("PhotoName", "");
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/* 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/. */
|
||||
|
||||
/* globals VCardPropertyEntryView, vCardIdGen */
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"VCardPropertyEntry",
|
||||
"resource:///modules/VCardUtils.jsm"
|
||||
);
|
||||
|
||||
class VCardCustomComponent extends HTMLElement {
|
||||
/** @type {VCardPropertyEntry[]} */
|
||||
vCardPropertyEntries = null;
|
||||
/** @type {HTMLInputElement[]} */
|
||||
inputEls = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
let template = document.getElementById("template-vcard-edit-custom");
|
||||
let clonedTemplate = template.content.cloneNode(true);
|
||||
this.appendChild(clonedTemplate);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (this.isConnected) {
|
||||
// FIXME: Add some Fluent strings so that we don't have to do this.
|
||||
let stringBundle = Services.strings.createBundle(
|
||||
"chrome://messenger/locale/addressbook/addressBook.properties"
|
||||
);
|
||||
|
||||
this.inputEls = this.querySelectorAll("input");
|
||||
let labelEls = this.querySelectorAll("label");
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let inputId = vCardIdGen.next().value;
|
||||
labelEls[i].textContent = stringBundle.GetStringFromName(
|
||||
`propertyCustom${i + 1}`
|
||||
);
|
||||
labelEls[i].htmlFor = inputId;
|
||||
this.inputEls[i].id = inputId;
|
||||
}
|
||||
this.fromVCardPropertyEntryToUI();
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
if (!this.isConnected) {
|
||||
this.inputEls = null;
|
||||
this.vCardPropertyEntries = null;
|
||||
}
|
||||
}
|
||||
|
||||
fromVCardPropertyEntryToUI() {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
this.inputEls[i].value = this.vCardPropertyEntries[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
fromUIToVCardPropertyEntry() {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
this.vCardPropertyEntries[i].value = this.inputEls[i].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("vcard-custom", VCardCustomComponent);
|
|
@ -2,11 +2,11 @@
|
|||
* 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/. */
|
||||
|
||||
/* globals VCardAdrComponent, VCardEmailComponent, VCardIMPPComponent,
|
||||
VCardNComponent, VCardFNComponent, VCardNickNameComponent,
|
||||
VCardNoteComponent, VCardOrgComponent, VCardRoleComponent,
|
||||
VCardSpecialDateComponent, VCardTelComponent, VCardTitleComponent,
|
||||
VCardTZComponent, VCardURLComponent */
|
||||
/* globals VCardAdrComponent, VCardCustomComponent, VCardEmailComponent,
|
||||
VCardIMPPComponent, VCardNComponent, VCardFNComponent,
|
||||
VCardNickNameComponent, VCardNoteComponent, VCardOrgComponent,
|
||||
VCardRoleComponent, VCardSpecialDateComponent, VCardTelComponent,
|
||||
VCardTitleComponent, VCardTZComponent, VCardURLComponent */
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
|
@ -106,6 +106,15 @@ class VCardEdit extends HTMLElement {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
if (!this._vCardProperties.getFirstEntry(`x-custom${i}`)) {
|
||||
this._vCardProperties.addEntry(
|
||||
new VCardPropertyEntry(`x-custom${i}`, {}, "text", "")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.updateView();
|
||||
}
|
||||
|
||||
|
@ -123,13 +132,28 @@ class VCardEdit extends HTMLElement {
|
|||
|
||||
this.addFieldsetActions();
|
||||
|
||||
this._orgComponent = null;
|
||||
|
||||
// Insert the vCard property entries.
|
||||
for (let vCardPropertyEntry of this.vCardProperties.entries) {
|
||||
this.insertVCardElement(vCardPropertyEntry, false);
|
||||
}
|
||||
|
||||
let customProperties = ["x-custom1", "x-custom2", "x-custom3", "x-custom4"];
|
||||
if (
|
||||
customProperties.some(name => this.vCardProperties.getFirstValue(name))
|
||||
) {
|
||||
// If one of these properties has a value, display all of them.
|
||||
let customFieldset = this.querySelector("#addr-book-edit-custom");
|
||||
let customEl =
|
||||
customFieldset.querySelector("vcard-custom") ||
|
||||
new VCardCustomComponent();
|
||||
customEl.vCardPropertyEntries = customProperties.map(name =>
|
||||
this._vCardProperties.getFirstEntry(name)
|
||||
);
|
||||
let addCustom = document.getElementById("vcard-add-custom");
|
||||
customFieldset.insertBefore(customEl, addCustom);
|
||||
addCustom.hidden = true;
|
||||
}
|
||||
|
||||
let nameEl = this.querySelector("vcard-n");
|
||||
this.firstName = nameEl.firstNameEl.querySelector("input");
|
||||
this.lastName = nameEl.lastNameEl.querySelector("input");
|
||||
|
@ -538,6 +562,7 @@ class VCardEdit extends HTMLElement {
|
|||
saveVCard() {
|
||||
for (let node of [
|
||||
...this.querySelectorAll("vcard-adr"),
|
||||
...this.querySelectorAll("vcard-custom"),
|
||||
...document.getElementById("vcard-email").children,
|
||||
...this.querySelectorAll("vcard-fn"),
|
||||
...this.querySelectorAll("vcard-impp"),
|
||||
|
@ -570,6 +595,13 @@ class VCardEdit extends HTMLElement {
|
|||
) {
|
||||
emailEntries[0].params.pref = "1";
|
||||
}
|
||||
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
let entry = this._vCardProperties.getFirstEntry(`x-custom${i}`);
|
||||
if (!entry.value) {
|
||||
this._vCardProperties.removeEntry(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -707,6 +739,21 @@ class VCardEdit extends HTMLElement {
|
|||
this.registerAddButton(addNote, "note", () => {
|
||||
addNote.hidden = true;
|
||||
});
|
||||
|
||||
let addCustom = document.getElementById("vcard-add-custom");
|
||||
addCustom.addEventListener("click", event => {
|
||||
let el = new VCardCustomComponent();
|
||||
el.vCardPropertyEntries = [
|
||||
this._vCardProperties.getFirstEntry("x-custom1"),
|
||||
this._vCardProperties.getFirstEntry("x-custom2"),
|
||||
this._vCardProperties.getFirstEntry("x-custom3"),
|
||||
this._vCardProperties.getFirstEntry("x-custom4"),
|
||||
];
|
||||
addCustom.parentNode.insertBefore(el, addCustom);
|
||||
|
||||
this.moveFocusIntoElement(el);
|
||||
addCustom.hidden = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
<!-- Scripts -->
|
||||
<script src="chrome://messenger/content/addressbook/edit/adr.js"></script>
|
||||
<script src="chrome://messenger/content/addressbook/edit/custom.js"></script>
|
||||
<script src="chrome://messenger/content/addressbook/edit/org.js"></script>
|
||||
<script src="chrome://messenger/content/addressbook/edit/email.js"></script>
|
||||
<script src="chrome://messenger/content/addressbook/edit/fn.js"></script>
|
||||
|
@ -126,6 +127,14 @@
|
|||
class="addr-book-edit-fieldset-button"
|
||||
type="button"></button>
|
||||
</fieldset>
|
||||
<!-- Custom -->
|
||||
<fieldset id="addr-book-edit-custom" class="addr-book-edit-fieldset fieldset-reset">
|
||||
<legend data-l10n-id="vcard-custom-header"/>
|
||||
<button id="vcard-add-custom"
|
||||
data-l10n-id="vcard-custom-add"
|
||||
class="addr-book-edit-fieldset-button"
|
||||
type="button"></button>
|
||||
</fieldset>
|
||||
</template>
|
||||
|
||||
<!-- Individual fields -->
|
||||
|
@ -322,6 +331,26 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Custom -->
|
||||
<template id="template-vcard-edit-custom">
|
||||
<div class="vcard-adr-inputs">
|
||||
<label for="custom1"/>
|
||||
<input type="text"/>
|
||||
</div>
|
||||
<div class="vcard-adr-inputs">
|
||||
<label for="custom2"/>
|
||||
<input type="text"/>
|
||||
</div>
|
||||
<div class="vcard-adr-inputs">
|
||||
<label for="custom3"/>
|
||||
<input type="text"/>
|
||||
</div>
|
||||
<div class="vcard-adr-inputs">
|
||||
<label for="custom4"/>
|
||||
<input type="text"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="template-vcard-edit-type">
|
||||
<select class="vcard-type-selection">
|
||||
<option value="work" data-l10n-id="vcard-entry-type-work"/>
|
||||
|
|
|
@ -19,6 +19,7 @@ messenger.jar:
|
|||
content/messenger/addressbook/abView-new.js (content/abView-new.js)
|
||||
# Edit view
|
||||
content/messenger/addressbook/edit/adr.js (content/vcard-edit/adr.js)
|
||||
content/messenger/addressbook/edit/custom.js (content/vcard-edit/custom.js)
|
||||
content/messenger/addressbook/edit/edit.js (content/vcard-edit/edit.js)
|
||||
content/messenger/addressbook/edit/email.js (content/vcard-edit/email.js)
|
||||
content/messenger/addressbook/edit/fn.js (content/vcard-edit/fn.js)
|
||||
|
|
|
@ -72,59 +72,6 @@ add_setup(async function() {
|
|||
END:VCARD
|
||||
`)
|
||||
);
|
||||
// Card 3.
|
||||
personalBook.addCard(
|
||||
VCardUtils.vCardToAbCard(formatVCard`
|
||||
BEGIN:VCARD
|
||||
EMAIL:xbasic3@invalid
|
||||
ANNIVERSARY:2005
|
||||
END:VCARD
|
||||
`)
|
||||
);
|
||||
// Card 4.
|
||||
personalBook.addCard(
|
||||
VCardUtils.vCardToAbCard(formatVCard`
|
||||
BEGIN:VCARD
|
||||
EMAIL:xbasic4@invalid
|
||||
ANNIVERSARY:2006-06
|
||||
END:VCARD
|
||||
`)
|
||||
);
|
||||
// Card 5.
|
||||
personalBook.addCard(
|
||||
VCardUtils.vCardToAbCard(formatVCard`
|
||||
BEGIN:VCARD
|
||||
EMAIL:xbasic5@invalid
|
||||
ANNIVERSARY:--12
|
||||
END:VCARD
|
||||
`)
|
||||
);
|
||||
// Card 6.
|
||||
personalBook.addCard(
|
||||
VCardUtils.vCardToAbCard(formatVCard`
|
||||
BEGIN:VCARD
|
||||
EMAIL:xbasic6@invalid
|
||||
ANNIVERSARY;VALUE=DATE:--0704
|
||||
END:VCARD
|
||||
`)
|
||||
);
|
||||
// Card 7.
|
||||
personalBook.addCard(
|
||||
VCardUtils.vCardToAbCard(formatVCard`
|
||||
BEGIN:VCARD
|
||||
EMAIL:xbasic7@invalid
|
||||
ANNIVERSARY:---30
|
||||
END:VCARD
|
||||
`)
|
||||
);
|
||||
// Card 8.
|
||||
personalBook.addCard(
|
||||
VCardUtils.vCardToAbCard(formatVCard`
|
||||
BEGIN:VCARD
|
||||
ORG:xbasic8org
|
||||
END:VCARD
|
||||
`)
|
||||
);
|
||||
|
||||
MailServices.accounts.createLocalMailAccount();
|
||||
let account = MailServices.accounts.accounts[0];
|
||||
|
@ -412,84 +359,6 @@ add_task(async function testDisplay() {
|
|||
items[5].children[1].lastChild.getAttribute("tz"),
|
||||
"Pacific/Auckland"
|
||||
);
|
||||
|
||||
// Test card 3:
|
||||
EventUtils.synthesizeMouseAtCenter(cardsList.getRowAtIndex(3), {}, abWindow);
|
||||
await TestUtils.waitForCondition(() =>
|
||||
BrowserTestUtils.is_visible(detailsPane)
|
||||
);
|
||||
Assert.ok(BrowserTestUtils.is_visible(otherInfoSection));
|
||||
items = otherInfoSection.querySelectorAll("li");
|
||||
Assert.equal(items.length, 1);
|
||||
Assert.equal(
|
||||
items[0].children[0].dataset.l10nId,
|
||||
"about-addressbook-entry-name-anniversary"
|
||||
);
|
||||
Assert.equal(items[0].children[1].textContent, "2005");
|
||||
|
||||
// Test card 4:
|
||||
EventUtils.synthesizeMouseAtCenter(cardsList.getRowAtIndex(4), {}, abWindow);
|
||||
await TestUtils.waitForCondition(() =>
|
||||
BrowserTestUtils.is_visible(detailsPane)
|
||||
);
|
||||
Assert.ok(BrowserTestUtils.is_visible(otherInfoSection));
|
||||
items = otherInfoSection.querySelectorAll("li");
|
||||
Assert.equal(items.length, 1);
|
||||
Assert.equal(
|
||||
items[0].children[0].dataset.l10nId,
|
||||
"about-addressbook-entry-name-anniversary"
|
||||
);
|
||||
Assert.equal(items[0].children[1].textContent, "June 2006");
|
||||
|
||||
// Test card 5:
|
||||
EventUtils.synthesizeMouseAtCenter(cardsList.getRowAtIndex(5), {}, abWindow);
|
||||
await TestUtils.waitForCondition(() =>
|
||||
BrowserTestUtils.is_visible(detailsPane)
|
||||
);
|
||||
Assert.ok(BrowserTestUtils.is_visible(otherInfoSection));
|
||||
items = otherInfoSection.querySelectorAll("li");
|
||||
Assert.equal(items.length, 1);
|
||||
Assert.equal(
|
||||
items[0].children[0].dataset.l10nId,
|
||||
"about-addressbook-entry-name-anniversary"
|
||||
);
|
||||
Assert.equal(items[0].children[1].textContent, "December");
|
||||
|
||||
// Test card 6:
|
||||
EventUtils.synthesizeMouseAtCenter(cardsList.getRowAtIndex(6), {}, abWindow);
|
||||
await TestUtils.waitForCondition(() =>
|
||||
BrowserTestUtils.is_visible(detailsPane)
|
||||
);
|
||||
Assert.ok(BrowserTestUtils.is_visible(otherInfoSection));
|
||||
items = otherInfoSection.querySelectorAll("li");
|
||||
Assert.equal(items.length, 1);
|
||||
Assert.equal(
|
||||
items[0].children[0].dataset.l10nId,
|
||||
"about-addressbook-entry-name-anniversary"
|
||||
);
|
||||
Assert.equal(items[0].children[1].textContent, "July 4");
|
||||
|
||||
// Test card 7:
|
||||
EventUtils.synthesizeMouseAtCenter(cardsList.getRowAtIndex(7), {}, abWindow);
|
||||
await TestUtils.waitForCondition(() =>
|
||||
BrowserTestUtils.is_visible(detailsPane)
|
||||
);
|
||||
Assert.ok(BrowserTestUtils.is_visible(otherInfoSection));
|
||||
items = otherInfoSection.querySelectorAll("li");
|
||||
Assert.equal(items.length, 1);
|
||||
Assert.equal(
|
||||
items[0].children[0].dataset.l10nId,
|
||||
"about-addressbook-entry-name-anniversary"
|
||||
);
|
||||
Assert.equal(items[0].children[1].textContent, "30");
|
||||
|
||||
// Test card 8:
|
||||
EventUtils.synthesizeMouseAtCenter(cardsList.getRowAtIndex(8), {}, abWindow);
|
||||
await TestUtils.waitForCondition(() =>
|
||||
BrowserTestUtils.is_visible(detailsPane)
|
||||
);
|
||||
Assert.equal(viewContactName.textContent, "xbasic8org");
|
||||
|
||||
Assert.ok(BrowserTestUtils.is_hidden(selectedCardsSection));
|
||||
|
||||
// Card 0, again, just to prove that everything was cleared properly.
|
||||
|
@ -517,6 +386,161 @@ add_task(async function testDisplay() {
|
|||
await closeAddressBookWindow();
|
||||
});
|
||||
|
||||
/**
|
||||
* Test the display of dates with various components missing.
|
||||
*/
|
||||
add_task(async function testDates() {
|
||||
let abWindow = await openAddressBookWindow();
|
||||
let otherInfoSection = abWindow.document.getElementById("otherInfo");
|
||||
|
||||
// Year only.
|
||||
|
||||
let yearCard = await addAndDisplayCard(formatVCard`
|
||||
BEGIN:VCARD
|
||||
EMAIL:xbasic3@invalid
|
||||
ANNIVERSARY:2005
|
||||
END:VCARD
|
||||
`);
|
||||
Assert.ok(BrowserTestUtils.is_visible(otherInfoSection));
|
||||
let items = otherInfoSection.querySelectorAll("li");
|
||||
Assert.equal(items.length, 1);
|
||||
Assert.equal(
|
||||
items[0].children[0].dataset.l10nId,
|
||||
"about-addressbook-entry-name-anniversary"
|
||||
);
|
||||
Assert.equal(items[0].children[1].textContent, "2005");
|
||||
|
||||
// Year and month.
|
||||
|
||||
let yearMonthCard = await addAndDisplayCard(formatVCard`
|
||||
BEGIN:VCARD
|
||||
EMAIL:xbasic4@invalid
|
||||
ANNIVERSARY:2006-06
|
||||
END:VCARD
|
||||
`);
|
||||
Assert.ok(BrowserTestUtils.is_visible(otherInfoSection));
|
||||
items = otherInfoSection.querySelectorAll("li");
|
||||
Assert.equal(items.length, 1);
|
||||
Assert.equal(
|
||||
items[0].children[0].dataset.l10nId,
|
||||
"about-addressbook-entry-name-anniversary"
|
||||
);
|
||||
Assert.equal(items[0].children[1].textContent, "June 2006");
|
||||
|
||||
// Month only.
|
||||
let monthCard = await addAndDisplayCard(formatVCard`
|
||||
BEGIN:VCARD
|
||||
EMAIL:xbasic5@invalid
|
||||
ANNIVERSARY:--12
|
||||
END:VCARD
|
||||
`);
|
||||
Assert.ok(BrowserTestUtils.is_visible(otherInfoSection));
|
||||
items = otherInfoSection.querySelectorAll("li");
|
||||
Assert.equal(items.length, 1);
|
||||
Assert.equal(
|
||||
items[0].children[0].dataset.l10nId,
|
||||
"about-addressbook-entry-name-anniversary"
|
||||
);
|
||||
Assert.equal(items[0].children[1].textContent, "December");
|
||||
|
||||
// Month and day.
|
||||
let monthDayCard = await addAndDisplayCard(formatVCard`
|
||||
BEGIN:VCARD
|
||||
EMAIL:xbasic6@invalid
|
||||
ANNIVERSARY;VALUE=DATE:--0704
|
||||
END:VCARD
|
||||
`);
|
||||
Assert.ok(BrowserTestUtils.is_visible(otherInfoSection));
|
||||
items = otherInfoSection.querySelectorAll("li");
|
||||
Assert.equal(items.length, 1);
|
||||
Assert.equal(
|
||||
items[0].children[0].dataset.l10nId,
|
||||
"about-addressbook-entry-name-anniversary"
|
||||
);
|
||||
Assert.equal(items[0].children[1].textContent, "July 4");
|
||||
|
||||
// Day only.
|
||||
let dayCard = await addAndDisplayCard(formatVCard`
|
||||
BEGIN:VCARD
|
||||
EMAIL:xbasic7@invalid
|
||||
ANNIVERSARY:---30
|
||||
END:VCARD
|
||||
`);
|
||||
Assert.ok(BrowserTestUtils.is_visible(otherInfoSection));
|
||||
items = otherInfoSection.querySelectorAll("li");
|
||||
Assert.equal(items.length, 1);
|
||||
Assert.equal(
|
||||
items[0].children[0].dataset.l10nId,
|
||||
"about-addressbook-entry-name-anniversary"
|
||||
);
|
||||
Assert.equal(items[0].children[1].textContent, "30");
|
||||
|
||||
await closeAddressBookWindow();
|
||||
personalBook.deleteCards([
|
||||
yearCard,
|
||||
yearMonthCard,
|
||||
monthCard,
|
||||
monthDayCard,
|
||||
dayCard,
|
||||
]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Only an organisation name.
|
||||
*/
|
||||
add_task(async function testOrganisationNameOnly() {
|
||||
let card = await addAndDisplayCard(
|
||||
VCardUtils.vCardToAbCard(formatVCard`
|
||||
BEGIN:VCARD
|
||||
ORG:organisation
|
||||
END:VCARD
|
||||
`)
|
||||
);
|
||||
|
||||
let abWindow = await getAddressBookWindow();
|
||||
let viewContactName = abWindow.document.getElementById("viewContactName");
|
||||
Assert.equal(viewContactName.textContent, "organisation");
|
||||
|
||||
await closeAddressBookWindow();
|
||||
personalBook.deleteCards([card]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that custom properties (Custom1 etc.) are displayed.
|
||||
*/
|
||||
add_task(async function testCustomProperties() {
|
||||
let card = VCardUtils.vCardToAbCard(formatVCard`
|
||||
BEGIN:VCARD
|
||||
FN:custom person
|
||||
X-CUSTOM3:x-custom three
|
||||
X-CUSTOM4:x-custom four
|
||||
END:VCARD
|
||||
`);
|
||||
card.setProperty("Custom2", "custom two");
|
||||
card.setProperty("Custom4", "custom four");
|
||||
card = await addAndDisplayCard(card);
|
||||
|
||||
let abWindow = await getAddressBookWindow();
|
||||
let otherInfoSection = abWindow.document.getElementById("otherInfo");
|
||||
|
||||
Assert.ok(BrowserTestUtils.is_visible(otherInfoSection));
|
||||
let items = otherInfoSection.querySelectorAll("li");
|
||||
Assert.equal(items.length, 3);
|
||||
// Custom 1 has no value, should not display.
|
||||
// Custom 2 has an old property value, should display that.
|
||||
Assert.equal(items[0].children[0].textContent, "Custom 2");
|
||||
Assert.equal(items[0].children[1].textContent, "custom two");
|
||||
// Custom 3 has a vCard property value, should display that.
|
||||
Assert.equal(items[1].children[0].textContent, "Custom 3");
|
||||
Assert.equal(items[1].children[1].textContent, "x-custom three");
|
||||
// Custom 4 has both types of value, the vCard value should be displayed.
|
||||
Assert.equal(items[2].children[0].textContent, "Custom 4");
|
||||
Assert.equal(items[2].children[1].textContent, "x-custom four");
|
||||
|
||||
await closeAddressBookWindow();
|
||||
personalBook.deleteCards([card]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks that the edit button is hidden for read-only contacts.
|
||||
*/
|
||||
|
@ -663,7 +687,7 @@ add_task(async function testReadOnlyActions() {
|
|||
// Check contacts with All Address Books displayed.
|
||||
|
||||
openAllAddressBooks();
|
||||
Assert.equal(cardsList.view.rowCount, 12);
|
||||
Assert.equal(cardsList.view.rowCount, 6);
|
||||
Assert.ok(BrowserTestUtils.is_hidden(detailsPane));
|
||||
|
||||
// Basic person from Personal Address Books.
|
||||
|
@ -818,6 +842,30 @@ add_task(async function testGoogleEscaping() {
|
|||
await promiseDirectoryRemoved(googleBook.URI);
|
||||
});
|
||||
|
||||
async function addAndDisplayCard(card) {
|
||||
if (typeof card == "string") {
|
||||
card = VCardUtils.vCardToAbCard(card);
|
||||
}
|
||||
card = personalBook.addCard(card);
|
||||
|
||||
let abWindow = await openAddressBookWindow();
|
||||
let abDocument = abWindow.document;
|
||||
let cardsList = abDocument.getElementById("cards");
|
||||
let detailsPane = abDocument.getElementById("detailsPane");
|
||||
|
||||
let index = cardsList.view.getIndexForUID(card.UID);
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
cardsList.getRowAtIndex(index),
|
||||
{},
|
||||
abWindow
|
||||
);
|
||||
await TestUtils.waitForCondition(() =>
|
||||
BrowserTestUtils.is_visible(detailsPane)
|
||||
);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
async function checkActionButtons(
|
||||
primaryEmail,
|
||||
displayName,
|
||||
|
|
|
@ -184,6 +184,11 @@ function getFields(entryName, addIfNeeded = false, count) {
|
|||
addButtonId = "vcard-add-org";
|
||||
expectFocusSelector = "vcard-title:last-of-type input";
|
||||
break;
|
||||
case "custom":
|
||||
fieldsSelector = "vcard-custom";
|
||||
addButtonId = "vcard-add-custom";
|
||||
expectFocusSelector = "vcard-custom:last-of-type input";
|
||||
break;
|
||||
default:
|
||||
throw new Error("entryName not found");
|
||||
}
|
||||
|
@ -2827,6 +2832,72 @@ add_task(async function test_special_date_field() {
|
|||
await closeAddressBookWindow();
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that custom properties (Custom1 etc.) are editable.
|
||||
*/
|
||||
add_task(async function testCustomProperties() {
|
||||
let card = VCardUtils.vCardToAbCard(formatVCard`
|
||||
BEGIN:VCARD
|
||||
FN:custom person
|
||||
X-CUSTOM3:x-custom three
|
||||
X-CUSTOM4:x-custom four
|
||||
END:VCARD
|
||||
`);
|
||||
card.setProperty("Custom2", "custom two");
|
||||
card.setProperty("Custom4", "custom four");
|
||||
card = personalBook.addCard(card);
|
||||
|
||||
let abWindow = await openAddressBookWindow();
|
||||
let abDocument = abWindow.document;
|
||||
let cardsList = abDocument.getElementById("cards");
|
||||
let detailsPane = abDocument.getElementById("detailsPane");
|
||||
let editButton = abDocument.getElementById("editButton");
|
||||
let saveEditButton = abDocument.getElementById("saveEditButton");
|
||||
|
||||
let index = cardsList.view.getIndexForUID(card.UID);
|
||||
EventUtils.synthesizeMouseAtCenter(
|
||||
cardsList.getRowAtIndex(index),
|
||||
{},
|
||||
abWindow
|
||||
);
|
||||
await TestUtils.waitForCondition(() =>
|
||||
BrowserTestUtils.is_visible(detailsPane)
|
||||
);
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(editButton, {}, abWindow);
|
||||
await inEditingMode();
|
||||
|
||||
let customField = getFields("custom")[0];
|
||||
let inputs = customField.querySelectorAll("input");
|
||||
Assert.equal(inputs.length, 4);
|
||||
Assert.equal(inputs[0].value, "");
|
||||
Assert.equal(inputs[1].value, "custom two");
|
||||
Assert.equal(inputs[2].value, "x-custom three");
|
||||
Assert.equal(inputs[3].value, "x-custom four");
|
||||
|
||||
inputs[0].value = "x-custom one";
|
||||
inputs[1].value = "x-custom two";
|
||||
inputs[3].value = "";
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(saveEditButton, {}, abWindow);
|
||||
await notInEditingMode(editButton);
|
||||
|
||||
card = personalBook.childCards.find(c => c.UID == card.UID);
|
||||
checkCardValues(card, {
|
||||
Custom2: null,
|
||||
Custom4: null,
|
||||
});
|
||||
checkVCardValues(card, {
|
||||
"x-custom1": [{ value: "x-custom one" }],
|
||||
"x-custom2": [{ value: "x-custom two" }],
|
||||
"x-custom3": [{ value: "x-custom three" }],
|
||||
"x-custom4": [],
|
||||
});
|
||||
|
||||
await closeAddressBookWindow();
|
||||
personalBook.deleteCards([card]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that we correctly fix Google's bad escaping of colons in values, and
|
||||
* other characters in URI values.
|
||||
|
|
|
@ -146,3 +146,9 @@ vcard-org-title = Title
|
|||
vcard-org-role = Role
|
||||
|
||||
vcard-org-org = Organization
|
||||
|
||||
# Custom properties
|
||||
|
||||
vcard-custom-header = Custom Properties
|
||||
|
||||
vcard-custom-add = Add custom properties
|
||||
|
|
|
@ -505,6 +505,10 @@ var typeMap = {
|
|||
"tel.cell": singleTextProperty("CellularNumber", "tel", { type: "cell" }),
|
||||
"url.work": singleTextProperty("WebPage1", "url", { type: "work" }, "url"),
|
||||
"url.home": singleTextProperty("WebPage2", "url", { type: "home" }, "url"),
|
||||
"x-custom1": singleTextProperty("Custom1", "x-custom1"),
|
||||
"x-custom2": singleTextProperty("Custom2", "x-custom2"),
|
||||
"x-custom3": singleTextProperty("Custom3", "x-custom3"),
|
||||
"x-custom4": singleTextProperty("Custom4", "x-custom4"),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,5 +12,9 @@ FN:contact number two
|
|||
NOTE:here's some unicode text…
|
||||
TITLE:"worker"
|
||||
N:two;contact;;;
|
||||
X-CUSTOM1;VALUE=TEXT:custom\, 1
|
||||
X-CUSTOM2;VALUE=TEXT:custom 2
|
||||
X-CUSTOM3;VALUE=TEXT:custom
3
|
||||
X-CUSTOM4;VALUE=TEXT:custom\n4
|
||||
UID:yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
|
||||
END:VCARD
|
||||
|
|
|
@ -16,6 +16,9 @@ var { AppConstants } = ChromeUtils.import(
|
|||
var { MailServices } = ChromeUtils.import(
|
||||
"resource:///modules/MailServices.jsm"
|
||||
);
|
||||
var { VCardPropertyEntry } = ChromeUtils.import(
|
||||
"resource:///modules/VCardUtils.jsm"
|
||||
);
|
||||
|
||||
async function subtest(cardConstructor) {
|
||||
let dirPrefId = MailServices.ab.newAddressBook(
|
||||
|
@ -35,6 +38,10 @@ async function subtest(cardConstructor) {
|
|||
|
||||
let contact2 = cardConstructor();
|
||||
contact2.UID = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy";
|
||||
contact2.setProperty("Custom1", "custom, 1");
|
||||
contact2.setProperty("Custom2", "custom\t2");
|
||||
contact2.setProperty("Custom3", "custom\r3");
|
||||
contact2.setProperty("Custom4", "custom\n4");
|
||||
contact2.displayName = "contact number two";
|
||||
contact2.firstName = "contact";
|
||||
contact2.lastName = "two";
|
||||
|
@ -46,10 +53,6 @@ async function subtest(cardConstructor) {
|
|||
contact2.setProperty("JobTitle", `"worker"`);
|
||||
contact2.setProperty("Notes", "here's some unicode text…");
|
||||
}
|
||||
contact2.setProperty("Custom1", "custom, 1");
|
||||
contact2.setProperty("Custom2", "custom\t2");
|
||||
contact2.setProperty("Custom3", "custom\r3");
|
||||
contact2.setProperty("Custom4", "custom\n4");
|
||||
contact2 = book.addCard(contact2);
|
||||
|
||||
let list = Cc["@mozilla.org/addressbook/directoryproperty;1"].createInstance(
|
||||
|
|
|
@ -200,13 +200,17 @@ add_task(function testFromToPropertyMap() {
|
|||
["LastName", "Test"],
|
||||
["FirstName", "Mike"],
|
||||
["PrimaryEmail", "mike@test.invalid"],
|
||||
["Custom1", "custom one"],
|
||||
["Custom2", "custom two"],
|
||||
["Custom3", "custom three"],
|
||||
["Custom4", "custom four"],
|
||||
];
|
||||
let properties = VCardProperties.fromPropertyMap(
|
||||
new Map(inProperties),
|
||||
"3.0"
|
||||
);
|
||||
|
||||
Assert.equal(properties.entries.length, 4, "entry count");
|
||||
Assert.equal(properties.entries.length, 8, "entry count");
|
||||
propertyEqual(
|
||||
properties.getFirstEntry("version"),
|
||||
{
|
||||
|
@ -247,9 +251,49 @@ add_task(function testFromToPropertyMap() {
|
|||
},
|
||||
"email entry"
|
||||
);
|
||||
propertyEqual(
|
||||
properties.getFirstEntry("x-custom1"),
|
||||
{
|
||||
name: "x-custom1",
|
||||
params: {},
|
||||
type: "text",
|
||||
value: "custom one",
|
||||
},
|
||||
"custom1 entry"
|
||||
);
|
||||
propertyEqual(
|
||||
properties.getFirstEntry("x-custom2"),
|
||||
{
|
||||
name: "x-custom2",
|
||||
params: {},
|
||||
type: "text",
|
||||
value: "custom two",
|
||||
},
|
||||
"custom2 entry"
|
||||
);
|
||||
propertyEqual(
|
||||
properties.getFirstEntry("x-custom3"),
|
||||
{
|
||||
name: "x-custom3",
|
||||
params: {},
|
||||
type: "text",
|
||||
value: "custom three",
|
||||
},
|
||||
"custom3 entry"
|
||||
);
|
||||
propertyEqual(
|
||||
properties.getFirstEntry("x-custom4"),
|
||||
{
|
||||
name: "x-custom4",
|
||||
params: {},
|
||||
type: "text",
|
||||
value: "custom four",
|
||||
},
|
||||
"custom4 entry"
|
||||
);
|
||||
|
||||
let outProperties = properties.toPropertyMap();
|
||||
Assert.equal(outProperties.size, 4, "property count");
|
||||
Assert.equal(outProperties.size, 8, "property count");
|
||||
for (let [key, value] of inProperties) {
|
||||
Assert.equal(outProperties.get(key), value, `${key} property`);
|
||||
}
|
||||
|
|
|
@ -31,12 +31,12 @@
|
|||
"TEL;TYPE=cell;VALUE=TEXT:567-890-1234",
|
||||
"URL;TYPE=work;VALUE=URL:http://127.0.0.1",
|
||||
"URL;TYPE=home;VALUE=URL:http://localhost",
|
||||
"X-CUSTOM1;VALUE=TEXT:Custom Field 1",
|
||||
"X-CUSTOM2;VALUE=TEXT:Custom Field 2",
|
||||
"X-CUSTOM3;VALUE=TEXT:Custom Field 3",
|
||||
"X-CUSTOM4;VALUE=TEXT:Custom Field 4",
|
||||
"UID:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
],
|
||||
"Custom1": "Custom Field 1",
|
||||
"Custom2": "Custom Field 2",
|
||||
"Custom3": "Custom Field 3",
|
||||
"Custom4": "Custom Field 4"
|
||||
]
|
||||
}
|
||||
],
|
||||
"bug_263304": [
|
||||
|
|
Загрузка…
Ссылка в новой задаче