new: [user-settings] Added user settings feature

pull/72/head
Sami Mokaddem 2021-10-08 10:27:40 +02:00
parent 7ab8a93fbd
commit 39fdb8ec0d
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
16 changed files with 418 additions and 2 deletions

View File

@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
use Migrations\AbstractMigration;
use Phinx\Db\Adapter\MysqlAdapter;
class UserSettings extends AbstractMigration
{
public $autoId = false; // turn off automatic `id` column create. We want it to be `int(10) unsigned`
public function change()
{
$table = $this->table('user_settings', [
'signed' => false,
'collation' => 'utf8mb4_unicode_ci',
]);
$table
->addColumn('id', 'integer', [
'autoIncrement' => true,
'limit' => 10,
'signed' => false,
])
->addPrimaryKey('id')
->addColumn('name', 'string', [
'default' => null,
'null' => false,
'limit' => 255,
'comment' => 'The name of the user setting',
])
->addColumn('value', 'text', [
'default' => null,
'null' => true,
'limit' => MysqlAdapter::TEXT_LONG,
'comment' => 'The value of the user setting',
])
->addColumn('user_id', 'integer', [
'default' => null,
'null' => true,
'signed' => false,
'length' => 10,
])
->addColumn('created', 'datetime', [
'default' => null,
'null' => false,
])
->addColumn('modified', 'datetime', [
'default' => null,
'null' => false,
]);
$table->addForeignKey('user_id', 'users', 'id', ['delete'=> 'CASCADE', 'update'=> 'CASCADE']);
$table->addIndex('name')
->addIndex('user_id')
->addIndex('created')
->addIndex('modified');
$table->create();
}
}

View File

@ -196,6 +196,7 @@ class CRUDComponent extends Component
}
}
}
$this->Controller->entity = $data;
$this->Controller->set('entity', $data);
}
@ -317,6 +318,7 @@ class CRUDComponent extends Component
}
}
}
$this->Controller->entity = $data;
$this->Controller->set('entity', $data);
}

View File

@ -0,0 +1,50 @@
<?php
namespace BreadcrumbNavigation;
require_once(APP . 'Controller' . DS . 'Component' . DS . 'Navigation' . DS . 'base.php');
class UserSettingsNavigation extends BaseNavigation
{
public function addLinks()
{
$bcf = $this->bcf;
$request = $this->request;
$this->bcf->addLink('UserSettings', 'index', 'Users', 'view', function ($config) use ($bcf, $request) {
if (!empty($request->getQuery('Users_id'))) {
$user_id = h($request->getQuery('Users_id'));
$linkData = [
'label' => __('View user ({0})', h($user_id)),
'url' => sprintf('/users/view/%s', h($user_id))
];
return $linkData;
}
return null;
});
$this->bcf->addLink('UserSettings', 'index', 'Users', 'edit', function ($config) use ($bcf, $request) {
if (!empty($request->getQuery('Users_id'))) {
$user_id = h($request->getQuery('Users_id'));
$linkData = [
'label' => __('Edit user ({0})', h($user_id)),
'url' => sprintf('/users/edit/%s', h($user_id))
];
return $linkData;
}
return null;
});
if (!empty($request->getQuery('Users_id'))) {
$this->bcf->addSelfLink('UserSettings', 'index');
}
if ($this->request->getParam('controller') == 'UserSettings' && $this->request->getParam('action') == 'index') {
if (!empty($this->request->getQuery('Users_id'))) {
$user_id = $this->request->getQuery('Users_id');
$this->bcf->addParent('UserSettings', 'index', 'Users', 'view', [
'textGetter' => [
'path' => 'username',
'varname' => 'settingsForUser',
],
'url' => "/users/view/{$user_id}"
]);
}
}
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace BreadcrumbNavigation;
require_once(APP . 'Controller' . DS . 'Component' . DS . 'Navigation' . DS . 'base.php');
class UsersNavigation extends BaseNavigation
{
public function addLinks()
{
$bcf = $this->bcf;
$request = $this->request;
$this->bcf->addLink('Users', 'view', 'UserSettings', 'index', function ($config) use ($bcf, $request) {
if (!empty($this->passedData[0])) {
$user_id = $this->passedData[0];
$linkData = [
'label' => __('User Setting ({0})', h($user_id)),
'url' => sprintf('/user-settings/index?Users.id=%s', h($user_id))
];
return $linkData;
}
return [];
});
$this->bcf->addLink('Users', 'edit', 'UserSettings', 'index', function ($config) use ($bcf, $request) {
if (!empty($this->passedData[0])) {
$user_id = $this->passedData[0];
$linkData = [
'label' => __('User Setting ({0})', h($user_id)),
'url' => sprintf('/user-settings/index?Users.id=%s', h($user_id))
];
return $linkData;
}
return [];
});
}
}

View File

@ -27,6 +27,7 @@ class NavigationComponent extends Component
'Broods' => 'network-wired',
'Roles' => 'id-badge',
'Users' => 'users',
'UserSettings' => 'user-cog',
'Inbox' => 'inbox',
'Outbox' => 'inbox',
'MetaTemplates' => 'object-group',
@ -128,6 +129,7 @@ class NavigationComponent extends Component
'Users',
'Tags',
'LocalTools',
'UserSettings',
];
foreach ($CRUDControllers as $controller) {
$bcf->setDefaultCRUDForModel($controller);

View File

@ -0,0 +1,93 @@
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Utility\Hash;
use Cake\Utility\Text;
use \Cake\Database\Expression\QueryExpression;
use Cake\Http\Exception\NotFoundException;
use Cake\Http\Exception\MethodNotAllowedException;
use Cake\Http\Exception\ForbiddenException;
class UserSettingsController extends AppController
{
public $quickFilterFields = [['name' => true], ['value' => true]];
public $filterFields = ['name', 'value', 'Users.id'];
public $containFields = ['Users'];
public function index()
{
$conditions = [];
$this->CRUD->index([
'conditions' => [],
'contain' => $this->containFields,
'filters' => $this->filterFields,
'quickFilters' => $this->quickFilterFields,
]);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
if (!empty($this->request->getQuery('Users_id'))) {
$settingsForUser = $this->UserSettings->Users->find()->where([
'id' => $this->request->getQuery('Users_id')
])->first();
$this->set('settingsForUser', $settingsForUser);
}
}
public function add($user_id = false)
{
$this->CRUD->add([
'redirect' => ['action' => 'index', $user_id],
'beforeSave' => function($data) use ($user_id) {
$data['user_id'] = $user_id;
return $data;
}
]);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$dropdownData = [
'user' => $this->UserSettings->Users->find('list', [
'sort' => ['username' => 'asc']
]),
];
$this->set(compact('dropdownData'));
$this->set('user_id', $user_id);
}
public function edit($id)
{
$entity = $this->UserSettings->find()->where([
'id' => $id
])->first();
$entity = $this->CRUD->edit($id, [
'redirect' => ['action' => 'index', $entity->user_id]
]);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$dropdownData = [
'user' => $this->UserSettings->Users->find('list', [
'sort' => ['username' => 'asc']
]),
];
$this->set(compact('dropdownData'));
$this->set('user_id', $this->entity->user_id);
$this->render('add');
}
public function delete($id)
{
$this->CRUD->delete($id);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
}
}

View File

@ -11,7 +11,7 @@ class UsersController extends AppController
{
public $filterFields = ['Individuals.uuid', 'username', 'Individuals.email', 'Individuals.first_name', 'Individuals.last_name'];
public $quickFilterFields = ['Individuals.uuid', ['username' => true], ['Individuals.first_name' => true], ['Individuals.last_name' => true], 'Individuals.email'];
public $containFields = ['Individuals', 'Roles'];
public $containFields = ['Individuals', 'Roles', 'UserSettings'];
public function index()
{

View File

@ -0,0 +1,9 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
class UserSetting extends AppModel
{
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class UserSettingsTable extends AppTable
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->addBehavior('Timestamp');
$this->belongsTo(
'Users'
);
$this->setDisplayField('name');
}
public function validationDefault(Validator $validator): Validator
{
$validator
->requirePresence(['name', 'user_id'], 'create')
->notEmptyString('name', __('Please fill this field'))
->notEmptyString('user_id', __('Please supply the user id to which this setting belongs to'));
return $validator;
}
}

View File

@ -34,6 +34,13 @@ class UsersTable extends AppTable
'cascadeCallbacks' => false
]
);
$this->hasMany(
'UserSettings',
[
'dependent' => true,
'cascadeCallbacks' => true
]
);
$this->setDisplayField('username');
}

View File

@ -0,0 +1,37 @@
<?php
echo $this->element('genericElements/Form/genericForm', [
'data' => [
'description' => __('User settings are used to register setting tied to user profile.'),
'model' => 'UserSettings',
'fields' => [
[
'field' => 'user_id',
'type' => 'dropdown',
'label' => __('User'),
'options' => $dropdownData['user'],
'value' => !empty($user_id) ? $user_id : '',
'disabled' => !empty($user_id),
],
[
'field' => 'name',
'label' => __('Setting Name'),
],
[
'field' => 'value',
'label' => __('Setting Value'),
'type' => 'codemirror',
'codemirror' => [
'height' => '10rem',
'mode' => [
'name' => 'text',
],
]
],
],
'submit' => [
'action' => $this->request->getParam('action')
]
]
]);
?>
</div>

View File

@ -0,0 +1,74 @@
<?php
$title = __('User Setting index');
if (!empty($settingsForUser)) {
$title .= __(' of {0}', $settingsForUser->username);
}
echo $this->element('genericElements/IndexTable/index_table', [
'data' => [
'data' => $data,
'top_bar' => [
'children' => [
[
'type' => 'simple',
'children' => [
'data' => [
'type' => 'simple',
'text' => __('Add Setting'),
'class' => 'btn btn-primary',
'popover_url' => sprintf('/userSettings/add/%s', h($this->request->getQuery('Users_id')))
]
]
],
[
'type' => 'search',
'button' => __('Filter'),
'placeholder' => __('Enter value to search'),
'data' => '',
'searchKey' => 'value'
]
]
],
'fields' => [
[
'name' => '#',
'sort' => 'id',
'data_path' => 'id',
],
[
'name' => __('User'),
'sort' => 'user.username',
'data_path' => 'user.username',
'url' => '/users/view/{{0}}',
'url_vars' => ['user.id']
],
[
'name' => __('Setting Name'),
'sort' => 'name',
'data_path' => 'name',
],
[
'name' => __('Setting Value'),
'sort' => 'value',
'data_path' => 'value',
'class' => 'font-monospace'
],
],
'title' => $title,
'description' => __('The list of user settings in this Cerebrate instance. All users can have setting tied to their user profile.'),
'actions' => [
[
'open_modal' => '/userSettings/edit/[onclick_params_data_path]',
'modal_params_data_path' => 'id',
'icon' => 'edit'
],
[
'open_modal' => '/userSettings/delete/[onclick_params_data_path]',
'modal_params_data_path' => 'id',
'icon' => 'trash'
],
]
]
]);
echo '</div>';
?>

View File

@ -75,6 +75,13 @@ echo $this->element('genericElements/IndexTable/index_table', [
'url' => '/roles/view/{{0}}',
'url_vars' => ['role.id']
],
[
'name' => __('# User Settings'),
'element' => 'count_summary',
'data_path' => 'user_settings',
'url' => '/user-settings/index?Users.id={{url_data}}',
'url_data_path' => 'id'
],
],
'title' => __('User index'),
'description' => __('The list of enrolled users in this Cerebrate instance. All of the users have or at one point had access to the system.'),

View File

@ -51,6 +51,11 @@ echo $this->element(
'url' => '/EncryptionKeys/index?Users.id={{0}}',
'url_params' => ['id'],
'title' => __('Encryption keys')
],
[
'url' => '/UserSettings/index?Users.id={{0}}',
'url_params' => ['id'],
'title' => __('User settings')
]
]
]

View File

@ -2,6 +2,9 @@
$controlParams = [
'options' => $fieldData['options'],
'empty' => $fieldData['empty'] ?? false,
'value' => $fieldData['value'] ?? [],
'multiple' => $fieldData['multiple'] ?? false,
'disabled' => $fieldData['disabled'] ?? false,
'class' => ($fieldData['class'] ?? '') . ' formDropdown form-select'
];
echo $this->FormFieldMassage->prepareFormElement($this->Form, $controlParams, $fieldData);

View File

@ -19,7 +19,7 @@ use Cake\Routing\Router;
<i class="me-1 <?= $this->FontAwesome->getClass('user-circle') ?>"></i>
<?= __('My Account') ?>
</a>
<a class="dropdown-item" href="<?= Router::url(['controller' => 'users', 'action' => 'userSettings', 'plugin' => null]) ?>">
<a class="dropdown-item" href="<?= Router::url(['controller' => 'user-settings', 'action' => 'index', 'plugin' => null, '?' => ['Users.id' => h($this->request->getAttribute('identity')['id'])]]) ?>">
<i class="me-1 <?= $this->FontAwesome->getClass('user-cog') ?>"></i>
<?= __('Settings') ?>
</a>