Merge branch 'fix'

This commit is contained in:
Yuri Kuznetsov
2024-04-07 18:31:07 +03:00
16 changed files with 440 additions and 327 deletions
@@ -165,7 +165,7 @@ class GoogleMaps implements Helper
$url = "https://maps.googleapis.com/maps/api/staticmap?" .
'center=' . $addressEncoded .
'format=' . $format .
'&format=' . $format .
'&size=' . $size .
'&key=' . $apiKey;
@@ -106,7 +106,10 @@ class EmailReminder
return;
}
if ($entity->hasLinkMultipleField('users')) {
if (
$entity->hasLinkMultipleField('users') &&
$entity->hasAttribute('usersColumns')
) {
$entity->loadLinkMultipleField('users', ['status' => 'acceptanceStatus']);
$status = $entity->getLinkMultipleColumn('users', 'status', $user->getId());
@@ -106,7 +106,8 @@ class SubmitPopupReminders implements JobDataLess
if (
$entity instanceof CoreEntity &&
$entity->hasLinkMultipleField('users')
$entity->hasLinkMultipleField('users') &&
$entity->hasAttribute('usersColumns')
) {
$entity->loadLinkMultipleField('users', ['status' => 'acceptanceStatus']);
@@ -113,7 +113,8 @@ class PopupNotificationsProvider implements Provider
if (
$entity instanceof CoreEntity &&
$entity->hasLinkMultipleField('users')
$entity->hasLinkMultipleField('users') &&
$entity->hasAttribute('usersColumns')
) {
$entity->loadLinkMultipleField('users', ['status' => 'acceptanceStatus']);
@@ -126,7 +126,9 @@ class LinkManager
$params['relationName'] :
lcfirst($entity) . $entityForeign;
$relationName = $this->nameUtil->addCustomPrefix($relationName);
if ($relationName[0] !== 'c' || !preg_match('/[A-Z]/', $relationName[1])) {
$relationName = $this->nameUtil->addCustomPrefix($relationName);
}
if ($this->isNameTooLong($relationName)) {
throw new Error("Relation name is too long.");
+1 -1
View File
@@ -11,7 +11,7 @@
#layout ul.panels > li {
padding: 5px 10px;
margin-bottom: 20px;
margin-bottom: 14px;
min-height: 80px;
border: 1px solid var(--select-item-border);
border-radius: var(--border-radius);
@@ -45,7 +45,9 @@
{{#if hasCustomLabel}}
data-custom-label="{{customLabel}}"
{{/if}}
data-no-label="{{noLabel}}" >
data-no-label="{{noLabel}}"
title="{{label}}"
>
<div class="left" style="width: calc(100% - 14px);">{{label}}</div>
<div class="right" style="width: 14px;">
<a
+1 -1
View File
@@ -26,7 +26,7 @@
<header>{{translate 'Available Fields' scope='Admin'}}</header>
<ul class="disabled cells clearfix">
{{#each disabledFields}}
<li class="cell" data-name="{{./this}}">
<li class="cell" data-name="{{./this}}" title="{{translate this scope=../scope category='fields'}}">
<div class="left" style="width: calc(100% - 14px);">
{{translate this scope=../scope category='fields'}}
</div>
+7 -1
View File
@@ -12,7 +12,12 @@
<header>{{translate 'Enabled' scope='Admin'}}</header>
<ul class="enabled connected">
{{#each layout}}
<li class="cell" draggable="true" {{#each ../dataAttributeList}}data-{{toDom this}}="{{prop ../this this}}" {{/each}}>
<li
class="cell"
draggable="true"
{{#each ../dataAttributeList}}data-{{toDom this}}="{{prop ../this this}}" {{/each}}
title="{{labelText}}"
>
<div class="left" style="width: calc(100% - 17px);">
<span>{{labelText}}</span>
</div>
@@ -40,6 +45,7 @@
class="cell"
draggable="true"
{{#each ../dataAttributeList}}data-{{toDom this}}="{{prop ../this this}}" {{/each}}
title="{{labelText}}"
>
<div class="left" style="width: calc(100% - 17px);">
<span>{{labelText}}</span>
+4 -3
View File
@@ -32,11 +32,12 @@ class UserAcl extends Acl {
// noinspection JSUnusedGlobalSymbols
checkModelRead(model, data, precise) {
if (model.isPortal()) {
if (this.get('portalPermission') === 'yes') {
// @todo Support getPermissionLevel.
/*if (model.isPortal()) {
if (this.getPermissionLevel('portalPermission') === 'yes') {
return true;
}
}
}*/
return this.checkModel(model, data, 'read', precise);
}
@@ -97,7 +97,19 @@ class LinkManagerEditModalView extends ModalView {
const allEntityList = this.getMetadata().getScopeEntityList()
.filter(item => {
return this.getMetadata().get(['scopes', item, 'customizable']);
const defs = /** @type {Record} */this.getMetadata().get(['scopes', item]) || {};
if (!defs.customizable) {
return false;
}
const emDefs = /** @type {Record} */defs.entityManager || {};
if (emDefs.relationships === false) {
return false;
}
return true;
})
.sort((v1, v2) => {
const t1 = this.translate(v1, 'scopeNames');
@@ -196,9 +208,19 @@ class LinkManagerEditModalView extends ModalView {
const entityList = (Object.keys(scopes) || [])
.filter(item => {
const d = scopes[item];
const defs = /** @type {Record} */scopes[item] || {};
return d.customizable && d.entity;
if (!defs.entity || !defs.customizable) {
return false;
}
const emDefs = /** @type {Record} */defs.entityManager || {};
if (emDefs.relationships === false) {
return false;
}
return true;
})
.sort((v1, v2) => {
const t1 = this.translate(v1, 'scopeNames');
@@ -665,10 +687,6 @@ class LinkManagerEditModalView extends ModalView {
Espo.Utils.lowerCaseFirst(this.scope) + entityForeign :
Espo.Utils.lowerCaseFirst(entityForeign) + this.scope;
if (relationName[0] !== 'c' || !/[A-Z]/.test(relationName[1])) {
relationName = 'c' + Espo.Utils.upperCaseFirst(relationName);
}
this.model.set('relationName', relationName);
break;
+10
View File
@@ -110,6 +110,7 @@ class FileFieldView extends LinkFieldView {
},
}
// noinspection JSCheckFunctionSignatures
data() {
const data = {
...super.data(),
@@ -123,6 +124,7 @@ class FileFieldView extends LinkFieldView {
data.valueIsSet = this.model.has(this.idName);
// noinspection JSValidateTypes
return data;
}
@@ -523,6 +525,9 @@ class FileFieldView extends LinkFieldView {
return maxFileSize;
}
/**
* @param {File} file
*/
uploadFile(file) {
let isCanceled = false;
@@ -627,6 +632,11 @@ class FileFieldView extends LinkFieldView {
});
}
/**
* @protected
* @param {File} file
* @return {Promise<unknown>}
*/
handleUploadingFile(file) {
return new Promise(resolve => resolve(file));
}
+103 -88
View File
@@ -26,113 +26,128 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/user/fields/avatar', ['views/fields/image'], function (Dep) {
import ImageFieldView from 'views/fields/image';
return Dep.extend({
class UserAvatarFieldView extends ImageFieldView {
setup: function () {
Dep.prototype.setup.call(this);
getAttributeList() {
if (this.isEditMode()) {
return super.getAttributeList();
}
this.on('after:inline-save', () => {
this.suspendCache = true;
return [];
}
this.reRender();
});
},
setup() {
super.setup();
handleUploadingFile: function (file) {
return new Promise((resolve, reject) => {
let fileReader = new FileReader();
this.on('after:inline-save', () => {
this.suspendCache = true;
fileReader.onload = (e) => {
this.createView('crop', 'views/modals/image-crop', {contents: e.target.result})
.then(view => {
view.render();
this.reRender();
});
}
let cropped = false;
/**
* @protected
* @param {File} file
* @return {Promise<unknown>}
*/
handleUploadingFile(file) {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
this.listenToOnce(view, 'crop', (dataUrl) => {
cropped = true;
fileReader.onload = (e) => {
this.createView('crop', 'views/modals/image-crop', {contents: e.target.result})
.then(view => {
view.render();
setTimeout(() => {
fetch(dataUrl)
.then(result => result.blob())
.then(blob => {
resolve(
new File([blob], 'avatar.jpg', {type: 'image/jpeg'})
);
});
}, 10);
});
let cropped = false;
this.listenToOnce(view, 'remove', () => {
if (!cropped) {
setTimeout(() => this.render(), 10);
this.listenToOnce(view, 'crop', dataUrl => {
cropped = true;
reject();
}
this.clearView('crop');
});
setTimeout(() => {
fetch(dataUrl)
.then(result => result.blob())
.then(blob => {
resolve(
new File([blob], 'avatar.jpg', {type: 'image/jpeg'})
);
});
}, 10);
});
};
fileReader.readAsDataURL(file);
this.listenToOnce(view, 'remove', () => {
if (!cropped) {
setTimeout(() => this.render(), 10);
reject();
}
this.clearView('crop');
});
});
};
fileReader.readAsDataURL(file);
});
}
getValueForDisplay() {
if (!this.isReadMode()) {
return '';
}
const id = this.model.get(this.idName);
const userId = this.model.id;
let t = this.cacheTimestamp = this.cacheTimestamp || Date.now();
if (this.suspendCache) {
t = Date.now();
}
const src = this.getBasePath() +
'?entryPoint=avatar&size=' + this.previewSize + '&id=' + userId +
'&t=' + t + '&attachmentId=' + (id || 'false');
// noinspection HtmlRequiredAltAttribute,RequiredAttributes
const $img = $('<img>')
.attr('src', src)
.attr('alt', this.labelText)
.css({
maxWidth: (this.imageSizes[this.previewSize] || {})[0],
maxHeight: (this.imageSizes[this.previewSize] || {})[1],
});
},
getValueForDisplay: function () {
if (!this.isReadMode()) {
return '';
if (!this.isDetailMode()) {
if (this.getCache()) {
t = this.getCache().get('app', 'timestamp');
}
let id = this.model.get(this.idName);
let userId = this.model.id;
const src = `${this.getBasePath()}?entryPoint=avatar&size=${this.previewSize}&id=${userId}&t=${t}`;
let t = this.cacheTimestamp = this.cacheTimestamp || Date.now();
if (this.suspendCache) {
t = Date.now();
}
let src = this.getBasePath() +
'?entryPoint=avatar&size=' + this.previewSize + '&id=' + userId +
'&t=' + t + '&attachmentId=' + (id || 'false');
let $img = $('<img>')
$img
.attr('width', '16')
.attr('src', src)
.css({
maxWidth: (this.imageSizes[this.previewSize] || {})[0],
maxHeight: (this.imageSizes[this.previewSize] || {})[1],
});
.css('maxWidth', '16px');
}
if (!this.isDetailMode()) {
if (this.getCache()) {
t = this.getCache().get('app', 'timestamp');
}
let src = this.getBasePath() + '?entryPoint=avatar&size=' +
this.previewSize + '&id=' + userId + '&t=' + t;
$img
.attr('width', '16')
.attr('src', src)
.css('maxWidth', '16px');
}
if (!id) {
return $img
.get(0)
.outerHTML;
}
return $('<a>')
.attr('data-id', id)
.attr('data-action', 'showImagePreview')
.attr('href', this.getBasePath() + '?entryPoint=image&id=' + id)
.append($img)
if (!id) {
return $img
.get(0)
.outerHTML;
},
});
});
}
return $('<a>')
.attr('data-id', id)
.attr('data-action', 'showImagePreview')
.attr('href', this.getBasePath() + '?entryPoint=image&id=' + id)
.append($img)
.get(0)
.outerHTML;
}
}
export default UserAvatarFieldView;
+133 -120
View File
@@ -26,140 +26,153 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/user/fields/generate-password', ['views/fields/base'], function (Dep) {
import BaseFieldView from 'views/fields/base';
return Dep.extend({
class UserGeneratePasswordFieldView extends BaseFieldView {
templateContent: '<button type="button" class="btn btn-default" data-action="generatePassword">' +
'{{translate \'Generate\' scope=\'User\'}}</button>',
templateContent = `
<button
type="button"
class="btn btn-default"
data-action="generatePassword"
>{{translate 'Generate' scope='User'}}</button>`
events: {
'click [data-action="generatePassword"]': function () {
this.actionGeneratePassword();
},
events = {
/** @this {UserGeneratePasswordFieldView} */
'click [data-action="generatePassword"]': function () {
this.actionGeneratePassword();
},
}
setup: function () {
Dep.prototype.setup.call(this);
setup() {
super.setup();
this.listenTo(this.model, 'change:password', (model, value, o) => {
if (o.isGenerated) {
return;
}
this.model.set({
passwordPreview: '',
});
});
this.strengthParams = this.options.strengthParams || {};
this.passwordStrengthLength = this.strengthParams.passwordStrengthLength ||
this.getConfig().get('passwordStrengthLength');
this.passwordStrengthLetterCount = this.strengthParams.passwordStrengthLetterCount ||
this.getConfig().get('passwordStrengthLetterCount');
this.passwordStrengthNumberCount = this.strengthParams.passwordStrengthNumberCount ||
this.getConfig().get('passwordStrengthNumberCount');
this.passwordGenerateLength = this.strengthParams.passwordGenerateLength ||
this.getConfig().get('passwordGenerateLength');
this.passwordGenerateLetterCount = this.strengthParams.passwordGenerateLetterCount ||
this.getConfig().get('passwordGenerateLetterCount');
this.passwordGenerateNumberCount = this.strengthParams.passwordGenerateNumberCount ||
this.getConfig().get('passwordGenerateNumberCount');
},
fetch: function () {
return {};
},
actionGeneratePassword: function () {
var length = this.passwordStrengthLength;
var letterCount = this.passwordStrengthLetterCount;
var numberCount = this.passwordStrengthNumberCount;
var generateLength = this.passwordGenerateLength || 10;
var generateLetterCount = this.passwordGenerateLetterCount || 4;
var generateNumberCount = this.passwordGenerateNumberCount || 2;
length = (typeof length === 'undefined') ? generateLength : length;
letterCount = (typeof letterCount === 'undefined') ? generateLetterCount : letterCount;
numberCount = (typeof numberCount === 'undefined') ? generateNumberCount : numberCount;
if (length < generateLength) length = generateLength;
if (letterCount < generateLetterCount) letterCount = generateLetterCount;
if (numberCount < generateNumberCount) numberCount = generateNumberCount;
var password = this.generatePassword(length, letterCount, numberCount, true);
this.model.set({
password: password,
passwordConfirm: password,
passwordPreview: password,
}, {isGenerated: true});
},
generatePassword: function (length, letters, numbers, bothCases) {
var chars = [
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
'0123456789',
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz',
];
var upperCase = 0;
var lowerCase = 0;
if (bothCases) {
upperCase = 1;
lowerCase = 1;
if (letters >= 2) {
letters = letters - 2;
} else {
letters = 0;
}
this.listenTo(this.model, 'change:password', (model, value, o) => {
if (o.isGenerated) {
return;
}
var either = length - (letters + numbers + upperCase + lowerCase);
if (value !== undefined) {
this.model.set('passwordPreview', null);
if (either < 0) {
either = 0;
return;
}
var setList = [letters, numbers, either, upperCase, lowerCase];
this.model.unset('passwordPreview');
});
var shuffle = function (array) {
var currentIndex = array.length, temporaryValue, randomIndex;
this.strengthParams = this.options.strengthParams || {};
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
this.passwordStrengthLength = this.strengthParams.passwordStrengthLength ||
this.getConfig().get('passwordStrengthLength');
return array;
};
this.passwordStrengthLetterCount = this.strengthParams.passwordStrengthLetterCount ||
this.getConfig().get('passwordStrengthLetterCount');
var array = setList.map(
function (len, i) {
return Array(len).fill(chars[i]).map(
function (x) {
return x[Math.floor(Math.random() * x.length)];
}
).join('');
}
).concat();
this.passwordStrengthNumberCount = this.strengthParams.passwordStrengthNumberCount ||
this.getConfig().get('passwordStrengthNumberCount');
return shuffle(array).join('');
},
this.passwordGenerateLength = this.strengthParams.passwordGenerateLength ||
this.getConfig().get('passwordGenerateLength');
});
});
this.passwordGenerateLetterCount = this.strengthParams.passwordGenerateLetterCount ||
this.getConfig().get('passwordGenerateLetterCount');
this.passwordGenerateNumberCount = this.strengthParams.passwordGenerateNumberCount ||
this.getConfig().get('passwordGenerateNumberCount');
}
fetch() {
return {};
}
actionGeneratePassword() {
let length = this.passwordStrengthLength;
let letterCount = this.passwordStrengthLetterCount;
let numberCount = this.passwordStrengthNumberCount;
const generateLength = this.passwordGenerateLength || 10;
const generateLetterCount = this.passwordGenerateLetterCount || 4;
const generateNumberCount = this.passwordGenerateNumberCount || 2;
length = (typeof length === 'undefined') ? generateLength : length;
letterCount = (typeof letterCount === 'undefined') ? generateLetterCount : letterCount;
numberCount = (typeof numberCount === 'undefined') ? generateNumberCount : numberCount;
if (length < generateLength) {
length = generateLength;
}
if (letterCount < generateLetterCount) {
letterCount = generateLetterCount;
}
if (numberCount < generateNumberCount) {
numberCount = generateNumberCount;
}
const password = this.generatePassword(length, letterCount, numberCount, true);
this.model.set({
password: password,
passwordConfirm: password,
passwordPreview: password,
}, {isGenerated: true});
}
generatePassword(length, letters, numbers, bothCases) {
const chars = [
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
'0123456789',
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz',
];
let upperCase = 0;
let lowerCase = 0;
if (bothCases) {
upperCase = 1;
lowerCase = 1;
if (letters >= 2) {
letters = letters - 2;
} else {
letters = 0;
}
}
let either = length - (letters + numbers + upperCase + lowerCase);
if (either < 0) {
either = 0;
}
const setList = [letters, numbers, either, upperCase, lowerCase];
const shuffle = function (array) {
let currentIndex = array.length, temporaryValue, randomIndex;
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
};
const array = setList.map(
(len, i) => Array(len).fill(chars[i]).map(
x => x[Math.floor(Math.random() * x.length)]
).join('')
).concat();
return shuffle(array).join('');
}
}
export default UserGeneratePasswordFieldView;
+133 -96
View File
@@ -26,122 +26,159 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/user/fields/password', ['views/fields/password'], function (Dep) {
import PasswordFieldView from 'views/fields/password';
return Dep.extend({
class UserPasswordFieldView extends PasswordFieldView {
validations: ['required', 'strength', 'confirm'],
validations = [
'required',
'strength',
'confirm',
]
setup: function () {
Dep.prototype.setup.call(this);
},
init() {
const tooltipItemList = [];
init: function () {
var tooltipItemList = [];
this.strengthParams = this.options.strengthParams || {
passwordStrengthLength: this.getConfig().get('passwordStrengthLength'),
passwordStrengthLetterCount: this.getConfig().get('passwordStrengthLetterCount'),
passwordStrengthNumberCount: this.getConfig().get('passwordStrengthNumberCount'),
passwordStrengthBothCases: this.getConfig().get('passwordStrengthBothCases'),
};
this.strengthParams = this.options.strengthParams || {
passwordStrengthLength: this.getConfig().get('passwordStrengthLength'),
passwordStrengthLetterCount: this.getConfig().get('passwordStrengthLetterCount'),
passwordStrengthNumberCount: this.getConfig().get('passwordStrengthNumberCount'),
passwordStrengthBothCases: this.getConfig().get('passwordStrengthBothCases'),
};
const minLength = this.strengthParams.passwordStrengthLength;
var minLength = this.strengthParams.passwordStrengthLength;
if (minLength) {
tooltipItemList.push(
'* ' + this.translate('passwordStrengthLength', 'messages', 'User').replace('{length}', minLength.toString())
);
if (minLength) {
tooltipItemList.push(
'* ' + this.translate('passwordStrengthLength', 'messages', 'User')
.replace('{length}', minLength.toString())
);
}
const requiredLetterCount = this.strengthParams.passwordStrengthLetterCount;
if (requiredLetterCount) {
tooltipItemList.push(
'* ' + this.translate('passwordStrengthLetterCount', 'messages', 'User')
.replace('{count}', requiredLetterCount.toString())
);
}
const requiredNumberCount = this.strengthParams.passwordStrengthNumberCount;
if (requiredNumberCount) {
tooltipItemList.push(
'* ' + this.translate('passwordStrengthNumberCount', 'messages', 'User')
.replace('{count}', requiredNumberCount.toString())
);
}
const bothCases = this.strengthParams.passwordStrengthBothCases;
if (bothCases) {
tooltipItemList.push(
'* ' + this.translate('passwordStrengthBothCases', 'messages', 'User')
);
}
if (tooltipItemList.length) {
this.tooltip = true;
this.tooltipText = this.translate('Requirements', 'labels', 'User') + ':\n' + tooltipItemList.join('\n');
}
super.init();
}
// noinspection JSUnusedGlobalSymbols
validateStrength() {
if (!this.model.get(this.name)) {
return;
}
const password = this.model.get(this.name);
const minLength = this.strengthParams.passwordStrengthLength;
if (minLength) {
if (password.length < minLength) {
const msg = this.translate('passwordStrengthLength', 'messages', 'User')
.replace('{length}', minLength.toString());
this.showValidationMessage(msg);
return true;
}
}
var requiredLetterCount = this.strengthParams.passwordStrengthLetterCount;
if (requiredLetterCount) {
tooltipItemList.push(
'* ' + this.translate('passwordStrengthLetterCount', 'messages', 'User').replace('{count}', requiredLetterCount.toString())
);
}
const requiredLetterCount = this.strengthParams.passwordStrengthLetterCount;
var requiredNumberCount = this.strengthParams.passwordStrengthNumberCount;
if (requiredNumberCount) {
tooltipItemList.push(
'* ' + this.translate('passwordStrengthNumberCount', 'messages', 'User').replace('{count}', requiredNumberCount.toString())
);
}
if (requiredLetterCount) {
let letterCount = 0;
var bothCases = this.strengthParams.passwordStrengthBothCases;
if (bothCases) {
tooltipItemList.push(
'* ' + this.translate('passwordStrengthBothCases', 'messages', 'User')
);
}
if (tooltipItemList.length) {
this.tooltip = true;
this.tooltipText = this.translate('Requirements', 'labels', 'User') + ':\n' + tooltipItemList.join('\n');
}
Dep.prototype.init.call(this);
},
validateStrength: function () {
if (!this.model.get(this.name)) return;
var password = this.model.get(this.name);
var minLength = this.strengthParams.passwordStrengthLength;
if (minLength) {
if (password.length < minLength) {
var msg = this.translate('passwordStrengthLength', 'messages', 'User').replace('{length}', minLength.toString());
this.showValidationMessage(msg);
return true;;
password.split('').forEach(c => {
if (c.toLowerCase() !== c.toUpperCase()) {
letterCount++;
}
});
if (letterCount < requiredLetterCount) {
const msg = this.translate('passwordStrengthLetterCount', 'messages', 'User')
.replace('{count}', requiredLetterCount.toString());
this.showValidationMessage(msg);
return true;
}
}
var requiredLetterCount = this.strengthParams.passwordStrengthLetterCount;
if (requiredLetterCount) {
var letterCount = 0;
password.split('').forEach(function (c) {
if (c.toLowerCase() !== c.toUpperCase()) letterCount++;
}, this);
const requiredNumberCount = this.strengthParams.passwordStrengthNumberCount;
if (letterCount < requiredLetterCount) {
var msg = this.translate('passwordStrengthLetterCount', 'messages', 'User').replace('{count}', requiredLetterCount.toString());
this.showValidationMessage(msg);
return true;;
if (requiredNumberCount) {
let numberCount = 0;
password.split('').forEach((c) => {
if (c >= '0' && c <= '9') {
numberCount++;
}
});
if (numberCount < requiredNumberCount) {
const msg = this.translate('passwordStrengthNumberCount', 'messages', 'User')
.replace('{count}', requiredNumberCount.toString());
this.showValidationMessage(msg);
return true;
}
}
var requiredNumberCount = this.strengthParams.passwordStrengthNumberCount;
if (requiredNumberCount) {
var numberCount = 0;
password.split('').forEach(function (c) {
if (c >= '0' && c <= '9') numberCount++;
}, this);
const bothCases = this.strengthParams.passwordStrengthBothCases;
if (numberCount < requiredNumberCount) {
var msg = this.translate('passwordStrengthNumberCount', 'messages', 'User').replace('{count}', requiredNumberCount.toString());
this.showValidationMessage(msg);
return true;;
if (bothCases) {
let ucCount = 0;
password.split('').forEach((c) => {
if (c.toLowerCase() !== c.toUpperCase() && c === c.toUpperCase()) {
ucCount++;
}
}
});
var bothCases = this.strengthParams.passwordStrengthBothCases;
if (bothCases) {
var ucCount = 0;
password.split('').forEach(function (c) {
if (c.toLowerCase() !== c.toUpperCase() && c === c.toUpperCase()) ucCount++;
}, this);
var lcCount = 0;
password.split('').forEach(function (c) {
if (c.toLowerCase() !== c.toUpperCase() && c === c.toLowerCase()) lcCount++;
}, this);
let lcCount = 0;
if (!ucCount || !lcCount) {
var msg = this.translate('passwordStrengthBothCases', 'messages', 'User');
this.showValidationMessage(msg);
return true;
password.split('').forEach(c => {
if (c.toLowerCase() !== c.toUpperCase() && c === c.toLowerCase()) {
lcCount++;
}
}
},
});
});
});
if (!ucCount || !lcCount) {
const msg = this.translate('passwordStrengthBothCases', 'messages', 'User');
this.showValidationMessage(msg);
return true;
}
}
}
}
export default UserPasswordFieldView;
+8 -4
View File
@@ -124,11 +124,15 @@ class UserEditRecordView extends EditRecordView {
this.hideField('sendAccessInfo');
if (!this.model.has('sendAccessInfo')) {
return;
}
this.model.set('sendAccessInfo', false);
}
controlSendAccessInfoFieldForNew() {
let skipSettingTrue = this.recordHelper.getFieldStateParam('sendAccessInfo', 'hidden') === false;
const skipSettingTrue = this.recordHelper.getFieldStateParam('sendAccessInfo', 'hidden') === false;
if (this.isPasswordSendable()) {
this.showField('sendAccessInfo');
@@ -173,7 +177,7 @@ class UserEditRecordView extends EditRecordView {
getGridLayout(callback) {
this.getHelper().layoutManager
.get(this.model.entityType, this.options.layoutName || this.layoutName, simpleLayout => {
let layout = Espo.Utils.cloneDeep(simpleLayout);
const layout = Espo.Utils.cloneDeep(simpleLayout);
layout.push({
"label": "Teams and Access Control",
@@ -279,7 +283,7 @@ class UserEditRecordView extends EditRecordView {
});
}
let gridLayout = {
const gridLayout = {
type: 'record',
layout: this.convertDetailLayout(layout),
};
@@ -302,7 +306,7 @@ class UserEditRecordView extends EditRecordView {
}
fetch() {
let data = super.fetch();
const data = super.fetch();
if (!this.isNew) {
if (