Merge branch 'dev_session' into 2.4

pull/5239/head
iglocska 2019-09-29 20:23:00 +02:00
commit 480e3b2969
No known key found for this signature in database
GPG Key ID: BEA224F1FEF113AC
18 changed files with 873 additions and 18 deletions

View File

@ -551,6 +551,13 @@ class ACLComponent extends Component
'verifyGPG' => array(),
'view' => array('*'),
),
'userSettings' => array(
'index' => array('*'),
'view' => array('*'),
'setSetting' => array('*'),
'getSetting' => array('*'),
'delete' => array('*')
),
'warninglists' => array(
'checkValue' => array('perm_auth'),
'delete' => array(),

View File

@ -249,6 +249,17 @@ class RestResponseComponent extends Component
'http_method' => 'GET'
)
),
'UserSetting' => array(
'setSetting' => array(
'description' => "POST a User setting object in JSON format to this API to create a new setting or update the equivalent existing setting. Admins/site admins can specify a user ID besides their own.",
'mandatory' => array('setting', 'value'),
'optional' => array('user_id')
),
'delete' => array(
'description' => "POST or DELETE to this API to delete an existing setting.",
'params' => array('id')
)
),
'Warninglist' => array(
'checkValue' => array(
'description' => "POST a JSON list with value(s) to check against the warninglists to get a JSON dictionary as a response with any hits, if there are any (with the key being the passed value triggering a warning).",
@ -1548,10 +1559,12 @@ class RestResponseComponent extends Component
$fieldsConstraint[$sf]['label'] = $label;
}
} else {
$fieldsConstraint[$field] = $this->__fieldsConstraint[$field];
$label = $scope . '.' . $field;
$fieldsConstraint[$field]['id'] = $label;
$fieldsConstraint[$field]['label'] = $label;
if (!empty($this->__fieldsConstraint[$field])) {
$fieldsConstraint[$field] = $this->__fieldsConstraint[$field];
$label = $scope . '.' . $field;
$fieldsConstraint[$field]['id'] = $label;
$fieldsConstraint[$field]['label'] = $label;
}
}
// add dynamic data and overwrite name collisions

View File

@ -2599,6 +2599,7 @@ class EventsController extends AppController
}
// send out the email
$emailResult = $this->Event->sendAlertEmailRouter($id, $this->Auth->user(), $this->Event->data['Event']['publish_timestamp']);
throw new Exception();
if (is_bool($emailResult) && $emailResult == true) {
// Performs all the actions required to publish an event
$result = $this->Event->publishRouter($id, null, $this->Auth->user());

View File

@ -0,0 +1,333 @@
<?php
/*
*
* Feature developed as part of a training given by CIRCL in Luxembourg on 26/09/2019
* Verbose comments for educational purposes only
*
*/
App::uses('AppController', 'Controller');
class UserSettingsController extends AppController
{
public $components = array('Session', 'RequestHandler');
public $paginate = array(
'limit' => 60,
'maxLimit' => 9999,
'order' => array(
'UserSetting.id' => 'DESC'
),
'contain' => array(
'User.id',
'User.email'
)
);
public function index()
{
$filterData = array(
'request' => $this->request,
'paramArray' => array('setting', 'user_id', 'sort', 'direction', 'page', 'limit'),
'named_params' => $this->params['named']
);
$exception = false;
$filters = $this->_harvestParameters($filterData, $exception);
$conditions = array();
if (!empty($filters['setting'])) {
$conditions['AND'][] = array(
'setting' => $filters['setting']
);
}
if (!empty($filters['user_id'])) {
if ($filters['user_id'] === 'all') {
$context = 'all';
} else if ($filters['user_id'] === 'me') {
$conditions['AND'][] = array(
'user_id' => $this->Auth->user('id')
);
$context = 'me';
} else if ($filters['user_id'] === 'org') {
$conditions['AND'][] = array(
'user_id' => $this->UserSetting->User->find(
'list', array(
'conditions' => array(
'User.org_id' => $this->Auth->user('org_id')
),
'fields' => array(
'User.id', 'User.id'
)
)
)
);
$context = 'org';
} else {
$conditions['AND'][] = array(
'user_id' => $filters['user_id']
);
}
}
if (!$this->_isSiteAdmin()) {
if ($this->_isAdmin()) {
$conditions['AND'][] = array(
'UserSetting.user_id' => $this->UserSetting->User->find(
'list', array(
'conditions' => array(
'User.org_id' => $this->Auth->user('org_id')
),
'fields' => array(
'User.id', 'User.id'
)
)
)
);
} else {
$conditions['AND'][] = array(
'UserSetting.user_id' => $this->Auth->user('id')
);
}
}
if ($this->_isRest()) {
$params = array(
'conditions' => $conditions
);
if (!empty($filters['page'])) {
$params['page'] = $filters['page'];
$params['limit'] = $this->paginate['limit'];
}
if (!empty($filters['limit'])) {
$params['limit'] = $filters['limit'];
}
$userSettings = $this->UserSetting->find('all', $params);
return $this->RestResponse->viewData($userSettings, $this->response->type());
} else {
$this->paginate['conditions'] = $conditions;
$this->set('data', $this->paginate());
$this->set('context', empty($context) ? 'null' : $context);
}
}
public function view($id)
{
// check if the ID is valid and whether a user setting with the given ID exists
if (empty($id) || !is_numeric($id)) {
throw new InvalidArgumentException(__('Invalid ID passed.'));
}
$userSetting = $this->UserSetting->find('first', array(
'recursive' => -1,
'conditions' => array(
'UserSetting.id' => $id
),
'contain' => array('User.id', 'User.org_id')
));
if (empty($userSetting)) {
throw new NotFoundException(__('Invalid user setting.'));
}
$checkAccess = $this->UserSetting->checkAccess($this->Auth->user(), $userSetting);
if (!$checkAccess) {
throw new NotFoundException(__('Invalid user setting.'));
}
if ($this->_isRest()) {
unset($userSetting['User']);
return $this->RestResponse->viewData($userSetting, $this->response->type());
} else {
$this->set($data, $userSetting);
}
}
public function setSetting($user_id = false, $setting = false)
{
// handle POST requests
if ($this->request->is('post')) {
// massage the request to allow for unencapsulated POST requests via the API
// {"key": "value"} instead of {"UserSetting": {"key": "value"}}
if (empty($this->request->data['UserSetting'])) {
$this->request->data = array('UserSetting' => $this->request->data);
}
if (!empty($user_id)) {
$this->request->data['UserSetting']['user_id'] = $user_id;
}
if (!empty($setting)) {
$this->request->data['UserSetting']['setting'] = $setting;
}
// force our user's ID as the user ID in all cases
$userSetting = array(
'user_id' => $this->Auth->user('id')
);
if (!empty($this->request->data['UserSetting']['user_id']) && is_numeric($this->request->data['UserSetting']['user_id'])) {
$user = $this->UserSetting->User->find('first', array(
'recursive' => -1,
'conditions' => array('User.id' => $this->request->data['UserSetting']['user_id']),
'fields' => array('User.org_id')
));
if (
$this->_isSiteAdmin() ||
($this->_isAdmin() && ($user['User']['org_id'] == $this->Auth->user('org_id')))
) {
$userSetting['user_id'] = $this->request->data['UserSetting']['user_id'];
}
}
if (empty($this->request->data['UserSetting']['setting'])) {
throw new MethodNotAllowedException(__('This endpoint expects both a setting and a value to be set.'));
} else {
if (!$this->UserSetting->checkSettingValidity($this->request->data['UserSetting']['setting'])) {
throw new MethodNotAllowedException(__('Invalid setting.'));
}
$userSetting['setting'] = $this->request->data['UserSetting']['setting'];
}
$userSetting['value'] = empty($this->request->data['UserSetting']['value']) ? '' :
json_encode(json_decode($this->request->data['UserSetting']['value'], true));
$existingSetting = $this->UserSetting->find('first', array(
'recursive' => -1,
'conditions' => array(
'UserSetting.user_id' => $userSetting['user_id'],
'UserSetting.setting' => $userSetting['setting']
)
));
if (empty($existingSetting)) {
$this->UserSetting->create();
} else {
$userSetting['id'] = $existingSetting['UserSetting']['id'];
}
// save the setting
$result = $this->UserSetting->save(array('UserSetting' => $userSetting));
if ($result) {
// if we've managed to save our setting
if ($this->_isRest()) {
// if we are dealing with an API request
$userSetting = $this->UserSetting->find('first', array(
'recursive' => -1,
'conditions' => array('UserSetting.id' => $this->UserSetting->id)
));
return $this->RestResponse->viewData($userSetting, $this->response->type());
} else {
// if we are dealing with a UI request, redirect the user to the user view with the proper flash message
$this->Flash->success(__('Setting saved.'));
$this->redirect(array('controller' => 'user_settings', 'action' => 'index', $this->Auth->User('id')));
}
} else {
// if we've failed saving our setting
if ($this->_isRest()) {
// if we are dealing with an API request
return $this->RestResponse->saveFailResponse('UserSettings', 'add', false, $this->UserSetting->validationErrors, $this->response->type());
} else {
/*
* if we are dealing with a UI request, simply set an error in a flash message
* and render the view of this endpoint, pre-populated with the submitted values.
*/
$this->Flash->error(__('Setting could not be saved.'));
}
}
}
if ($this->_isRest()) {
// GET request via the API should describe the endpoint
return $this->RestResponse->describe('UserSettings', 'setSetting', false, $this->response->type());
} else {
// load the valid settings from the model
$validSettings = $this->UserSetting->validSettings;
if ($this->_isSiteAdmin()) {
$users = $this->UserSetting->User->find('list', array(
'recursive' => -1,
'fields' => array('User.id', 'User.email')
));
} else if ($this->_isAdmin()) {
$users = $this->UserSetting->User->find('list', array(
'recursive' => -1,
'conditions' => array('User.org_id' => $this->Auth->user('org_id')),
'fields' => array('User.id', 'User.email')
));
} else {
$users = array($this->Auth->user('id') => $this->Auth->user('email'));
}
if (!empty($user_id) && $this->request->is('get')) {
$this->request->data['UserSetting']['user_id'] = $user_id;
}
$this->set('users', $users);
$this->set('validSettings', $validSettings);
}
}
public function getSetting($user_id, $setting)
{
if (!$this->UserSetting->checkSettingValidity($setting)) {
throw new MethodNotAllowedException(__('Invalid setting.'));
}
$userSetting = $this->UserSetting->find('first', array(
'recursive' => -1,
'conditions' => array(
'UserSetting.user_id' => $user_id,
'UserSetting.setting' => $setting
),
'contain' => array('User.id', 'User.org_id')
));
$checkAccess = $this->UserSetting->checkAccess($this->Auth->user(), $userSetting, $user_id);
if (empty($checkAccess)) {
throw new MethodNotAllowedException(__('Invalid setting.'));
}
if (!empty($userSetting)) {
$userSetting = json_encode($userSetting['UserSetting']['value']);
} else {
$userSetting = '[]';
}
return $this->RestResponse->viewData($userSetting, $this->response->type(), false, true);
}
public function delete($id = false)
{
if ($this->request->is('get') && $this->_isRest()) {
/*
* GET request via the API should describe the endpoint
* Unlike with the add() endpoint, we want to run this check before doing anything else,
* in order to allow us to reach this endpoint without passing a valid ID
*/
return $this->RestResponse->describe('UserSettings', 'delete', false, $this->response->type());
}
// check if the ID is valid and whether a user setting with the given ID exists
if (empty($id) || !is_numeric($id)) {
throw new InvalidArgumentException(__('Invalid ID passed.'));
}
$userSetting = $this->UserSetting->find('first', array(
'recursive' => -1,
'conditions' => array(
'UserSetting.id' => $id
),
'contain' => array('User.id', 'User.org_id')
));
if (empty($userSetting)) {
throw new NotFoundException(__('Invalid user setting.'));
}
$checkAccess = $this->UserSetting->checkAccess($this->Auth->user(), $userSetting);
if (!$checkAccess) {
throw new NotFoundException(__('Invalid user setting.'));
}
if ($this->request->is('post') || $this->request->is('delete')) {
// Delete the setting that we were after.
$result = $this->UserSetting->delete($userSetting['UserSetting']['id']);
if ($result) {
// set the response for both the UI and API
$message = __('Setting deleted.');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('UserSettings', 'delete', $id, $this->response->type(), $message);
} else {
$this->Flash->success($message);
}
} else {
// set the response for both the UI and API
$message = __('Setting could not be deleted.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('UserSettings', 'delete', $id, $message, $this->response->type());
} else {
$this->Flash->error($message);
}
}
/*
* The API responses stopped executing this function and returned a serialised response to the user.
* For UI users, redirect to where they issued the request from.
*/
$this->redirect($this->referer());
} else {
throw new MethodNotAllowedException(__('Expecting POST or DELETE request.'));
}
}
}

View File

@ -75,7 +75,8 @@ class AppModel extends Model
13 => false, 14 => false, 15 => false, 18 => false, 19 => false, 20 => false,
21 => false, 22 => false, 23 => false, 24 => false, 25 => false, 26 => false,
27 => false, 28 => false, 29 => false, 30 => false, 31 => false, 32 => false,
33 => false, 34 => false, 35 => false, 36 => false, 37 => false, 38 => false
33 => false, 34 => false, 35 => false, 36 => false, 37 => false, 38 => false,
39 => false
);
public $advanced_updates_description = array(
@ -1246,6 +1247,19 @@ class AppModel extends Model
$sqlArray[] = "ALTER TABLE servers ADD priority int(11) NOT NULL DEFAULT 0;";
$indexArray[] = array('servers', 'priority');
break;
case 39:
$sqlArray[] = "CREATE TABLE IF NOT EXISTS user_settings (
`id` int(11) NOT NULL AUTO_INCREMENT,
`key` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` text,
`user_id` int(11) NOT NULL,
`timestamp` int(11) NOT NULL,
PRIMARY KEY (id),
INDEX `key` (`key`),
INDEX `user_id` (`user_id`),
INDEX `timestamp` (`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
break;
case 'fixNonEmptySharingGroupID':
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
@ -1514,6 +1528,17 @@ class AppModel extends Model
return ucfirst($field) . ' cannot be empty.';
}
public function valueIsJson($value)
{
$field = array_keys($value);
$field = $field[0];
$json_decoded = json_decode($value[$field]);
if ($json_decoded === null) {
return __('Invalid JSON.');
}
return true;
}
public function valueIsID($value)
{
$field = array_keys($value);

View File

@ -799,7 +799,6 @@ class Attribute extends AppModel
$this->complexTypeTool = new ComplexTypeTool();
$this->data['Attribute']['value'] = $this->complexTypeTool->refangValue($this->data['Attribute']['value'], $this->data['Attribute']['type']);
if (!empty($this->data['Attribute']['object_id']) && empty($this->data['Attribute']['object_relation'])) {
return false;
}

View File

@ -2980,10 +2980,13 @@ class Event extends AppModel
$sgModel = ClassRegistry::init('SharingGroup');
$userCount = count($users);
$this->UserSetting = ClassRegistry::init('UserSetting');
foreach ($users as $k => $user) {
$body = $this->__buildAlertEmailBody($event[0], $user, $oldpublish, $sgModel);
$bodyNoEnc = "A new or modified event was just published on " . $this->__getAnnounceBaseurl() . "/events/view/" . $event[0]['Event']['id'];
$this->User->sendEmail(array('User' => $user), $body, $bodyNoEnc, $subject);
if ($this->UserSetting->checkPublishFilter($user, $event[0])) {
$body = $this->__buildAlertEmailBody($event[0], $user, $oldpublish, $sgModel);
$bodyNoEnc = "A new or modified event was just published on " . $this->__getAnnounceBaseurl() . "/events/view/" . $event[0]['Event']['id'];
$this->User->sendEmail(array('User' => $user), $body, $bodyNoEnc, $subject);
}
if ($processId) {
$this->Job->id = $processId;
$this->Job->saveField('progress', $k / $userCount * 100);

View File

@ -202,7 +202,8 @@ class User extends AppModel
'finderQuery' => '',
'counterQuery' => ''
),
'Post'
'Post',
'UserSetting'
);
public $actsAs = array(
@ -605,7 +606,7 @@ class User extends AppModel
public function getAuthUser($id)
{
if (empty($id)) {
throw new Exception('Invalid user ID.');
throw new NotFoundException('Invalid user ID.');
}
$conditions = array('User.id' => $id);
$user = $this->find('first', array('conditions' => $conditions, 'recursive' => -1,'contain' => array('Organisation', 'Role', 'Server')));

252
app/Model/UserSetting.php Normal file
View File

@ -0,0 +1,252 @@
<?php
App::uses('AppModel', 'Model');
class UserSetting extends AppModel
{
public $useTable = 'user_settings';
public $recursive = -1;
public $actsAs = array(
'SysLogLogable.SysLogLogable' => array(
'userModel' => 'User',
'userKey' => 'user_id',
'change' => 'full'),
'Containable',
);
public $validate = array(
'json' => array(
'isValidJson' => array(
'rule' => array('isValidJson'),
)
)
);
public $belongsTo = array(
'User'
);
public $validSettings = array(
'publish_alert_filter' => array(
'placeholder' => array(
'AND' => array(
'NOT' => array(
'EventTag.name' => array(
'%osint%'
)
),
'OR' => array(
'Tag.name' => array(
'tlp:green',
'tlp:amber',
'tlp:red',
'%privint%'
)
)
)
)
)
);
// massage the data before we send it off for validation before saving anything
public function beforeValidate($options = array())
{
parent::beforeValidate();
// add a timestamp if it is not set
if (empty($this->data['UserSetting']['timestamp'])) {
$this->data['UserSetting']['timestamp'] = time();
}
if (!empty($this->data['UserSetting']['value']) && $this->data['UserSetting']['value'] !== 'null') {
if (is_array($this->data['UserSetting']['value'])) {
$this->data['UserSetting']['value'] = json_encode($this->data['UserSetting']['value']);
}
} else {
$this->data['UserSetting']['value'] = '[]';
}
debug($this->data);
return true;
}
// Once we run a find, let us decode the JSON field so we can interact with the contents as if it was an array
public function afterFind($results, $primary = false)
{
foreach ($results as $k => $v) {
$results[$k]['UserSetting']['value'] = json_decode($v['UserSetting']['value'], true);
}
return $results;
}
public function checkSettingValidity($setting)
{
return isset($this->validSettings[$setting]);
}
/*
* canModify expects an auth user object or a user ID and a loaded setting as input parameters
* check if the user can modify/remove the given entry
* returns true for site admins
* returns true for org admins if setting["User"]["org_id"] === $user["org_id"]
* returns true for any user if setting["user_id"] === $user["id"]
*/
public function checkAccess($user, $setting, $user_id = false)
{
if (is_numeric($user)) {
$user = $this->User->getAuthUser($user);
}
if (empty($setting) && !empty($user_id) && is_numeric($user_id)) {
$userToCheck = $this->User->find('first', array(
'conditions' => array('User.id' => $user_id),
'recursive' => -1
));
if (empty($userToCheck)) {
return false;
}
$setting = array(
'User' => array(
'org_id' => $userToCheck['User']['org_id']
),
'UserSetting' => array(
'user_id' => $userToCheck['User']['id']
)
);
}
if ($user['Role']['perm_site_admin']) {
return true;
} else if ($user['Role']['perm_admin']) {
if ($user['org_id'] === $setting['User']['org_id']) {
return true;
}
} else {
if (
$user['id'] === $setting['UserSetting']['user_id'] &&
(!Configure::check('MISP.disableUserSelfManagement') || Configure::check('MISP.disableUserSelfManagement'))
) {
return true;
}
}
return false;
}
/*
* Check whether the event is something the user is interested (to be alerted on)
*
*/
public function checkPublishFilter($user, $event)
{
$rule = $this->find('first', array(
'recursive' => -1,
'conditions' => array(
'UserSetting.user_id' => $user['id'],
'UserSetting.setting' => 'publish_alert_filter'
)
));
// We should return true if no setting has been configured, or there's a setting with an empty value
if (empty($rule) || empty($rule['UserSetting']['value'])) {
return true;
}
// recursively evaluate the boolean tree to true/false and return the value
$result = $this->__recursiveConvert($rule['UserSetting']['value'], $event);
if (isset($result[0])) {
return $result[0];
} else {
return false;
}
}
/*
* Convert a complex rule set recursively
* takes as params a rule branch and an event to check against
* evaluate whether the rule set evaluates as true/false
* The full evaluation involves resolving the boolean branches
* valid boolean operators are OR, AND, NOT all capitalised as strings
*/
private function __recursiveConvert($rule, $event)
{
$toReturn = array();
if (is_array($rule)) {
foreach ($rule as $k => $v) {
if (in_array($k, array('OR', 'NOT', 'AND'))) {
$parts = $this->__recursiveConvert($v, $event);
$temp = null;
foreach ($parts as $partValue) {
if ($temp === null) {
$temp = ($k === 'NOT') ? !$partValue : $partValue;
} else {
if ($k === 'OR') {
$temp = $temp || $partValue;
} elseif ($k === 'AND') {
$temp = $temp && $partValue;
} else {
$temp = $temp && !$partValue;
}
}
}
$toReturn []= $temp;
} else {
$toReturn []= $this->__checkEvent($k, $v, $event);
}
}
return $toReturn;
} else {
return false;
}
}
/*
* Checks if an event matches the given rule
* valid filters:
* - AttributeTag.name
* - EventTag.name
* - Tag.name (checks against both event and attribute tags)
* - Orgc.uuid
* - Orgc.name
* Values passed can be used for direct string comparisons or alternatively
* as substring matches by encapsulating the string in a pair of "%" characters
* Each rule can take a list of values
*/
private function __checkEvent($rule, $lookup_values, $event)
{
if (!is_array($lookup_values)) {
$lookup_values = array($lookup_values);
}
foreach ($lookup_values as $k => $v) {
$lookup_values[$k] = mb_strtolower($v);
}
if ($rule === 'AttributeTag.name') {
$values = array_merge(
Hash::extract($event, 'Attribute.{n}.AttributeTag.{n}.Tag.name'),
Hash::extract($event, 'Object.{n}.Attribute.{n}.AttributeTag.{n}.Tag.name')
);
} else if ($rule === 'EventTag.name') {
$values = Hash::extract($event, 'EventTag.{n}.Tag.name');
} else if ($rule === 'Orgc.name') {
$values = array($event['Event']['Orgc']['name']);
} else if ($rule === 'Orgc.uuid') {
$values = array($event['Event']['Orgc']['uuid']);
} else if ($rule === 'Tag.name') {
$values = array_merge(
Hash::extract($event, 'Attribute.{n}.AttributeTag.{n}.Tag.name'),
Hash::extract($event, 'Object.{n}.Attribute.{n}.AttributeTag.{n}.Tag.name'),
Hash::extract($event, 'EventTag.{n}.Tag.name')
);
}
if (!empty($values)) {
foreach ($values as $extracted_value) {
$extracted_value = mb_strtolower($extracted_value);
foreach ($lookup_values as $lookup_value) {
$lookup_value_trimmed = trim($lookup_value, "%");
if (strlen($lookup_value_trimmed) != strlen($lookup_value)) {
if (strpos($extracted_value, $lookup_value_trimmed) !== false) {
return true;
}
} else {
if ($extracted_value === $lookup_value) {
return true;
}
}
}
}
}
return false;
}
}

View File

@ -18,9 +18,7 @@
if (isset($action['postLink'])) {
echo $this->Form->postLink(
'',
array(
'url' => $url
),
$url,
array(
'class' => $this->FontAwesome->getClass($action['icon']) . ' black ' . (empty($action['class']) ? '' : h($action['class'])),
'title' => empty($action['title']) ? '' : h($action['title']),

View File

@ -1,7 +1,12 @@
<?php
$data = h(Hash::extract($row, $field['data_path']));
echo sprintf(
'<div style="white-space:pre;" class="blue bold">%s</div>',
json_encode($data, JSON_PRETTY_PRINT)
'<div class="json_container_%s"></div>',
h($k)
);
?>
<script type="text/javascript">
$(document).ready(function() {
$('.json_container_<?php echo h($k);?>').html(syntaxHighlightJson(<?php echo json_encode($data); ?>, 4));
});
</script>

View File

@ -446,6 +446,16 @@
'url' => '/users/view/me',
'text' => __('My Profile')
));
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'user_settings_index_me',
'url' => '/user_settings/index/user_id:me',
'text' => __('My Settings')
));
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'user_settings_set',
'url' => '/user_settings/setSetting',
'text' => __('Set Setting')
));
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'url' => '/users/dashboard',
'text' => __('Dashboard')
@ -668,6 +678,16 @@
));
}
if ($isAdmin) {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'user_settings_index',
'url' => '/user_settings/index/user_id:all',
'text' => __('User settings')
));
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'user_settings_set',
'url' => '/user_settings/setSetting',
'text' => __('Set Setting')
));
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'contact',
'url' => '/admin/users/email',
@ -913,7 +933,6 @@
}
}
break;
case 'decayingModel':
if ($isAdmin) {
if ($isSiteAdmin && ($menuItem === 'view' || $menuItem === 'index')) {

View File

@ -147,6 +147,14 @@
'text' => __('My Profile'),
'url' => '/users/view/me'
),
array(
'text' => __('My Settings'),
'url' => '/user_settings/index/user_id:me'
),
array(
'text' => __('Set Setting'),
'url' => '/user_settings/setSetting'
),
array(
'text' => __('Dashboard'),
'url' => '/users/dashboard'
@ -270,6 +278,14 @@
'text' => __('List Users'),
'url' => '/admin/users/index'
),
array(
'text' => __('List User Settings'),
'url' => '/user_settings/index/user_id:all'
),
array(
'text' => __('Set User Setting'),
'url' => '/user_settings/setSetting'
),
array(
'text' => __('Add User'),
'url' => '/admin/users/add'

View File

@ -0,0 +1,3 @@
<?php
echo h(Hash::extract($row, $field['data_path'])[0]);
?>

View File

@ -0,0 +1,103 @@
<?php
/*
* echo $this->element('/genericElements/IndexTable/index_table', array(
* 'top_bar' => (
* // search/filter bar information compliant with ListTopBar
* ),
* 'data' => array(
// the actual data to be used
* ),
* 'fields' => array(
* // field list with information for the paginator
* ),
* 'title' => optional title,
* 'description' => optional description
* ));
*
*/
echo '<div class="index">';
echo $this->element('/genericElements/IndexTable/index_table', array(
'data' => array(
'data' => $data,
'top_bar' => array(
'children' => array(
array(
'type' => 'simple',
'children' => array(
array(
'active' => $context === 'me',
'url' => $baseurl . '/user_settings/index/user_id:me',
'text' => __('Me'),
),
array(
'active' => $context === 'org',
'url' => $baseurl . '/user_settings/index/user_id:org',
'text' => __('Organisation'),
'requirement' => $isAdmin
),
array(
'active' => $context === 'all',
'url' => $baseurl . '/user_settings/index/user_id:all',
'text' => __('All'),
'requirement' => $isSiteAdmin
)
)
)
)
),
'fields' => array(
array(
'name' => __('Id'),
'sort' => 'id',
'class' => 'short',
'data_path' => 'UserSetting.id'
),
array(
'name' => __('User'),
'sort' => 'User.email',
'class' => 'short',
'data_path' => 'User.email'
),
array(
'name' => __('Setting'),
'class' => 'short',
'sort' => 'type',
'data_path' => 'UserSetting.setting'
),
array(
'name' => __('Value'),
'sort' => 'type',
'element' => 'json',
'data_path' => 'UserSetting.value'
)
),
'title' => __('User settings management'),
'description' => __('Manage the individual user settings.'),
'actions' => array(
array(
'url' => '/user_settings/setSetting',
'url_params_data_paths' => array(
'UserSetting.user_id',
'UserSetting.setting'
),
'icon' => 'edit'
),
array(
'url' => '/user_settings/delete',
'url_params_data_paths' => array(
'UserSetting.id'
),
'icon' => 'trash',
'postLink' => true,
'postLinkConfirm' => __('Are you sure you wish to delete this entry?')
)
)
)
));
echo '</div>';
if ($context === 'me' || (!$isAdmin && !$isSiteAdmin)) {
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'user_settings_index_me'));
} else {
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'user_settings_index'));
}
?>

View File

@ -0,0 +1,77 @@
<?php
echo sprintf(
'<div class="usersetting form">%s<fieldset><legend>%s</legend>%s</fieldset>%s%s</div>',
$this->Form->create('UserSetting'),
__('Set User Setting'),
sprintf(
'%s%s%s',
$this->Form->input(
'user_id',
array(
'div' => 'clear',
'class' => 'input input-xxlarge',
'options' => array($users),
'disabled' => count($users) === 1
)
),
$this->Form->input(
'setting',
array(
'div' => 'clear',
'class' => 'input input-xxlarge',
'options' => array_combine(array_keys($validSettings), array_keys($validSettings))
)
),
$this->Form->input(
'value',
array(
'div' => 'clear',
'class' => 'input input-xxlarge',
'type' => 'textarea',
)
)
),
$this->Form->button(__('Submit'), array('class' => 'btn btn-primary')),
$this->Form->end()
);
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'user_settings_set'));
?>
<script type="text/javascript">
var validSettings = <?php echo json_encode($validSettings); ?>;
$(document).ready(function() {
loadUserSettingValue();
changeUserSettingPlaceholder();
$('#UserSettingSetting').on('change', function() {
loadUserSettingValue();
changeUserSettingPlaceholder();
});
$('#UserSettingUserId').on('change', function() {
loadUserSettingValue();
changeUserSettingPlaceholder();
});
});
function loadUserSettingValue() {
var user_id = $('#UserSettingUserId').val();
var setting = $('#UserSettingSetting').val();
$.ajax({
type:"get",
url: baseurl + "/user_settings/getSetting/" + user_id + "/" + setting,
success: function (data, textStatus) {
if (data === '[]') {
var data = '';
} else {
data = JSON.parse(data);
data = JSON.stringify(data, undefined, 4);
}
$('#UserSettingValue').val(data);
}
});
}
function changeUserSettingPlaceholder() {
var setting = $('#UserSettingSetting').val();
if (setting in validSettings) {
$('#UserSettingValue').attr("placeholder", "Example:\n" + JSON.stringify(validSettings[setting]["placeholder"], undefined, 4));
}
}
</script>

View File

View File

@ -4285,7 +4285,7 @@ function syntaxHighlightJson(json, indent) {
if (typeof json == 'string') {
json = JSON.parse(json);
}
json = JSON.stringify(json, undefined, 2);
json = JSON.stringify(json, undefined, indent);
json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/(?:\r\n|\r|\n)/g, '<br>').replace(/ /g, '&nbsp;');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'json_number';