├── COPYING.txt ├── Plugin └── Backend │ └── Magento │ └── Config │ └── Model │ └── Config │ └── Structure │ └── Element │ └── Field │ ├── DisplayHintAndValue.php │ ├── DisplayOverrideValue.php │ └── FieldPlugin.php ├── README.md ├── composer.json ├── etc ├── acl.xml ├── adminhtml │ ├── di.xml │ └── system.xml ├── config.xml └── module.xml ├── registration.php └── view └── adminhtml ├── layout └── adminhtml_system_config_edit.xml ├── templates ├── config_field_info.phtml └── override_tooltip.phtml └── web └── js └── copy-config-field-info.js /COPYING.txt: -------------------------------------------------------------------------------- 1 | Copyright © 2022-present Anass touati 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 | -------------------------------------------------------------------------------- /Plugin/Backend/Magento/Config/Model/Config/Structure/Element/Field/DisplayHintAndValue.php: -------------------------------------------------------------------------------- 1 | config, true)) { 38 | return $result; 39 | } 40 | return $result . $this->getAdditionalHTML($result, $subject); 41 | } 42 | 43 | /** 44 | * HTML Renderer 45 | * 46 | * @param string $comment 47 | * @param MagentoField $field 48 | * @return string 49 | */ 50 | public function getAdditionalHTML(string $comment, MagentoField $field): string 51 | { 52 | $block = $this->blockFactory->createBlock(Template::class); 53 | $fieldPath = $this->getConfigPath($field); 54 | $block 55 | ->setTemplate('AnassTouatiCoder_InstantConfigurationCopy::config_field_info.phtml') 56 | ->setBreakLine(strlen($comment)) 57 | ->addData($this->config) 58 | ->setPath($fieldPath); 59 | 60 | if (in_array($field->getType(), self::ALLOWED_FIELD_TYPE_LIST)) { 61 | $block->setFieldId($this->getFieldHTMLId($field)) 62 | ->setFieldType($field->getType()); 63 | } 64 | 65 | return $block->toHtml(); 66 | } 67 | 68 | /** 69 | * {@inheritdoc } 70 | */ 71 | protected function initConfig(): void 72 | { 73 | $this->config = [ 74 | 'display_path' => $this->scopeConfig->isSetFlag(self::XML_CONFIG_PATH_ENABLE_HINTS_PATH), 75 | 'display_value' => $this->scopeConfig->isSetFlag(self::XML_CONFIG_PATH_SYSTEM_FIELD_VALUE) 76 | ]; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Plugin/Backend/Magento/Config/Model/Config/Structure/Element/Field/DisplayOverrideValue.php: -------------------------------------------------------------------------------- 1 | escaper = $escaper; 51 | $this->storeManager = $storeManager; 52 | $this->request = $request; 53 | parent::__construct($scopeConfig, $blockFactory); 54 | } 55 | 56 | /** 57 | * Main entry 58 | * 59 | * @param MagentoField $subject 60 | * @param string $result 61 | * @return string 62 | */ 63 | public function afterGetLabel(MagentoField $subject, $result) 64 | { 65 | if (!$this->config['display_tooltip']) { 66 | return $result; 67 | } 68 | 69 | return $result . $this->getToolTipHTML($subject, $result); 70 | } 71 | 72 | /** 73 | * Get Tooltip html output 74 | * 75 | * @param MagentoField $field 76 | * @return string 77 | */ 78 | private function getToolTipHTML(MagentoField $field, $label) 79 | { 80 | $tooltip = ''; 81 | $lines = []; 82 | 83 | foreach ($this->storeManager->getWebsites(false) as $website) { 84 | if ($this->getWebsiteParam() || $this->getStoreParam()) { 85 | continue; 86 | } 87 | // Only show website specific values in default scope 88 | if ($scopeLine = $this->getScopeHint($field, self::SCOPE_TYPE_WEBSITES, $website)) { 89 | $lines[] = $scopeLine; 90 | } 91 | } 92 | foreach ($this->storeManager->getStores(false) as $store) { 93 | if ($this->getStoreParam()) { 94 | continue; 95 | } 96 | if (($websiteId = $this->getWebsiteParam()) && ($store->getWebsiteId() != $websiteId)) { 97 | continue; 98 | } 99 | // show store specific values in default scope and in parent website scope 100 | if ($scopeLine = $this->getScopeHint($field, self::SCOPE_TYPE_STORES, $store)) { 101 | $lines[] = $scopeLine; 102 | } 103 | } 104 | if (count($lines) > 0) { 105 | $tooltipContent = implode('
', $lines); 106 | $tooltip = $this->blockFactory->createBlock(Template::class) 107 | ->setFieldLabel($label) 108 | ->setTemplate('AnassTouatiCoder_InstantConfigurationCopy::override_tooltip.phtml') 109 | ->setToolTipContent($tooltipContent) 110 | ->toHtml(); 111 | } 112 | 113 | return $tooltip; 114 | } 115 | 116 | /** 117 | * {@inheritdoc } 118 | */ 119 | protected function initConfig(): void 120 | { 121 | $this->config = [ 122 | 'display_tooltip' => $this->scopeConfig->isSetFlag(self::XML_CONFIG_PATH_SYSTEM_OVERRIDE_VALUES) 123 | ]; 124 | } 125 | 126 | /** 127 | * Get scope data 128 | * 129 | * @param MagentoField $field 130 | * @param string $scopeType 131 | * @param $scope 132 | * @return Phrase|string 133 | */ 134 | private function getScopeHint(MagentoField $field, string $scopeType, $scope) 135 | { 136 | $path = $this->getConfigPath($field); 137 | $scopeLine = ''; 138 | if ($websiteId = $this->getWebsiteParam()) { 139 | $currentValue = $this->scopeConfig->getValue( 140 | $path, 141 | ScopeInterface::SCOPE_WEBSITE, 142 | $websiteId 143 | ); 144 | } else { 145 | $currentValue = $this->scopeConfig->getValue($path); 146 | } 147 | $scopeValue = $this->scopeConfig->getValue($path, $scopeType, $scope->getId()); 148 | 149 | if (is_array($currentValue) || is_array($scopeValue)) { 150 | return $scopeLine; 151 | } 152 | 153 | $currentValue = (string)$currentValue; 154 | $scopeValue = (string)$scopeValue; 155 | 156 | if ($scopeValue !== $currentValue) { 157 | $scopeValue = $this->escaper->escapeHtml($scopeValue); 158 | 159 | switch ($scopeType) { 160 | case self::SCOPE_TYPE_STORES: 161 | return __( 162 | 'Store %1:
"%2"', 163 | $scope->getCode(), 164 | $this->getValueLabel($field, $scopeValue) 165 | ); 166 | case self::SCOPE_TYPE_WEBSITES: 167 | return __( 168 | 'Website %1:
"%2"', 169 | $scope->getCode(), 170 | $this->getValueLabel($field, $scopeValue) 171 | ); 172 | } 173 | } 174 | return $scopeLine; 175 | } 176 | 177 | /** 178 | * Get Value 179 | * 180 | * @param MagentoField $field 181 | * @param string $scopeValue 182 | * @return string|Phrase 183 | */ 184 | private function getValueLabel(MagentoField $field, string $scopeValue) 185 | { 186 | $scopeValue = trim($scopeValue); 187 | if ($field->hasOptions()) { 188 | if ($field->getType() === 'multiselect' && strpos($scopeValue, ',') !== false) { 189 | return implode( 190 | ', ', 191 | array_map( 192 | function ($key) use ($field) { 193 | return $this->getValueLabel($field, $key); 194 | }, 195 | explode(',', $scopeValue) 196 | ) 197 | ); 198 | } 199 | foreach ($field->getOptions() as $option) { 200 | if (is_array($option) && $option['value'] == $scopeValue) { 201 | return $option['label']; 202 | } 203 | } 204 | } 205 | return $scopeValue; 206 | } 207 | 208 | /** 209 | * Get Website ID parameter from request 210 | * 211 | * @return string|null 212 | */ 213 | private function getWebsiteParam(): ?string 214 | { 215 | return $this->request->getParam('website'); 216 | } 217 | 218 | /** 219 | * Get store ID parameter from request 220 | * 221 | * @return string|null 222 | */ 223 | private function getStoreParam(): ?string 224 | { 225 | return $this->request->getParam('store'); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /Plugin/Backend/Magento/Config/Model/Config/Structure/Element/Field/FieldPlugin.php: -------------------------------------------------------------------------------- 1 | scopeConfig = $scopeConfig; 49 | $this->blockFactory = $blockFactory; 50 | $this->initConfig(); 51 | } 52 | 53 | /** 54 | * Get active HTML Renderer 55 | * 56 | * @return void 57 | */ 58 | abstract protected function initConfig(): void; 59 | 60 | /** 61 | * Get field config path 62 | * 63 | * @param MagentoField $field 64 | * @return string|null 65 | */ 66 | protected function getConfigPath(MagentoField $field): ?string 67 | { 68 | return $field->getConfigPath() ?: $field->getPath(); 69 | } 70 | 71 | /** 72 | * Get Field HTML ID 73 | * 74 | * @param MagentoField $field 75 | * @return array|string|string[] 76 | */ 77 | protected function getFieldHTMLId(MagentoField $field) 78 | { 79 | return str_replace('/', '_', $field->getPath()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Magento 2 Module AnassTouatiCoder InstantConfigurationCopy

2 |
3 |

Copy field path and value, display its override values in parent scope

4 | Supported Magento Versions 5 | Latest Stable Version 6 | Composer Downloads 7 | Maintained - Yes 8 | 9 |
10 | 11 | ``anasstouaticoder/magento2-module-instantconfigurationcopy`` 12 | 13 | - [Main Functionalities](#markdown-header-main-functionalities) 14 | - [Installation](#markdown-header-installation) 15 | - [Configuration](#markdown-header-configuration) 16 | - [Specifications](#markdown-header-specifications) 17 | - [usage](#markdown-header-usage) 18 | - [License](#markdown-header-license) 19 | 20 | 21 | ## Main Functionalities 22 | Provide easy way to copy fields path and value in configuration section 23 | 24 | ## Installation 25 | \* = in production please use the `--keep-generated` option 26 | 27 | ### install from composer 2 28 | 29 | - In magento project root directory run command `composer require anasstouaticoder/magento2-module-instantconfigurationcopy` 30 | - Enable the module by running `php bin/magento module:enable AnassTouatiCoder_InstantConfigurationCopy` 31 | - Flush the cache by running `php bin/magento cache:flush` 32 | 33 | 34 | ### Zip file 35 | 36 | - Unzip the zip file in `app/code/AnassTouatiCoder` 37 | - Enable the module by running `php bin/magento module:enable AnassTouatiCoder_InstantConfigurationCopy` 38 | - Flush the cache by running `php bin/magento cache:flush` 39 | 40 | ## Configuration 41 | 42 | ### Type 1 : In Back Office 43 | After installing the plugin, it is easy to activate it, login in as administrator, then navigate 44 | to Stores => Configuration => Advanced => Developer => Debug => Enable System Config Path Hints For Admin or 45 | Enable System Config copy value For Admin, finally select yes and save. 46 | now under each configuration field you see that there is path : configuration path, copy path button and copy value button 47 | 48 | ### Type 2 : Using Magento CLI 49 | 50 | Enabling display field paths and copy path button : `bin/magento config:set 'anasstouaticoder_dev/system_config/system_path_hint 1` 51 | 52 | Enabling copy value button: `bin/magento config:set 'anasstouaticoder_dev/system_config/system_field_value 1` 53 | 54 | Enabling display override value of sub scopes : `bin/magento config:set 'anasstouaticoder_dev/system_config/system_override_values 1` 55 | 56 | ## Specifications 57 | 58 | This plugin provides easy way to copy configuration field paths or its value to the clipboard. It also displays all values configured in sub scopes websites or in a modal. 59 | 60 | For copying value and displaying sub values in modal parts, both support these field types : [text, textarea, select, and multiselect]() 61 | 62 | ## Usage 63 | 64 | The idea behind this plugin is to make admins and developers lives much easier, instead of them searching inside the page DOM using inspect tool or in Project files, the plugin is providing them a shortcut to path configuration and copy button to clipboard. 65 | [See plugin wiki](https://github.com/anasstouaticoder/magento2-module-instantconfigurationcopy/wiki/Project-Demo) 66 | 67 | ## License 68 | 69 | [MIT](https://opensource.org/licenses/MIT) 70 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "anasstouaticoder/magento2-module-instantconfigurationcopy", 3 | "description": "The InstantConfigurationCopy module provides easy way to copy configuration field information for admin in back office Magento 2.", 4 | "keywords": [ 5 | "magento 2", 6 | "back office", 7 | "config hint path", 8 | "config value", 9 | "copy past", 10 | "copy to clipboard", 11 | "hint path" 12 | ], 13 | "authors": [ 14 | { 15 | "name": "Anass TOUATI", 16 | "email": "anass1touati@gmail.com", 17 | "homepage": "https://www.linkedin.com/in/anass-touati-9a3b028b/", 18 | "role": "Leader" 19 | } 20 | ], 21 | "require": { 22 | "anasstouaticoder/magento2-module-base": "^1", 23 | "magento/framework": "*", 24 | "magento/module-config": "*", 25 | "php": "~7.0||~7.1||~7.2||~7.3||~7.4.0||~8.1||~8.2" 26 | }, 27 | "type": "magento2-module", 28 | "version": "1.2.5", 29 | "minimum-stability": "dev", 30 | "prefer-stable": true, 31 | "license": [ 32 | "MIT" 33 | ], 34 | "autoload": { 35 | "files": [ 36 | "registration.php" 37 | ], 38 | "psr-4": { 39 | "AnassTouatiCoder\\InstantConfigurationCopy\\": "" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /etc/acl.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /etc/adminhtml/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 12 | 13 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /etc/adminhtml/system.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 12 | 13 |
15 | 16 | anass_Touati_coder 17 | AnassTouatiCoder_InstantConfigurationCopy::anasstouaticoder_instantconfigurationcopy 18 | 20 | 21 | 23 | 24 | 26 | Magento\Config\Model\Config\Source\Yesno 27 | 28 | 30 | 31 |
Allowed Field Types

Text, Textarea, Select and 34 | Multiselect

]]>
35 | Magento\Config\Model\Config\Source\Yesno 36 |
37 | 39 | 40 |
Allowed Field Types

Text, Textarea, Select and 43 | Multiselect

]]>
44 | Magento\Config\Model\Config\Source\Yesno 45 |
46 |
47 |
48 |
49 |
50 | -------------------------------------------------------------------------------- /etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 12 | 13 | 14 | 15 | 1 16 | 1 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 | 2 | 10 | 12 | 13 | 46 | -------------------------------------------------------------------------------- /view/adminhtml/web/js/copy-config-field-info.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2023 3 | * MIT License 4 | * Module AnassTouatiCoder_InstantConfigurationCopy 5 | * Author Anass TOUATI anass1touati@gmail.com 6 | */ 7 | require([ 8 | 'jquery', 9 | 'mage/translate' 10 | ], function ($, $t) { 11 | 12 | // Use copy to clipboard for all browsers 13 | function copyToClipboard(text) 14 | { 15 | if (navigator.clipboard && navigator.clipboard.writeText) { 16 | return navigator.clipboard.writeText(text); 17 | } else { 18 | if (window.clipboardData && window.clipboardData.setData) { 19 | // IE: prevent textarea being shown while dialog is visible 20 | return window.clipboardData.setData("Text", text); 21 | 22 | } else if (document.queryCommandSupported && 23 | document.queryCommandSupported("copy")) { 24 | let textarea = document.createElement("textarea"); 25 | textarea.textContent = text; 26 | // Prevent scrolling to bottom of page in MS Edge 27 | textarea.style.position = "fixed"; 28 | document.body.appendChild(textarea); 29 | textarea.select(); 30 | try { 31 | // Security exception may be thrown by some browsers 32 | return document.execCommand("copy"); 33 | } catch (ex) { 34 | console.warn("Copy to clipboard failed.", ex); 35 | return false; 36 | } finally { 37 | document.body.removeChild(textarea); 38 | } 39 | } 40 | } 41 | } 42 | document.addEventListener('click', function (e) { 43 | let target = e.target, aTouatiCopyValueText = $t('Copy Value'), 44 | aTouatiCopyPathValueText = $t('Copy Hint Path'); 45 | 46 | if (target.classList.contains('atouati-config-path-Copy') || target.classList.contains('atouati-config-value-Copy')) { 47 | let element = e.target, id = element.id; 48 | if (target.classList.contains('atouati-config-path-Copy')) { 49 | const path = element.dataset.path; 50 | copyToClipboard(path); 51 | if (window.ATouatiCopyValue) { 52 | document.querySelector('#' + window.ATouatiCopyValue).innerHTML = aTouatiCopyValueText; 53 | } 54 | if (window.ATouatiCopyPath) { 55 | document.querySelector('#' + window.ATouatiCopyPath).innerHTML = aTouatiCopyPathValueText; 56 | } 57 | element.innerHTML = $t('Hint Path Copied'); 58 | window.ATouatiCopyPath = id; 59 | } else if (target.classList.contains('atouati-config-value-Copy')) { 60 | let fieldId = element.dataset.fieldId, value = false; 61 | if (element.dataset.fieldValueType === 'multiselect') { 62 | let selectedValues = []; 63 | let multiSelectElement = document.getElementById(fieldId); 64 | for (let i = 0; i < multiSelectElement.options.length; i++) { 65 | if (multiSelectElement.options[i].selected) { 66 | selectedValues.push(multiSelectElement.options[i].value); 67 | } 68 | } 69 | value = selectedValues.join(', '); 70 | } else { 71 | let SelectedElement = document.getElementById(fieldId); 72 | if (SelectedElement === null) { 73 | // field does not have value, its just block 74 | return; 75 | } 76 | if (SelectedElement.tagName === "TABLE") { 77 | // Do not support grid field 78 | return; 79 | } 80 | value = SelectedElement ? SelectedElement.value : false; 81 | } 82 | if (value === false) { 83 | return; 84 | } 85 | copyToClipboard(value); 86 | if (window.ATouatiCopyPath) { 87 | document.querySelector('#' + window.ATouatiCopyPath).innerHTML = aTouatiCopyPathValueText; 88 | } 89 | if (window.ATouatiCopyValue) { 90 | document.querySelector('#' + window.ATouatiCopyValue).innerHTML = aTouatiCopyValueText; 91 | } 92 | element.innerHTML = $t('Value Copied'); 93 | window.ATouatiCopyValue = id; 94 | } 95 | } 96 | }); 97 | }); 98 | --------------------------------------------------------------------------------