new: [localTools] Setting validation

pull/67/head
mokaddem 2021-07-05 17:15:21 +02:00
parent bc7e2baf83
commit d9bef3dc0c
8 changed files with 102 additions and 27 deletions

View File

@ -184,7 +184,7 @@ class CRUDComponent extends Component
$this->Controller->set('entity', $data);
}
private function prepareValidationMessage($errors)
public function prepareValidationMessage($errors)
{
$validationMessage = '';
if (!empty($errors)) {
@ -280,8 +280,9 @@ class CRUDComponent extends Component
$validationErrors = $data->getErrors();
$validationMessage = $this->prepareValidationMessage($validationErrors);
$message = __(
__('{0} could not be modified.'),
$this->ObjectAlias
'{0} could not be modified.{1}',
$this->ObjectAlias,
empty($validationMessage) ? '' : PHP_EOL . __('Reason:{0}', $validationMessage)
);
if ($this->Controller->ParamHandler->isRest()) {
} else if ($this->Controller->ParamHandler->isAjax()) {

View File

@ -152,9 +152,6 @@ class LocalToolsController extends AppController
if (!empty($responsePayload)) {
return $responsePayload;
}
if ($this->ParamHandler->isAjax() && !empty($this->ajaxResponsePayload)) {
return $this->ajaxResponsePayload;
}
$localConnectors = $this->LocalTools->extractMeta($this->LocalTools->getConnectors());
$dropdownData = ['connectors' => []];
$connector = false;

View File

@ -97,26 +97,29 @@ class MispConnector extends CommonConnectorTools
]
];
public $version = '0.1';
public $parameters = [
public $settings = [
'url' => [
'required' => true,
// 'validation' => 'url',
'type' => 'text'
],
'authkey' => [
'required' => true,
// 'validation' => 'authkeyLength',
'type' => 'text'
],
'skip_ssl' => [
// 'validation' => 'boolean',
'type' => 'boolean'
],
'test_param' => [
'options' => [
'John',
'Doe'
]
]
];
public function addSettingValidatorRules($validator)
{
return $validator
->requirePresence('url')
->notEmpty('url', __('An URL must be provided'))
->requirePresence('authkey')
->notEmpty('authkey', __('An Authkey must be provided'))
->lengthBetween('authkey', [40, 40], __('The authkey must be 40 character long'))
->boolean('skip_ssl');
}
public function addExposedFunction(string $functionName): void
{
$this->exposedFunctions[] = $functionName;

View File

@ -4,6 +4,7 @@ namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\ORM\RulesChecker;
use Cake\Validation\Validator;
use Migrations\Migrations;
use Cake\Filesystem\Folder;
@ -33,7 +34,14 @@ class LocalToolsTable extends AppTable
public function validationDefault(Validator $validator): Validator
{
return $validator;
return $validator->add('settings', 'validSettings', [
'rule' => 'isValidSettings',
'provider' => 'table',
'message' => __('Invalid settings'),
'on' => function ($context) {
return !empty($context['data']['settings']);
}
]);
}
public function loadConnector(string $connectorName): void
@ -133,7 +141,7 @@ class LocalToolsTable extends AppTable
'connector' => $connector_type,
'connector_version' => $connector_class->version,
'connector_description' => $connector_class->description,
'connector_parameters' => $connector_class->parameters ?? []
'connector_settings' => $connector_class->settings ?? []
];
if ($includeConnections) {
$connector['connections'] = $this->healthCheck($connector_type, $connector_class);
@ -298,4 +306,32 @@ class LocalToolsTable extends AppTable
}
return $connection;
}
public function isValidSettings($settings, array $context)
{
$settings = json_decode($settings, true);
$validationErrors = $this->getLocalToolsSettingValidationErrors($context['data']['id'], $settings);
return $this->getValidationMessage($validationErrors);
}
public function getValidationMessage($validationErrors)
{
$messages = [];
foreach ($validationErrors as $key => $errors) {
$messages[] = sprintf('%s: %s', $key, implode(', ', $errors));
}
return empty($messages) ? true : implode('; ', $messages);
}
public function getLocalToolsSettingValidationErrors($connectionId, array $settings): array
{
$connector = array_values($this->getConnectorByConnectionId($connectionId))[0];
$errors = [];
if (method_exists($connector, 'addSettingValidatorRules')) {
$validator = new Validator();
$validator = $connector->addSettingValidatorRules($validator);
$errors = $validator->validate($settings);
}
return $errors;
}
}

View File

@ -21,7 +21,7 @@
'type' => 'codemirror',
'codemirror' => [
'height' => '10rem',
'hints' => $connectors[0]['connector_parameters']
'hints' => $connectors[0]['connector_settings']
]
],
[

View File

@ -1,9 +1,4 @@
<?php
// $randomVal = Cake\Utility\Security::randomString(8);
// $params['type'] = 'textarea';
// $textareaClass = "area-for-codemirror-{$randomVal}";
// $params['class'] = [$textareaClass];
// echo $this->FormFieldMassage->prepareFormElement($this->Form, $params, $fieldData);
echo $this->element('genericElements/codemirror', [
'data' => $fieldData,
'params' => $params,

View File

@ -68,9 +68,23 @@
}
cm.save()
})
registerObserver(cm, $textArea[0])
synchronizeClasses(cm, $textArea[0])
postInit()
}
// Used to synchronize textarea classes (such as `is-invalid` for content validation)
function registerObserver(cm, textarea) {
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.attributeName == 'class') {
synchronizeClasses(cm, textarea)
}
});
});
observer.observe(textarea, {attributes: true})
}
function postInit() {
if ($(cm.getInputField()).closest('.modal').length) { // editor is in modal
setTimeout(() => {
@ -79,6 +93,14 @@
}
}
function synchronizeClasses(cm, textarea) {
const classes = Array.from(textarea.classList).filter(c => !c.startsWith('area-for-codemirror'))
const $wrapper = $(cm.getWrapperElement())
classes.forEach(c => {
$wrapper.toggleClass(c)
});
}
function jsonHints() {
const cur = cm.getCursor()
const token = cm.getTokenAt(cur)
@ -130,7 +152,7 @@
if (str.length > 0) {
let validHints = []
let hint
for (let i = 0; hints.length < cmOptions.hintOptions.maxHints && i < validHints.length; i++) {
for (let i = 0; validHints.length < cmOptions.hintOptions.maxHints && i < hints.length; i++) {
if (hints[i].text.startsWith(str)) {
validHints.push(hints[i])
}

View File

@ -6,3 +6,24 @@
.CodeMirror-gutters {
border-radius: 0.25rem 0 0 0.25rem;
}
.CodeMirror.is-invalid {
border-color: #dc3545;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right calc(0.375em + 0.1875rem) center;
background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);
}
.CodeMirror.is-invalid .CodeMirror-gutters {
/* background-color: #dc3545 !important; */
}
.CodeMirror.is-invalid .CodeMirror-linenumber {
/* color: white !important; */
}
.CodeMirror-hints {
z-index: 1060 !important; /* Make sure hint is above modal */
}