mirror of
https://github.com/espocrm/espocrm.git
synced 2026-06-28 06:56:05 +00:00
formula check syntax
This commit is contained in:
68
application/Espo/Controllers/Formula.php
Normal file
68
application/Espo/Controllers/Formula.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Api\Request;
|
||||
|
||||
use Espo\Tools\Formula\Service;
|
||||
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\ForbiddenSilent;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class Formula
|
||||
{
|
||||
private Service $service;
|
||||
|
||||
public function __construct(Service $service, User $user)
|
||||
{
|
||||
$this->service = $service;
|
||||
|
||||
if (!$user->isAdmin()) {
|
||||
throw new ForbiddenSilent();
|
||||
}
|
||||
}
|
||||
|
||||
public function postActionCheckSyntax(Request $request): stdClass
|
||||
{
|
||||
$expression = $request->getParsedBody()->expression ?? null;
|
||||
|
||||
if (!$expression || !is_string($expression)) {
|
||||
throw new BadRequest("No or non-string expression.");
|
||||
}
|
||||
|
||||
return $this->service
|
||||
->checkSyntax($expression)
|
||||
->toStdClass();
|
||||
}
|
||||
}
|
||||
@@ -31,4 +31,18 @@ namespace Espo\Core\Formula\Exceptions;
|
||||
|
||||
class SyntaxError extends Error
|
||||
{
|
||||
private $shortMessage = null;
|
||||
|
||||
public static function create(string $message, ?string $shortMessage = null): self
|
||||
{
|
||||
$obj = new static($message);
|
||||
$obj->shortMessage = $shortMessage;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function getShortMessage(): ?string
|
||||
{
|
||||
return $this->shortMessage ?? $this->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +260,10 @@ class Parser
|
||||
}
|
||||
|
||||
if ($braceCounter !== 0) {
|
||||
throw new SyntaxError('Incorrect round brackets in expression ' . $expression . '.');
|
||||
throw SyntaxError::create(
|
||||
'Incorrect round brackets in expression ' . $expression . '.',
|
||||
'Incorrect round brackets.'
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
|
||||
9
application/Espo/Resources/i18n/en_US/Formula.json
Normal file
9
application/Espo/Resources/i18n/en_US/Formula.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"labels": {
|
||||
"Check Syntax": "Check Syntax"
|
||||
},
|
||||
"messages": {
|
||||
"checkSyntaxSuccess": "Syntax is correct.",
|
||||
"checkSyntaxError": "Syntax error."
|
||||
}
|
||||
}
|
||||
3
application/Espo/Resources/metadata/scopes/Formula.json
Normal file
3
application/Espo/Resources/metadata/scopes/Formula.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"languageIsGlobal": true
|
||||
}
|
||||
57
application/Espo/Tools/Formula/Service.php
Normal file
57
application/Espo/Tools/Formula/Service.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\Formula;
|
||||
|
||||
use Espo\Core\Formula\Parser;
|
||||
use Espo\Core\Formula\Exceptions\SyntaxError;
|
||||
|
||||
class Service
|
||||
{
|
||||
private Parser $parser;
|
||||
|
||||
public function __construct(Parser $parser)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
public function checkSyntax(string $expression): SyntaxCheckResult
|
||||
{
|
||||
try {
|
||||
$this->parser->parse($expression);
|
||||
|
||||
$result = SyntaxCheckResult::createSuccess();
|
||||
}
|
||||
catch (SyntaxError $e) {
|
||||
return SyntaxCheckResult::createError($e);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
83
application/Espo/Tools/Formula/SyntaxCheckResult.php
Normal file
83
application/Espo/Tools/Formula/SyntaxCheckResult.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\Formula;
|
||||
|
||||
use Espo\Core\Formula\Exceptions\SyntaxError;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class SyntaxCheckResult
|
||||
{
|
||||
private bool $isSuccess = false;
|
||||
|
||||
private ?string $message = null;
|
||||
|
||||
private function __construct(bool $isSuccess)
|
||||
{
|
||||
$this->isSuccess = $isSuccess;
|
||||
}
|
||||
|
||||
public static function createSuccess(): self
|
||||
{
|
||||
return new self(true);
|
||||
}
|
||||
|
||||
public static function createError(SyntaxError $exception): self
|
||||
{
|
||||
$obj = new self(false);
|
||||
|
||||
$obj->message = $exception->getShortMessage();
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function isSuccess(): bool
|
||||
{
|
||||
return $this->isSuccess;
|
||||
}
|
||||
|
||||
public function getMessage(): ?string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function toStdClass(): stdClass
|
||||
{
|
||||
$data = (object) [];
|
||||
|
||||
$data->isSuccess = $this->isSuccess();
|
||||
|
||||
if (!$this->isSuccess) {
|
||||
$data->message = $this->message;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,35 @@
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="{{#if hasInsert}}col-md-10 col-sm-10 col-xs-12{{else}}col-md-12{{/if}}">
|
||||
<div class="{{#if hasSide}}col-md-10 col-sm-10 col-xs-12{{else}}col-md-12{{/if}}">
|
||||
<div id="{{containerId}}">{{value}}</div>
|
||||
</div>
|
||||
{{#if hasInsert}}
|
||||
{{#if hasSide}}
|
||||
<div class="col-md-2 col-sm-2 col-xs-12">
|
||||
<div class="button-container">
|
||||
<div class="btn-group pull-right">
|
||||
<button type="button" class="btn btn-default btn-sm dropdown-toggle btn-icon" data-toggle="dropdown"><span class="fas fa-plus"></span></button>
|
||||
{{#if hasCheckSyntax}}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default btn-sm dropdown-toggle btn-icon"
|
||||
data-action="checkSyntax"
|
||||
title="{{translate 'Check Syntax' scope='Formula'}}"
|
||||
><span class="fas fa-play"></span></button>
|
||||
{{/if}}
|
||||
{{#if hasInsert}}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default btn-sm dropdown-toggle btn-icon"
|
||||
data-toggle="dropdown"
|
||||
><span class="fas fa-plus"></span></button>
|
||||
<ul class="dropdown-menu pull-right">
|
||||
{{#if targetEntityType}}
|
||||
<li><a href="javascript:" data-action="addAttribute">{{translate 'Attribute'}}</a></li>
|
||||
{{/if}}
|
||||
<li><a href="javascript:" data-action="addFunction">{{translate 'Function'}}</a></li>
|
||||
</ul>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -40,6 +40,10 @@ define('views/fields/formula', 'views/fields/text', function (Dep) {
|
||||
|
||||
maxLineEditCount: 200,
|
||||
|
||||
insertDisabled: false,
|
||||
|
||||
checkSyntaxDisabled: false,
|
||||
|
||||
events: {
|
||||
'click [data-action="addAttribute"]': function () {
|
||||
this.addAttribute();
|
||||
@@ -47,6 +51,9 @@ define('views/fields/formula', 'views/fields/text', function (Dep) {
|
||||
'click [data-action="addFunction"]': function () {
|
||||
this.addFunction();
|
||||
},
|
||||
'click [data-action="checkSyntax"]': function () {
|
||||
this.checkSyntax();
|
||||
},
|
||||
},
|
||||
|
||||
setup: function () {
|
||||
@@ -69,7 +76,8 @@ define('views/fields/formula', 'views/fields/text', function (Dep) {
|
||||
this.params.targetEntityType ||
|
||||
this.targetEntityType;
|
||||
|
||||
this.insertDisabled = this.options.insertDisabled;
|
||||
this.insertDisabled = this.insertDisabled || this.options.insertDisabled;
|
||||
this.checkSyntaxDisabled = this.checkSyntaxDisabled || this.options.checkSyntaxDisabled;
|
||||
|
||||
this.containerId = 'editor-' + Math.floor((Math.random() * 10000) + 1).toString();
|
||||
|
||||
@@ -107,9 +115,13 @@ define('views/fields/formula', 'views/fields/text', function (Dep) {
|
||||
|
||||
data: function () {
|
||||
var data = Dep.prototype.data.call(this);
|
||||
|
||||
data.containerId = this.containerId;
|
||||
data.targetEntityType = this.targetEntityType;
|
||||
data.hasSide = !this.insertDisabled && !this.checkSyntaxDisabled;
|
||||
|
||||
data.hasInsert = !this.insertDisabled;
|
||||
data.hasCheckSyntax = !this.checkSyntaxDisabled;
|
||||
|
||||
return data;
|
||||
},
|
||||
@@ -346,5 +358,41 @@ define('views/fields/formula', 'views/fields/text', function (Dep) {
|
||||
return attributeList;
|
||||
},
|
||||
|
||||
checkSyntax: function () {
|
||||
let expression = this.editor.getValue();
|
||||
|
||||
if (!expression) {
|
||||
Espo.Ui.success(
|
||||
this.translate('checkSyntaxSuccess', 'messages', 'Formula')
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Espo.Ajax
|
||||
.postRequest('Formula/action/checkSyntax', {
|
||||
expression: expression,
|
||||
})
|
||||
.then(response => {
|
||||
if (response.isSuccess) {
|
||||
Espo.Ui.success(
|
||||
this.translate('checkSyntaxSuccess', 'messages', 'Formula')
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let message = this.translate('checkSyntaxError', 'messages', 'Formula');
|
||||
|
||||
if (response.message) {
|
||||
message += ' ' + response.message;
|
||||
}
|
||||
|
||||
Espo.Ui.error(message);
|
||||
|
||||
return;
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user