diff options
author | John Molakvoæ <skjnldsv@users.noreply.github.com> | 2019-03-29 08:05:09 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-03-29 08:05:09 +0100 |
commit | 0444744e0b4432406bf32dcb7ab7492dfe97cd70 (patch) | |
tree | 5a1b91b7c97a1059f11882c7f83cf556d6b28859 /src | |
parent | f91cb46b898525aadae1ddf44259ccc19a2159a4 (diff) | |
parent | 1fb881b3dcdd540eb5df589f4bc654df33a4ac8a (diff) |
Merge pull request #991 from nextcloud/enhancement/ablabel
Add ABLABEL and ITEMX.property support
Diffstat (limited to 'src')
-rw-r--r-- | src/components/ContactDetails.vue | 12 | ||||
-rw-r--r-- | src/components/ContactDetails/ContactDetailsProperty.vue | 69 | ||||
-rw-r--r-- | src/components/Properties/PropertyMultipleText.vue | 7 | ||||
-rw-r--r-- | src/components/Properties/PropertyText.vue | 7 | ||||
-rw-r--r-- | src/mixins/PropertyMixin.js | 37 | ||||
-rw-r--r-- | src/models/contact.js | 40 |
6 files changed, 155 insertions, 17 deletions
diff --git a/src/components/ContactDetails.vue b/src/components/ContactDetails.vue index 1fca2038..75014703 100644 --- a/src/components/ContactDetails.vue +++ b/src/components/ContactDetails.vue @@ -280,11 +280,13 @@ export default { * @returns {Array} */ sortedProperties() { - return this.localContact.properties.slice(0).sort((a, b) => { - return ( - rfcProps.fieldOrder.indexOf(a.name) - rfcProps.fieldOrder.indexOf(b.name) - ) - }) + return this.localContact.properties + .slice(0) + .sort((a, b) => { + const nameA = a.name.split('.').pop() + const nameB = b.name.split('.').pop() + return rfcProps.fieldOrder.indexOf(nameA) - rfcProps.fieldOrder.indexOf(nameB) + }) }, /** diff --git a/src/components/ContactDetails/ContactDetailsProperty.vue b/src/components/ContactDetails/ContactDetailsProperty.vue index 048b3e20..7ed39cfa 100644 --- a/src/components/ContactDetails/ContactDetailsProperty.vue +++ b/src/components/ContactDetails/ContactDetailsProperty.vue @@ -27,7 +27,7 @@ :property="property" :is-last-property="isLastProperty" :class="{'property--last': isLastProperty}" :contact="contact" :prop-name="propName" :prop-type="propType" :options="sortedModelOptions" :is-read-only="isReadOnly" - @delete="deleteProp" /> + @delete="deleteProp" @update="updateProp" /> </template> <script> @@ -101,7 +101,7 @@ export default { // is this the first property of its kind isFirstProperty() { if (this.index > 0) { - return this.sortedProperties[this.index - 1].name !== this.propName + return this.sortedProperties[this.index - 1].name.split('.').pop() !== this.propName } return true }, @@ -109,7 +109,7 @@ export default { isLastProperty() { // array starts at 0, length starts at 1 if (this.index < this.sortedProperties.length - 1) { - return this.sortedProperties[this.index + 1].name !== this.propName + return this.sortedProperties[this.index + 1].name.split('.').pop() !== this.propName } return true }, @@ -126,6 +126,11 @@ export default { * @returns {string} */ propName() { + // ! is this a ITEMXX.XXX property?? + if (this.propGroup[1]) { + return this.propGroup[1] + } + return this.property.name }, /** @@ -139,6 +144,7 @@ export default { if (this.propModel && this.propModel.force) { return this.propModel.force } + return this.property.getDefaultType() }, @@ -172,6 +178,25 @@ export default { }, /** + * Return the id and type of a property group + * e.g ITEMXX.tel => ['ITEMXX', 'tel'] + * + * @returns {Array} + */ + propGroup() { + return this.property.name.split('.') + }, + + /** + * Return the associated X-ABLABEL if any + * + * @returns {Property} + */ + propLabel() { + return this.contact.vCard.getFirstProperty(`${this.propGroup[0]}.x-ablabel`) + }, + + /** * Returns the closest match to the selected type * or return the default selected as a new object if * none exists @@ -180,6 +205,13 @@ export default { */ selectType: { get() { + // ! if ABLABEL is present, this is a priority + if (this.propLabel) { + return { + id: this.propLabel.name, + name: this.propLabel.getFirstValue() + } + } if (this.propModel && this.propModel.options && this.type) { let selectedType = this.type @@ -219,8 +251,27 @@ export default { return null }, set(data) { - // ical.js take types as arrays - this.type = data.id.split(',') + // if a custom label exists and this is the one we selected + if (this.propLabel && data.id === this.propLabel.name) { + this.propLabel.setValue(data.name) + // only one can coexist + this.type = [] + } else { + // ical.js take types as arrays + this.type = data.id.split(',') + // only one can coexist + this.contact.vCard.removeProperty(`${this.propGroup[0]}.x-ablabel`) + + // checking if there is any other property in this group + const groups = this.contact.jCal[1] + .map(prop => prop[0]) + .filter(name => name.startsWith(`${this.propGroup[0]}.`)) + if (groups.length === 1) { + // then this prop is the latest of its group + // -> converting back to simple prop + this.property.jCal[0] = this.propGroup[1] + } + } this.$emit('updatedcontact') } @@ -281,8 +332,16 @@ export default { * Delete this property */ deleteProp() { + console.info('removing', this.property, this.propGroup) this.contact.vCard.removeProperty(this.property) this.$emit('updatedcontact') + }, + + /** + * Update this property + */ + updateProp() { + this.$emit('updatedcontact') } } } diff --git a/src/components/Properties/PropertyMultipleText.vue b/src/components/Properties/PropertyMultipleText.vue index 68f7be9d..1ce64281 100644 --- a/src/components/Properties/PropertyMultipleText.vue +++ b/src/components/Properties/PropertyMultipleText.vue @@ -29,9 +29,10 @@ <div class="property__row"> <!-- type selector --> <multiselect v-if="propModel.options" v-model="localType" - :options="options" :searchable="false" :placeholder="t('contacts', 'Select type')" - :disabled="isReadOnly" class="property__label" track-by="id" - label="name" @input="updateType" /> + :options="options" :placeholder="t('contacts', 'Select type')" + :taggable="true" tag-placeholder="create" :disabled="isReadOnly" + class="property__label" track-by="id" label="name" + @tag="createLabel" @input="updateType" /> <!-- if we do not support any type on our model but one is set anyway --> <div v-else-if="selectType" class="property__label"> diff --git a/src/components/Properties/PropertyText.vue b/src/components/Properties/PropertyText.vue index 02ee97d2..c13baf1d 100644 --- a/src/components/Properties/PropertyText.vue +++ b/src/components/Properties/PropertyText.vue @@ -29,9 +29,10 @@ <div class="property__row"> <!-- type selector --> <multiselect v-if="propModel.options" v-model="localType" - :options="options" :searchable="false" :placeholder="t('contacts', 'Select type')" - :disabled="isReadOnly" class="property__label" track-by="id" - label="name" @input="updateType" /> + :options="options" :placeholder="t('contacts', 'Select type')" + :taggable="true" tag-placeholder="create" :disabled="isReadOnly" + class="property__label" track-by="id" label="name" + @tag="createLabel" @input="updateType" /> <!-- if we do not support any type on our model but one is set anyway --> <div v-else-if="selectType" class="property__label"> diff --git a/src/mixins/PropertyMixin.js b/src/mixins/PropertyMixin.js index 5092420e..8a86e645 100644 --- a/src/mixins/PropertyMixin.js +++ b/src/mixins/PropertyMixin.js @@ -21,6 +21,7 @@ */ import debounce from 'debounce' import Contact from 'Models/contact' +import ICAL from 'ical.js' export default { props: { @@ -129,6 +130,40 @@ export default { updateType: debounce(function(e) { // https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier this.$emit('update:selectType', this.localType) - }, 500) + }, 500), + + createLabel(label) { + let propGroup = this.property.name + if (!this.property.name.startsWith('nextcloud')) { + propGroup = `nextcloud${this.getNcGroupCount() + 1}.${this.property.name}` + this.property.jCal[0] = propGroup + } + const group = propGroup.split('.')[0] + const name = propGroup.split('.')[1] + + this.contact.vCard.addPropertyWithValue(`${group}.x-ablabel`, label) + + // force update the main design sets + if (ICAL.design.vcard.property[name]) { + ICAL.design.vcard.property[propGroup] + = ICAL.design.vcard.property[name] + } + if (ICAL.design.vcard3.property[name]) { + ICAL.design.vcard3.property[propGroup] + = ICAL.design.vcard3.property[name] + } + + this.$emit('update') + }, + + getNcGroupCount() { + const props = this.contact.jCal[1] + .map(prop => prop[0].split('.')[0]) // itemxxx.adr => itemxxx + .filter(name => name.startsWith('nextcloud')) // filter nextcloudxxx.adr + .map(prop => parseInt(prop.split('nextcloud')[1])) // nextcloudxxx => xxx + return props.length > 0 + ? Math.max.apply(null, props) // get max iteration of nextcloud grouped props + : 0 + } } } diff --git a/src/models/contact.js b/src/models/contact.js index 2d383dd9..14ea06ff 100644 --- a/src/models/contact.js +++ b/src/models/contact.js @@ -35,6 +35,40 @@ const isEmpty = value => { return (Array.isArray(value) && value.join('') === '') || (!Array.isArray(value) && value === '') } +/** + * Parse a jCal and update the global designset + * if any grouped property is found + * + * @param {Array} jCal the contact ICAL.js jCal + * @returns {Boolean} + */ +const updateDesignSet = jCal => { + let result = false + jCal[1].forEach(prop => { + const propGroup = prop[0].split('.') + + // if this is a grouped property, update the designSet + if (propGroup.length === 2 && ( + ICAL.design.vcard.property[propGroup[1]] + || ICAL.design.vcard3.property[propGroup[1]] + )) { + // force update the main design sets + if (ICAL.design.vcard.property[propGroup[1]]) { + ICAL.design.vcard.property[prop[0]] + = ICAL.design.vcard.property[propGroup[1]] + result = true + } + if (ICAL.design.vcard3.property[propGroup[1]]) { + ICAL.design.vcard3.property[prop[0]] + = ICAL.design.vcard3.property[propGroup[1]] + + result = true + } + } + }) + return result +} + export default class Contact { /** @@ -54,6 +88,12 @@ export default class Contact { throw new Error('Only one contact is allowed in the vcard data') } + // add grouped properties to the design set + // if any found, refresh the contact jCal + if (updateDesignSet(jCal)) { + jCal = ICAL.parse(vcard) + } + this.jCal = jCal this.addressbook = addressbook this.vCard = new ICAL.Component(this.jCal) |