├── 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
├── etc
├── module.xml
├── config.xml
├── adminhtml
│ ├── di.xml
│ └── system.xml
└── acl.xml
├── COPYING.txt
├── composer.json
├── Plugin
└── Backend
│ └── Magento
│ └── Config
│ └── Model
│ └── Config
│ └── Structure
│ └── Element
│ └── Field
│ ├── FieldPlugin.php
│ ├── DisplayHintAndValue.php
│ └── DisplayOverrideValue.php
└── README.md
/registration.php:
--------------------------------------------------------------------------------
1 |
2 |
10 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/etc/module.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/etc/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
12 |
13 |
14 |
15 | 1
16 | 1
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/etc/adminhtml/di.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
12 |
13 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/etc/acl.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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.2.18",
23 | "magento/framework": "*",
24 | "magento/module-config": "*",
25 | "php": "^7.0 || ^8.0"
26 | },
27 | "type": "magento2-module",
28 | "version": "1.2.6",
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 |
--------------------------------------------------------------------------------
/view/adminhtml/templates/config_field_info.phtml:
--------------------------------------------------------------------------------
1 |
19 | getDisplayPath()): ?>
20 | = $block->getBreakLine() ? '
' : '' ?>
21 | = $escaper->escapeHtml(__('Path:')) ?>
22 | = $escaper->escapeHtml($block->getPath()) ?>
23 |
27 |
28 |
29 | getDisplayValue()): ?>
30 |
36 |
37 |
--------------------------------------------------------------------------------
/view/adminhtml/templates/override_tooltip.phtml:
--------------------------------------------------------------------------------
1 |
17 |
23 |
24 |
25 | = /* @noEscape */
26 | $block->getToolTipContent(); ?>
27 |
28 |
46 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 TypesText, Textarea, Select and
34 | Multiselect
]]>
35 | Magento\Config\Model\Config\Source\Yesno
36 |
37 |
39 |
40 |
Allowed Field TypesText, Textarea, Select and
43 | Multiselect
]]>
44 | Magento\Config\Model\Config\Source\Yesno
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Magento 2 Module AnassTouatiCoder InstantConfigurationCopy
2 |
3 |
Copy field path and value, display its override values in parent scope
4 |

5 |

6 |

7 |

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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------