5 changes: 2 additions & 3 deletions INSTALL/INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ sudo add-apt-repository ppa:ondrej/php
- for apache

```bash
sudo apt install apache2 mariadb-server git php8.2-intl php8.2-mbstring php8.2-dom php8.2-xml unzip php8.2-ldap php8.2-sqlite3 ph8.2p-curl sqlite libapache2-mod-php php8.2-mysql
sudo apt install apache2 mariadb-server git php8.2 php8.2-intl php8.2-mbstring php8.2-dom php8.2-xml unzip php8.2-ldap php8.2-sqlite3 php8.2-curl sqlite libapache2-mod-php php8.2-mysql
```

- for nginx

```bash
sudo apt install nginx mariadb-server git php8.2-intl php8.2-mbstring php8.2-dom php8.2-xml unzip php8.2-ldap php8.2-sqlite3 ph8.2p-curl sqlite php8.2-mysql
sudo apt install nginx mariadb-server git php8.2 php8.2-intl php8.2-mbstring php8.2-dom php8.2-xml unzip php8.2-ldap php8.2-sqlite3 php8.2-curl sqlite php8.2-mysql
```


Expand All @@ -48,7 +48,6 @@ Install composer:
~~~bash
cd
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
sudo mv composer.phar /usr/local/bin/composer
Expand Down
8 changes: 8 additions & 0 deletions config/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@
Log::setConfig(Configure::consume('Log'));
Security::setSalt(Configure::consume('Security.salt'));

Configure::write('Session', [
'defaults' => 'php',
'ini' => [
'session.cookie_httponly' => true,
'session.cookie_secure' => true,
]
]);

/*
* Setup detectors for mobile and tablet.
*/
Expand Down
1 change: 1 addition & 0 deletions config/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
// Register scoped middleware for in scopes.
$builder->registerMiddleware('csrf', new CsrfProtectionMiddleware([
'httponly' => true,
'secure' => true,
]));
/*
* Apply a middleware to the current route scope.
Expand Down
78 changes: 74 additions & 4 deletions src/Command/SummaryCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,14 @@ protected function _collectChangedForNationality($nationality, $days, $folderPat
fwrite($file_input, $message);
$this->io->out($message);
$logsUsers = $this->_fetchLogsForUsers($userID, $days);
$modifiedUsers = $this->_formatLogsForTable($logsUsers);
$userByIDs = Hash::combine($userForOrg, '{n}.id', '{n}');
$logsUserMetaFields = $this->_fetchLogsForUserMetaFields($userID, $days);
$logsUserMetaFields = $this->_formatUserMetafieldLogs($logsUserMetaFields, $userByIDs);
$logsUsersCombined = array_merge($logsUsers, $logsUserMetaFields);
usort($logsUsersCombined, function($a, $b) {
return $a['created'] < $b['created'] ? -1 : 1;
});
$modifiedUsers = $this->_formatLogsForTable($logsUsersCombined);
foreach ($modifiedUsers as $row) {
fputcsv($file_input, $row);
}
Expand Down Expand Up @@ -167,6 +174,45 @@ protected function _fetchLogsForUsers(array $userIDs = [], int $days=7): array
]);
}

protected function _fetchLogsForUserMetaFields(array $userIDs = [], int $days=7): array
{
if (empty($userIDs)) {
return [];
}
$logs = $this->_fetchLogs([
'contain' => ['Users'],
'conditions' => [
'model' => 'MetaFields',
'request_action IN' => ['add', 'edit', 'delete'],
'AuditLogs.created >=' => FrozenTime::now()->subDays($days),
]
]);
$metaFieldLogs = array_filter($logs, function ($log) use ($userIDs) {
return !empty($log['changed']['scope']) && $log['changed']['scope'] === 'user' && in_array($log['changed']['parent_id'], $userIDs);
});
$metaFieldDeletionLogs = array_filter($logs, function ($log) use ($userIDs) {
return $log['request_action'] === 'delete';
});
foreach ($metaFieldDeletionLogs as $i => $log) {
$latestAssociatedLog = $this->_fetchLogs([
'contain' => ['Users'],
'conditions' => [
'model' => 'MetaFields',
'request_action IN' => ['add'],
'model_id' => $log['model_id'],
],
'order' => ['AuditLogs.created' => 'DESC'],
'limit' => 1,
]);
if (!empty($latestAssociatedLog)) {
$metaFieldDeletionLogs[$i]['changed']['orig_value'] = $latestAssociatedLog[0]['changed']['value'];
$metaFieldDeletionLogs[$i]['changed']['value'] = '';
}
}
$allLogs = array_merge($metaFieldLogs, $metaFieldDeletionLogs);
return $allLogs;
}

protected function _fetchLogsForOrgs(array $orgIDs = [], int $days = 7): array
{
if (empty($orgIDs)) {
Expand Down Expand Up @@ -201,18 +247,42 @@ protected function _fetchLogsForIndividuals(array $individualID = [], int $days

protected function _fetchLogs(array $options=[]): array
{
$logs = $this->AuditLogs->find()
$query = $this->AuditLogs->find()
->contain($options['contain'])
->where($options['conditions'])
->where($options['conditions']);
if (!empty($options['order'])) {
$query = $query->order($options['order']);
}
if (!empty($options['limit'])) {
$query = $query
->limit($options['limit'])
->page(1);
}
$logs = $query
->enableHydration(false)
->all()->toList();
return array_map(function ($log) {
$log['changed'] = is_resource($log['changed']) ? stream_get_contents($log['changed']) : $log['changed'];
$log['changed'] = json_decode($log['changed']);
$log['changed'] = json_decode($log['changed'], true);
return $log;
}, $logs);
}

protected function _formatUserMetafieldLogs($logEntries, $userByIDs): array
{
return array_map(function($log) use ($userByIDs) {
$log['model'] = 'Users';
$log['request_action'] = 'edit';
$log['changed'] = [
$log['model_title'] => [
$log['changed']['orig_value'] ?? '',
$log['changed']['value']
]
];
return $log;
}, $logEntries);
}

protected function _formatLogsForTable($logEntries): array
{
$header = ['Model', 'Action', 'Editor user', 'Log ID', 'Datetime', 'Change'];
Expand Down
8 changes: 6 additions & 2 deletions src/Controller/Component/CRUDComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,18 @@ public function index(array $options): void
$query->order($sort . ' ' . $direction);
}
}
if ($this->metaFieldsSupported() && !$this->Controller->ParamHandler->isRest()) {
if ($this->metaFieldsSupported()) {
$query = $this->includeRequestedMetaFields($query);
}

if (!$this->Controller->ParamHandler->isRest()) {
$this->setRequestedEntryAmount();
} else if (!empty($this->request->getQuery('limit'))) {
$this->Controller->paginate['limit'] = PHP_INT_MAX; // Make sure to download the entire filtered table
}
$data = $this->Controller->paginate($query, $this->Controller->paginate ?? []);
$totalCount = $this->Controller->getRequest()->getAttribute('paging')[$this->TableAlias]['count'];
if ($this->Controller->ParamHandler->isRest()) {
$data = $this->Controller->paginate($query, $this->Controller->paginate ?? []);
if (isset($options['hidden'])) {
$data->each(function($value, $key) use ($options) {
$hidden = is_array($options['hidden']) ? $options['hidden'] : [$options['hidden']];
Expand Down Expand Up @@ -795,6 +796,9 @@ protected function setRequestedEntryAmount()
$user = $this->Controller->ACL->getUser();
$tableSettings = IndexSetting::getTableSetting($user, $this->Table);
if (!empty($tableSettings['number_of_element'])) {
if ($tableSettings['number_of_element'] === 'all') {
$tableSettings['number_of_element'] = 10000; // Even with all, sure not to return too much data
}
$this->Controller->paginate['limit'] = intval($tableSettings['number_of_element']);
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/Controller/Component/NavigationComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class NavigationComponent extends Component
'Instance' => 'server',
'Tags' => 'tags',
'API' => 'code',
'EnumerationCollections' => 'list',
];

public function initialize(array $config): void
Expand Down Expand Up @@ -163,6 +164,7 @@ public function getFullConfig($bcf, $request)
'LocalTools',
'UserSettings',
'MailingLists',
'EnumerationCollections',
];
foreach ($CRUDControllers as $controller) {
$bcf->setDefaultCRUDForModel($controller);
Expand Down
28 changes: 21 additions & 7 deletions src/Controller/UserSettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,21 +100,34 @@ public function edit($id)
'id' => $id
])->first();

if (!$this->isLoggedUserAllowedToEdit($entity)) {
throw new NotFoundException(__('Invalid {0}.', 'user setting'));
$currentUser = $this->ACL->getUser();
$validUsers = [];
$individual_ids = [];
if (!$currentUser['role']['perm_admin']) {
if ($currentUser['role']['perm_org_admin']) {
$validUsers = $this->Users->find('list')->select(['id', 'username'])->order(['username' => 'asc'])->where(['organisation_id' => $currentUser['organisation']['id']])->all()->toArray();
} else {
$validUsers = [$currentUser['id'] => $currentUser['username']];
}
} else {
$validUsers = $this->Users->find('list')->select(['id', 'username'])->order(['username' => 'asc'])->all()->toArray();
}

$entity = $this->CRUD->edit($id, [
'redirect' => ['action' => 'index', $entity->user_id]
'redirect' => ['action' => 'index', $entity->user_id],
'beforeSave' => function ($data) use ($validUsers) {
if (!in_array($data['user_id'], array_keys($validUsers))) {
throw new MethodNotAllowedException(__('You cannot edit the given user.'));
}
return $data;
}
]);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$dropdownData = [
'user' => $this->UserSettings->Users->find('list', [
'sort' => ['username' => 'asc']
]),
'user' => $validUsers,
];
$this->set(compact('dropdownData'));
$this->set('user_id', $this->entity->user_id);
Expand Down Expand Up @@ -259,8 +272,9 @@ private function isLoggedUserAllowedToEdit($setting): bool
if (empty($setting)) {
return false;
}
} else {
$isAllowed = $setting->user_id == $currentUser->id;
}
$isAllowed = $setting->user_id == $currentUser->id;
}
return $isAllowed;
}
Expand Down
6 changes: 5 additions & 1 deletion src/Controller/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ public function add()
} else {
$validRoles = $this->Users->Roles->find('list')->order(['name' => 'asc'])->all()->toArray();
}
$defaultRole = $this->Users->Roles->find()->select(['id'])->where(['is_default' => true])->first()->toArray();
$defaultRole = $this->Users->Roles->find()->select(['id'])->where(['is_default' => true])->first();
if (empty($defaultRole)) {
$defaultRole = $this->Users->Roles->find()->select(['id'])->first();
}
$defaultRole = $defaultRole->toArray();
$individuals = $this->Users->Individuals->find('list', $individuals_params)->toArray();
$this->CRUD->add([
'beforeMarshal' => function($data) {
Expand Down
2 changes: 1 addition & 1 deletion src/VERSION.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"version": "1.14",
"version": "1.15",
"application": "Cerebrate"
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<option value="50" <?= $numberOfElement == 50 ? 'selected' : '' ?>><?= __('50') ?></option>
<option value="100" <?= $numberOfElement == 100 ? 'selected' : '' ?>><?= __('100') ?></option>
<option value="200" <?= $numberOfElement == 200 ? 'selected' : '' ?>><?= __('200') ?></option>
<option value="all" <?= $numberOfElement == 'all' ? 'selected' : '' ?>><?= __('All') ?></option>
</select>
</label>

Expand Down