├── Api ├── ConfigRecordRepositoryInterface.php └── Data │ ├── ConfigRecordInterface.php │ └── ConfigRecordSearchResultsInterface.php ├── COPYING.txt ├── Controller └── Adminhtml │ ├── ConfigRecord.php │ └── ConfigRecord │ ├── Index.php │ └── Revert.php ├── LICENSE ├── Logger ├── Handler.php └── Logger.php ├── Model ├── ConfigRecord.php ├── ConfigRecord │ └── DataProvider.php ├── ConfigRecordRepository.php └── ResourceModel │ ├── ConfigRecord.php │ └── ConfigRecord │ └── Collection.php ├── Plugin └── App │ └── Config │ └── Value.php ├── README.md ├── Service ├── Cache.php └── System │ └── Config │ └── Record.php ├── Ui └── Component │ └── Listing │ └── Column │ └── ConfigRecordActions.php ├── composer.json ├── etc ├── acl.xml ├── adminhtml │ ├── di.xml │ ├── menu.xml │ └── routes.xml ├── db_schema.xml ├── di.xml └── module.xml ├── registration.php └── view └── adminhtml ├── layout └── configrecord_configrecord_index.xml ├── ui_component └── ronangr1_systemconfigwhodidthislogger_configrecord_listing.xml └── web └── css └── source └── _module.less /Api/ConfigRecordRepositoryInterface.php: -------------------------------------------------------------------------------- 1 | setActiveMenu(self::ADMIN_RESOURCE) 19 | ->addBreadcrumb(__('Config'), __('Config')) 20 | ->addBreadcrumb(__('Record'), __('Record')); 21 | return $resultPage; 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /Controller/Adminhtml/ConfigRecord/Index.php: -------------------------------------------------------------------------------- 1 | resultPageFactory->create(); 27 | $resultPage->getConfig()->getTitle()->prepend(__("Config Records")); 28 | return $resultPage; 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /Controller/Adminhtml/ConfigRecord/Revert.php: -------------------------------------------------------------------------------- 1 | resultRedirectFactory->create(); 33 | $id = $this->getRequest()->getParam('entity_id'); 34 | if ($id) { 35 | try { 36 | $configRecord = $this->configRecordRepository->get($id); 37 | if($configRecord->getEntityId()) { 38 | $this->writer->save($configRecord->getPath(), $configRecord->getOldValue(), $configRecord->getScope()); 39 | $this->configRecordRepository->delete($configRecord); 40 | $this->cache->clean(); 41 | $this->messageManager->addSuccessMessage(__('You reverted the record.')); 42 | } 43 | return $resultRedirect->setPath('*/*/'); 44 | } catch (\Exception $e) { 45 | $this->messageManager->addErrorMessage($e->getMessage()); 46 | return $resultRedirect->setPath('*/*/edit', ['entity_id' => $id]); 47 | } 48 | } 49 | $this->messageManager->addErrorMessage(__('We can\'t find a record to revert.')); 50 | return $resultRedirect->setPath('*/*/'); 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2022-present 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Logger/Handler.php: -------------------------------------------------------------------------------- 1 | _init(ResourceModel\ConfigRecord::class); 19 | } 20 | 21 | public function getEntityId(): string 22 | { 23 | return $this->getData(self::ENTITY_ID); 24 | } 25 | 26 | /** 27 | * @param string|int $entityId 28 | * @return \Ronangr1\SystemConfigWhoDidThisLogger\Api\Data\ConfigRecordInterface 29 | */ 30 | public function setEntityId($entityId): ConfigRecordInterface 31 | { 32 | return $this->setData(self::ENTITY_ID, $entityId); 33 | } 34 | 35 | public function getAuthor(): ?string 36 | { 37 | return $this->getData(self::AUTHOR); 38 | } 39 | 40 | public function setAuthor(string $author): ConfigRecordInterface 41 | { 42 | return $this->setData(self::AUTHOR, $author); 43 | } 44 | 45 | public function getPath(): ?string 46 | { 47 | return $this->getData(self::PATH); 48 | } 49 | 50 | public function setPath(string $path): ConfigRecordInterface 51 | { 52 | return $this->setData(self::PATH, $path); 53 | } 54 | 55 | public function getOldValue(): ?string 56 | { 57 | return $this->getData(self::OLD_VALUE); 58 | } 59 | 60 | public function setOldValue(string $oldValue): ConfigRecordInterface 61 | { 62 | return $this->setData(self::OLD_VALUE, $oldValue); 63 | } 64 | 65 | public function getNewValue(): ?string 66 | { 67 | return $this->getData(self::NEW_VALUE); 68 | } 69 | 70 | public function setNewValue(string $newValue): ConfigRecordInterface 71 | { 72 | return $this->setData(self::NEW_VALUE, $newValue); 73 | } 74 | 75 | public function getScope(): string 76 | { 77 | return $this->getData(self::SCOPE); 78 | } 79 | 80 | public function setScope(string $scope): ConfigRecordInterface 81 | { 82 | return $this->setData(self::SCOPE, $scope); 83 | } 84 | 85 | public function getRecordedAt(): ?string 86 | { 87 | return $this->getData(self::RECORDED_AT); 88 | } 89 | 90 | public function setRecordedAt(string $recordAt): ConfigRecordInterface 91 | { 92 | return $this->setData(self::RECORDED_AT, $recordAt); 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /Model/ConfigRecord/DataProvider.php: -------------------------------------------------------------------------------- 1 | collection = $collectionFactory->create(); 32 | $this->dataPersistor = $dataPersistor; 33 | parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); 34 | } 35 | 36 | public function getData(): ?array 37 | { 38 | if (isset($this->loadedData)) { 39 | return $this->loadedData; 40 | } 41 | $items = $this->collection->getItems(); 42 | foreach ($items as $model) { 43 | $this->loadedData[$model->getId()] = $model->getData(); 44 | } 45 | $data = $this->dataPersistor->get('ronangr1_systemconfigwhodidthislogger_configrecord'); 46 | 47 | if (!empty($data)) { 48 | $model = $this->collection->getNewEmptyItem(); 49 | $model->setData($data); 50 | $this->loadedData[$model->getId()] = $model->getData(); 51 | $this->dataPersistor->clear('ronangr1_systemconfigwhodidthislogger_configrecord'); 52 | } 53 | 54 | return $this->loadedData; 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /Model/ConfigRecordRepository.php: -------------------------------------------------------------------------------- 1 | resource->save($configRecord); 42 | } catch (\Exception $exception) { 43 | throw new CouldNotSaveException(__( 44 | 'Could not save the Config Record : %1', 45 | $exception->getMessage() 46 | )); 47 | } 48 | return $configRecord; 49 | } 50 | 51 | public function get(string $entityId): ConfigRecordInterface 52 | { 53 | $configRecord = $this->configRecordFactory->create(); 54 | $this->resource->load($configRecord, $entityId); 55 | if (!$configRecord->getId()) { 56 | throw new NoSuchEntityException(__('Config Record with id "%1" does not exist.', $entityId)); 57 | } 58 | return $configRecord; 59 | } 60 | 61 | public function getList( 62 | SearchCriteriaInterface $criteria 63 | ): ConfigRecordSearchResultsInterface 64 | { 65 | $collection = $this->configRecordCollectionFactory->create(); 66 | 67 | $this->collectionProcessor->process($criteria, $collection); 68 | 69 | $searchResults = $this->searchResultsFactory->create(); 70 | $searchResults->setSearchCriteria($criteria); 71 | 72 | $items = []; 73 | foreach ($collection as $model) { 74 | $items[] = $model; 75 | } 76 | 77 | $searchResults->setItems($items); 78 | $searchResults->setTotalCount($collection->getSize()); 79 | return $searchResults; 80 | } 81 | 82 | /** 83 | * @throws \Magento\Framework\Exception\CouldNotDeleteException 84 | */ 85 | public function delete(ConfigRecordInterface $configRecord): bool 86 | { 87 | try { 88 | $configRecordModel = $this->configRecordFactory->create(); 89 | $this->resource->load($configRecordModel, $configRecord->getEntityId()); 90 | $this->resource->delete($configRecordModel); 91 | } catch (\Exception $exception) { 92 | throw new CouldNotDeleteException(__( 93 | 'Could not delete the Config Record: %1', 94 | $exception->getMessage() 95 | )); 96 | } 97 | return true; 98 | } 99 | 100 | public function deleteById(string $entityId): bool 101 | { 102 | return $this->delete($this->get($entityId)); 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /Model/ResourceModel/ConfigRecord.php: -------------------------------------------------------------------------------- 1 | _init(self::TABLE_NAME, 'entity_id'); 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /Model/ResourceModel/ConfigRecord/Collection.php: -------------------------------------------------------------------------------- 1 | _init( 21 | ConfigRecord::class, 22 | ConfigRecordResourceModel::class 23 | ); 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /Plugin/App/Config/Value.php: -------------------------------------------------------------------------------- 1 | authSession->getUser(); 29 | if ($user->getId()) { 30 | if ($subject->getOldValue()) { 31 | $this->recordService->record([ 32 | "author" => sprintf("%s (%s)", $user->getName(), $user->getEmail()), 33 | "path" => $subject->getPath(), 34 | "scope" => $subject->getScope(), 35 | "old_value" => $subject->getOldValue(), 36 | "new_value" => $subject->getValue() 37 | ]); 38 | } 39 | } 40 | } catch (\Exception $e) { 41 | $this->logger->debug($e->getMessage()); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Module for Magento 2 2 | 3 | [![Latest Stable Version](https://img.shields.io/packagist/v/ronangr1/module-systemconfigwhodidthislogger.svg?style=flat-square)](https://packagist.org/packages/ronangr1/module-systemconfigwhodidthislogger) 4 | [![License: MIT](https://img.shields.io/github/license/ronangr1/m2-systemconfigwhodidthislogger.svg?style=flat-square)](./LICENSE) 5 | [![Packagist](https://img.shields.io/packagist/dt/ronangr1/module-systemconfigwhodidthislogger.svg?style=flat-square)](https://packagist.org/packages/ronangr1/module-systemconfigwhodidthislogger/stats) 6 | [![Packagist](https://img.shields.io/packagist/dm/ronangr1/module-systemconfigwhodidthislogger.svg?style=flat-square)](https://packagist.org/packages/ronangr1/module-systemconfigwhodidthislogger/stats) 7 | 8 | This module allows you to record any changes made to the configuration of your Magento 2 application 9 | 10 | - [Setup](#setup) 11 | - [Composer installation](#composer-installation) 12 | - [Setup the module](#setup-the-module) 13 | - [Documentation](#documentation) 14 | - [Support](#support) 15 | - [Authors](#authors) 16 | - [License](#license) 17 | 18 | ## Setup 19 | 20 | Magento 2 Open Source or Commerce edition is required. 21 | 22 | ### Composer installation 23 | 24 | Run the following composer command: 25 | 26 | ``` 27 | composer require ronangr1/module-systemconfigwhodidthislogger 28 | ``` 29 | 30 | ### Setup the module 31 | 32 | Run the following magento command: 33 | 34 | ``` 35 | bin/magento setup:upgrade 36 | ``` 37 | 38 | **If you are in production mode, do not forget to recompile and redeploy the static resources.** 39 | 40 | ## Documentation 41 | 42 | Go to `Store > Config Records` to see who the fuck changed the configuration values. 43 | 44 | ## Support 45 | 46 | Raise a new [request](https://github.com/ronangr1/M2-SystemConfigWhoDidThisLogger/issues) to the issue tracker. 47 | 48 | ## Authors 49 | 50 | - **ronangr1** - *Maintainer* - [![GitHub followers](https://img.shields.io/github/followers/ronangr1.svg?style=social)](https://github.com/ronangr1) 51 | - **Contributors** - *Contributor* - [![GitHub contributors](https://img.shields.io/github/contributors/ronangr1/m2-systemconfigwhodidthislogger.svg?style=flat-square)](https://github.com/ronangr1/M2-SystemConfigWhoDidThisLogger/graphs/contributors) 52 | 53 | ## License 54 | 55 | This project is licensed under the MIT License - see the [LICENSE](./LICENSE) details. 56 | 57 | ***That's all folks!*** 58 | -------------------------------------------------------------------------------- /Service/Cache.php: -------------------------------------------------------------------------------- 1 | cacheTypes; 27 | foreach ($types as $type) { 28 | $this->cacheTypeList->cleanType($type); 29 | } 30 | foreach ($this->cacheFrontendPool as $cacheFrontend) { 31 | $cacheFrontend->getBackend()->clean(); 32 | } 33 | } catch (\Exception) { 34 | return false; 35 | } 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Service/System/Config/Record.php: -------------------------------------------------------------------------------- 1 | configRecordFactory->create(); 33 | $record->setData($data); 34 | $this->configRecordRepository->save($record); 35 | } catch (\Exception) { 36 | return false; 37 | } 38 | return true; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Ui/Component/Listing/Column/ConfigRecordActions.php: -------------------------------------------------------------------------------- 1 | urlBuilder = $urlBuilder; 29 | parent::__construct($context, $uiComponentFactory, $components, $data); 30 | } 31 | 32 | public function prepareDataSource(array $dataSource): array 33 | { 34 | if (isset($dataSource['data']['items'])) { 35 | foreach ($dataSource['data']['items'] as & $item) { 36 | if (isset($item['entity_id'])) { 37 | $item[$this->getData('name')] = [ 38 | 'revert' => [ 39 | 'href' => $this->urlBuilder->getUrl( 40 | static::URL_PATH_REVERT, 41 | [ 42 | 'entity_id' => $item['entity_id'] 43 | ] 44 | ), 45 | 'label' => __('Revert'), 46 | 'confirm' => [ 47 | 'title' => __('Revert'), 48 | 'message' => __('Are you sure you wan\'t to revert this record?') 49 | ] 50 | ] 51 | ]; 52 | } 53 | } 54 | } 55 | 56 | return $dataSource; 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ronangr1/module-systemconfigwhodidthislogger", 3 | "description": "This module allows you to record any changes made to the configuration of your Magento 2 application\n\n", 4 | "type": "magento2-module", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "ronangr1", 9 | "email": "guerin.ronan@proton.me" 10 | } 11 | ], 12 | "minimum-stability": "dev", 13 | "require": { 14 | "php": ">=8.0" 15 | }, 16 | "autoload": { 17 | "files": [ 18 | "registration.php" 19 | ], 20 | "psr-4": { 21 | "Ronangr1\\SystemConfigWhoDidThisLogger\\": "" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /etc/acl.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /etc/adminhtml/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 11 | 12 | Magento\Backend\Model\Auth\Session\Proxy 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | config 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /etc/adminhtml/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /etc/adminhtml/routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /etc/db_schema.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 | -------------------------------------------------------------------------------- /etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | Magento\Framework\Filesystem\Driver\File 12 | 13 | 14 | 15 | 16 | systemConfigChangesLogger 17 | 18 | Ronangr1\SystemConfigWhoDidThisLogger\Logger\Handler 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | system_config_record 28 | 29 | Ronangr1\SystemConfigWhoDidThisLogger\Model\ResourceModel\ConfigRecord\Collection 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | Ronangr1\SystemConfigWhoDidThisLogger\Model\ResourceModel\ConfigRecord\Grid\Collection 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /view/adminhtml/ui_component/ronangr1_systemconfigwhodidthislogger_configrecord_listing.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | ronangr1_systemconfigwhodidthislogger_configrecord_listing.ronangr1_systemconfigwhodidthislogger_configrecord_listing_data_source 12 | 13 | 14 | 15 | ronangr1_systemconfigwhodidthislogger_configrecord_columns 16 | 17 | ronangr1_systemconfigwhodidthislogger_configrecord_listing.ronangr1_systemconfigwhodidthislogger_configrecord_listing_data_source 18 | 19 | 20 | 21 | 22 | 23 | entity_id 24 | 25 | 26 | 27 | Ronangr1_SystemConfigWhoDidThisLogger::ConfigRecord 28 | 29 | 30 | id 31 | entity_id 32 | 33 | 34 | 35 | 36 | 37 | true 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | ronangr1_systemconfigwhodidthislogger_configrecord_listing.ronangr1_systemconfigwhodidthislogger_configrecord_listing.ronangr1_systemconfigwhodidthislogger_configrecord_columns.ids 48 | true 49 | entity_id 50 | 51 | 52 | 53 | ronangr1_systemconfigwhodidthislogger_configrecord_listing.ronangr1_systemconfigwhodidthislogger_configrecord_listing.ronangr1_systemconfigwhodidthislogger_configrecord_columns_editor 54 | startEdit 55 | 56 | ${ $.$data.rowIndex } 57 | true 58 | 59 | 60 | 61 | 62 | 63 | 64 | entity_id 65 | 66 | 67 | 68 | 69 | - 70 | false 71 | 10 72 | 73 | 74 | 75 | text 76 | asc 77 | 78 | 79 | 80 | 81 | 82 | - 83 | false 84 | 20 85 | 86 | 87 | 88 | text 89 | 90 | 91 | 92 | 93 | 94 | - 95 | false 96 | 50 97 | 98 | 99 | 100 | text 101 | 102 | 103 | 104 | 105 | 106 | - 107 | false 108 | 30 109 | 110 | 111 | 112 | text 113 | 114 | 115 | 116 | 117 | 118 | - 119 | false 120 | 40 121 | false 122 | 200 123 | 124 | 125 | 126 | text 127 | 128 | 129 | 130 | 131 | 132 | - 133 | false 134 | 40 135 | false 136 | 200 137 | 138 | 139 | 140 | text 141 | 142 | 143 | 144 | 145 | 146 | - 147 | false 148 | 60 149 | 150 | 151 | 152 | text 153 | 154 | 155 | 156 | 157 | 158 | entity_id 159 | false 160 | 107 161 | 162 | true 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /view/adminhtml/web/css/source/_module.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright © ronangr1. All rights reserved. 3 | * See COPYING.txt for license details. 4 | */ 5 | 6 | & when (@media-common = true) { 7 | .revert-action { 8 | .action-menu-item { 9 | .lib-link-as-button(); 10 | .lib-button-primary(); 11 | } 12 | } 13 | } 14 | --------------------------------------------------------------------------------