new: [userSettings] Added complete support of user settings
Including support of bookmarks, sidebar behavior and themingpull/72/head
parent
a2e3ad76dd
commit
78180fa90f
|
@ -132,7 +132,12 @@ class AppController extends Controller
|
||||||
$this->set('ajax', $this->request->is('ajax'));
|
$this->set('ajax', $this->request->is('ajax'));
|
||||||
$this->request->getParam('prefix');
|
$this->request->getParam('prefix');
|
||||||
$this->set('baseurl', Configure::read('App.fullBaseUrl'));
|
$this->set('baseurl', Configure::read('App.fullBaseUrl'));
|
||||||
$this->set('bsTheme', Configure::read('Cerebrate')['ui.bsTheme']);
|
|
||||||
|
if (!empty($user) && !empty($user->user_settings_by_name_with_fallback['ui.bsTheme']['value'])) {
|
||||||
|
$this->set('bsTheme', $user->user_settings_by_name_with_fallback['ui.bsTheme']['value']);
|
||||||
|
} else {
|
||||||
|
$this->set('bsTheme', Configure::read('Cerebrate')['ui.bsTheme']);
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->modelClass == 'Tags.Tags') {
|
if ($this->modelClass == 'Tags.Tags') {
|
||||||
$this->set('metaGroup', !empty($this->isAdmin) ? 'Administration' : 'Cerebrate');
|
$this->set('metaGroup', !empty($this->isAdmin) ? 'Administration' : 'Cerebrate');
|
||||||
|
|
|
@ -13,7 +13,7 @@ class UserSettingsNavigation extends BaseNavigation
|
||||||
if (!empty($request->getQuery('Users_id'))) {
|
if (!empty($request->getQuery('Users_id'))) {
|
||||||
$user_id = h($request->getQuery('Users_id'));
|
$user_id = h($request->getQuery('Users_id'));
|
||||||
$linkData = [
|
$linkData = [
|
||||||
'label' => __('View user ({0})', h($user_id)),
|
'label' => __('View user [{0}]', h($user_id)),
|
||||||
'url' => sprintf('/users/view/%s', h($user_id))
|
'url' => sprintf('/users/view/%s', h($user_id))
|
||||||
];
|
];
|
||||||
return $linkData;
|
return $linkData;
|
||||||
|
@ -24,7 +24,7 @@ class UserSettingsNavigation extends BaseNavigation
|
||||||
if (!empty($request->getQuery('Users_id'))) {
|
if (!empty($request->getQuery('Users_id'))) {
|
||||||
$user_id = h($request->getQuery('Users_id'));
|
$user_id = h($request->getQuery('Users_id'));
|
||||||
$linkData = [
|
$linkData = [
|
||||||
'label' => __('Edit user ({0})', h($user_id)),
|
'label' => __('Edit user [{0}]', h($user_id)),
|
||||||
'url' => sprintf('/users/edit/%s', h($user_id))
|
'url' => sprintf('/users/edit/%s', h($user_id))
|
||||||
];
|
];
|
||||||
return $linkData;
|
return $linkData;
|
||||||
|
|
|
@ -5,31 +5,83 @@ require_once(APP . 'Controller' . DS . 'Component' . DS . 'Navigation' . DS . 'b
|
||||||
|
|
||||||
class UsersNavigation extends BaseNavigation
|
class UsersNavigation extends BaseNavigation
|
||||||
{
|
{
|
||||||
|
public function addRoutes()
|
||||||
|
{
|
||||||
|
$this->bcf->addRoute('Users', 'settings', [
|
||||||
|
'label' => __('User settings'),
|
||||||
|
'url' => '/users/settings/',
|
||||||
|
'icon' => 'user-cog'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addParents()
|
||||||
|
{
|
||||||
|
// $this->bcf->addParent('Users', 'settings', 'Users', 'view');
|
||||||
|
}
|
||||||
|
|
||||||
public function addLinks()
|
public function addLinks()
|
||||||
{
|
{
|
||||||
$bcf = $this->bcf;
|
$bcf = $this->bcf;
|
||||||
$request = $this->request;
|
$request = $this->request;
|
||||||
$this->bcf->addLink('Users', 'view', 'UserSettings', 'index', function ($config) use ($bcf, $request) {
|
$passedData = $this->request->getParam('pass');
|
||||||
if (!empty($this->passedData[0])) {
|
$this->bcf->addLink('Users', 'view', 'UserSettings', 'index', function ($config) use ($bcf, $request, $passedData) {
|
||||||
$user_id = $this->passedData[0];
|
if (!empty($passedData[0])) {
|
||||||
|
$user_id = $passedData[0];
|
||||||
$linkData = [
|
$linkData = [
|
||||||
'label' => __('User Setting ({0})', h($user_id)),
|
'label' => __('Account settings', h($user_id)),
|
||||||
|
'url' => sprintf('/users/settings/%s', h($user_id))
|
||||||
|
];
|
||||||
|
return $linkData;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
$this->bcf->addLink('Users', 'view', 'UserSettings', 'index', function ($config) use ($bcf, $request, $passedData) {
|
||||||
|
if (!empty($passedData[0])) {
|
||||||
|
$user_id = $passedData[0];
|
||||||
|
$linkData = [
|
||||||
|
'label' => __('User Setting [{0}]', h($user_id)),
|
||||||
'url' => sprintf('/user-settings/index?Users.id=%s', h($user_id))
|
'url' => sprintf('/user-settings/index?Users.id=%s', h($user_id))
|
||||||
];
|
];
|
||||||
return $linkData;
|
return $linkData;
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
$this->bcf->addLink('Users', 'edit', 'UserSettings', 'index', function ($config) use ($bcf, $request) {
|
$this->bcf->addLink('Users', 'edit', 'UserSettings', 'index', function ($config) use ($bcf, $request, $passedData) {
|
||||||
if (!empty($this->passedData[0])) {
|
if (!empty($passedData[0])) {
|
||||||
$user_id = $this->passedData[0];
|
$user_id = $passedData[0];
|
||||||
$linkData = [
|
$linkData = [
|
||||||
'label' => __('User Setting ({0})', h($user_id)),
|
'label' => __('Account settings', h($user_id)),
|
||||||
|
'url' => sprintf('/users/settings/%s', h($user_id))
|
||||||
|
];
|
||||||
|
return $linkData;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
$this->bcf->addLink('Users', 'edit', 'UserSettings', 'index', function ($config) use ($bcf, $request, $passedData) {
|
||||||
|
if (!empty($passedData[0])) {
|
||||||
|
$user_id = $passedData[0];
|
||||||
|
$linkData = [
|
||||||
|
'label' => __('User Setting [{0}]', h($user_id)),
|
||||||
'url' => sprintf('/user-settings/index?Users.id=%s', h($user_id))
|
'url' => sprintf('/user-settings/index?Users.id=%s', h($user_id))
|
||||||
];
|
];
|
||||||
return $linkData;
|
return $linkData;
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$this->bcf->addLink('Users', 'settings', 'Users', 'view', function ($config) use ($bcf, $request, $passedData) {
|
||||||
|
if (!empty($passedData[0])) {
|
||||||
|
$user_id = $passedData[0];
|
||||||
|
$linkData = [
|
||||||
|
'label' => __('View user', h($user_id)),
|
||||||
|
'url' => sprintf('/users/view/%s', h($user_id))
|
||||||
|
];
|
||||||
|
return $linkData;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
$this->bcf->addSelfLink('Users', 'settings', [
|
||||||
|
'label' => __('Account settings')
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ class NavigationComponent extends Component
|
||||||
public function getUserBookmarks(): array
|
public function getUserBookmarks(): array
|
||||||
{
|
{
|
||||||
$userSettingTable = TableRegistry::getTableLocator()->get('UserSettings');
|
$userSettingTable = TableRegistry::getTableLocator()->get('UserSettings');
|
||||||
$setting = $userSettingTable->getSettingByName($this->request->getAttribute('identity'), 'ui.sidebar.bookmarks');
|
$setting = $userSettingTable->getSettingByName($this->request->getAttribute('identity'), 'ui.bookmarks');
|
||||||
$bookmarks = is_null($setting) ? [] : json_decode($setting->value, true);
|
$bookmarks = is_null($setting) ? [] : json_decode($setting->value, true);
|
||||||
|
|
||||||
$links = array_map(function($bookmark) {
|
$links = array_map(function($bookmark) {
|
||||||
|
@ -316,11 +316,11 @@ class BreadcrumbFactory
|
||||||
$this->endpoints[$sourceController][$sourceAction]['after'] = $parents;
|
$this->endpoints[$sourceController][$sourceAction]['after'] = $parents;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addSelfLink(string $controller, string $action)
|
public function addSelfLink(string $controller, string $action, array $options=[])
|
||||||
{
|
{
|
||||||
$this->addLink($controller, $action, $controller, $action, [
|
$this->addLink($controller, $action, $controller, $action, array_merge($options, [
|
||||||
'selfLink' => true
|
'selfLink' => true,
|
||||||
]);
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addLink(string $sourceController, string $sourceAction, string $targetController, string $targetAction, $overrides = [])
|
public function addLink(string $sourceController, string $sourceAction, string $targetController, string $targetAction, $overrides = [])
|
||||||
|
|
|
@ -117,7 +117,7 @@ class UserSettingsController extends AppController
|
||||||
$this->render('view');
|
$this->render('view');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setSetting($settingsName)
|
public function setSetting($settingsName = false)
|
||||||
{
|
{
|
||||||
if (!$this->request->is('get')) {
|
if (!$this->request->is('get')) {
|
||||||
$setting = $this->UserSettings->getSettingByName($this->ACL->getUser(), $settingsName);
|
$setting = $this->UserSettings->getSettingByName($this->ACL->getUser(), $settingsName);
|
||||||
|
@ -137,6 +137,39 @@ class UserSettingsController extends AppController
|
||||||
$this->set('settingName', $settingsName);
|
$this->set('settingName', $settingsName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function saveSetting()
|
||||||
|
{
|
||||||
|
if ($this->request->is('post')) {
|
||||||
|
$data = $this->ParamHandler->harvestParams([
|
||||||
|
'name',
|
||||||
|
'value'
|
||||||
|
]);
|
||||||
|
$setting = $this->UserSettings->getSettingByName($this->ACL->getUser(), $data['name']);
|
||||||
|
if (is_null($setting)) { // setting not found, create it
|
||||||
|
$result = $this->UserSettings->createSetting($this->ACL->getUser(), $data['name'], $data['value']);
|
||||||
|
} else {
|
||||||
|
$result = $this->UserSettings->editSetting($this->ACL->getUser(), $data['name'], $data['value']);
|
||||||
|
}
|
||||||
|
$success = !empty($result);
|
||||||
|
$message = $success ? __('Setting saved') : __('Could not save setting');
|
||||||
|
$this->CRUD->setResponseForController('setSetting', $success, $message, $result);
|
||||||
|
$responsePayload = $this->CRUD->getResponsePayload();
|
||||||
|
if (!empty($responsePayload)) {
|
||||||
|
return $responsePayload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBookmarks($forSidebar=false)
|
||||||
|
{
|
||||||
|
$bookmarks = $this->UserSettings->getSettingByName($this->ACL->getUser(), $this->UserSettings->BOOKMARK_SETTING_NAME);
|
||||||
|
$bookmarks = json_decode($bookmarks['value'], true);
|
||||||
|
$this->set('user_id', $this->ACL->getUser()->id);
|
||||||
|
$this->set('bookmarks', $bookmarks);
|
||||||
|
$this->set('forSidebar', $forSidebar);
|
||||||
|
$this->render('/element/UserSettings/saved-bookmarks');
|
||||||
|
}
|
||||||
|
|
||||||
public function saveBookmark()
|
public function saveBookmark()
|
||||||
{
|
{
|
||||||
if (!$this->request->is('get')) {
|
if (!$this->request->is('get')) {
|
||||||
|
@ -152,4 +185,19 @@ class UserSettingsController extends AppController
|
||||||
$this->set('user_id', $this->ACL->getUser()->id);
|
$this->set('user_id', $this->ACL->getUser()->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function deleteBookmark()
|
||||||
|
{
|
||||||
|
if (!$this->request->is('get')) {
|
||||||
|
$result = $this->UserSettings->deleteBookmark($this->ACL->getUser(), $this->request->getData());
|
||||||
|
$success = !empty($result);
|
||||||
|
$message = $success ? __('Bookmark deleted') : __('Could not delete bookmark');
|
||||||
|
$this->CRUD->setResponseForController('deleteBookmark', $success, $message, $result);
|
||||||
|
$responsePayload = $this->CRUD->getResponsePayload();
|
||||||
|
if (!empty($responsePayload)) {
|
||||||
|
return $responsePayload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->set('user_id', $this->ACL->getUser()->id);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -151,6 +151,11 @@ class UsersController extends AppController
|
||||||
public function settings()
|
public function settings()
|
||||||
{
|
{
|
||||||
$this->set('user', $this->ACL->getUser());
|
$this->set('user', $this->ACL->getUser());
|
||||||
|
$all = $this->Users->UserSettings->getSettingsFromProviderForUser($this->ACL->getUser()['id'], true);
|
||||||
|
$this->set('settingsProvider', $all['settingsProvider']);
|
||||||
|
$this->set('settings', $all['settings']);
|
||||||
|
$this->set('settingsFlattened', $all['settingsFlattened']);
|
||||||
|
$this->set('notices', $all['notices']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register()
|
public function register()
|
||||||
|
|
|
@ -6,11 +6,14 @@ use App\Model\Entity\AppModel;
|
||||||
use Cake\ORM\Entity;
|
use Cake\ORM\Entity;
|
||||||
use Authentication\PasswordHasher\DefaultPasswordHasher;
|
use Authentication\PasswordHasher\DefaultPasswordHasher;
|
||||||
|
|
||||||
|
require_once(APP . 'Model' . DS . 'Table' . DS . 'SettingProviders' . DS . 'UserSettingsProvider.php');
|
||||||
|
use App\Settings\SettingsProvider\UserSettingsProvider;
|
||||||
|
|
||||||
class User extends AppModel
|
class User extends AppModel
|
||||||
{
|
{
|
||||||
protected $_hidden = ['password', 'confirm_password'];
|
protected $_hidden = ['password', 'confirm_password'];
|
||||||
|
|
||||||
protected $_virtual = ['user_settings_by_name'];
|
protected $_virtual = ['user_settings_by_name', 'user_settings_by_name_with_fallback'];
|
||||||
|
|
||||||
protected function _getUserSettingsByName()
|
protected function _getUserSettingsByName()
|
||||||
{
|
{
|
||||||
|
@ -23,6 +26,22 @@ class User extends AppModel
|
||||||
return $settingsByName;
|
return $settingsByName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function _getUserSettingsByNameWithFallback()
|
||||||
|
{
|
||||||
|
if (!isset($this->SettingsProvider)) {
|
||||||
|
$this->SettingsProvider = new UserSettingsProvider();
|
||||||
|
}
|
||||||
|
$settingsByNameWithFallback = [];
|
||||||
|
if (!empty($this->user_settings)) {
|
||||||
|
foreach ($this->user_settings as $i => $setting) {
|
||||||
|
$settingsByNameWithFallback[$setting->name] = $setting->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$settingsProvider = $this->SettingsProvider->getSettingsConfiguration($settingsByNameWithFallback);
|
||||||
|
$settingsFlattened = $this->SettingsProvider->flattenSettingsConfiguration($settingsProvider);
|
||||||
|
return $settingsFlattened;
|
||||||
|
}
|
||||||
|
|
||||||
protected function _setPassword(string $password) : ?string
|
protected function _setPassword(string $password) : ?string
|
||||||
{
|
{
|
||||||
if (strlen($password) > 0) {
|
if (strlen($password) > 0) {
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Settings\SettingsProvider;
|
||||||
|
|
||||||
|
use Cake\ORM\TableRegistry;
|
||||||
|
|
||||||
|
require_once(APP . 'Model' . DS . 'Table' . DS . 'SettingProviders' . DS . 'BaseSettingsProvider.php');
|
||||||
|
|
||||||
|
use App\Settings\SettingsProvider\BaseSettingsProvider;
|
||||||
|
|
||||||
|
class UserSettingsProvider extends BaseSettingsProvider
|
||||||
|
{
|
||||||
|
protected function generateSettingsConfiguration()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
__('Appearance') => [
|
||||||
|
__('User Interface') => [
|
||||||
|
'ui.bsTheme' => [
|
||||||
|
'description' => 'The Bootstrap theme to use for the application',
|
||||||
|
'default' => 'default',
|
||||||
|
'name' => 'UI Theme',
|
||||||
|
'options' => (function () {
|
||||||
|
$instanceTable = TableRegistry::getTableLocator()->get('Instance');
|
||||||
|
$themes = $instanceTable->getAvailableThemes();
|
||||||
|
return array_combine($themes, $themes);
|
||||||
|
})(),
|
||||||
|
'severity' => 'info',
|
||||||
|
'type' => 'select'
|
||||||
|
],
|
||||||
|
'ui.sidebar.expanded' => [
|
||||||
|
'name' => __('Sidebar expanded'),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'description' => __('Should the left navigation sidebar expanded and locked.'),
|
||||||
|
'default' => false,
|
||||||
|
'severity' => 'info',
|
||||||
|
],
|
||||||
|
'ui.sidebar.include_bookmarks' => [
|
||||||
|
'name' => __('Include bookmarks in the sidebar'),
|
||||||
|
'type' => 'boolean',
|
||||||
|
'description' => __('Should bookmarks links included in the sidebar.'),
|
||||||
|
'default' => false,
|
||||||
|
'severity' => 'info',
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
__('Account Security') => [
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,13 @@ use App\Model\Table\AppTable;
|
||||||
use Cake\ORM\Table;
|
use Cake\ORM\Table;
|
||||||
use Cake\Validation\Validator;
|
use Cake\Validation\Validator;
|
||||||
|
|
||||||
|
require_once(APP . 'Model' . DS . 'Table' . DS . 'SettingProviders' . DS . 'UserSettingsProvider.php');
|
||||||
|
use App\Settings\SettingsProvider\UserSettingsProvider;
|
||||||
|
|
||||||
class UserSettingsTable extends AppTable
|
class UserSettingsTable extends AppTable
|
||||||
{
|
{
|
||||||
|
protected $BOOKMARK_SETTING_NAME = 'ui.bookmarks';
|
||||||
|
|
||||||
public function initialize(array $config): void
|
public function initialize(array $config): void
|
||||||
{
|
{
|
||||||
parent::initialize($config);
|
parent::initialize($config);
|
||||||
|
@ -16,6 +21,8 @@ class UserSettingsTable extends AppTable
|
||||||
'Users'
|
'Users'
|
||||||
);
|
);
|
||||||
$this->setDisplayField('name');
|
$this->setDisplayField('name');
|
||||||
|
|
||||||
|
$this->SettingsProvider = new UserSettingsProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function validationDefault(Validator $validator): Validator
|
public function validationDefault(Validator $validator): Validator
|
||||||
|
@ -27,6 +34,35 @@ class UserSettingsTable extends AppTable
|
||||||
return $validator;
|
return $validator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSettingsFromProviderForUser($user_id, $full = false): array
|
||||||
|
{
|
||||||
|
$settingsTmp = $this->getSettingsForUser($user_id)->toArray();
|
||||||
|
$settings = [];
|
||||||
|
foreach ($settingsTmp as $setting) {
|
||||||
|
$settings[$setting->name] = $setting->value;
|
||||||
|
}
|
||||||
|
if (empty($full)) {
|
||||||
|
return $settings;
|
||||||
|
} else {
|
||||||
|
$settingsProvider = $this->SettingsProvider->getSettingsConfiguration($settings);
|
||||||
|
$settingsFlattened = $this->SettingsProvider->flattenSettingsConfiguration($settingsProvider);
|
||||||
|
$notices = $this->SettingsProvider->getNoticesFromSettingsConfiguration($settingsProvider, $settings);
|
||||||
|
return [
|
||||||
|
'settings' => $settings,
|
||||||
|
'settingsProvider' => $settingsProvider,
|
||||||
|
'settingsFlattened' => $settingsFlattened,
|
||||||
|
'notices' => $notices,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSettingsForUser($user_id)
|
||||||
|
{
|
||||||
|
return $this->find()->where([
|
||||||
|
'user_id' => $user_id,
|
||||||
|
])->all();
|
||||||
|
}
|
||||||
|
|
||||||
public function getSettingByName($user, $name)
|
public function getSettingByName($user, $name)
|
||||||
{
|
{
|
||||||
return $this->find()->where([
|
return $this->find()->where([
|
||||||
|
@ -60,8 +96,7 @@ class UserSettingsTable extends AppTable
|
||||||
|
|
||||||
public function saveBookmark($user, $data)
|
public function saveBookmark($user, $data)
|
||||||
{
|
{
|
||||||
$bookmarkSettingName = 'ui.sidebar.bookmarks';
|
$setting = $this->getSettingByName($user, $this->BOOKMARK_SETTING_NAME);
|
||||||
$setting = $this->getSettingByName($user, $bookmarkSettingName);
|
|
||||||
$bookmarkData = [
|
$bookmarkData = [
|
||||||
'label' => $data['bookmark_label'],
|
'label' => $data['bookmark_label'],
|
||||||
'name' => $data['bookmark_name'],
|
'name' => $data['bookmark_name'],
|
||||||
|
@ -69,12 +104,34 @@ class UserSettingsTable extends AppTable
|
||||||
];
|
];
|
||||||
if (is_null($setting)) { // setting not found, create it
|
if (is_null($setting)) { // setting not found, create it
|
||||||
$bookmarksData = json_encode([$bookmarkData]);
|
$bookmarksData = json_encode([$bookmarkData]);
|
||||||
$result = $this->createSetting($user, $bookmarkSettingName, $bookmarksData);
|
$result = $this->createSetting($user, $this->BOOKMARK_SETTING_NAME, $bookmarksData);
|
||||||
} else {
|
} else {
|
||||||
$bookmarksData = json_decode($setting->value);
|
$bookmarksData = json_decode($setting->value);
|
||||||
$bookmarksData[] = $bookmarkData;
|
$bookmarksData[] = $bookmarkData;
|
||||||
$bookmarksData = json_encode($bookmarksData);
|
$bookmarksData = json_encode($bookmarksData);
|
||||||
$result = $this->editSetting($user, $bookmarkSettingName, $bookmarksData);
|
$result = $this->editSetting($user, $this->BOOKMARK_SETTING_NAME, $bookmarksData);
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteBookmark($user, $data)
|
||||||
|
{
|
||||||
|
$setting = $this->getSettingByName($user, $this->BOOKMARK_SETTING_NAME);
|
||||||
|
$bookmarkData = [
|
||||||
|
'name' => $data['bookmark_name'],
|
||||||
|
'url' => $data['bookmark_url'],
|
||||||
|
];
|
||||||
|
if (is_null($setting)) { // Can't delete something that doesn't exist
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
$bookmarksData = json_decode($setting->value, true);
|
||||||
|
foreach ($bookmarksData as $i => $savedBookmark) {
|
||||||
|
if ($savedBookmark['name'] == $bookmarkData['name'] && $savedBookmark['url'] == $bookmarkData['url']) {
|
||||||
|
unset($bookmarksData[$i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$bookmarksData = json_encode($bookmarksData);
|
||||||
|
$result = $this->editSetting($user, $this->BOOKMARK_SETTING_NAME, $bookmarksData);
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -652,21 +652,21 @@ class BoostrapTable extends BootstrapGeneric {
|
||||||
$key = $field;
|
$key = $field;
|
||||||
}
|
}
|
||||||
$cellValue = Hash::get($row, $key);
|
$cellValue = Hash::get($row, $key);
|
||||||
$html .= $this->genCell($cellValue, $field, $row);
|
$html .= $this->genCell($cellValue, $field, $row, $i);
|
||||||
}
|
}
|
||||||
} else { // indexed array
|
} else { // indexed array
|
||||||
foreach ($row as $cellValue) {
|
foreach ($row as $cellValue) {
|
||||||
$html .= $this->genCell($cellValue, $field, $row);
|
$html .= $this->genCell($cellValue, $field, $row, $i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$html .= $this->closeNode('tr');
|
$html .= $this->closeNode('tr');
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function genCell($value, $field=[], $row=[])
|
private function genCell($value, $field=[], $row=[], $i=0)
|
||||||
{
|
{
|
||||||
if (isset($field['formatter'])) {
|
if (isset($field['formatter'])) {
|
||||||
$cellContent = $field['formatter']($value, $row);
|
$cellContent = $field['formatter']($value, $row, $i);
|
||||||
} else if (isset($field['element'])) {
|
} else if (isset($field['element'])) {
|
||||||
$cellContent = $this->btHelper->getView()->element($field['element'], [
|
$cellContent = $this->btHelper->getView()->element($field['element'], [
|
||||||
'data' => [$value],
|
'data' => [$value],
|
||||||
|
|
|
@ -1,30 +1,57 @@
|
||||||
<?php
|
<?php
|
||||||
// $Parsedown = new Parsedown();
|
$bookmarks = !empty($loggedUser->user_settings_by_name['ui.bookmarks']['value']) ? json_decode($loggedUser->user_settings_by_name['ui.bookmarks']['value'], true) : []
|
||||||
// echo $Parsedown->text($md);
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<h2><?= __('Home') ?></h2>
|
<h3>
|
||||||
|
<?= $this->Bootstrap->icon('bookmark', [
|
||||||
|
'class' => ['fa-fw']
|
||||||
|
]); ?>
|
||||||
|
<?= __('Bookmarks') ?>
|
||||||
|
</h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<?php foreach ($statistics as $modelName => $statistics): ?>
|
<? if (!empty($bookmarks)): ?>
|
||||||
|
<ul class="col-sm-12 col-md-10 col-l-8 col-xl-8 mb-3">
|
||||||
|
<?php foreach ($bookmarks as $bookmark) : ?>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<a href="<?= h($bookmark['url']) ?>" class="link-primary w-bold">
|
||||||
|
<?= h($bookmark['label']) ?>
|
||||||
|
</a>
|
||||||
|
<span class="ms-3 fw-light"><?= h($bookmark['name']) ?></span>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="fw-light"><?= __('No bookmarks') ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
<?= $this->Bootstrap->icon('chart-bar', [
|
||||||
|
'class' => ['fa-fw']
|
||||||
|
]); ?>
|
||||||
|
<?= __('Activity') ?>
|
||||||
|
</h3>
|
||||||
|
<div class="row">
|
||||||
|
<?php foreach ($statistics as $modelName => $statistics) : ?>
|
||||||
<div class="col-sm-6 col-md-5 col-l-4 col-xl-3 mb-3">
|
<div class="col-sm-6 col-md-5 col-l-4 col-xl-3 mb-3">
|
||||||
<?php
|
<?php
|
||||||
$exploded = explode('.', $modelName);
|
$exploded = explode('.', $modelName);
|
||||||
$modelForDisplay = $exploded[count($exploded)-1];
|
$modelForDisplay = $exploded[count($exploded) - 1];
|
||||||
$panelTitle = $this->Html->link(
|
$panelTitle = $this->Html->link(
|
||||||
h($modelForDisplay),
|
h($modelForDisplay),
|
||||||
$this->Url->build([
|
$this->Url->build([
|
||||||
'controller' => $modelForDisplay,
|
'controller' => $modelForDisplay,
|
||||||
'action' => 'index',
|
'action' => 'index',
|
||||||
]),
|
]),
|
||||||
['class' => 'text-white text-decoration-none fw-light stretched-link']
|
['class' => 'text-white text-decoration-none fw-light stretched-link']
|
||||||
);
|
);
|
||||||
echo $this->element('widgets/highlight-panel', [
|
echo $this->element('widgets/highlight-panel', [
|
||||||
'titleHtml' => $panelTitle,
|
'titleHtml' => $panelTitle,
|
||||||
'number' => $statistics['amount'],
|
'number' => $statistics['amount'],
|
||||||
'variation' => $statistics['variation'] ?? '',
|
'variation' => $statistics['variation'] ?? '',
|
||||||
'chartData' => $statistics['timeline'] ?? []
|
'chartData' => $statistics['timeline'] ?? []
|
||||||
]);
|
]);
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</div>
|
</div>
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
echo $this->element('genericElements/Form/genericForm', [
|
echo $this->element('genericElements/Form/genericForm', [
|
||||||
'data' => [
|
'data' => [
|
||||||
'description' => __('Authkeys are used for API access. A user can have more than one authkey, so if you would like to use separate keys per tool that queries Cerebrate, add additional keys. Use the comment field to make identifying your keys easier.'),
|
'description' => __('Application setting form'),
|
||||||
'fields' => [
|
'fields' => [
|
||||||
[
|
[
|
||||||
'field' => 'name',
|
'field' => 'name',
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
echo $this->element('genericElements/Form/genericForm', [
|
||||||
|
'data' => [
|
||||||
|
'title' => __('Delete a bookmark'),
|
||||||
|
'description' => __('Specify the name and the URL of the bookmark to be deleted from your user profile.'),
|
||||||
|
'model' => 'UserSettings',
|
||||||
|
'fields' => [
|
||||||
|
[
|
||||||
|
'field' => 'bookmark_name',
|
||||||
|
'label' => __('Name'),
|
||||||
|
'placeholder' => __('Home page'),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'field' => 'bookmark_url',
|
||||||
|
'label' => __('URL'),
|
||||||
|
'placeholder' => '/instance/home',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'submit' => [
|
||||||
|
'action' => $this->request->getParam('action')
|
||||||
|
]
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
?>
|
||||||
|
</div>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
echo $this->element('genericElements/Form/genericForm', [
|
||||||
|
'data' => [
|
||||||
|
'description' => __('User settings form'),
|
||||||
|
'fields' => [
|
||||||
|
[
|
||||||
|
'field' => 'name',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'field' => 'value'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'submit' => [
|
||||||
|
'action' => $this->request->getParam('action')
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
|
@ -11,6 +11,7 @@ $variantFromSeverity = [
|
||||||
'warning' => 'warning',
|
'warning' => 'warning',
|
||||||
'info' => 'info',
|
'info' => 'info',
|
||||||
];
|
];
|
||||||
|
$this->set('variantFromSeverity', $variantFromSeverity);
|
||||||
$includeScrollspy = !empty($includeScrollspy);
|
$includeScrollspy = !empty($includeScrollspy);
|
||||||
|
|
||||||
$groupedContent = [];
|
$groupedContent = [];
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
$forSidebar = !empty($forSidebar);
|
||||||
|
|
||||||
|
$table = $this->Bootstrap->table([
|
||||||
|
'hover' => false,
|
||||||
|
], [
|
||||||
|
'fields' => [
|
||||||
|
['key' => 'label', 'label' => __('Label')],
|
||||||
|
['key' => 'name', 'label' => __('Name')],
|
||||||
|
['key' => 'url', 'label' => __('URL'), 'formatter' => function ($value, $row) {
|
||||||
|
return sprintf('<span class="font-monospace">%s</span>', h($value));
|
||||||
|
}],
|
||||||
|
['key' => 'action', 'label' => __('Action'), 'formatter' => function ($value, $row, $index) {
|
||||||
|
return $this->Bootstrap->button([
|
||||||
|
'icon' => 'trash',
|
||||||
|
'variant' => 'danger',
|
||||||
|
'size' => 'sm',
|
||||||
|
'params' => [
|
||||||
|
'onclick' => sprintf('deleteBookmark(window.bookmarks[%s])', $index),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
'items' => $bookmarks,
|
||||||
|
'caption' => empty($bookmarks) ? __('No bookmark saved') : ''
|
||||||
|
]);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php if (!empty($forSidebar)) : ?>
|
||||||
|
<li class="bookmarks">
|
||||||
|
<?php foreach ($bookmarks as $parentName => $entry) : ?>
|
||||||
|
<?= $this->element('layouts/sidebar/bookmark-entry', [
|
||||||
|
'entry' => $entry,
|
||||||
|
])
|
||||||
|
?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?= $this->element('layouts/sidebar/bookmark-add') ?>
|
||||||
|
</li>
|
||||||
|
<?php else : ?>
|
||||||
|
<div class="bookmark-table-container m-2">
|
||||||
|
<button class="btn btn-primary mb-2" onclick="openSaveBookmarkModal()">
|
||||||
|
<?= __('Create bookmark') ?>
|
||||||
|
</button>
|
||||||
|
<?= $table ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.bookmarks = <?= json_encode($bookmarks) ?>;
|
||||||
|
</script>
|
||||||
|
<?php endif; ?>
|
|
@ -15,7 +15,7 @@ use Cake\Routing\Router;
|
||||||
</div>
|
</div>
|
||||||
</h6>
|
</h6>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<a class="dropdown-item" href="<?= Router::url(['controller' => 'users', 'action' => 'view', 'plugin' => null]) ?>">
|
<a class="dropdown-item" href="<?= Router::url(['controller' => 'users', 'action' => 'view', 'plugin' => null, h($this->request->getAttribute('identity')['id'])]) ?>">
|
||||||
<i class="me-1 <?= $this->FontAwesome->getClass('user-circle') ?>"></i>
|
<i class="me-1 <?= $this->FontAwesome->getClass('user-circle') ?>"></i>
|
||||||
<?= __('My Account') ?>
|
<?= __('My Account') ?>
|
||||||
</a>
|
</a>
|
||||||
|
@ -24,7 +24,7 @@ use Cake\Routing\Router;
|
||||||
href="<?= Router::url(['controller' => 'users', 'action' => 'settings', 'plugin' => null, h($this->request->getAttribute('identity')['id'])]) ?>"
|
href="<?= Router::url(['controller' => 'users', 'action' => 'settings', 'plugin' => null, h($this->request->getAttribute('identity')['id'])]) ?>"
|
||||||
>
|
>
|
||||||
<i class="me-1 <?= $this->FontAwesome->getClass('user-cog') ?>"></i>
|
<i class="me-1 <?= $this->FontAwesome->getClass('user-cog') ?>"></i>
|
||||||
<?= __('Settings') ?>
|
<?= __('Account Settings') ?>
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<a class="dropdown-item dropdown-item-outline-danger" href="<?= Router::url(['controller' => 'users', 'action' => 'logout', 'plugin' => null]) ?>">
|
<a class="dropdown-item dropdown-item-outline-danger" href="<?= Router::url(['controller' => 'users', 'action' => 'logout', 'plugin' => null]) ?>">
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
|
<?php
|
||||||
|
$bookmarkIncluded = $loggedUser->user_settings_by_name_with_fallback['ui.sidebar.include_bookmarks']['value'];
|
||||||
|
?>
|
||||||
<div class="sidebar-wrapper d-flex flex-column">
|
<div class="sidebar-wrapper d-flex flex-column">
|
||||||
<div class="sidebar-scroll">
|
<div class="sidebar-scroll">
|
||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
<ul class="sidebar-elements">
|
<ul class="sidebar-elements">
|
||||||
<?php foreach ($menu as $category => $categorized) : ?>
|
<?php foreach ($menu as $category => $categorized) : ?>
|
||||||
<?php if ($category == '__bookmarks') : ?>
|
<?php if ($category == '__bookmarks') : ?>
|
||||||
<?= $this->element('layouts/sidebar/category', ['label' => __('Bookmarks'), 'class' => 'bookmark-categ']) ?>
|
<?php if ($bookmarkIncluded) : ?>
|
||||||
<li class="bookmarks">
|
<?= $this->element('layouts/sidebar/category', ['label' => __('Bookmarks'), 'class' => 'bookmark-categ']) ?>
|
||||||
<?php foreach ($categorized as $parentName => $entry) : ?>
|
<?= $this->element('UserSettings/saved-bookmarks', [
|
||||||
<?= $this->element('layouts/sidebar/bookmark-entry', [
|
'bookmarks' => $categorized,
|
||||||
'entry' => $entry,
|
'forSidebar' => true,
|
||||||
])
|
]) ?>
|
||||||
?>
|
<?php endif; ?>
|
||||||
<?php endforeach; ?>
|
<?php else: ?>
|
||||||
<?= $this->element('layouts/sidebar/bookmark-add') ?>
|
|
||||||
</li>
|
|
||||||
<?php else : ?>
|
|
||||||
<?= $this->element('layouts/sidebar/category', ['label' => $category]) ?>
|
<?= $this->element('layouts/sidebar/category', ['label' => $category]) ?>
|
||||||
<?php foreach ($categorized as $parentName => $parent) : ?>
|
<?php foreach ($categorized as $parentName => $parent) : ?>
|
||||||
<?= $this->element('layouts/sidebar/entry', [
|
<?= $this->element('layouts/sidebar/entry', [
|
||||||
|
|
|
@ -5,6 +5,7 @@ echo $this->Bootstrap->button([
|
||||||
'title' => __('Add new bookmark'),
|
'title' => __('Add new bookmark'),
|
||||||
'variant' => 'primary',
|
'variant' => 'primary',
|
||||||
'size' => 'sm',
|
'size' => 'sm',
|
||||||
|
'class' => 'mb-1',
|
||||||
'params' => [
|
'params' => [
|
||||||
'id' => 'btn-add-bookmark',
|
'id' => 'btn-add-bookmark',
|
||||||
]
|
]
|
||||||
|
|
|
@ -17,7 +17,7 @@ use Cake\Core\Configure;
|
||||||
|
|
||||||
$cakeDescription = 'Cerebrate';
|
$cakeDescription = 'Cerebrate';
|
||||||
|
|
||||||
$sidebarOpen = $loggedUser->user_settings_by_name['ui.sidebar.expanded']->value;
|
$sidebarOpen = $loggedUser->user_settings_by_name_with_fallback['ui.sidebar.expanded']['value'];
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
|
|
@ -152,6 +152,49 @@ function focusSearchResults(evt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openSaveBookmarkModal(bookmark_url = '') {
|
||||||
|
const url = '/user-settings/saveBookmark';
|
||||||
|
UI.submissionModal(url).then(([modalFactory, ajaxApi]) => {
|
||||||
|
const $input = modalFactory.$modal.find('input[name="bookmark_url"]')
|
||||||
|
$input.val(bookmark_url)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteBookmark(bookmark, forSidebar=false) {
|
||||||
|
const url = '/user-settings/deleteBookmark'
|
||||||
|
AJAXApi.quickFetchAndPostForm(url, {
|
||||||
|
bookmark_name: bookmark.name,
|
||||||
|
bookmark_url: bookmark.url,
|
||||||
|
}, {
|
||||||
|
provideFeedback: true,
|
||||||
|
statusNode: $('.bookmark-table-container'),
|
||||||
|
}).then((apiResult) => {
|
||||||
|
const url = `/userSettings/getBookmarks/${forSidebar ? '1' : '0'}`
|
||||||
|
UI.reload(url, $('.bookmark-table-container').parent())
|
||||||
|
const theToast = UI.toast({
|
||||||
|
variant: 'success',
|
||||||
|
title: apiResult.message,
|
||||||
|
bodyHtml: $('<div/>').append(
|
||||||
|
$('<span/>').text('Cancel deletion operation.'),
|
||||||
|
$('<button/>').addClass(['btn', 'btn-primary', 'btn-sm', 'ms-3']).text('Restore bookmark').click(function () {
|
||||||
|
const urlRestore = '/user-settings/saveBookmark'
|
||||||
|
AJAXApi.quickFetchAndPostForm(urlRestore, {
|
||||||
|
bookmark_label: bookmark.label,
|
||||||
|
bookmark_name: bookmark.name,
|
||||||
|
bookmark_url: bookmark.url,
|
||||||
|
}, {
|
||||||
|
provideFeedback: true,
|
||||||
|
statusNode: $('.bookmark-table-container')
|
||||||
|
}).then(() => {
|
||||||
|
const url = `/userSettings/getBookmarks/${forSidebar ? '1' : '0'}`
|
||||||
|
UI.reload(url, $('.bookmark-table-container').parent())
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}).catch((e) => { })
|
||||||
|
}
|
||||||
|
|
||||||
var UI
|
var UI
|
||||||
$(document).ready(() => {
|
$(document).ready(() => {
|
||||||
if (typeof UIFactory !== "undefined") {
|
if (typeof UIFactory !== "undefined") {
|
||||||
|
@ -179,10 +222,6 @@ $(document).ready(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
$('.sidebar #btn-add-bookmark').click(() => {
|
$('.sidebar #btn-add-bookmark').click(() => {
|
||||||
const url = '/user-settings/saveBookmark';
|
openSaveBookmarkModal(window.location.pathname)
|
||||||
UI.submissionModal(url).then(([modalFactory, ajaxApi]) => {
|
|
||||||
const $input = modalFactory.$modal.find('input[name="bookmark_url"]')
|
|
||||||
$input.val(window.location.pathname)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue