Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 268009e5f6 | |||
| 3e54b58c8c | |||
| edc9d00be3 | |||
| fc93a3d029 | |||
| acc0b71a7a | |||
| 1f3a0bb5dd | |||
| 94881de082 | |||
| cbec1cbbe5 | |||
| 71dd872618 | |||
| bfed154feb | |||
| 5a19b90b13 | |||
| 33b0ca8824 | |||
| 540c58a564 |
@@ -861,13 +861,16 @@ class Parser
|
||||
$offset = -1;
|
||||
|
||||
while (true) {
|
||||
$index = strrpos($expression, $operator, $offset);
|
||||
$index = strrpos($modifiedExpression, $operator, $offset);
|
||||
|
||||
if ($index === false) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($expressionOutOfParenthesisList[$index]) {
|
||||
if (
|
||||
$expressionOutOfParenthesisList[$index] &&
|
||||
!$this->isAtAnotherOperator($index, $operator, $modifiedExpression)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -878,38 +881,26 @@ class Parser
|
||||
continue;
|
||||
}
|
||||
|
||||
$possibleRightOperator = null;
|
||||
if ($operator === '+' || $operator === '-') {
|
||||
$j = $index - 1;
|
||||
|
||||
if (strlen($operator) === 1) {
|
||||
if ($index < strlen($expression) - 1) {
|
||||
$possibleRightOperator = trim($operator . $expression[$index + 1]);
|
||||
while ($j >= 0) {
|
||||
$char = $expression[$j];
|
||||
|
||||
if ($this->isWhiteSpace($char)) {
|
||||
$j--;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (array_key_exists($char, $this->operatorMap)) {
|
||||
continue 2;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$possibleRightOperator &&
|
||||
$possibleRightOperator != $operator &&
|
||||
!empty($this->operatorMap[$possibleRightOperator])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$possibleLeftOperator = null;
|
||||
|
||||
if (strlen($operator) === 1) {
|
||||
if ($index > 0) {
|
||||
$possibleLeftOperator = trim($expression[$index - 1] . $operator);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$possibleLeftOperator &&
|
||||
$possibleLeftOperator != $operator &&
|
||||
!empty($this->operatorMap[$possibleLeftOperator])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$firstPart = substr($expression, 0, $index);
|
||||
$secondPart = substr($expression, $index + strlen($operator));
|
||||
|
||||
@@ -1056,6 +1047,43 @@ class Parser
|
||||
return new Attribute($expression);
|
||||
}
|
||||
|
||||
private function isAtAnotherOperator(int $index, string $operator, string $expression): bool
|
||||
{
|
||||
$possibleRightOperator = null;
|
||||
|
||||
if (strlen($operator) === 1) {
|
||||
if ($index < strlen($expression) - 1) {
|
||||
$possibleRightOperator = trim($operator . $expression[$index + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$possibleRightOperator &&
|
||||
$possibleRightOperator != $operator &&
|
||||
!empty($this->operatorMap[$possibleRightOperator])
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$possibleLeftOperator = null;
|
||||
|
||||
if (strlen($operator) === 1) {
|
||||
if ($index > 0) {
|
||||
$possibleLeftOperator = trim($expression[$index - 1] . $operator);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$possibleLeftOperator &&
|
||||
$possibleLeftOperator != $operator &&
|
||||
!empty($this->operatorMap[$possibleLeftOperator])
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param (StatementRef|IfRef|WhileRef)[] $statementList
|
||||
* @throws SyntaxError
|
||||
|
||||
@@ -842,6 +842,10 @@ class Htmlizer
|
||||
|
||||
private function handleIteration(string $template): string
|
||||
{
|
||||
if ($template === '') {
|
||||
return $template;
|
||||
}
|
||||
|
||||
if (!extension_loaded('dom')) {
|
||||
$this->log?->warning("Extension 'dom' is not enabled. HTML templating functionality is restricted.");
|
||||
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
"isSystem": true
|
||||
},
|
||||
"uriOptionalProtocol": {
|
||||
"pattern": "([a-zA-Z0-9]+\\:\\/\\/)?[a-zA-Z0-9%\\.\\/\\?\\:@\\-_=#$!+*\\(\\)',]+\\.([a-zA-Z0-9%\\&\\.\\/\\?\\:@\\-_=#$!+*\\(\\)',])*",
|
||||
"pattern": "([a-zA-Z0-9]+\\:\\/\\/)?[a-zA-Z0-9%\\.\\/\\?\\:@\\-_=#$!+*\\(\\)',]+\\.([a-zA-Z0-9%\\&\\.\\/\\?\\:@\\-_=#$!+*\\(\\)',~])*",
|
||||
"isSystem": true
|
||||
},
|
||||
"uri": {
|
||||
"pattern": "([a-zA-Z0-9]+\\:\\/\\/){1}[a-zA-Z0-9%\\.\\/\\?\\:@\\-_=#$!+*\\(\\)',]+\\.([a-zA-Z0-9%\\&\\.\\/\\?\\:@\\-_=#$!+*\\(\\)',])*",
|
||||
"pattern": "([a-zA-Z0-9]+\\:\\/\\/){1}[a-zA-Z0-9%\\.\\/\\?\\:@\\-_=#$!+*\\(\\)',]+\\.([a-zA-Z0-9%\\&\\.\\/\\?\\:@\\-_=#$!+*\\(\\)',~])*",
|
||||
"isSystem": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,8 +624,8 @@ class CalendarView extends View {
|
||||
if (this.allDayScopeList.includes(event.scope)) {
|
||||
event.allDay = event.allDayCopy = true;
|
||||
|
||||
if (!notInitial && end) {
|
||||
start = end;
|
||||
if (/*!notInitial &&*/ end) {
|
||||
start = end.clone();
|
||||
|
||||
if (
|
||||
!event.dateEndDate &&
|
||||
@@ -636,6 +636,10 @@ class CalendarView extends View {
|
||||
}
|
||||
}
|
||||
|
||||
if (start.isSame(end)) {
|
||||
end.add(1, 'days');
|
||||
}
|
||||
|
||||
if (start) {
|
||||
event.start = start.toDate();
|
||||
}
|
||||
@@ -1197,7 +1201,8 @@ class CalendarView extends View {
|
||||
|
||||
const event = this.convertToFcEvent(attributes);
|
||||
|
||||
this.calendar.addEvent(event);
|
||||
// true passed to prevent duplicates after re-fetch.
|
||||
this.calendar.addEvent(event, true);
|
||||
}
|
||||
|
||||
updateModel(model) {
|
||||
|
||||
@@ -204,6 +204,8 @@ class Language {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
load(callback, disableCache, loadDefault) {
|
||||
this.off('sync');
|
||||
|
||||
if (callback) {
|
||||
this.once('sync', callback);
|
||||
}
|
||||
|
||||
@@ -53,13 +53,32 @@ class UrlMultipleFieldView extends ArrayFieldView {
|
||||
value = this.strip(value);
|
||||
}
|
||||
|
||||
if (value === decodeURI(value)) {
|
||||
value = encodeURI(value);
|
||||
try {
|
||||
if (value === decodeURI(value)) {
|
||||
value = encodeURI(value);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`Malformed URI ${value}.`);
|
||||
}
|
||||
|
||||
super.addValueFromUi(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {string} value
|
||||
* @return {string}
|
||||
*/
|
||||
decodeURI(value) {
|
||||
try {
|
||||
return decodeURI(value);
|
||||
} catch (e) {
|
||||
console.warn(`Malformed URI ${value}.`);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {string}
|
||||
@@ -88,7 +107,7 @@ class UrlMultipleFieldView extends ArrayFieldView {
|
||||
return $('<a>')
|
||||
.attr('href', this.prepareUrl(value))
|
||||
.attr('target', '_blank')
|
||||
.text(decodeURI(value));
|
||||
.text(this.decodeURI(value));
|
||||
});
|
||||
|
||||
return $list
|
||||
@@ -111,7 +130,7 @@ class UrlMultipleFieldView extends ArrayFieldView {
|
||||
.attr('href', this.prepareUrl(value))
|
||||
.css('user-drag', 'none')
|
||||
.attr('target', '_blank')
|
||||
.text(decodeURI(value))
|
||||
.text(this.decodeURI(value))
|
||||
);
|
||||
|
||||
return $item.get(0).outerHTML;
|
||||
|
||||
@@ -70,7 +70,7 @@ class UrlFieldView extends VarcharFieldView {
|
||||
return;
|
||||
}
|
||||
|
||||
const decoded = parsedValue ? decodeURI(parsedValue) : '';
|
||||
const decoded = parsedValue ? this.decodeURI(parsedValue) : '';
|
||||
|
||||
this.$element.val(decoded);
|
||||
});
|
||||
@@ -80,7 +80,22 @@ class UrlFieldView extends VarcharFieldView {
|
||||
getValueForDisplay() {
|
||||
const value = this.model.get(this.name);
|
||||
|
||||
return value ? decodeURI(value) : null;
|
||||
return value ? this.decodeURI(value) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {string} value
|
||||
* @return {string}
|
||||
*/
|
||||
decodeURI(value) {
|
||||
try {
|
||||
return decodeURI(value);
|
||||
} catch (e) {
|
||||
console.warn(`Malformed URI ${value}.`);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,8 +109,14 @@ class UrlFieldView extends VarcharFieldView {
|
||||
value = this.strip(value);
|
||||
}
|
||||
|
||||
if (value === decodeURI(value)) {
|
||||
value = encodeURI(value);
|
||||
try {
|
||||
if (value === decodeURI(value)) {
|
||||
value = encodeURI(value);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`Malformed URI ${value}.`);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
@@ -97,6 +97,10 @@ class AddDashletModalView extends ModalView {
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
this.dashletList.forEach(item => {
|
||||
this.translations[item] = this.translate(item, 'dashlets');
|
||||
});
|
||||
}
|
||||
|
||||
afterRender() {
|
||||
@@ -125,6 +129,10 @@ class AddDashletModalView extends ModalView {
|
||||
const lowerCaseText = text.toLowerCase();
|
||||
|
||||
this.dashletList.forEach(item => {
|
||||
if (!(item in this.translations)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const label = this.translations[item].toLowerCase();
|
||||
|
||||
for (const word of label.split(' ')) {
|
||||
|
||||
@@ -193,12 +193,18 @@ class RoleRecordTableView extends View {
|
||||
|
||||
let level = null;
|
||||
|
||||
const levelList = this.getLevelList(scope, action);
|
||||
|
||||
if (scope in aclData) {
|
||||
if (access === 'enabled') {
|
||||
if (aclData[scope] !== true) {
|
||||
if (action in aclData[scope]) {
|
||||
level = aclData[scope][action];
|
||||
}
|
||||
|
||||
if (level === null) {
|
||||
level = levelList[levelList.length - 1];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
level = 'no';
|
||||
@@ -209,7 +215,7 @@ class RoleRecordTableView extends View {
|
||||
level: level,
|
||||
name: scope + '-' + action,
|
||||
action: action,
|
||||
levelList: this.getLevelList(scope, action),
|
||||
levelList: levelList,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "8.2.3",
|
||||
"version": "8.2.5",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "espocrm",
|
||||
"version": "8.2.3",
|
||||
"version": "8.2.5",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "espocrm",
|
||||
"version": "8.2.3",
|
||||
"version": "8.2.5",
|
||||
"description": "Open-source CRM.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1380,4 +1380,46 @@ class EvaluatorTest extends \PHPUnit\Framework\TestCase
|
||||
|
||||
$this->assertEquals(2, $vars->j);;
|
||||
}
|
||||
|
||||
public function testStringsWithOperator(): void
|
||||
{
|
||||
$expression = "\$a = '='";
|
||||
|
||||
$vars = (object) [];
|
||||
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$this->evaluator->process($expression, null, $vars);
|
||||
|
||||
$this->assertEquals("=", $vars->a);
|
||||
}
|
||||
|
||||
public function testAssignAndLogical1(): void
|
||||
{
|
||||
$expression = "\$a = 'a' == 'a'";
|
||||
|
||||
$vars = (object) [];
|
||||
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$this->evaluator->process($expression, null, $vars);
|
||||
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$this->evaluator->process($expression);
|
||||
|
||||
$this->assertTrue($vars->a);
|
||||
}
|
||||
|
||||
public function testAssignAndLogical2(): void
|
||||
{
|
||||
$expression = "\$a = 'a' == 'a' && 'b' == 'b'";
|
||||
|
||||
$vars = (object) [];
|
||||
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$this->evaluator->process($expression, null, $vars);
|
||||
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$this->evaluator->process($expression);
|
||||
|
||||
$this->assertTrue($vars->a);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user