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 / TemplateActions.php

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

352 lines 11.0 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;
4
5 use FluentCart\Api\Resource\ShopResource;
6 use FluentCart\Api\StoreSettings;
7 use FluentCart\Api\Taxonomy;
8 use FluentCart\App\CPT\FluentProducts;
9 //use FluentCart\App\Hooks\Handlers\ShortCodes\Buttons\AddToCartShortcode;
10 use FluentCart\App\Hooks\Handlers\ShortCodes\SingleProductShortCode;
11 use FluentCart\App\Modules\Data\ProductDataSetup;
12 use FluentCart\App\Modules\Data\ProductQuery;
13 use FluentCart\App\Services\Renderer\ProductListRenderer;
14 use FluentCart\App\Services\Renderer\ProductRenderer;
15 use FluentCart\App\Services\Renderer\ShopAppRenderer;
16 use FluentCart\App\Services\TemplateService;
17 use FluentCart\Framework\Support\Arr;
18
19 class TemplateActions
20 {
21 public function register()
22 {
23 add_action('fluent_cart/template/main_content', [$this, 'renderMainContent']);
24 add_action('fluent_cart/template/product_archive', [$this, 'renderProductArchive']);
25 add_action('fluent_cart/product/render_product_header', [$this, 'renderProductHeader']);
26
27 // Universal hook for after_product_content — fires on both classic and block themes
28 // Block themes render <!-- wp:post-content --> which runs the_content filter
29 add_filter('the_content', function ($content) {
30 if (!is_singular('fluent-products')) {
31 return $content;
32 }
33
34 global $post;
35
36 if (!$post || $post->post_type !== FluentProducts::CPT_NAME) {
37 return $content;
38 }
39
40 ob_start();
41 do_action('fluent_cart/product/after_product_content', $post->ID);
42 $extra = ob_get_clean();
43
44 if ($extra) {
45 $content .= $extra;
46 }
47
48 return $content;
49 }, 999);
50
51 add_shortcode('fluent_cart_product_header', function ($atts = []) {
52 $atts = shortcode_atts([
53 'id' => 0,
54 ], $atts);
55
56 $productId = absint($atts['id']);
57
58 if (!$productId) {
59 $productId = get_the_ID();
60 }
61
62 if (!$productId) {
63 return 'Product ID not found';
64 }
65
66 $product = ProductDataSetup::getProductModel($productId);
67 if (!$product || !$product->detail) {
68 return 'Product not found';
69 }
70
71 ob_start();
72 $this->renderProductHeader($product->ID);
73 $content = ob_get_clean();
74
75 return $this->tempFixShortcodeContent($content);
76 });
77
78 add_shortcode('fluent_cart_related_products', function ($atts = []) {
79 $atts = shortcode_atts([
80 'id' => 0,
81 ], $atts);
82
83 $productId = absint($atts['id']);
84
85 if (!$productId) {
86 $productId = get_the_ID();
87 }
88
89 if (!$productId) {
90 return __('Product ID not found', 'fluent-cart');
91 }
92
93 $product = ProductDataSetup::getProductModel($productId);
94 if (!$product || !$product->detail) {
95 return __('Product not found', 'fluent-cart');
96 }
97
98 // On the product page this shortcode is the block theme's
99 // equivalent of what filterSingleProductContent appends for
100 // classic themes, so it answers to the same setting and filter.
101 // Placed anywhere else it is an explicit choice and still renders.
102 if (is_singular(FluentProducts::CPT_NAME)) {
103 $showRelevant = (new StoreSettings())->get('show_relevant_product_in_single_page') == 'yes';
104 $showRelevant = apply_filters(
105 'fluent_cart/single_product_page/show_relevant_products',
106 $showRelevant,
107 $productId
108 );
109
110 if (!$showRelevant) {
111 return '';
112 }
113 }
114
115 $products = ShopResource::getSimilarProducts($productId, false);
116 if (empty($products)) {
117 return '';
118 }
119
120 ob_start();
121 (new ProductListRenderer(
122 $products,
123 __('Related Products', 'fluent-cart'),
124 'fct-similar-product-list-container'
125 ))->render();
126
127 $content = ob_get_clean();
128
129 return $this->tempFixShortcodeContent($content);
130 });
131
132 add_action('fluent_cart/template/before_content', [$this, 'renderArchiveHeader']);
133
134 }
135
136 public function renderMainContent()
137 {
138 $isTaxPages = is_tax(get_object_taxonomies('fluent-products'));
139 if ($isTaxPages) {
140 do_action('fluent_cart/template/product_archive');
141 }
142 }
143
144 public function renderArchiveHeader()
145 {
146 if (TemplateService::getCurrentFcPageType() !== 'product_taxonomy') {
147 return;
148 }
149
150 ?>
151 <div class="fct-archive-header-wrap">
152 <h1 class="fct-archive-title">
153 <?php
154 $queried_object = get_queried_object();
155 if ($queried_object && !empty($queried_object->name)) {
156 echo esc_html($queried_object->name);
157 } else {
158 echo esc_html(get_the_archive_title());
159 }
160 ?>
161 </h1>
162 <?php
163 $termDescription = term_description();
164 if ($termDescription) {
165 ?>
166 <div class="fct-archive-description">
167 <?php echo wp_kses_post(wpautop($termDescription)); ?>
168 </div>
169 <?php
170 }
171 ?>
172 </div>
173 <?php
174
175 }
176
177 public function renderProductArchive()
178 {
179 $queried_object = get_queried_object();
180
181 if (empty($queried_object->taxonomy) || !is_tax(get_object_taxonomies('fluent-products'))) {
182 return;
183 }
184
185 $productsQuery = (new ProductQuery([
186 'is_main_query' => true,
187 'paginate' => 'simple',
188 'with' => ['detail', 'variants']
189 ]));
190
191 $publicTaxonomies = Taxonomy::getTaxonomies();
192
193
194 unset($publicTaxonomies[$queried_object->taxonomy]);
195
196 $connectedTerms = $productsQuery->getConnectedTerms(array_values($publicTaxonomies), true);
197 //TODO: also add the child terms
198 $taxFilters = [];
199
200 foreach ($connectedTerms as $taxonomyName => $term) {
201
202 $taxObj = get_taxonomy($taxonomyName);
203 if (!$taxObj) {
204 continue;
205 }
206
207 $taxFilters[$taxonomyName] = [
208 'options' => $term['terms'],
209 'term' => $taxonomyName,
210 'label' => $taxObj->label
211 ];
212 }
213
214 $products = $productsQuery->get();
215 (new ShopAppRenderer($products, [
216 'default_filters' => $productsQuery->getDefaultFilters(),
217 'custom_filters' => [
218 'taxonomies' => [
219 $taxFilters
220 ],
221 'search' => true,
222 'price_range' => true,
223 ],
224 'pagination_type' => 'simple'
225 ]))->render();
226 }
227
228 public function initSingleProductHooks()
229 {
230 add_filter('the_title', function ($title, $post_id) {
231 if (apply_filters('fluent_cart/disable_auto_single_product_page', false)) {
232 return $title;
233 }
234
235 global $post;
236 global $wp_query;
237
238 if (
239 // this is the main query for a single product page
240 $wp_query->is_main_query()
241 // post_id is get_queried id
242 && $post_id == $wp_query->get_queried_object_id()
243 && $post->post_type === FluentProducts::CPT_NAME
244 ) {
245 return '';
246 }
247
248 return $title;
249 }, 10, 2);
250 add_filter('get_the_excerpt', function ($excerpt, $post) {
251 if (apply_filters('fluent_cart/disable_auto_single_product_page', false)) {
252 return $excerpt;
253 }
254
255 global $wp_query;
256
257 if (
258 $wp_query->is_main_query()
259 && $post->ID == $wp_query->get_queried_object_id()
260 && $post->post_type === FluentProducts::CPT_NAME
261 ) {
262 return '';
263 }
264
265 return $excerpt;
266 }, 10, 2);
267
268 add_filter('the_content', [$this, 'filterSingleProductContent'], 999);
269 }
270
271 public function filterSingleProductContent($content)
272 {
273 if (apply_filters('fluent_cart/disable_auto_single_product_page', false)) {
274 return $content;
275 }
276
277 global $post;
278 global $wp_query;
279
280 if (!$wp_query->is_main_query() && $post->post_type !== FluentProducts::CPT_NAME) {
281 return $content;
282 }
283
284 remove_filter('the_content', [$this, 'filterSingleProductContent']);
285 ob_start();
286 do_action('fluent_cart/product/render_product_header', $post->ID);
287 $headerContent = ob_get_clean();
288 $content = $headerContent . $content;
289
290 $storeSettings = new StoreSettings();
291
292 $showRelevant = $storeSettings->get('show_relevant_product_in_single_page') == 'yes';
293 $showRelevant = apply_filters('fluent_cart/single_product_page/show_relevant_products', $showRelevant, $post->ID);
294 if ($showRelevant) {
295 $products = ShopResource::getSimilarProducts($post->ID, false);
296 ob_start();
297 (new ProductListRenderer(
298 $products,
299 __('Related Products', 'fluent-cart'),
300 'fct-similar-product-list-container'
301 ))->render();
302
303 $relevantProducts = ob_get_clean();
304 $content .= $relevantProducts;
305 }
306
307 return $content;
308 }
309
310 public function renderProductHeader($productId = false)
311 {
312 if (!$productId) {
313 $productId = get_the_ID();
314 }
315
316 if (!$productId) {
317 return;
318 }
319
320 $product = ProductDataSetup::getProductModel($productId);
321 if (!$product || !$product->detail) {
322 return;
323 }
324
325 $storeSettings = new StoreSettings();
326 (new ProductRenderer($product, [
327 'view_type' => $storeSettings->get('variation_view', 'both'),
328 'column_type' => $storeSettings->get('variation_columns', 'masonry')
329 ]))->render();
330 }
331
332 private function tempFixShortcodeContent($content)
333 {
334
335 if (!$content) {
336 return $content;
337 }
338
339 // Remove empty <p> tags (including those with whitespace), <br> tags, and new lines
340 $cleaned = preg_replace([
341 '/<p>\s*<\/p>/i', // Remove empty <p> tags
342 '/<p>\s*<br\s*\/?>\s*<\/p>/i', // Remove <p> tags containing only <br>
343 '/<br\s*\/?>/i', // Remove all <br> tags
344 '/[\r\n]+/' // Remove all new lines
345 ], '', $content);
346
347 // Remove extra whitespace between tags to clean up the output
348 return preg_replace('/>\s+</', '><', $cleaned);
349 }
350
351 }
352