Merge branch 'develop' into add-integration-tests

pull/80/head
Luciano Righetti 2022-01-18 14:29:54 +01:00
commit f48c1a5a17
29 changed files with 410 additions and 95 deletions

Binary file not shown.

Binary file not shown.

View File

@ -10,22 +10,22 @@ class TagSystem extends AbstractMigration
$tags = $this->table('tags_tags');
$tags->addColumn('namespace', 'string', [
'default' => null,
'limit' => 255,
'limit' => 191,
'null' => true,
])
->addColumn('predicate', 'string', [
'default' => null,
'limit' => 255,
'limit' => 191,
'null' => true,
])
->addColumn('value', 'string', [
'default' => null,
'limit' => 255,
'limit' => 191,
'null' => true,
])
->addColumn('name', 'string', [
'default' => null,
'limit' => 255,
'limit' => 191,
'null' => false,
])
->addColumn('colour', 'string', [
@ -66,7 +66,7 @@ class TagSystem extends AbstractMigration
])
->addColumn('fk_model', 'string', [
'default' => null,
'limit' => 255,
'limit' => 191,
'null' => false,
'comment' => 'The model name of the entity being tagged'
])
@ -86,4 +86,4 @@ class TagSystem extends AbstractMigration
$tagged->addIndex(['tag_id', 'fk_id', 'fk_model'], ['unique' => true])
->update();
}
}
}

View File

@ -46,10 +46,10 @@ class TagBehavior extends Behavior
$config = $this->getConfig();
$tagsAssoc = $config['tagsAssoc'];
$taggedAssoc = $config['taggedAssoc'];
$table = $this->_table;
$tableAlias = $this->_table->getAlias();
$assocConditions = ['Tagged.fk_model' => $tableAlias];
if (!$table->hasAssociation('Tagged')) {
@ -114,7 +114,6 @@ class TagBehavior extends Behavior
$property = $this->getConfig('tagsAssoc.propertyName');
$options['accessibleFields'][$property] = true;
$options['associated']['Tags']['accessibleFields']['id'] = true;
if (isset($data['tags'])) {
if (!empty($data['tags'])) {
$data[$property] = $this->normalizeTags($data['tags']);
@ -131,7 +130,6 @@ class TagBehavior extends Behavior
if (!$tag->isNew()) {
continue;
}
$existingTag = $this->getExistingTag($tag->name);
if (!$existingTag) {
continue;
@ -176,15 +174,14 @@ class TagBehavior extends Behavior
$result[] = array_merge($common, ['id' => $existingTag->id]);
continue;
}
$result[] = array_merge(
$common,
[
'name' => $tagIdentifier,
'colour' => '#924da6'
]
);
}
return $result;
}
@ -312,7 +309,7 @@ class TagBehavior extends Behavior
$key = 'Tags.' . $finderField;
$taggedAlias = 'Tagged';
$foreignKey = $this->getConfig('tagsAssoc.foreignKey');
if (!empty($filterValue['AND'])) {
$subQuery = $this->buildQuerySnippet($filterValue['AND'], $finderField, $OperatorAND);
$modelAlias = $this->_table->getAlias();
@ -352,4 +349,4 @@ class TagBehavior extends Behavior
return $query;
}
}
}

View File

@ -54,7 +54,6 @@ class AppController extends Controller
public function initialize(): void
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
$this->loadComponent('RestResponse');

View File

@ -64,8 +64,30 @@ class AuthKeysController extends AppController
public function add()
{
$this->set('metaGroup', $this->isAdmin ? 'Administration' : 'Cerebrate');
$validUsers = [];
$userConditions = [];
$currentUser = $this->ACL->getUser();
if (empty($currentUser['role']['perm_admin'])) {
if (empty($currentUser['role']['perm_org_admin'])) {
$userConditions['id'] = $currentUser['id'];
} else {
$role_ids = $this->Users->Roles->find()->where(['perm_admin' => 0])->all()->extract('id')->toList();
$userConditions['role_id IN'] = $role_ids;
}
}
$users = $this->Users->find('list');
if (!empty($userConditions)) {
$users->where($userConditions);
}
$users = $users->order(['username' => 'asc'])->all()->toList();
$this->CRUD->add([
'displayOnSuccess' => 'authkey_display'
'displayOnSuccess' => 'authkey_display',
'beforeSave' => function($data) use ($users) {
if (!in_array($data['user_id'], array_keys($users))) {
return false;
}
return $data;
}
]);
$responsePayload = $this->CRUD->getResponsePayload([
'displayOnSuccess' => 'authkey_display'
@ -75,9 +97,7 @@ class AuthKeysController extends AppController
}
$this->loadModel('Users');
$dropdownData = [
'user' => $this->Users->find('list', [
'sort' => ['username' => 'asc']
])
'user' => $users
];
$this->set(compact('dropdownData'));
}

View File

@ -68,6 +68,7 @@ class ACLComponent extends Component
'view' => ['perm_admin']
],
'EncryptionKeys' => [
'view' => ['*'],
'add' => ['*'],
'edit' => ['*'],
'delete' => ['*'],
@ -109,7 +110,7 @@ class ACLComponent extends Component
'batchAction' => ['perm_admin'],
'broodTools' => ['perm_admin'],
'connectionRequest' => ['perm_admin'],
'connectLocal' => ['perm_admin'],
// 'connectLocal' => ['perm_admin'],
'delete' => ['perm_admin'],
'edit' => ['perm_admin'],
'exposedTools' => ['OR' => ['perm_admin', 'perm_sync']],

View File

@ -175,6 +175,9 @@ class CRUDComponent extends Component
$data = $this->Table->patchEntity($data, $input, $patchEntityParams);
if (isset($params['beforeSave'])) {
$data = $params['beforeSave']($data);
if ($data === false) {
throw new NotFoundException(__('Could not save {0} due to the input failing to meet expectations. Your input is bad and you should feel bad.', $this->ObjectAlias));
}
}
$savedData = $this->Table->save($data);
if ($savedData !== false) {

View File

@ -39,7 +39,15 @@ class EncryptionKeysController extends AppController
public function delete($id)
{
$this->CRUD->delete($id);
$orgConditions = [];
$individualConditions = [];
$dropdownData = [];
$currentUser = $this->ACL->getUser();
$params = [];
if (empty($currentUser['role']['perm_admin'])) {
$params = $this->buildBeforeSave($params, $currentUser, $orgConditions, $individualConditions, $dropdownData);
}
$this->CRUD->delete($id, $params);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
@ -47,49 +55,38 @@ class EncryptionKeysController extends AppController
$this->set('metaGroup', 'ContactDB');
}
public function add()
private function buildBeforeSave(array $params, $currentUser, array &$orgConditions, array &$individualConditions, array &$dropdownData): array
{
$orgConditions = [];
$individualConditions = [];
$currentUser = $this->ACL->getUser();
$params = ['redirect' => $this->referer()];
if (empty($currentUser['role']['perm_admin'])) {
$orgConditions = [
'id' => $currentUser['organisation_id']
$orgConditions = [
'id' => $currentUser['organisation_id']
];
if (empty($currentUser['role']['perm_org_admin'])) {
$individualConditions = [
'id' => $currentUser['individual_id']
];
if (empty($currentUser['role']['perm_org_admin'])) {
$individualConditions = [
'id' => $currentUser['individual_id']
];
}
$params['beforeSave'] = function($entity) use($currentUser) {
if ($entity['owner_model'] === 'organisation') {
$entity['owner_id'] = $currentUser['organisation_id'];
}
$params['beforeSave'] = function($entity) use($currentUser) {
if ($entity['owner_model'] === 'organisation') {
$entity['owner_id'] = $currentUser['organisation_id'];
} else {
if ($currentUser['role']['perm_org_admin']) {
$this->loadModel('Alignments');
$validIndividuals = $this->Alignments->find('list', [
'keyField' => 'individual_id',
'valueField' => 'id',
'conditions' => ['organisation_id' => $currentUser['organisation_id']]
])->toArray();
if (!isset($validIndividuals[$entity['owner_id']])) {
throw new MethodNotAllowedException(__('Selected individual cannot be linked by the current user.'));
}
} else {
if ($currentUser['role']['perm_org_admin']) {
$this->loadModel('Alignments');
$validIndividuals = $this->Alignments->find('list', [
'keyField' => 'individual_id',
'valueField' => 'id',
'conditions' => ['organisation_id' => $currentUser['organisation_id']]
])->toArray();
if (!isset($validIndividuals[$entity['owner_id']])) {
throw new MethodNotAllowedException(__('Selected individual cannot be linked by the current user.'));
}
} else {
if ($entity['owner_id'] !== $currentUser['id']) {
throw new MethodNotAllowedException(__('Selected individual cannot be linked by the current user.'));
}
if ($entity['owner_id'] !== $currentUser['id']) {
throw new MethodNotAllowedException(__('Selected individual cannot be linked by the current user.'));
}
}
return $entity;
};
}
$this->CRUD->add($params);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
}
return $entity;
};
$this->loadModel('Organisations');
$this->loadModel('Individuals');
$dropdownData = [
@ -102,13 +99,35 @@ class EncryptionKeysController extends AppController
'conditions' => $individualConditions
])
];
return $params;
}
public function add()
{
$orgConditions = [];
$individualConditions = [];
$dropdownData = [];
$currentUser = $this->ACL->getUser();
$params = [
'redirect' => $this->referer()
];
if (empty($currentUser['role']['perm_admin'])) {
$params = $this->buildBeforeSave($params, $currentUser, $orgConditions, $individualConditions, $dropdownData);
}
$this->CRUD->add($params);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$this->set(compact('dropdownData'));
$this->set('metaGroup', 'ContactDB');
}
public function edit($id = false)
{
$conditions = [];
$orgConditions = [];
$individualConditions = [];
$dropdownData = [];
$currentUser = $this->ACL->getUser();
$params = [
'fields' => [
@ -117,9 +136,7 @@ class EncryptionKeysController extends AppController
'redirect' => $this->referer()
];
if (empty($currentUser['role']['perm_admin'])) {
if (empty($currentUser['role']['perm_org_admin'])) {
}
$params = $this->buildBeforeSave($params, $currentUser, $orgConditions, $individualConditions, $dropdownData);
}
$this->CRUD->edit($id, $params);
$responsePayload = $this->CRUD->getResponsePayload();
@ -130,4 +147,16 @@ class EncryptionKeysController extends AppController
$this->set('metaGroup', 'ContactDB');
$this->render('add');
}
public function view($id = false)
{
$this->CRUD->view($id, [
'contain' => ['Individuals', 'Organisations']
]);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$this->set('metaGroup', 'ContactDB');
}
}

View File

@ -70,6 +70,12 @@ class InstanceController extends AppController
usort($status, function($a, $b) {
return strcmp($b['id'], $a['id']);
});
if ($this->ParamHandler->isRest()) {
return $this->RestResponse->viewData([
'status' => $status,
'updateAvailables' => $migrationStatus['updateAvailables'],
], 'json');
}
$this->set('status', $status);
$this->set('updateAvailables', $migrationStatus['updateAvailables']);
}
@ -140,6 +146,14 @@ class InstanceController extends AppController
{
$this->Settings = $this->getTableLocator()->get('Settings');
$all = $this->Settings->getSettings(true);
if ($this->ParamHandler->isRest()) {
return $this->RestResponse->viewData([
'settingsProvider' => $all['settingsProvider'],
'settings' => $all['settings'],
'settingsFlattened' => $all['settingsFlattened'],
'notices' => $all['notices'],
], 'json');
}
$this->set('settingsProvider', $all['settingsProvider']);
$this->set('settings', $all['settings']);
$this->set('settingsFlattened', $all['settingsFlattened']);

View File

@ -340,6 +340,7 @@ class LocalToolsController extends AppController
}
}
/*
public function connectLocal($local_tool_id)
{
$params = [
@ -355,10 +356,8 @@ class LocalToolsController extends AppController
$params['target_tool_id'] = $postParams['target_tool_id'];
$result = $this->LocalTools->encodeLocalConnection($params);
// Send message to remote inbox
debug($result);
} else {
$target_tools = $this->LocalTools->findConnectable($local_tool);
debug($target_tools);
if (empty($target_tools)) {
throw new NotFoundException(__('No tools found to connect.'));
}
@ -369,4 +368,5 @@ class LocalToolsController extends AppController
]);
}
}
*/
}

View File

@ -9,6 +9,8 @@ use \Cake\Database\Expression\QueryExpression;
use Cake\Http\Exception\NotFoundException;
use Cake\Http\Exception\MethodNotAllowedException;
use Cake\Http\Exception\ForbiddenException;
use Cake\Http\Exception\UnauthorizedException;
class UserSettingsController extends AppController
{
@ -19,8 +21,12 @@ class UserSettingsController extends AppController
public function index()
{
$conditions = [];
$currentUser = $this->ACL->getUser();
if (empty($currentUser['role']['perm_admin'])) {
$conditions['user_id'] = $currentUser->id;
}
$this->CRUD->index([
'conditions' => [],
'conditions' => $conditions,
'contain' => $this->containFields,
'filters' => $this->filterFields,
'quickFilters' => $this->quickFilterFields,
@ -39,6 +45,9 @@ class UserSettingsController extends AppController
public function view($id)
{
if (!$this->isLoggedUserAllowedToEdit($id)) {
throw new NotFoundException(__('Invalid {0}.', 'user setting'));
}
$this->CRUD->view($id, [
'contain' => ['Users']
]);
@ -50,10 +59,13 @@ class UserSettingsController extends AppController
public function add($user_id = false)
{
$currentUser = $this->ACL->getUser();
$this->CRUD->add([
'redirect' => ['action' => 'index', $user_id],
'beforeSave' => function($data) use ($user_id) {
$data['user_id'] = $user_id;
'beforeSave' => function ($data) use ($currentUser) {
if (empty($currentUser['role']['perm_admin'])) {
$data['user_id'] = $currentUser->id;
}
return $data;
}
]);
@ -61,10 +73,13 @@ class UserSettingsController extends AppController
if (!empty($responsePayload)) {
return $responsePayload;
}
$allUsers = $this->UserSettings->Users->find('list', ['keyField' => 'id', 'valueField' => 'username'])->order(['username' => 'ASC']);
if (empty($currentUser['role']['perm_admin'])) {
$allUsers->where(['id' => $currentUser->id]);
$user_id = $currentUser->id;
}
$dropdownData = [
'user' => $this->UserSettings->Users->find('list', [
'sort' => ['username' => 'asc']
]),
'user' => $allUsers->all()->toArray(),
];
$this->set(compact('dropdownData'));
$this->set('user_id', $user_id);
@ -75,6 +90,11 @@ class UserSettingsController extends AppController
$entity = $this->UserSettings->find()->where([
'id' => $id
])->first();
if (!$this->isLoggedUserAllowedToEdit($entity)) {
throw new NotFoundException(__('Invalid {0}.', 'user setting'));
}
$entity = $this->CRUD->edit($id, [
'redirect' => ['action' => 'index', $entity->user_id]
]);
@ -94,6 +114,9 @@ class UserSettingsController extends AppController
public function delete($id)
{
if (!$this->isLoggedUserAllowedToEdit($id)) {
throw new NotFoundException(__('Invalid {0}.', 'user setting'));
}
$this->CRUD->delete($id);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
@ -160,7 +183,7 @@ class UserSettingsController extends AppController
}
}
public function getBookmarks($forSidebar=false)
public function getBookmarks($forSidebar = false)
{
$bookmarks = $this->UserSettings->getSettingByName($this->ACL->getUser(), $this->UserSettings->BOOKMARK_SETTING_NAME);
$bookmarks = json_decode($bookmarks['value'], true);
@ -200,4 +223,29 @@ class UserSettingsController extends AppController
$this->set('user_id', $this->ACL->getUser()->id);
}
}
/**
* isLoggedUserAllowedToEdit
*
* @param int|\App\Model\Entity\UserSetting $setting
* @return boolean
*/
private function isLoggedUserAllowedToEdit($setting): bool
{
$currentUser = $this->ACL->getUser();
$isAllowed = false;
if (!empty($currentUser['role']['perm_admin'])) {
$isAllowed = true;
} else {
if (is_numeric($setting)) {
$setting = $this->UserSettings->find()->where([
'id' => $setting
])->first();
if (empty($setting)) {
return false;
}
}
$isAllowed = $setting->user_id == $currentUser->id;
}
return $isAllowed;
}
}

View File

@ -97,8 +97,16 @@ class UsersController extends AppController
public function edit($id = false)
{
$currentUser = $this->ACL->getUser();
if (empty($id) || (empty($currentUser['role']['perm_org_admin']) && empty($currentUser['role']['perm_site_admin']))) {
if (empty($id)) {
$id = $currentUser['id'];
} else {
if ((empty($currentUser['role']['perm_org_admin']) && empty($currentUser['role']['perm_admin']))) {
if ($id !== $currentUser['id']) {
throw new MethodNotAllowedException(__('You are not authorised to edit that user.'));
} else {
$id = $currentUser['id'];
}
}
}
$params = [
@ -111,12 +119,15 @@ class UsersController extends AppController
'password'
],
'fields' => [
'id', 'individual_id', 'username', 'disabled', 'password', 'confirm_password'
'password', 'confirm_password'
]
];
if (!empty($this->ACL->getUser()['role']['perm_admin'])) {
$params['fields'][] = 'individual_id';
$params['fields'][] = 'username';
$params['fields'][] = 'role_id';
$params['fields'][] = 'organisation_id';
$params['fields'][] = 'disabled';
}
$this->CRUD->edit($id, $params);
$responsePayload = $this->CRUD->getResponsePayload();

View File

@ -2,6 +2,8 @@
namespace CommonConnectorTools;
use Cake\ORM\Locator\LocatorAwareTrait;
use Cake\Log\Log;
use Cake\Log\Engine\FileLog;
class CommonConnectorTools
{
@ -20,6 +22,35 @@ class CommonConnectorTools
const STATE_CANCELLED = 'Request cancelled';
const STATE_DECLINED = 'Request declined by remote';
public function __construct()
{
Log::setConfig("LocalToolDebug", [
'className' => FileLog::class,
'path' => LOGS,
'file' => "{$this->connectorName}-debug",
'scopes' => [$this->connectorName],
'levels' => ['notice', 'info', 'debug'],
]);
Log::setConfig("LocalToolError", [
'className' => FileLog::class,
'path' => LOGS,
'file' => "{$this->connectorName}-error",
'scopes' => [$this->connectorName],
'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'],
]);
}
protected function logDebug($message)
{
Log::debug($message, [$this->connectorName]);
}
protected function logError($message, $scope=[])
{
Log::error($message, [$this->connectorName]);
}
public function addExposedFunction(string $functionName): void
{
$this->exposedFunctions[] = $functionName;

View File

@ -122,6 +122,11 @@ class MispConnector extends CommonConnectorTools
'type' => 'boolean'
],
];
public $settingsPlaceholder = [
'url' => 'https://your.misp.intance',
'authkey' => '',
'skip_ssl' => '0',
];
public function addSettingValidatorRules($validator)
{
@ -183,6 +188,7 @@ class MispConnector extends CommonConnectorTools
$settings = json_decode($connection->settings, true);
$http = $this->genHTTPClient($connection, $options);
$url = sprintf('%s%s', $settings['url'], $relativeURL);
$this->logDebug(sprintf('%s %s %s', __('Posting data') . PHP_EOL, "POST {$url}" . PHP_EOL, json_encode($data)));
return $http->post($url, $data, $options);
}
@ -234,14 +240,18 @@ class MispConnector extends CommonConnectorTools
if (!empty($params['softError'])) {
return $response;
}
throw new NotFoundException(__('Could not retrieve the requested resource.'));
$errorMsg = __('Could not post to the requested resource for `{0}`. Remote returned:', $url) . PHP_EOL . $response->getStringBody();
$this->logError($errorMsg);
throw new NotFoundException($errorMsg);
}
}
private function postData(string $url, array $params): Response
{
if (empty($params['connection'])) {
throw new NotFoundException(__('No connection object received.'));
$errorMsg = __('No connection object received.');
$this->logError($errorMsg);
throw new NotFoundException($errorMsg);
}
$url = $this->urlAppendParams($url, $params);
if (!is_string($params['body'])) {
@ -251,7 +261,9 @@ class MispConnector extends CommonConnectorTools
if ($response->isOk()) {
return $response;
} else {
throw new NotFoundException(__('Could not post to the requested resource. Remote returned:') . PHP_EOL . $response->getStringBody());
$errorMsg = __('Could not post to the requested resource for `{0}`. Remote returned:', $url) . PHP_EOL . $response->getStringBody();
$this->logError($errorMsg);
throw new NotFoundException($errorMsg);
}
}

View File

@ -46,6 +46,22 @@ class SkeletonConnector extends CommonConnectorTools
'redirect' => 'serverSettingsAction'
]
];
public $settings = [
'url' => [
'type' => 'text'
],
'authkey' => [
'type' => 'text'
],
'skip_ssl' => [
'type' => 'boolean'
],
];
public $settingsPlaceholder = [
'url' => 'https://your.url',
'authkey' => '',
'skip_ssl' => '0',
];
public function health(Object $connection): array
{

View File

@ -0,0 +1,11 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
use Cake\ORM\Entity;
class Outbox extends AppModel
{
}

View File

@ -143,7 +143,8 @@ class LocalToolsTable extends AppTable
'connector' => $connector_type,
'connector_version' => $connector_class->version,
'connector_description' => $connector_class->description,
'connector_settings' => $connector_class->settings ?? []
'connector_settings' => $connector_class->settings ?? [],
'connector_settings_placeholder' => $connector_class->settingsPlaceholder ?? [],
];
if ($includeConnections) {
$connector['connections'] = $this->healthCheck($connector_type, $connector_class);
@ -288,6 +289,7 @@ class LocalToolsTable extends AppTable
return $jsonReply;
}
/*
public function findConnectable($local_tool): array
{
$connectors = $this->getInterconnectors($local_tool['connector']);
@ -297,8 +299,8 @@ class LocalToolsTable extends AppTable
$validTargets[$connector['connects'][1]] = 1;
}
}
}
*/
public function fetchConnection($id): object
{

View File

@ -11,7 +11,7 @@ use App\Settings\SettingsProvider\UserSettingsProvider;
class UserSettingsTable extends AppTable
{
protected $BOOKMARK_SETTING_NAME = 'ui.bookmarks';
public $BOOKMARK_SETTING_NAME = 'ui.bookmarks';
public function initialize(array $config): void
{

View File

@ -637,13 +637,13 @@ class BoostrapTable extends BootstrapGeneric {
],
]);
foreach ($this->items as $i => $row) {
$body .= $this->genRow($row);
$body .= $this->genRow($row, $i);
}
$body .= $this->closeNode('tbody');
return $body;
}
private function genRow($row)
private function genRow($row, $rowIndex)
{
$html = $this->openNode('tr',[
'class' => [
@ -658,21 +658,21 @@ class BoostrapTable extends BootstrapGeneric {
$key = $field;
}
$cellValue = Hash::get($row, $key);
$html .= $this->genCell($cellValue, $field, $row, $i);
$html .= $this->genCell($cellValue, $field, $row, $rowIndex);
}
} else { // indexed array
foreach ($row as $cellValue) {
$html .= $this->genCell($cellValue, $field, $row, $i);
foreach ($row as $i => $cellValue) {
$html .= $this->genCell($cellValue, 'index', $row, $rowIndex);
}
}
$html .= $this->closeNode('tr');
return $html;
}
private function genCell($value, $field=[], $row=[], $i=0)
private function genCell($value, $field=[], $row=[], $rowIndex=0)
{
if (isset($field['formatter'])) {
$cellContent = $field['formatter']($value, $row, $i);
$cellContent = $field['formatter']($value, $row, $rowIndex);
} else if (isset($field['element'])) {
$cellContent = $this->btHelper->getView()->element($field['element'], [
'data' => [$value],

View File

@ -0,0 +1,32 @@
<?php
echo $this->element(
'/genericElements/SingleViews/single_view',
[
'data' => $entity,
'fields' => [
[
'key' => __('ID'),
'path' => 'id'
],
[
'key' => __('Type'),
'path' => 'type'
],
[
'key' => __('Owner'),
'path' => 'owner_id',
'owner_model_path' => 'owner_model',
'type' => 'owner'
],
[
'key' => __('Revoked'),
'path' => 'revoked'
],
[
'key' => __('Key'),
'path' => 'encryption_key'
]
]
]
);

View File

@ -23,7 +23,8 @@
),
array(
'field' => 'tag_list',
'type' => 'tags'
'type' => 'tags',
'requirements' => $this->request->getParam('action') === 'edit'
),
),
'metaTemplates' => empty($metaTemplates) ? [] : $metaTemplates,

View File

@ -22,7 +22,8 @@
'codemirror' => [
'height' => '10rem',
'hints' => $connectors[0]['connector_settings']
]
],
'placeholder' => json_encode($connectors[0]['connector_settings_placeholder'], JSON_FORCE_OBJECT | JSON_PRETTY_PRINT)
],
[
'field' => 'description',

View File

@ -89,12 +89,14 @@ echo $this->element('genericElements/IndexTable/index_table', [
'url_params_data_paths' => ['id'],
'icon' => 'eye'
],
/*
[
'open_modal' => '/localTools/connectLocal/[onclick_params_data_path]',
'modal_params_data_path' => 'id',
'reload_url' => sprintf('/localTools/connectorIndex/%s', h($connectorName)),
'icon' => 'plug'
],
*/
[
'open_modal' => '/localTools/edit/[onclick_params_data_path]',
'modal_params_data_path' => 'id',

View File

@ -7,17 +7,13 @@
array(
'field' => 'name'
),
array(
'field' => 'description',
'type' => 'textarea'
),
array(
'field' => 'uuid',
'label' => 'UUID',
'type' => 'uuid'
),
array(
'field' => 'URL'
'field' => 'url'
),
array(
'field' => 'nationality'

View File

@ -0,0 +1,6 @@
<?php
echo $this->element('/genericElements/IndexTable/Fields/owner', [
'field' => $field,
'row' => $data
]);
?>

View File

@ -52,6 +52,7 @@ $sidebarOpen = $loggedUser->user_settings_by_name_with_fallback['ui.sidebar.expa
<?= $this->Html->script('CodeMirror/addon/lint/json-lint') ?>
<?= $this->Html->script('CodeMirror/addon/edit/matchbrackets') ?>
<?= $this->Html->script('CodeMirror/addon/edit/closebrackets') ?>
<?= $this->Html->script('CodeMirror/addon/display/placeholder') ?>
<?= $this->Html->css('CodeMirror/codemirror') ?>
<?= $this->Html->css('CodeMirror/codemirror-additional') ?>
<?= $this->Html->css('CodeMirror/addon/hint/show-hint') ?>

View File

@ -26,4 +26,8 @@
.CodeMirror-hints {
z-index: 1060 !important; /* Make sure hint is above modal */
}
.CodeMirror pre.CodeMirror-placeholder {
color: #999;
}

View File

@ -0,0 +1,78 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function (CodeMirror) {
CodeMirror.defineOption("placeholder", "", function (cm, val, old) {
var prev = old && old != CodeMirror.Init;
if (val && !prev) {
cm.on("blur", onBlur);
cm.on("change", onChange);
cm.on("swapDoc", onChange);
CodeMirror.on(cm.getInputField(), "compositionupdate", cm.state.placeholderCompose = function () { onComposition(cm) })
onChange(cm);
} else if (!val && prev) {
cm.off("blur", onBlur);
cm.off("change", onChange);
cm.off("swapDoc", onChange);
CodeMirror.off(cm.getInputField(), "compositionupdate", cm.state.placeholderCompose)
clearPlaceholder(cm);
var wrapper = cm.getWrapperElement();
wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
}
if (val && !cm.hasFocus()) onBlur(cm);
});
function clearPlaceholder(cm) {
if (cm.state.placeholder) {
cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);
cm.state.placeholder = null;
}
}
function setPlaceholder(cm) {
clearPlaceholder(cm);
var elt = cm.state.placeholder = document.createElement("pre");
elt.style.cssText = "height: 0; overflow: visible";
elt.style.direction = cm.getOption("direction");
elt.className = "CodeMirror-placeholder CodeMirror-line-like";
var placeHolder = cm.getOption("placeholder")
if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder)
elt.appendChild(placeHolder)
cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
}
function onComposition(cm) {
setTimeout(function () {
var empty = false
if (cm.lineCount() == 1) {
var input = cm.getInputField()
empty = input.nodeName == "TEXTAREA" ? !cm.getLine(0).length
: !/[^\u200b]/.test(input.querySelector(".CodeMirror-line").textContent)
}
if (empty) setPlaceholder(cm)
else clearPlaceholder(cm)
}, 20)
}
function onBlur(cm) {
if (isEmpty(cm)) setPlaceholder(cm);
}
function onChange(cm) {
var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");
if (empty) setPlaceholder(cm);
else clearPlaceholder(cm);
}
function isEmpty(cm) {
return (cm.lineCount() === 1) && (cm.getLine(0) === "");
}
});