diff options
author | Marius David Wieschollek <passwords.public@mdns.eu> | 2021-11-28 22:29:10 +0300 |
---|---|---|
committer | Marius David Wieschollek <passwords.public@mdns.eu> | 2021-11-28 22:29:10 +0300 |
commit | 06e881e4073eede08478c1b8b550efaf956091ba (patch) | |
tree | fefaab012801303ca235e383d537d4d308d0acaf | |
parent | 2fe313b2a838df1fca64c9d83ee77d9efa571101 (diff) |
Fix issues with new custom fields creation
Signed-off-by: Marius David Wieschollek <passwords.public@mdns.eu>
-rw-r--r-- | src/vue/Components/Password/CustomProperty.vue | 59 | ||||
-rw-r--r-- | src/vue/Components/Password/View.vue | 160 |
2 files changed, 109 insertions, 110 deletions
diff --git a/src/vue/Components/Password/CustomProperty.vue b/src/vue/Components/Password/CustomProperty.vue index c84f690..1fca9e0 100644 --- a/src/vue/Components/Password/CustomProperty.vue +++ b/src/vue/Components/Password/CustomProperty.vue @@ -91,10 +91,7 @@ showField() { if(this.type === 'file') return false; if(this.type === 'data' && !this.value.startsWith('ext:field/')) return false; - if(this.editable || this.value !== '') { - return true; - } - return false; + return this.editable || this.value !== ''; }, classList() { let result = "password-view-customproperty"; @@ -115,24 +112,30 @@ } return 'text'; }, + labelMaxLength() { + return 368 - this.value.length; + }, + valueMaxLength() { + return 368 - this.value.length; + }, labelErrorText() { - return LocalisationService.translate(['PasswordEditMaxAllowedCharacter', this.getMaxLength('label', this.label.length)]); + return LocalisationService.translate(['PasswordEditValidationMaxLength', this.labelMaxLength]); }, valueErrorText() { if(!this.validateUrl()) { - return LocalisationService.translate('PasswordEditInvalidValue'); + return LocalisationService.translate('PasswordEditValidationInvalidValue'); } if(!this.validateEmail()) { - return LocalisationService.translate('PasswordEditInvalidValue'); + return LocalisationService.translate('PasswordEditValidationInvalidValue'); } - return LocalisationService.translate(['PasswordEditMaxAllowedCharacter', this.getMaxLength(this.type, this.value.length)]); + return LocalisationService.translate(['PasswordEditValidationMaxLength', this.valueMaxLength]); } }, methods: { copy() { if(this.editable) return; - var type = (this.type === "secret" ? "password":"text"); + let type = (this.type === "secret" ? "password":"text"); let data = this.value; MessageService.send({type: 'clipboard.write', payload: {type: type, value: data}}).catch(ErrorManager.catch); @@ -151,38 +154,20 @@ } return this.field.label; }, - validateUrl(url) { + validateUrl() { if(this.type !== "url") return true; - var urlRegex = /^(https?|ftps?|ssh):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i; - var uncRegex = /^\\\\([^\\:\|\[\]\/";<>+=,?* _]+)\\([\u0020-\u0021\u0023-\u0029\u002D-\u002E\u0030-\u0039\u0040-\u005A\u005E-\u007B\u007E-\u00FF]{1,80})(((?:\\[\u0020-\u0021\u0023-\u0029\u002D-\u002E\u0030-\u0039\u0040-\u005A\u005E-\u007B\u007E-\u00FF]{1,255})+?|)(?:\\((?:[\u0020-\u0021\u0023-\u0029\u002B-\u002E\u0030-\u0039\u003B\u003D\u0040-\u005B\u005D-\u007B]{1,255}){1}(?:\:(?=[\u0001-\u002E\u0030-\u0039\u003B-\u005B\u005D-\u00FF]|\:)(?:([\u0001-\u002E\u0030-\u0039\u003B-\u005B\u005D-\u00FF]+(?!\:)|[\u0001-\u002E\u0030-\u0039\u003B-\u005B\u005D-\u00FF]*)(?:\:([\u0001-\u002E\u0030-\u0039\u003B-\u005B\u005D-\u00FF]+)|))|)))|)$/; + let urlRegex = /^(https?|ftps?|ssh):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i; + let uncRegex = /^\\\\([^\\:\|\[\]\/";<>+=,?* _]+)\\([\u0020-\u0021\u0023-\u0029\u002D-\u002E\u0030-\u0039\u0040-\u005A\u005E-\u007B\u007E-\u00FF]{1,80})(((?:\\[\u0020-\u0021\u0023-\u0029\u002D-\u002E\u0030-\u0039\u0040-\u005A\u005E-\u007B\u007E-\u00FF]{1,255})+?|)(?:\\((?:[\u0020-\u0021\u0023-\u0029\u002B-\u002E\u0030-\u0039\u003B\u003D\u0040-\u005B\u005D-\u007B]{1,255}){1}(?:\:(?=[\u0001-\u002E\u0030-\u0039\u003B-\u005B\u005D-\u00FF]|\:)(?:([\u0001-\u002E\u0030-\u0039\u003B-\u005B\u005D-\u00FF]+(?!\:)|[\u0001-\u002E\u0030-\u0039\u003B-\u005B\u005D-\u00FF]*)(?:\:([\u0001-\u002E\u0030-\u0039\u003B-\u005B\u005D-\u00FF]+)|))|)))|)$/; - if(urlRegex.test(this.value) || uncRegex.test(this.value)) { - return true; - } - return false; + return urlRegex.test(this.value) || uncRegex.test(this.value); }, validateEmail() { if(this.type !== "email") return true; - const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; - return re.test(String(this.value).toLowerCase()); - }, - getMaxLength(type, length) { - switch(type) { - case 'secret': - var typeLength = 256; - break; - case 'label': - var typeLength = 48; - break; - default: - var typeLength = 320; - } - var maxAllowedField = typeLength - length; - maxAllowedField = (maxAllowedField < this.maxLength ? maxAllowedField:this.maxLength); - return maxAllowedField + length; + const emailRegex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return emailRegex.test(String(this.value).toLowerCase()); }, updateLabel() { - if(this.getMaxLength('label', this.label.length) >= this.label.length) { + if(this.labelMaxLength >= this.label.length) { this.labelError = false; this.field.label = this.label; return true; @@ -192,7 +177,7 @@ }, updateValue() { if(this.validateEmail() && this.validateUrl()) { - if(this.getMaxLength(this.type, this.value.length) >= this.value.length) { + if(this.valueMaxLength >= this.value.length) { this.valueError = false; } this.field.value = this.value; @@ -210,12 +195,11 @@ this.field.type = this.type; } - this.$emit('updateField'); + this.$emit('updated', this.field); this.$emit('error', this.field, false); } else { this.$emit('error', this.field, true); } - } }, @@ -238,7 +222,6 @@ } } } - }; </script> diff --git a/src/vue/Components/Password/View.vue b/src/vue/Components/Password/View.vue index 4ac4a15..9842872 100644 --- a/src/vue/Components/Password/View.vue +++ b/src/vue/Components/Password/View.vue @@ -17,9 +17,9 @@ <custom-property :field="field" :editable="isEditMode" v-for="field in customFields" - :key="field.label" - v-on:updateField="updateCustomField" + :key="field.id" v-on:error="handleValidationError" + v-on:updated="handleFieldUpdate" :maxLength="customFieldLength"/> <property :editable="false" :field="field" v-for="field in statFields" :key="field.name"/> </div> @@ -27,6 +27,7 @@ </template> <script> + import {v4 as uuid} from 'uuid'; import Icon from '@vue/Components/Icon'; import {Password} from 'passwords-client/models'; import Property from '@vue/Components/Password/Property'; @@ -45,12 +46,11 @@ data() { return { - isEditMode : false, - defaultFields : this.getDefaultFields(), - customFields : this.getCustomFields(), - updatedFields : {}, - errorQueue : [], - customFieldLength: 8192 + isEditMode : false, + defaultFields: this.getDefaultFields(), + customFields : this.getCustomFields(), + updatedValues: {}, + errorQueue : [] }; }, @@ -66,14 +66,8 @@ } return 'reqular'; }, - sharedIcon() { - if(this.password.getProperty('shared') === true) { - return 'users'; - } - return 'user-shield'; - }, - showNewCustomField() { - return this.allowNewCustomField(); + customFieldLength() { + return 8192 - JSON.stringify(this.getValidatedCustomFields()).length; }, canEdit() { return !this.isEditMode && this.password.isEditable(); @@ -110,6 +104,33 @@ } return fields; }, + getCustomFields() { + let editFields = []; + + for(let field of this.password.getCustomFields()) { + editFields.push( + { + id : uuid(), + label: field.label, + value: field.value, + type : field.type + } + ); + } + + if(editFields.length < 20) { + editFields.push( + { + id : uuid(), + label: '', + value: '', + type : 'text' + } + ); + } + + return editFields; + }, getFieldObject(property, type, editable, required, allowCopy, maxLength) { return { name : property, @@ -121,33 +142,6 @@ maxLength }; }, - getCustomFields() { - let result = this.password.getProperty('customFields'); - result.push(this.getNewCustomField()); - this.customFieldLength = 8192 - JSON.stringify(result).length; - return result; - }, - getNewCustomField() { - return ( - { - label: '', - value: '', - type : 'text' - } - ); - }, - allowNewCustomField() { - if(this.customFields === undefined - || this.customFields.length >= 20) { - return false; - } - if(this.updatedFields !== undefined - && this.updatedFields.customFields !== undefined - && this.updatedFields.customFields.length >= 20) { - return false; - } - return true; - }, async updateFavorite() { let data = { id : this.password.getId(), @@ -169,10 +163,14 @@ this.isEditMode = !this.isEditMode; }, async save() { - if(Object.keys(this.updatedFields).length !== 0) { - this.removeEmptyCustomFields(); - this.updatedFields.id = this.password.getId(); - let response = await MessageService.send({type: 'password.update', payload: {data: this.updatedFields}}); + let customFields = this.getValidatedCustomFields(); + if(JSON.stringify(customFields) !== JSON.stringify(this.password.getCustomFields())) { + this.updatedValues.customFields = customFields; + } + + if(Object.keys(this.updatedValues).length !== 0) { + this.updatedValues.id = this.password.getId(); + let response = await MessageService.send({type: 'password.update', payload: {data: this.updatedValues}}); if(response.getPayload().success) { ToastService.success('ToastPasswordUpdated').catch(ErrorManager.catch); this.password.setProperties(response.getPayload().data); @@ -180,36 +178,32 @@ ToastService.error(response.getPayload().message).catch(ErrorManager.catch); } - this.updatedFields = {}; + this.updatedValues = {}; } }, updateField(field, value) { - this.updatedFields[field] = value; + this.updatedValues[field] = value; }, - updateCustomField() { - this.updatedFields.customFields = this.customFields; - this.customFieldLength = 8192 - JSON.stringify(this.customFields).length; - if(!this.allowNewCustomField()) return; - let emptyFieldAvailable = false; - this.updatedFields.customFields.forEach((e) => { - if(e.label === '' && e.value === '' && e.type !== 'data' && e.type !== 'file') { - emptyFieldAvailable = true; - } - }); - if(!emptyFieldAvailable) { - this.customFields.push(this.getNewCustomField()); + getValidatedCustomFields() { + let json = []; + + for(let field of this.customFields) { + if(field.label === '' || field.value === '' || ['text', 'secret', 'email', 'url', ' file', 'data'].indexOf(field.type) === -1) continue; + + json.push( + { + label: field.label, + type : field.type, + value: field.value + } + ); } - }, - removeEmptyCustomFields() { - if(this.updatedFields.customFields === undefined) return; - this.updatedFields.customFields.forEach((e) => { - if((e.label === '' && e.value === '' && e.type !== 'data' && e.type !== 'file') - || e.label === 'ext:field/' && e.type === 'data') { - - let i = this.updatedFields.customFields.indexOf(e); - this.updatedFields.customFields.splice(i, 1); - } - }); + + if(json.length > 20) { + json = json.slice(0, 20); + } + + return json; }, handleValidationError(field, error) { if(this.errorQueue.indexOf(field) !== -1) { @@ -221,6 +215,28 @@ this.errorQueue.push(field); } } + }, + handleFieldUpdate(field) { + let hasEmpty = false; + for(let customField of this.customFields) { + if(customField.id === field.id) { + customField.type = field.type; + customField.label = field.label; + customField.value = field.value; + } + + if(customField.label === '' || customField.value === '') hasEmpty = true; + } + + if(hasEmpty) return; + this.customFields.push( + { + id : uuid(), + label: '', + value: '', + type : 'text' + } + ); } } }; |