├── COPYING.txt
├── LICENSE.txt
├── Plugin
├── CategorySelectedLayout.php
├── CmsSelectableLayout.php
└── ProductSelectedLayout.php
├── README.md
├── composer.json
├── docs
└── img.png
├── etc
├── di.xml
└── module.xml
└── registration.php
/COPYING.txt:
--------------------------------------------------------------------------------
1 | Copyright © 2023-present GTStudio
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.
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright © 2023-present GTStudio
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.
--------------------------------------------------------------------------------
/Plugin/CategorySelectedLayout.php:
--------------------------------------------------------------------------------
1 | themeFactory = $themeFactory;
35 | $this->design = $design;
36 | $this->layoutProcessorFactory = $layoutProcessorFactory;
37 | }
38 |
39 | /**
40 | * Get the processor instance.
41 | *
42 | * @return LayoutProcessor
43 | */
44 | private function getLayoutProcessor(): LayoutProcessor
45 | {
46 | return $this->layoutProcessorFactory->create(
47 | [
48 | 'theme' => $this->themeFactory->create(
49 | $this->design->getConfigurationDesignTheme(Area::AREA_FRONTEND)
50 | )
51 | ]
52 | );
53 | }
54 |
55 | /**
56 | * @param LayoutUpdateManager $subject
57 | * @param $result
58 | * @param CategoryInterface $category
59 | * @return array
60 | */
61 | public function afterFetchAvailableFiles(LayoutUpdateManager $subject, $result, CategoryInterface $category)
62 | {
63 | if (!$category->getId()) {
64 | return [];
65 | }
66 |
67 | $handles = $this->getLayoutProcessor()->getAvailableHandles();
68 |
69 | return array_filter(
70 | array_map(
71 | function (string $handle) use ($category): ?string {
72 | preg_match(
73 | '/^catalog\_category\_view\_selectable\_(' . $category->getId() . '|all)\_([a-z0-9]+)/i',
74 | $handle,
75 | $selectable
76 | );
77 | if (!empty($selectable[2])) {
78 | return "{$selectable[1]}_{$selectable[2]}";
79 | }
80 |
81 | return null;
82 | },
83 | $handles
84 | )
85 | );
86 | }
87 |
88 | /**
89 | * Extract custom layout attribute value.
90 | *
91 | * @param CategoryInterface $category
92 | * @return mixed
93 | */
94 | private function extractAttributeValue(CategoryInterface $category)
95 | {
96 | if ($category instanceof Category && !$category->hasData(CustomAttributesDataInterface::CUSTOM_ATTRIBUTES)) {
97 | return $category->getData('custom_layout_update_file');
98 | }
99 | if ($attr = $category->getCustomAttribute('custom_layout_update_file')) {
100 | return $attr->getValue();
101 | }
102 |
103 | return null;
104 | }
105 |
106 | /**
107 | * @param LayoutUpdateManager $subject
108 | * @param null $result
109 | * @param CategoryInterface $category
110 | * @param DataObject $intoSettings
111 | * @return void
112 | */
113 | public function afterExtractCustomSettings(
114 | LayoutUpdateManager $subject,
115 | $result,
116 | CategoryInterface $category,
117 | DataObject $intoSettings
118 | ): void {
119 | if ($category->getId() && $value = $this->extractAttributeValue($category)) {
120 | $handles = $intoSettings->getPageLayoutHandles() ?? [];
121 | $handles = array_merge(
122 | $handles,
123 | ['selectable' => $value]
124 | );
125 | $intoSettings->setPageLayoutHandles($handles);
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/Plugin/CmsSelectableLayout.php:
--------------------------------------------------------------------------------
1 | layoutProcessorFactory = $layoutProcessorFactory;
41 | $this->themeFactory = $themeFactory;
42 | $this->design = $design;
43 | $this->identityMap = $identityMap;
44 | $this->pageRepository = $pageRepository;
45 | }
46 |
47 | /**
48 | * Adopt page's identifier to be used as layout handle.
49 | *
50 | * @param PageInterface $page
51 | * @return string
52 | */
53 | private function sanitizeIdentifier(PageInterface $page): string
54 | {
55 | return $page->getIdentifier() === null ? '' : str_replace('/', '_', $page->getIdentifier());
56 | }
57 |
58 | /**
59 | * Get the processor instance.
60 | *
61 | * @return LayoutProcessor
62 | */
63 | private function getLayoutProcessor(): LayoutProcessor
64 | {
65 | return $this->layoutProcessorFactory->create(
66 | [
67 | 'theme' => $this->themeFactory->create(
68 | $this->design->getConfigurationDesignTheme(Area::AREA_FRONTEND)
69 | )
70 | ]
71 | );
72 | }
73 |
74 | /**
75 | * @param CustomLayoutManager $subject
76 | * @param array $result
77 | * @param PageInterface $page
78 | * @return array
79 | */
80 | public function afterFetchAvailableFiles(CustomLayoutManager $subject, array $result, PageInterface $page): array
81 | {
82 | $identifier = $this->sanitizeIdentifier($page);
83 | $handles = $this->getLayoutProcessor()->getAvailableHandles();
84 |
85 | return array_filter(
86 | array_map(
87 | function (string $handle) use ($identifier): ?string {
88 | preg_match(
89 | '/^cms\_page\_view\_selectable\_(' . preg_quote($identifier) . '|all)\_([a-z0-9]+)/i',
90 | $handle,
91 | $selectable
92 | );
93 | if (!empty($selectable[2])) {
94 | return "{$selectable[1]}_{$selectable[2]}";
95 | }
96 |
97 | return null;
98 | },
99 | $handles
100 | )
101 | );
102 | }
103 |
104 | /**
105 | * @param CustomLayoutManager $subject
106 | * @param null $result
107 | * @param PageLayout $layout
108 | * @param CustomLayoutSelectedInterface $layoutSelected
109 | * @return void
110 | * @throws LocalizedException
111 | */
112 | public function afterApplyUpdate(
113 | CustomLayoutManager $subject,
114 | $result,
115 | PageLayout $layout,
116 | CustomLayoutSelectedInterface $layoutSelected
117 | ): void {
118 | $page = $this->identityMap->get($layoutSelected->getPageId());
119 | if (!$page) {
120 | $page = $this->pageRepository->getById($layoutSelected->getPageId());
121 | }
122 |
123 | $layout->addPageLayoutHandles(
124 | ['selectable' => $layoutSelected->getLayoutFileId()],
125 | 'cms_page_view'
126 | );
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/Plugin/ProductSelectedLayout.php:
--------------------------------------------------------------------------------
1 | themeFactory = $themeFactory;
35 | $this->design = $design;
36 | $this->layoutProcessorFactory = $layoutProcessorFactory;
37 | }
38 |
39 | /**
40 | * @param LayoutUpdateManager $subject
41 | * @param $result
42 | * @param ProductInterface $product
43 | * @return array
44 | */
45 | public function afterFetchAvailableFiles(LayoutUpdateManager $subject, $result, ProductInterface $product)
46 | {
47 | if (!$product->getSku()) {
48 | return [];
49 | }
50 |
51 | $identifier = $this->sanitizeSku($product);
52 | $handles = $this->getLayoutProcessor()->getAvailableHandles();
53 |
54 | return array_filter(
55 | array_map(
56 | function (string $handle) use ($identifier): ?string {
57 | preg_match(
58 | '/^catalog\_product\_view\_selectable\_(' . preg_quote($identifier) . '|all)\_([a-z0-9]+)/i',
59 | $handle,
60 | $selectable
61 | );
62 | if (!empty($selectable[2])) {
63 | return "{$selectable[1]}_{$selectable[2]}";
64 | }
65 |
66 | return null;
67 | },
68 | $handles
69 | )
70 | );
71 | }
72 |
73 | /**
74 | * Get the processor instance.
75 | *
76 | * @return LayoutProcessor
77 | */
78 | private function getLayoutProcessor(): LayoutProcessor
79 | {
80 | return $this->layoutProcessorFactory->create(
81 | [
82 | 'theme' => $this->themeFactory->create(
83 | $this->design->getConfigurationDesignTheme(Area::AREA_FRONTEND)
84 | )
85 | ]
86 | );
87 | }
88 |
89 | /**
90 | * Extract custom layout attribute value.
91 | *
92 | * @param ProductInterface $product
93 | * @return mixed
94 | */
95 | private function extractAttributeValue(ProductInterface $product)
96 | {
97 | if ($product instanceof Product && !$product->hasData(CustomAttributesDataInterface::CUSTOM_ATTRIBUTES)) {
98 | return $product->getData('custom_layout_update_file');
99 | }
100 | if ($attr = $product->getCustomAttribute('custom_layout_update_file')) {
101 | return $attr->getValue();
102 | }
103 |
104 | return null;
105 | }
106 |
107 | /**
108 | * Adopt product's SKU to be used as layout handle.
109 | *
110 | * @param ProductInterface $product
111 | * @return string
112 | */
113 | private function sanitizeSku(ProductInterface $product): string
114 | {
115 | return rawurlencode($product->getSku());
116 | }
117 |
118 | /**
119 | * @param LayoutUpdateManager $subject
120 | * @param null $result
121 | * @param ProductInterface $product
122 | * @param DataObject $intoSettings
123 | * @return void
124 | */
125 | public function afterExtractCustomSettings(
126 | LayoutUpdateManager $subject,
127 | $result,
128 | ProductInterface $product,
129 | DataObject $intoSettings
130 | ): void {
131 | if ($product->getSku() && $value = $this->extractAttributeValue($product)) {
132 | $handles = $intoSettings->getPageLayoutHandles() ?? [];
133 | $handles = array_merge(
134 | $handles,
135 | ['selectable' => $value]
136 | );
137 | $intoSettings->setPageLayoutHandles($handles);
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Module Gtstudio Selectable Layout
2 |
3 | gtstudio/module-selected-layout
4 |
5 | - [Main Functionalities](#markdown-header-main-functionalities)
6 | - [Usage](#markdown-header-usage)
7 | - [Installation](#markdown-header-installation)
8 | - [Specifications](#markdown-header-specifications)
9 |
10 |
11 | ## Main Functionalities
12 | This module Make Custom Layout Update file selectable generally available in all categories, products and cms pages.
13 | Based on this request : https://github.com/magento/magento2/issues/26901
14 |
15 | ## Usage
16 | With this module, you will be able to create generals selectable layouts updates like this :
17 |
18 | `catalog_category_view_selectable_all_mycustomLayout`
19 | `catalog_product_view_selectable_all_mycustomLayout`
20 | `cms_page_view_selectable_all_mycustomLayout`
21 |
22 | So this layout update will be available on all cms pages, categories or products on field "Custom Layout Update"
23 |
24 | 
25 |
26 | ## Installation
27 | \* = in production please use the `--keep-generated` option
28 |
29 | ### Type 1: Zip file
30 |
31 | - Unzip the zip file in `app/code/Gtstudio`
32 | - Enable the module by running `php bin/magento module:enable Gtstudio_DarkMode`
33 | - Apply database updates by running `php bin/magento setup:upgrade`\*
34 | - Flush the cache by running `php bin/magento cache:flush`
35 |
36 | ### Type 2: Composer
37 |
38 | - Install the module composer by running `composer require gtstudio/module-selected-layout`
39 | - enable the module by running `php bin/magento module:enable Gtstudio_SelectedLayout`
40 | - apply database updates by running `php bin/magento setup:upgrade`\*
41 | - Flush the cache by running `php bin/magento cache:flush`
42 |
43 |
44 | ## Specifications
45 |
46 | - Plugin `Magento\Cms\Model\Page\CustomLayout\CustomLayoutManager`
47 | - Plugin `Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager`
48 | - Plugin `Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager`
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gtstudio/module-selected-layout",
3 | "version": "1.0.3",
4 | "description": "Add general selectable layout functionality on CMS pages,category and products",
5 | "type": "magento2-module",
6 | "require": {
7 | "magento/framework": "*",
8 | "php": ">=7.3"
9 | },
10 | "license": "MIT",
11 | "autoload": {
12 | "files": [
13 | "registration.php"
14 | ],
15 | "psr-4": {
16 | "Gtstudio\\SelectedLayout\\": ""
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/docs/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gabrielgts/magento2-selected-layout/ba93551a4f63922aa591e0f66ec401ab7e2ef53f/docs/img.png
--------------------------------------------------------------------------------
/etc/di.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
10 |
13 |
14 |
15 |
18 |
19 |
20 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/etc/module.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/registration.php:
--------------------------------------------------------------------------------
1 |