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 / CPT / FluentProducts.php

FluentProducts.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.4, at app/CPT/FluentProducts.php

421 lines 15.6 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\CPT;
4
5
6 use FluentCart\Api\StoreSettings;
7 use FluentCart\App\App;
8 use FluentCart\App\Services\URL;
9 use FluentCart\App\Vite;
10 use FluentCart\Framework\Support\Arr;
11
12 class FluentProducts
13 {
14
15 const CPT_NAME = 'fluent-products';
16
17 protected $showStandaloneMenu = false;
18
19 public function register()
20 {
21 add_filter('get_edit_post_link', function ($link, $postId) {
22 if ($this->showStandaloneMenu) {
23 return $link;
24 }
25
26 $post = get_post($postId);
27 if ($post && $post->post_type === 'fluent-products') {
28 return URL::getDashboardUrl('products/' . $postId);
29 }
30
31 return $link;
32 }, 99, 2);
33
34
35 add_action('init', function () {
36
37 $this->showStandaloneMenu = apply_filters('fluent_cart/show_standalone_product_menu', false);
38
39 $this->registerPostType();
40 $this->registerProductTaxonomies();
41 });
42
43 add_action('admin_enqueue_scripts', function () {
44 $screen = get_current_screen();
45 $isProductEditingScreen = $screen && $screen->post_type === 'fluent-products' && $screen->base === 'post';
46
47 if ($this->showStandaloneMenu) {
48 if ($isProductEditingScreen && App::request()->get('custom-editor') === 'true') {
49 wp_enqueue_style('wp-admin');
50 $this->enqueueCustomEditorStyles();
51 }
52 return;
53 }
54
55 if (!$isProductEditingScreen) {
56 if ($screen && $screen->post_type === 'fluent-products' && ($screen->base == 'edit-tags' || $screen->base == 'term')) {
57 $this->customizeTaxonomyScreen();
58 }
59 return;
60 }
61
62 // Make sure the default admin styles are enqueued
63 wp_enqueue_style('wp-admin');
64
65 $this->enqueueCustomEditorStyles();
66
67 wp_register_script('fluent-products-inline-js', '', [], FLUENTCART_VERSION, true);
68 wp_enqueue_script('fluent-products-inline-js');
69
70 $custom_js = "window.addEventListener('click', function (event) {
71 const anchor = event.target.closest('a');
72 if (anchor) {
73 event.preventDefault();
74 let href = anchor.getAttribute('href');
75 if (href && href != '#' && !href.startsWith('javascript:')) {
76 window.open(href, '_blank');
77 }
78 }
79 });";
80
81 // wp_add_inline_script('fluent-products-inline-js', $custom_js);
82
83 });
84
85 // add_action('elementor/editor/after_enqueue_scripts', function () {
86 // $this->registerElementorScript();
87 // });
88
89 add_action('enqueue_block_editor_assets', function () {
90 global $post;
91
92 // Only apply to book post type
93 if (!isset($post->post_type) || 'fluent-products' !== $post->post_type) {
94 return;
95 }
96
97
98 wp_add_inline_script(
99 'wp-blocks',
100 "
101 wp.domReady( function() {
102 // Force fullscreen mode
103 wp.data.dispatch( 'core/edit-post' ).toggleFeature( 'fullscreenMode', true );
104
105 // Hide the fullscreen toggle button
106 const style = document.createElement( 'style' );
107 style.textContent = '.edit-post-fullscreen-mode-close, .components-button[aria-label=\"Exit fullscreen\"], .edit-post-fullscreen-mode-close ~ .edit-post-header-toolbar__left button[aria-label=\"Exit fullscreen\"] { display: none !important; }';
108 document.head.appendChild( style );
109 } );
110 "
111 );
112 });
113
114 add_action('update_post_meta', [$this, 'handleThumbChange'], 10, 4);
115 add_action('added_post_meta', [$this, 'handleThumbChange'], 10, 4);
116 }
117
118 public function registerElementorScript()
119 {
120 $custom_js = "console.log('doc ready - editor context');
121
122 // Wait for Elementor editor to be ready
123 jQuery(window).on('elementor:init', () => {
124 console.log('Elementor editor initialized');
125
126 jQuery(document).on('click', '#elementor-editor-wrapper-v2 .MuiButtonGroup-root > button', function() {
127 console.log('Button clicked!', this);
128
129 let content = '';
130
131 try {
132 const previewFrame = jQuery('#elementor-preview-iframe')[0];
133 if (previewFrame && previewFrame.contentDocument) {
134 const previewContent = previewFrame.contentDocument.querySelector('.elementor');
135 if (previewContent) {
136 content = previewContent.outerHTML;
137 console.log('Content from preview frame:', content.substring(0, 200) + '...');
138 }
139 }
140 } catch (e) {
141 console.log('Could not access preview frame:', e);
142 }
143
144 // Fallback: Get structured data
145 if (!content && typeof elementor !== 'undefined') {
146 try {
147 content = JSON.stringify(elementor.documents.currentDocument.container.children.toJSON());
148 } catch (e) {
149 console.log('Could not get elementor data:', e);
150 }
151 }
152
153 // Send message to parent window
154 window.parent.postMessage(
155 {
156 type: 'gutenbergContentChanged',
157 content: content,
158 timestamp: Date.now()
159 },
160 '*'
161 );
162 });
163 });";
164
165 wp_add_inline_script('elementor-editor', $custom_js);
166 }
167
168 public function registerPostType()
169 {
170 $productSlug = (new StoreSettings())->get('product_slug') ?? 'item';
171 $urlSlug = apply_filters('fluent_cart/front_url_slug', $productSlug, []);
172 $urlWithFront = apply_filters('fluent_cart/product_url_with_front', true, [
173 'slug' => $urlSlug
174 ]);
175
176 $singularName = __('Product', 'fluent-cart');
177
178 if (defined('WC_PLUGIN_FILE')) {
179 $singularName = __('Product (FluentCart)', 'fluent-cart');
180 }
181
182 register_post_type(self::CPT_NAME, [
183 'capability_type' => 'post',
184 // 'capabilities' => [
185 // 'edit_post' => 'edit_your_post_type',
186 // 'read_post' => 'read_your_post_type',
187 // 'delete_post' => 'do_not_allow', // block trash/delete
188 // 'edit_posts' => 'edit_your_post_types',
189 // 'edit_others_posts' => 'edit_others_your_post_types',
190 // 'publish_posts' => 'publish_your_post_types',
191 // 'read_private_posts' => 'read_private_your_post_types',
192 // ],
193 // 'map_meta_cap' => true,
194 'label' => __('Products', 'fluent-cart'),
195 'labels' => [
196 'name' => __('Products', 'fluent-cart'),
197 'singular_name' => $singularName,
198 'add_new' => _x('Add New Product', 'product', 'fluent-cart'),
199 'add_new_item' => __('Add New Product', 'fluent-cart'),
200 'edit_item' => __('Edit Product', 'fluent-cart'),
201 'view_item' => __('View Product', 'fluent-cart'),
202 'search_items' => __('Search products', 'fluent-cart'),
203 ],
204 //'_edit_link' => 'admin.php?page=fluent-cart#/products/%d/pricing',
205 'description' => __('FluentCart products post type', 'fluent-cart'),
206 'public' => true,
207 'hierarchical' => false,
208 'exclude_from_search' => false,
209 'publicly_queryable' => true,
210 'show_ui' => true,
211 'show_in_menu' => $this->showStandaloneMenu,
212 'show_in_nav_menus' => true,
213 'show_in_admin_bar' => true,
214 'menu_position' => 24,
215 'has_archive' => true,
216 'show_in_rest' => true,
217 // 'rest_base' => $urlSlug, // Optional: customize REST API base
218 'rest_controller_class' => 'WP_REST_Posts_Controller', // Optional: use default controller
219 'supports' => [
220 'title',
221 'editor',
222 'excerpt',
223 'thumbnail',
224 'author',
225 'revisions',
226 'custom-fields',
227 ],
228 'rewrite' => [
229 'slug' => $urlSlug,
230 'with_front' => $urlWithFront,
231 'feeds' => true,
232 'pages' => true,
233 ],
234 'query_var' => $urlSlug,
235 'can_export' => true,
236 'delete_with_user' => false
237 ]);
238
239 $this->disableCreateAndEditForFluentProducts();
240 }
241
242 public function disableCreateAndEditForFluentProducts()
243 {
244 add_action('admin_init', function () {
245 // return for ajax and rest api
246 if ((defined('DOING_AJAX') && DOING_AJAX) || (defined('REST_REQUEST') && REST_REQUEST)) {
247 return;
248 }
249
250 global $pagenow;
251
252 if ($pagenow != 'post-new.php') {
253 return;
254 }
255
256 // disable direct create product
257 $postType = $_GET['post_type'] ?? '';
258 if ($postType == self::CPT_NAME) {
259 wp_redirect(admin_url('admin.php?page=fluent-cart#/products/?add-new=true'));
260 exit;
261 }
262 });
263 }
264
265 public function registerProductTaxonomies()
266 {
267 register_taxonomy('product-categories', self::CPT_NAME, [
268 'hierarchical' => true,
269 'show_ui' => true,
270 'show_admin_column' => true,
271 'show_in_rest' => true,
272 'query_var' => true,
273 'rewrite' => ['slug' => 'product-categories'],
274 'labels' => [
275 'name' => __('Categories', 'fluent-cart'),
276 'singular_name' => __('Category', 'fluent-cart'),
277 'search_items' => __('Search Categories', 'fluent-cart'),
278 'all_items' => __('All Categories', 'fluent-cart'),
279 'parent_item' => __('Parent Category', 'fluent-cart'),
280 'parent_item_colon' => __('Parent Category:', 'fluent-cart'),
281 'edit_item' => __('Edit Category', 'fluent-cart'),
282 'update_item' => __('Update Category', 'fluent-cart'),
283 'add_new_item' => __('Add New Category', 'fluent-cart'),
284 'new_item_name' => __('New Category Name', 'fluent-cart'),
285 'menu_name' => __('Product Category', 'fluent-cart'),
286 'not_found' => __('No categories found.', 'fluent-cart'),
287 ],
288 ]);
289
290 register_taxonomy('product-brands', self::CPT_NAME, [
291 'hierarchical' => true,
292 'show_ui' => true,
293 'show_in_rest' => true,
294 'show_admin_column' => false,
295 'query_var' => true,
296 'rewrite' => ['slug' => 'product-brands'],
297 'labels' => [
298 'name' => __('Brands', 'fluent-cart'),
299 'singular_name' => __('Brand', 'fluent-cart'),
300 'search_items' => __('All Brand', 'fluent-cart'),
301 'all_items' => __('All Brands', 'fluent-cart'),
302 'parent_item' => __('Parent Brand', 'fluent-cart'),
303 'edit_item' => __('Edit Brand', 'fluent-cart'),
304 'update_item' => __('Update Brand', 'fluent-cart'),
305 'add_new_item' => __('Add New Brand', 'fluent-cart'),
306 'new_item_name' => __('New Brand Name', 'fluent-cart'),
307 'menu_name' => __('Product Brand', 'fluent-cart'),
308 'view_item' => __('View Brand', 'fluent-cart'),
309 'not_found' => __('No Brand found', 'fluent-cart'),
310 ],
311 ]);
312
313 // product-tags is deliberately NOT registered: FluentCart ships
314 // categories and brands only (product decision 2026-08-06). The former
315 // dead consumers (Product tag getters, shop shortcode tag= filter)
316 // were removed with that decision.
317 }
318
319 private function enqueueCustomEditorStyles()
320 {
321 $custom_css =
322 '#editor .editor-sidebar__panel .editor-post-summary .editor-post-trash,' .
323 '#wpadminbar,' .
324 '#adminmenu,' .
325 '#adminmenuback,' .
326 '#adminmenuwrap {' .
327 'display: none !important;' .
328 '}' .
329 '#wpbody-content .interface-interface-skeleton {' .
330 'left: 0 !important;' .
331 'top: 0 !important;' .
332 '}' .
333 '#wpcontent, #wpfooter {' .
334 'margin-left: 0 !important;' .
335 'margin-right: 0 !important;' .
336 '}';
337
338 wp_add_inline_style('wp-admin', $custom_css);
339 }
340
341 public function customizeTaxonomyScreen()
342 {
343
344 Vite::enqueueScript('fluent_cart_admin_global_js',
345 'admin/global.js',
346 );
347
348 Vite::enqueueStyle('fluent_cart_admin_app_css',
349 'styles/tailwind/style.css',
350 );
351
352 Vite::enqueueStyle('fluent_cart_taxonomy_css',
353 'styles/tailwind/taxonomy.scss',
354 );
355
356 add_action('in_admin_header', function () {
357 ?>
358 <div style="margin-left: -20px;" class="fc_taxonomy_menu" id="fct_admin_menu_holder">
359 <?php do_action('fluent_cart/admin_menu'); ?>
360 </div>
361 <?php
362 });
363 }
364
365 public function handleThumbChange($meta_id, $object_id, $meta_key, $_meta_value)
366 {
367 if ($meta_key !== '_thumbnail_id') {
368 return;
369 }
370
371
372 $post = get_post($object_id);
373
374
375 if ($post->post_type !== FluentProducts::CPT_NAME) {
376 return;
377 }
378
379
380 //get post meta
381 $oldGallery = get_post_meta($object_id, FluentProducts::CPT_NAME . '-gallery-image', true);
382
383 if (!is_array($oldGallery)) {
384 $oldGallery = [];
385 }
386
387 $currentThumbnailId = $_meta_value;
388
389
390 //get thumbnail image with all info like title id and url
391 $currentThumbnail = wp_prepare_attachment_for_js($currentThumbnailId);
392 $currentThumbnail = [
393 'id' => $currentThumbnailId,
394 'url' => Arr::get($currentThumbnail, 'url'),
395 'name' => Arr::get($currentThumbnail, 'title')
396 ];
397
398 $currentThumbnailId = Arr::get($currentThumbnail, 'id');
399
400
401 $found = false;
402 $index = 0;
403 foreach ($oldGallery as $index => $image) {
404 if ($image['id'] == $currentThumbnailId) {
405 $found = true;
406 break;
407 }
408 }
409
410
411 //if found bring it on top
412 if ($found) {
413 unset($oldGallery[$index]);
414 array_unshift($oldGallery, $image);
415 } else {
416 array_unshift($oldGallery, $currentThumbnail);
417 }
418 update_post_meta($object_id, FluentProducts::CPT_NAME . '-gallery-image', $oldGallery);
419 }
420 }
421