diff --git a/core/src/Revolution/Processors/System/Log/GetList.php b/core/src/Revolution/Processors/System/Log/GetList.php index 6558be5951b..0c62c835acb 100644 --- a/core/src/Revolution/Processors/System/Log/GetList.php +++ b/core/src/Revolution/Processors/System/Log/GetList.php @@ -12,13 +12,23 @@ namespace MODX\Revolution\Processors\System\Log; use MODX\Revolution\Formatter\modManagerDateFormatter; +use MODX\Revolution\modAccessPolicy; +use MODX\Revolution\modAccessPolicyTemplate; use MODX\Revolution\modCategory; +use MODX\Revolution\modChunk; use MODX\Revolution\modContext; use MODX\Revolution\modContextSetting; +use MODX\Revolution\modContentType; +use MODX\Revolution\modDashboard; +use MODX\Revolution\modDashboardWidget; use MODX\Revolution\modDocument; use MODX\Revolution\modManagerLog; use MODX\Revolution\modMenu; +use MODX\Revolution\modNamespace; +use MODX\Revolution\modPlugin; use MODX\Revolution\modResource; +use MODX\Revolution\modResourceGroup; +use MODX\Revolution\modSnippet; use MODX\Revolution\modStaticResource; use MODX\Revolution\modSymLink; use MODX\Revolution\modSystemSetting; @@ -27,6 +37,8 @@ use MODX\Revolution\modUserSetting; use MODX\Revolution\modWebLink; use MODX\Revolution\Processors\Processor; +use MODX\Revolution\Sources\modMediaSource; +use MODX\Revolution\Transport\modTransportPackage; use xPDO\Om\xPDOObject; /** @@ -194,9 +206,10 @@ public function prepareLog(modManagerLog $log) $path = $this->modx->getOption("{$ns}.core_path", null, $nsCorePath) . 'model/'; $this->modx->addPackage($ns, $path); } + $obj = null; if (!empty($logArray['classKey']) && !empty($logArray['item'])) { $logArray['name'] = $logArray['classKey'] . ' (' . $logArray['item'] . ')'; - /** @var xPDOObject $obj */ + /** @var xPDOObject|null $obj */ $obj = $this->modx->getObject($logArray['classKey'], $logArray['item']); if ($obj && ($obj->get($obj->getPK()) === $logArray['item'])) { $nameField = $this->getNameField($logArray['classKey']); @@ -209,6 +222,14 @@ public function prepareLog(modManagerLog $log) } else { $logArray['name'] = $log->get('item'); } + + $managerUrl = $this->getManagerUrl( + $logArray['classKey'] ?? '', + $logArray['item'] ?? '', + $obj + ); + $logArray['managerUrl'] = $managerUrl; + $customFormat = $this->getProperty('dateFormat'); $logArray['occurred'] = !empty($customFormat) ? $this->formatter->format($logArray['occurred'], $customFormat) @@ -218,6 +239,126 @@ public function prepareLog(modManagerLog $log) return $logArray; } + /** + * Build manager URL for a log entry so the object can be opened in the manager. + * + * @param string $classKey Class key of the logged object + * @param string $item Primary key value stored in the log + * @param xPDOObject|null $obj Loaded object when available (used for context key, menu text, etc.) + * @return string|null Relative URL (e.g. ?a=resource/update&id=1) or null when no link is supported + */ + protected function getManagerUrl(string $classKey, string $item, ?xPDOObject $obj = null): ?string + { + if (empty($classKey) || $item === '' || $item === 'unknown') { + return null; + } + + $action = null; + $paramKey = 'id'; + $paramValue = $item; + + switch ($classKey) { + case modResource::class: + case modWebLink::class: + case modSymLink::class: + case modStaticResource::class: + case modDocument::class: + $action = 'resource/update'; + break; + case modContext::class: + $action = 'context/update'; + $paramKey = 'key'; + $paramValue = ($obj !== null) ? (string) $obj->get('key') : $item; + if ($paramValue === '') { + return null; + } + break; + case modTemplate::class: + $action = 'element/template/update'; + break; + case modChunk::class: + $action = 'element/chunk/update'; + break; + case modSnippet::class: + $action = 'element/snippet/update'; + break; + case modPlugin::class: + $action = 'element/plugin/update'; + break; + case modCategory::class: + $action = 'element/category/update'; + break; + case modUser::class: + $action = 'security/user/update'; + break; + case modMenu::class: + $action = 'element/menu/update'; + $paramKey = 'text'; + $paramValue = ($obj !== null) ? (string) $obj->get('text') : $item; + if ($paramValue === '') { + return null; + } + break; + case modSystemSetting::class: + return null; + case modContextSetting::class: + $action = 'context/update'; + $paramKey = 'key'; + $paramValue = ($obj !== null) ? (string) $obj->get('context_key') : $item; + if ($paramValue === '') { + return null; + } + break; + case modUserSetting::class: + $action = 'security/user/update'; + $paramValue = ($obj !== null) ? (string) $obj->get('user') : $item; + break; + case modAccessPolicy::class: + $action = 'security/access/policy/update'; + break; + case modAccessPolicyTemplate::class: + $action = 'security/access/policy/template/update'; + break; + case modResourceGroup::class: + $action = 'security/resourcegroup/update'; + break; + case modMediaSource::class: + $action = 'source/update'; + break; + case modNamespace::class: + $action = 'workspace/namespace/update'; + $paramKey = 'name'; + $paramValue = ($obj !== null) ? (string) $obj->get('name') : $item; + if ($paramValue === '') { + return null; + } + break; + case modDashboardWidget::class: + $action = 'system/dashboard/widget/update'; + break; + case modDashboard::class: + $action = 'system/dashboard/update'; + break; + case modTransportPackage::class: + $action = 'workspace/packages/view'; + $paramKey = 'signature'; + $paramValue = ($obj !== null) ? (string) $obj->get('signature') : $item; + if ($paramValue === '') { + return null; + } + break; + case modContentType::class: + $action = 'system/contenttype/update'; + break; + default: + return null; + } + + $params = [$paramKey => $paramValue]; + + return '?a=' . $action . '&' . http_build_query($params, '', '&', PHP_QUERY_RFC3986); + } + /** * Get the name field of the class * @param string $classKey diff --git a/manager/assets/modext/widgets/system/modx.grid.manager.log.js b/manager/assets/modext/widgets/system/modx.grid.manager.log.js index 82088d5cb05..8db2df95bd3 100644 --- a/manager/assets/modext/widgets/system/modx.grid.manager.log.js +++ b/manager/assets/modext/widgets/system/modx.grid.manager.log.js @@ -168,7 +168,7 @@ MODx.grid.ManagerLog = function(config) { ,baseParams: { action: 'System/Log/GetList' } - ,fields: ['id','user','username','occurred','action','classKey','item','name','menu'] + ,fields: ['id','user','username','occurred','action','classKey','item','name','managerUrl','menu'] ,showActionsColumn: false ,autosave: false ,paging: true @@ -193,13 +193,32 @@ MODx.grid.ManagerLog = function(config) { header: _('object') ,dataIndex: 'name' ,width: 300 - ,renderer: Ext.util.Format.htmlEncode + ,renderer: function(value, metaData, record) { + return this.renderObjectCell(value, metaData, record); + } + ,scope: this }] ,tbar: this.getTbar() }); MODx.grid.ManagerLog.superclass.constructor.call(this,config); }; Ext.extend(MODx.grid.ManagerLog,MODx.grid.Grid, { + renderObjectCell: function(value, metaData, record) { + var managerUrl = record.data.managerUrl; + var item = record.data.item; + if (!managerUrl || item === undefined || item === null) { + return Ext.util.Format.htmlEncode(value); + } + var baseUrl = MODx.config.manager_url || ''; + var href = baseUrl + managerUrl; + var idSuffix = ' (' + item + ')'; + if (value && String(value).slice(-idSuffix.length) === idSuffix) { + var nameWithoutId = value.slice(0, value.length - idSuffix.length); + return Ext.util.Format.htmlEncode(nameWithoutId) + ' ' + this.renderLink(idSuffix, { href: href, target: '_blank' }); + } + return this.renderLink(value, { href: href, target: '_blank' }); + }, + getTbar: function() { var tbar = [{ xtype: 'button'