fix: prevent a backbone fieldview race condition that can delete user input (#25950)

* prevent a backbone fieldview race condition that can delete user input

eslint for effected

* Fixing quality

* Allow _super in fields.js

* Fixing new issues

* Revert some changes

* Fixing errors, formatting

* Fix bug

* Fix eslint rule allowing _super

* Refactor code

Co-authored-by: Dylan Grafmyre <dylan@opencraft.com>
Co-authored-by: Paulo Viadanna <paulo@opencraft.com>
This commit is contained in:
dylan-grafmyre
2021-05-04 09:17:15 -07:00
committed by GitHub
parent 20713b5c56
commit 8995f1bf32

View File

@@ -1,4 +1,5 @@
(function(define, undefined) {
/* eslint no-underscore-dangle: ["error", { "allow": ["_super"] }] */
(function(define, undef) {
'use strict';
define([
'gettext', 'jquery', 'underscore', 'backbone',
@@ -25,7 +26,7 @@
fieldType: 'generic',
className: function() {
return 'u-field' + ' u-field-' + this.fieldType + ' u-field-' + this.options.valueAttribute;
return 'u-field u-field-' + this.fieldType + ' u-field-' + this.options.valueAttribute;
},
tagName: 'div',
@@ -100,21 +101,22 @@
return HtmlUtils.setHtml(this.$('.u-field-title'), title);
},
getMessage: function(message_status) {
if ((message_status + 'Message') in this) {
return this[message_status + 'Message'].call(this);
getMessage: function(messageStatus) {
if ((messageStatus + 'Message') in this) {
return this[messageStatus + 'Message'].call(this);
} else if (this.showMessages) {
return HtmlUtils.joinHtml(this.indicators[message_status], this.messages[message_status]);
return HtmlUtils.joinHtml(this.indicators[messageStatus], this.messages[messageStatus]);
}
return this.indicators[message_status];
return this.indicators[messageStatus];
},
showHelpMessage: function(message) {
var msg = message;
if (_.isUndefined(message) || _.isNull(message)) {
message = this.helpMessage;
msg = this.helpMessage;
}
this.$('.u-field-message-notification').html('');
HtmlUtils.setHtml(this.$('.u-field-message-help'), message);
HtmlUtils.setHtml(this.$('.u-field-message-help'), msg);
},
getNotificationMessage: function() {
@@ -139,19 +141,19 @@
},
showSuccessMessage: function() {
var successMessage = this.getMessage('success');
var context = Date.now(),
successMessage = this.getMessage('success'),
view = this;
this.showNotificationMessage(successMessage);
if (this.options.refreshPageOnSave) {
if ("focusNextID" in this.options) {
$.cookie('focus_id', this.options.focusNextID );
if ('focusNextID' in this.options) {
$.cookie('focus_id', this.options.focusNextID);
}
location.reload(true);
}
var view = this;
var context = Date.now();
this.lastSuccessMessageContext = context;
setTimeout(function() {
@@ -167,11 +169,14 @@
},
showErrorMessage: function(xhr) {
var errors,
validationErrorMessage,
message;
if (xhr.status === 400) {
try {
var errors = JSON.parse(xhr.responseText),
validationErrorMessage = errors.field_errors[this.options.valueAttribute].user_message,
message = HtmlUtils.joinHtml(this.indicators.validationError, validationErrorMessage);
errors = JSON.parse(xhr.responseText);
validationErrorMessage = errors.field_errors[this.options.valueAttribute].user_message;
message = HtmlUtils.joinHtml(this.indicators.validationError, validationErrorMessage);
this.showNotificationMessage(message);
} catch (error) {
this.showNotificationMessage(this.getMessage('error'));
@@ -202,19 +207,19 @@
},
saveAttributes: function(attributes, options) {
var view = this;
var defaultOptions = {
contentType: 'application/merge-patch+json',
patch: true,
wait: true,
success: function() {
view.saveSucceeded();
},
error: function(model, xhr) {
view.showErrorMessage(xhr);
}
};
if (this.persistChanges === true) {
var view = this;
var defaultOptions = {
contentType: 'application/merge-patch+json',
patch: true,
wait: true,
success: function() {
view.saveSucceeded();
},
error: function(model, xhr) {
view.showErrorMessage(xhr);
}
};
this.showInProgressMessage();
this.model.save(attributes, _.extend(defaultOptions, options));
}
@@ -383,7 +388,12 @@
updateValueInField: function() {
var value = (_.isUndefined(this.modelValue()) || _.isNull(this.modelValue())) ? '' : this.modelValue();
this.$('.u-field-value input').val(value);
var fieldHasFocus = (document.activeElement === this.$('.u-field-value input')[0]);
var fieldChanged = this.fieldValue() !== value;
if (!fieldHasFocus || !fieldChanged) {
this.$('.u-field-value input').val(value);
}
},
saveValue: function() {
@@ -419,7 +429,7 @@
editable: this.editable,
title: this.options.title,
screenReaderTitle: this.options.screenReaderTitle || this.options.title,
titleVisible: this.options.titleVisible !== undefined ? this.options.titleVisible : true,
titleVisible: this.options.titleVisible !== undef ? this.options.titleVisible : true,
iconName: this.options.iconName,
showBlankOption: (!this.options.required || !this.modelValueIsSet()),
groupOptions: this.createGroupOptions(),
@@ -468,8 +478,9 @@
},
displayValue: function(value) {
var option;
if (value) {
var option = this.optionForValue(value);
option = this.optionForValue(value);
return (option ? option[1] : '');
} else {
return '';
@@ -477,11 +488,19 @@
},
updateValueInField: function() {
var value; // str
var fieldHasFocus; // bool
var fieldChanged; // bool
if (this.editable !== 'never') {
this.$('.u-field-value select').val(this.modelValue() || '');
value = this.modelValue() || '';
fieldHasFocus = (document.activeElement === this.$('.u-field-value select')[0]);
fieldChanged = this.fieldValue() !== value;
if (!fieldHasFocus || !fieldChanged) {
this.$('.u-field-value select').val(value);
}
}
var value = this.displayValue(this.modelValue() || '');
value = this.displayValue(this.modelValue() || '');
if (this.modelValueIsSet() === false) {
value = this.options.placeholderValue || '';
}
@@ -613,24 +632,21 @@
'aria-live': 'assertive',
'aria-atomic': true
});
}
else if (remainingCharCount < 60) {
} else if (remainingCharCount < 60) {
$charCount.attr('aria-atomic', 'false');
}
else if (remainingCharCount < 70) {
} else if (remainingCharCount < 70) {
$charCount.attr({
'aria-live': 'polite',
'aria-atomic': true
});
}
$charCount.text(curCharCount);
}
},
adjustTextareaHeight: function() {
if (this.persistChanges === false) { return; }
var textarea = this.$('textarea');
if (this.persistChanges === false) { return; }
textarea.css('height', 'auto').css('height', textarea.prop('scrollHeight') + 10);
},