PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.4
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.4
1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk 1.2.0 All 47 releases
fluent-cart / app / Modules / Templating / Bricks / BricksLoader.php

BricksLoader.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.4, at app/Modules/Templating/Bricks/BricksLoader.php

323 lines 9.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Modules\Templating\Bricks;
4
5 use Bricks\Elements;
6 use FluentCart\Framework\Support\Arr;
7 use FluentCart\App\Models\Product;
8
9 class BricksLoader
10 {
11 const TEMPLATE_TYPE_PRODUCT = 'fct_product';
12
13 public function register()
14 {
15 if (!defined('BRICKS_VERSION')) {
16 return;
17 }
18
19 add_action('init', [$this, 'loadElements'], 20);
20
21 (new DynamicData())->register();
22
23 add_filter('bricks/setup/control_options', [$this, 'addTemplateTypes']);
24 add_filter('bricks/database/content_type', [$this, 'setContentType'], 10, 2);
25 add_filter('bricks/active_templates', [$this, 'setActiveTemplates'], 10, 3);
26
27 add_action('wp_footer', [$this, 'addDynamicFieldClassesScript']);
28
29 add_filter('fluent_cart/template/disable_taxonomy_fallback', function ($result) {
30 $bricks_data = \Bricks\Database::get_template_data('archive');
31
32 if ($bricks_data) {
33 return true;
34 }
35
36 return $result;
37 });
38
39 add_filter('fluent_cart/products_views/preload_collection_bricks', [$this, 'preloadProductCollectionsAjax'], 10, 2);
40
41 }
42
43 public function loadElements()
44 {
45 $elements = [
46 'ProductTitle' => 'fct-product-title',
47 'ProductShortDescription' => 'fct-product-short-description',
48 'ProductContent' => 'fct-product-content',
49 'ProductStock' => 'fct-product-stock',
50 'PriceRange' => 'fct-price-range',
51 'BuySection' => 'fct-product-buy-section',
52 'ProductGallery' => 'fct-product-gallery',
53 'ProductsCollection' => 'fct-products',
54 ];
55
56 foreach ($elements as $elementKey => $elementName) {
57 $elementFile = FLUENTCART_PLUGIN_PATH . "app/Modules/Templating/Bricks/Elements/$elementKey.php";
58 $className = "\\FluentCart\\App\\Modules\\Templating\\Bricks\\Elements\\$elementKey";
59 Elements::register_element($elementFile, $elementName, $className);
60 }
61
62 }
63
64 /**
65 * Add FluentCart template types to the Bricks template type dropdown.
66 */
67 public function addTemplateTypes($controlOptions)
68 {
69 if (!isset($controlOptions['templateTypes']) || !is_array($controlOptions['templateTypes'])) {
70 return $controlOptions;
71 }
72
73 $templateTypes = $controlOptions['templateTypes'];
74
75 if (isset($templateTypes[self::TEMPLATE_TYPE_PRODUCT])) {
76 return $controlOptions;
77 }
78
79 $fluentCartType = [
80 self::TEMPLATE_TYPE_PRODUCT => __('FluentCart - Product', 'fluent-cart'),
81 ];
82
83 if (array_key_exists('content', $templateTypes)) {
84 $keys = array_keys($templateTypes);
85 $offset = array_search('content', $keys, true) + 1;
86
87 $templateTypes = array_slice($templateTypes, 0, $offset, true)
88 + $fluentCartType
89 + array_slice($templateTypes, $offset, null, true);
90 } else {
91 $templateTypes = array_merge($templateTypes, $fluentCartType);
92 }
93
94 $controlOptions['templateTypes'] = $templateTypes;
95
96 return $controlOptions;
97 }
98
99 /**
100 * Let Bricks resolve FluentCart single product templates as product templates.
101 */
102 public function setContentType($contentType, $postId)
103 {
104 if (is_search()) {
105 return $contentType;
106 }
107
108 if (is_singular('fluent-products')) {
109 return self::TEMPLATE_TYPE_PRODUCT;
110 }
111
112 return $contentType;
113 }
114
115 /**
116 * Make the FluentCart product template type render on single product pages.
117 */
118 public function setActiveTemplates($activeTemplates, $postId, $contentType)
119 {
120 if ($contentType !== self::TEMPLATE_TYPE_PRODUCT || !is_singular('fluent-products')) {
121 return $activeTemplates;
122 }
123
124 if (!empty($activeTemplates[self::TEMPLATE_TYPE_PRODUCT])) {
125 $activeTemplates['content'] = $activeTemplates[self::TEMPLATE_TYPE_PRODUCT];
126 return $activeTemplates;
127 }
128
129 $templateId = $this->getProductTemplateId($postId);
130
131 if (!$templateId) {
132 return $activeTemplates;
133 }
134
135 $activeTemplates['content'] = $templateId;
136 $activeTemplates[self::TEMPLATE_TYPE_PRODUCT] = $templateId;
137
138 return $activeTemplates;
139 }
140
141 protected function getProductTemplateId($postId)
142 {
143 if (!class_exists('\Bricks\Templates')) {
144 return 0;
145 }
146
147 $templateIds = \Bricks\Templates::get_templates_by_type(self::TEMPLATE_TYPE_PRODUCT);
148
149 if (empty($templateIds) || !is_array($templateIds)) {
150 return 0;
151 }
152
153 $foundTemplates = [];
154 $defaultTemplateId = 0;
155
156 foreach ($templateIds as $templateId) {
157 $conditions = \Bricks\Helpers::get_template_setting('templateConditions', $templateId);
158
159 if (!$conditions) {
160 if (!$defaultTemplateId) {
161 $defaultTemplateId = absint($templateId);
162 }
163 continue;
164 }
165
166 $foundTemplates = \Bricks\Database::screen_conditions($foundTemplates, $templateId, $conditions, $postId, '');
167 }
168
169 if (!empty($foundTemplates)) {
170 $scores = array_keys($foundTemplates);
171 return $foundTemplates[max($scores)];
172 }
173
174 return $defaultTemplateId;
175 }
176
177 public function preloadProductCollectionsAjax($view, $args)
178 {
179 $products = $args['products'];
180 $clientId = Arr::get($args, 'client_id', '');
181
182 $settings = get_transient('fc_bx_collection_' . $clientId);
183 if (!$settings) {
184 return $view;
185 }
186
187 do_action('fluent_cart/bricks/rendering_ajax_collection');
188
189 ob_start();
190 $post_index = 1;
191
192 foreach ($products as $product) {
193 $post = get_post($product->ID);
194 setup_postdata($post);
195 BricksHelper::setFormCurrentPost($post);
196 BricksHelper::renderCollectionCard($settings, $product, $post_index, $clientId);
197
198 $post_index++;
199 }
200 wp_reset_postdata();
201
202 return ob_get_clean();
203 }
204
205 public function addDynamicFieldClassesScript()
206 {
207 ?>
208 <script>
209 document.addEventListener('DOMContentLoaded', function() {
210 const classMap = {
211 'product-title': 'fct-dynamic-product-title',
212 'product-image': 'fct-dynamic-product-image',
213 'product-excerpt': 'fct-dynamic-product-excerpt',
214 'product-price': 'fct-dynamic-product-price',
215 'product-button': 'fct-dynamic-product-button'
216 };
217
218 const applyDynamicFieldClass = function(marker) {
219 if (marker.hasAttribute('data-fct-field-class-applied')) {
220 return;
221 }
222
223 const className = classMap[marker.getAttribute('data-fct-field')];
224
225 if (!className) {
226 marker.setAttribute('data-fct-field-class-applied', '1');
227 return;
228 }
229
230 let dynamicDiv = marker.closest('.dynamic');
231
232 if (!dynamicDiv) {
233 dynamicDiv = marker.parentElement;
234 while (dynamicDiv && !dynamicDiv.classList.contains('dynamic')) {
235 dynamicDiv = dynamicDiv.parentElement;
236 }
237 }
238
239 if (dynamicDiv) {
240 dynamicDiv.classList.add(className);
241 }
242
243 marker.setAttribute('data-fct-field-class-applied', '1');
244 };
245
246 let scheduled = false;
247 const pendingNodes = new Set();
248
249 const processNode = function(node) {
250 if (!node || node.nodeType !== 1) {
251 return;
252 }
253
254 const markers = node.matches('[data-fct-field]')
255 ? [node]
256 : node.querySelectorAll('[data-fct-field]');
257
258 markers.forEach(applyDynamicFieldClass);
259 };
260
261 const scheduleNode = function(node) {
262 pendingNodes.add(node);
263
264 if (scheduled) {
265 return;
266 }
267
268 scheduled = true;
269 requestAnimationFrame(function() {
270 pendingNodes.forEach(processNode);
271 pendingNodes.clear();
272 scheduled = false;
273 });
274 };
275
276 document.querySelectorAll('[data-fluent-cart-shop-app-product-list]').forEach(function(productList) {
277 processNode(productList);
278
279 new MutationObserver(function(mutations) {
280 mutations.forEach(function(mutation) {
281 mutation.addedNodes.forEach(scheduleNode);
282 });
283 }).observe(productList, {
284 childList: true,
285 subtree: true
286 });
287 });
288 });
289 </script>
290 <?php
291 }
292
293 /**
294 * Get product options for select controls.
295 * Limits to 50 recent products for editor performance.
296 * Users can search or use manual product ID for others.
297 */
298 public static function getProductOptions()
299 {
300 if (!class_exists('\FluentCart\App\Models\Product')) {
301 return [];
302 }
303
304 $options = [];
305
306 try {
307 $products = Product::query()
308 ->where('post_status', 'publish')
309 ->orderBy('post_date', 'DESC')
310 ->limit(50)
311 ->get();
312
313 foreach ($products as $product) {
314 $options[$product->ID] = $product->post_title;
315 }
316 } catch (\Exception $e) {
317 // Silently fail if models aren't available
318 }
319
320 return $options;
321 }
322 }
323