summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@users.noreply.github.com>2019-03-29 08:05:09 +0100
committerGitHub <noreply@github.com>2019-03-29 08:05:09 +0100
commit0444744e0b4432406bf32dcb7ab7492dfe97cd70 (patch)
tree5a1b91b7c97a1059f11882c7f83cf556d6b28859 /src
parentf91cb46b898525aadae1ddf44259ccc19a2159a4 (diff)
parent1fb881b3dcdd540eb5df589f4bc654df33a4ac8a (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.vue12
-rw-r--r--src/components/ContactDetails/ContactDetailsProperty.vue69
-rw-r--r--src/components/Properties/PropertyMultipleText.vue7
-rw-r--r--src/components/Properties/PropertyText.vue7
-rw-r--r--src/mixins/PropertyMixin.js37
-rw-r--r--src/models/contact.js40
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)