PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
1.6.5 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 All 48 releases
fluent-cart / app / Modules / Templating / TemplateActions.php

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

355 lines 11.1 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([
216 'products' => $products,
217 'total' => $products->total(),
218 ], [
219 'default_filters' => $productsQuery->getDefaultFilters(),
220 'custom_filters' => [
221 'taxonomies' => [
222 $taxFilters
223 ],
224 'search' => true,
225 'price_range' => true,
226 ],
227 'pagination_type' => 'simple'
228 ]))->render();
229 }
230
231 public function initSingleProductHooks()
232 {
233 add_filter('the_title', function ($title, $post_id) {
234 if (apply_filters('fluent_cart/disable_auto_single_product_page', false)) {
235 return $title;
236 }
237
238 global $post;
239 global $wp_query;
240
241 if (
242 // this is the main query for a single product page
243 $wp_query->is_main_query()
244 // post_id is get_queried id
245 && $post_id == $wp_query->get_queried_object_id()
246 && $post->post_type === FluentProducts::CPT_NAME
247 ) {
248 return '';
249 }
250
251 return $title;
252 }, 10, 2);
253 add_filter('get_the_excerpt', function ($excerpt, $post) {
254 if (apply_filters('fluent_cart/disable_auto_single_product_page', false)) {
255 return $excerpt;
256 }
257
258 global $wp_query;
259
260 if (
261 $wp_query->is_main_query()
262 && $post->ID == $wp_query->get_queried_object_id()
263 && $post->post_type === FluentProducts::CPT_NAME
264 ) {
265 return '';
266 }
267
268 return $excerpt;
269 }, 10, 2);
270
271 add_filter('the_content', [$this, 'filterSingleProductContent'], 999);
272 }
273
274 public function filterSingleProductContent($content)
275 {
276 if (apply_filters('fluent_cart/disable_auto_single_product_page', false)) {
277 return $content;
278 }
279
280 global $post;
281 global $wp_query;
282
283 if (!$wp_query->is_main_query() && $post->post_type !== FluentProducts::CPT_NAME) {
284 return $content;
285 }
286
287 remove_filter('the_content', [$this, 'filterSingleProductContent']);
288 ob_start();
289 do_action('fluent_cart/product/render_product_header', $post->ID);
290 $headerContent = ob_get_clean();
291 $content = $headerContent . $content;
292
293 $storeSettings = new StoreSettings();
294
295 $showRelevant = $storeSettings->get('show_relevant_product_in_single_page') == 'yes';
296 $showRelevant = apply_filters('fluent_cart/single_product_page/show_relevant_products', $showRelevant, $post->ID);
297 if ($showRelevant) {
298 $products = ShopResource::getSimilarProducts($post->ID, false);
299 ob_start();
300 (new ProductListRenderer(
301 $products,
302 __('Related Products', 'fluent-cart'),
303 'fct-similar-product-list-container'
304 ))->render();
305
306 $relevantProducts = ob_get_clean();
307 $content .= $relevantProducts;
308 }
309
310 return $content;
311 }
312
313 public function renderProductHeader($productId = false)
314 {
315 if (!$productId) {
316 $productId = get_the_ID();
317 }
318
319 if (!$productId) {
320 return;
321 }
322
323 $product = ProductDataSetup::getProductModel($productId);
324 if (!$product || !$product->detail) {
325 return;
326 }
327
328 $storeSettings = new StoreSettings();
329 (new ProductRenderer($product, [
330 'view_type' => $storeSettings->get('variation_view', 'both'),
331 'column_type' => $storeSettings->get('variation_columns', 'masonry')
332 ]))->render();
333 }
334
335 private function tempFixShortcodeContent($content)
336 {
337
338 if (!$content) {
339 return $content;
340 }
341
342 // Remove empty <p> tags (including those with whitespace), <br> tags, and new lines
343 $cleaned = preg_replace([
344 '/<p>\s*<\/p>/i', // Remove empty <p> tags
345 '/<p>\s*<br\s*\/?>\s*<\/p>/i', // Remove <p> tags containing only <br>
346 '/<br\s*\/?>/i', // Remove all <br> tags
347 '/[\r\n]+/' // Remove all new lines
348 ], '', $content);
349
350 // Remove extra whitespace between tags to clean up the output
351 return preg_replace('/>\s+</', '><', $cleaned);
352 }
353
354 }
355