Compare commits

...

13 Commits

Author SHA1 Message Date
Yuri Kuznetsov 268009e5f6 8.2.5 2024-06-07 14:35:06 +03:00
Yuri Kuznetsov 3e54b58c8c formula parser assign operator fix 2024-06-06 13:57:46 +03:00
Yuri Kuznetsov edc9d00be3 calendar all day fix 2024-05-20 11:41:31 +03:00
Yuri Kuznetsov fc93a3d029 fix empty template 2024-05-10 12:52:35 +03:00
Yuri Kuznetsov acc0b71a7a fix url decode 2024-05-09 10:50:41 +03:00
Yuri Kuznetsov 1f3a0bb5dd allow tilde in URL 2024-05-09 10:35:24 +03:00
Yuri Kuznetsov 94881de082 8.2.4 2024-05-08 11:32:01 +03:00
Yuri Kuznetsov cbec1cbbe5 fix parser 2024-05-04 23:00:46 +03:00
Yuri Kuznetsov 71dd872618 calendar duplicate event fix 2024-04-30 14:15:39 +03:00
Yuri Kuznetsov bfed154feb language load off 2024-04-26 11:01:54 +03:00
Yuri Kuznetsov 5a19b90b13 fix role empty stream level on ui 2024-04-22 12:38:33 +03:00
Yuri Kuznetsov 33b0ca8824 fix add dashlet 2024-04-20 11:32:41 +03:00
Yuri Kuznetsov 540c58a564 fix add dashlet quick seach 2024-04-20 08:35:16 +03:00
12 changed files with 182 additions and 47 deletions
+58 -30
View File
@@ -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) {
+2
View File
@@ -204,6 +204,8 @@ class Language {
* @returns {Promise}
*/
load(callback, disableCache, loadDefault) {
this.off('sync');
if (callback) {
this.once('sync', callback);
}
+23 -4
View File
@@ -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;
+25 -4
View File
@@ -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;
+8
View File
@@ -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(' ')) {
+7 -1
View File
@@ -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,
});
});
}
+2 -2
View File
@@ -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
View File
@@ -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);
}
}