diff --git a/config/bootstrap.php b/config/bootstrap.php index 739122d..6da31ba 100644 --- a/config/bootstrap.php +++ b/config/bootstrap.php @@ -173,6 +173,14 @@ Mailer::setConfig(Configure::consume('Email')); 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. */ diff --git a/config/routes.php b/config/routes.php index ac1ab26..32712cd 100644 --- a/config/routes.php +++ b/config/routes.php @@ -49,6 +49,7 @@ $routes->scope('/', function (RouteBuilder $builder) { // Register scoped middleware for in scopes. $builder->registerMiddleware('csrf', new CsrfProtectionMiddleware([ 'httponly' => true, + 'secure' => true, ])); /* * Apply a middleware to the current route scope. diff --git a/src/Command/SummaryCommand.php b/src/Command/SummaryCommand.php index cf5debf..f7622a1 100644 --- a/src/Command/SummaryCommand.php +++ b/src/Command/SummaryCommand.php @@ -80,7 +80,14 @@ class SummaryCommand extends Command 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); } @@ -167,6 +174,45 @@ class SummaryCommand extends Command ]); } + 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)) { @@ -201,18 +247,42 @@ class SummaryCommand extends Command 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']; diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index 9891b20..a1ecba1 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -96,17 +96,18 @@ class CRUDComponent extends Component $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']]; @@ -795,6 +796,9 @@ class CRUDComponent extends Component $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']); } } diff --git a/src/Controller/Component/NavigationComponent.php b/src/Controller/Component/NavigationComponent.php index 3c606e8..452d575 100644 --- a/src/Controller/Component/NavigationComponent.php +++ b/src/Controller/Component/NavigationComponent.php @@ -37,6 +37,7 @@ class NavigationComponent extends Component 'Instance' => 'server', 'Tags' => 'tags', 'API' => 'code', + 'EnumerationCollections' => 'list', ]; public function initialize(array $config): void @@ -163,6 +164,7 @@ class NavigationComponent extends Component 'LocalTools', 'UserSettings', 'MailingLists', + 'EnumerationCollections', ]; foreach ($CRUDControllers as $controller) { $bcf->setDefaultCRUDForModel($controller); diff --git a/src/VERSION.json b/src/VERSION.json index 0b24643..423dd7e 100644 --- a/src/VERSION.json +++ b/src/VERSION.json @@ -1,4 +1,4 @@ { - "version": "1.14", + "version": "1.15", "application": "Cerebrate" } diff --git a/templates/element/genericElements/ListTopBar/group_table_action/numberOfElement.php b/templates/element/genericElements/ListTopBar/group_table_action/numberOfElement.php index 6b69c50..e3913d1 100644 --- a/templates/element/genericElements/ListTopBar/group_table_action/numberOfElement.php +++ b/templates/element/genericElements/ListTopBar/group_table_action/numberOfElement.php @@ -10,6 +10,7 @@ $numberOfElementSelectSeed = 'seed-' . mt_rand(); +