diff --git a/application/Espo/Controllers/Admin.php b/application/Espo/Controllers/Admin.php index 39d8fa0ff2..92a9a92817 100644 --- a/application/Espo/Controllers/Admin.php +++ b/application/Espo/Controllers/Admin.php @@ -54,7 +54,8 @@ class Admin private Manager $adminNotificationManager, private SystemRequirements $systemRequirements, private ScheduledJob $scheduledJob, - private DataManager $dataManager + private DataManager $dataManager, + private Config\SystemConfig $systemConfig, ) { if (!$this->user->isAdmin()) { throw new Forbidden(); @@ -179,9 +180,14 @@ class Admin * database: array>, * permission: array>, * } + * @throws Forbidden */ public function getActionSystemRequirementList(): object { + if (!$this->user->isSuperAdmin() && $this->systemConfig->isRestrictedMode()) { + throw new Forbidden(); + } + return (object) $this->systemRequirements->getAllRequiredList(); } } diff --git a/application/Espo/Core/Utils/Config/SystemConfig.php b/application/Espo/Core/Utils/Config/SystemConfig.php index c92eac916c..949f82871c 100644 --- a/application/Espo/Core/Utils/Config/SystemConfig.php +++ b/application/Espo/Core/Utils/Config/SystemConfig.php @@ -50,4 +50,14 @@ class SystemConfig { return (string) $this->config->get('version'); } + + /** + * Is restricted mode. + * + * @since 9.1.8 + */ + public function isRestrictedMode(): bool + { + return (bool) $this->config->get('restrictedMode'); + } } diff --git a/application/Espo/Entities/Email.php b/application/Espo/Entities/Email.php index 4d7b1d8c51..f3616975ab 100644 --- a/application/Espo/Entities/Email.php +++ b/application/Espo/Entities/Email.php @@ -232,6 +232,10 @@ class Email extends Entity return $this->getFromContainer(self::ATTR_BODY_PLAIN); } + if (!$this->isHtml()) { + return $this->getBody(); + } + $body = $this->getBody() ?: ''; return EmailUtil::stripHtml($body) ?: null; diff --git a/client/res/templates/admin/system-requirements/index.tpl b/client/res/templates/admin/system-requirements/index.tpl index aa31016b00..073cd85c60 100644 --- a/client/res/templates/admin/system-requirements/index.tpl +++ b/client/res/templates/admin/system-requirements/index.tpl @@ -2,16 +2,23 @@ {{translate 'System Requirements' scope='Admin'}} -
- - +{{#if noAccess}} +
+
+ {{translate 'error403' category='messages'}} +
+
+{{else}} +
+
+ - - + + {{#each phpRequirementList}} {{/each}} - -
{{translate 'PHP Settings' scope='Admin'}}
@@ -21,27 +28,27 @@ {{#if acceptable}} {{translate 'Success' scope='Admin'}} {{else}} - {{translate 'Fail' scope='Admin'}} - {{#ifEqual type 'lib'}} ({{translate 'extension is missing' scope='Admin'}}) {{/ifEqual}} - {{#ifEqual type 'param'}} ({{required}} {{translate 'is recommended' scope='Admin'}}) {{/ifEqual}} + {{translate 'Fail' scope='Admin'}} + {{#ifEqual type 'lib'}} ({{translate 'extension is missing' scope='Admin'}}) {{/ifEqual}} + {{#ifEqual type 'param'}} ({{required}} {{translate 'is recommended' scope='Admin'}}) {{/ifEqual}} {{/if}}
-
+ + + -
- - +
+
+ - - + + {{#each databaseRequirementList}} {{/each}} - -
{{translate 'Database Settings' scope='Admin'}}
@@ -50,29 +57,29 @@ {{actual}} {{#if acceptable}} - {{translate 'Success' scope='Admin'}} + {{translate 'Success' scope='Admin'}} {{else}} - {{translate 'Fail' scope='Admin'}} - {{#ifEqual type 'param'}} ({{required}} {{translate 'is recommended' scope='Admin'}}) {{/ifEqual}} + {{translate 'Fail' scope='Admin'}} + {{#ifEqual type 'param'}} ({{required}} {{translate 'is recommended' scope='Admin'}}) {{/ifEqual}} {{/if}}
-
+ + + -
- - +
+
+ - - + + {{#each permissionRequirementList}} {{/each}} - -
{{translate 'Permissions' scope='Admin'}}
@@ -81,24 +88,25 @@ {{translate type scope='Admin' category='systemRequirements'}} {{#if acceptable}} - {{translate 'Success' scope='Admin'}} + {{translate 'Success' scope='Admin'}} {{else}} - {{translate 'Fail' scope='Admin'}} + {{translate 'Fail' scope='Admin'}} {{/if}}
-
+ + + -
-
- +{{/if}} diff --git a/client/src/views/admin/formula-sandbox/index.js b/client/src/views/admin/formula-sandbox/index.js index d1c5a5ccfd..54eaada510 100644 --- a/client/src/views/admin/formula-sandbox/index.js +++ b/client/src/views/admin/formula-sandbox/index.js @@ -36,7 +36,6 @@ export default class extends View { targetEntityType = null storageKey = 'formulaSandbox' - setup() { const entityTypeList = [''].concat( this.getMetadata() @@ -132,7 +131,7 @@ export default class extends View { return; } - let dataToStore = { + const dataToStore = { script: this.model.get('script'), targetType: this.model.get('targetType'), targetId: this.model.get('targetId'), @@ -143,6 +142,13 @@ export default class extends View { }); } + /** + * @return {import('views/record/detail').default} + */ + getRecordView() { + return this.getView('record'); + } + createRecordView() { return this.createView('record', 'views/admin/formula-sandbox/record/edit', { selector: '.record', @@ -157,7 +163,7 @@ export default class extends View { this.setPageTitle(this.getLanguage().translate('Formula Sandbox', 'labels', 'Admin')); } - run() { + async run() { const script = this.model.get('script'); this.model.set({ @@ -175,50 +181,62 @@ export default class extends View { return; } - Espo.Ajax - .postRequest('Formula/action/run', { + this.getRecordView().disableActionItems(); + Espo.Ui.notifyWait(); + + /** @var {Record} */ + let response; + + try { + response = await Espo.Ajax.postRequest('Formula/action/run', { expression: script, targetId: this.model.get('targetId'), targetType: this.model.get('targetType'), - }) - .then(response => { - this.model.set('output', response.output || null); - - let errorMessage = null; - - if (!response.isSuccess) { - errorMessage = response.message || null; - } - - this.model.set('errorMessage', errorMessage); - - if (response.isSuccess) { - Espo.Ui.success( - this.translate('runSuccess', 'messages', 'Formula') - ); - - return; - } - - if (response.isSyntaxError) { - let msg = this.translate('checkSyntaxError', 'messages', 'Formula'); - - if (response.message) { - msg += ' ' + response.message; - } - - Espo.Ui.error(msg); - - return; - } - - let msg = this.translate('runError', 'messages', 'Formula'); - - if (response.message) { - msg += ' ' + response.message; - } - - Espo.Ui.error(msg); }); + } catch (e) { + this.getRecordView().enableActionItems(); + + return; + } + + this.getRecordView().enableActionItems(); + + this.model.set('output', response.output || null); + + let errorMessage = null; + + if (!response.isSuccess) { + errorMessage = response.message || null; + } + + this.model.set('errorMessage', errorMessage); + + if (response.isSuccess) { + Espo.Ui.success( + this.translate('runSuccess', 'messages', 'Formula') + ); + + return; + } + + if (response.isSyntaxError) { + let msg = this.translate('checkSyntaxError', 'messages', 'Formula'); + + if (response.message) { + msg += ' ' + response.message; + } + + Espo.Ui.error(msg); + + return; + } + + let msg = this.translate('runError', 'messages', 'Formula'); + + if (response.message) { + msg += ' ' + response.message; + } + + Espo.Ui.error(msg); } } diff --git a/client/src/views/admin/system-requirements/index.js b/client/src/views/admin/system-requirements/index.js index 2ec2377735..38752a275a 100644 --- a/client/src/views/admin/system-requirements/index.js +++ b/client/src/views/admin/system-requirements/index.js @@ -27,6 +27,7 @@ ************************************************************************/ import View from 'view'; +import Ajax from 'ajax'; export default class extends View { @@ -35,25 +36,40 @@ export default class extends View { /** @type {Record} */ requirements + /** + * @private + * @type {boolean} + */ + noAccess = false + data() { return { phpRequirementList: this.requirements.php, databaseRequirementList: this.requirements.database, permissionRequirementList: this.requirements.permission, + noAccess: this.noAccess, }; } setup() { this.requirements = {}; + this.wait(this.loadData()); + } + + async loadData() { Espo.Ui.notifyWait(); - const promise = Espo.Ajax.getRequest('Admin/action/systemRequirementList').then(requirements => { - this.requirements = requirements; + try { + this.requirements = await Espo.Ajax.getRequest('Admin/action/systemRequirementList'); + } catch(e) { + this.noAccess = true; - Espo.Ui.notify(); - }); + if ('errorIsHandled' in e) { + e.errorIsHandled = true; + } + } - this.wait(promise); + Espo.Ui.notify(); } } diff --git a/frontend/less/espo/misc/selectize/custom.less b/frontend/less/espo/misc/selectize/custom.less index a02d08900d..271f6010a3 100644 --- a/frontend/less/espo/misc/selectize/custom.less +++ b/frontend/less/espo/misc/selectize/custom.less @@ -1,5 +1,5 @@ .filter > .form-group .field .selectize-control, -.selectize-control.input-sm { +.selectize-control.input-sm.form-control { &.multi { .selectize-input { font-size: var(--font-size-small); diff --git a/tests/unit/Espo/Entities/EmailTest.php b/tests/unit/Espo/Entities/EmailTest.php index 6a0a69c892..08dc9ab7fd 100644 --- a/tests/unit/Espo/Entities/EmailTest.php +++ b/tests/unit/Espo/Entities/EmailTest.php @@ -495,6 +495,7 @@ class EmailTest extends TestCase public function testBodyPlain(): void { $this->email->setBody('
 &'); + $this->email->setIsHtml(); $bodyPlain = $this->email->getBodyPlain(); $this->assertEquals(" \r\n &", $bodyPlain);