PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / 2.3.0
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster v2.3.0
2.6.0 trunk 1.1.0 1.1.4 1.2.1 1.2.2 1.2.3 1.2.5 1.2.9 1.3.1 1.3.2 1.5.0 1.5.1 1.5.4 1.5.7 1.5.8 1.5.9 1.6.2 1.6.5 1.6.8 1.7.2 1.7.4 1.7.5 1.7.9 1.8.1 All 42 releases
sellkit / includes / admin / class-steps.php

class-steps.php in SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster 2.3.0, at includes/admin/class-steps.php

533 lines 14.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 defined( 'ABSPATH' ) || die();
4
5 /**
6 * Steps class.
7 *
8 * @since 1.1.0
9 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
10 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
11 */
12 class Sellkit_Admin_Steps {
13
14 /**
15 * Class instance.
16 *
17 * @since 1.1.0
18 * @var Sellkit_Admin_Steps
19 */
20 private static $instance = null;
21
22 /**
23 * Class instance.
24 *
25 * @since 1.1.0
26 * @var string
27 */
28 const SELLKIT_STEP_POST_TYPE = 'sellkit_step';
29
30 /**
31 * Get a class instance.
32 *
33 * @since 1.1.0
34 *
35 * @return Sellkit_Admin_Steps Class instance.
36 */
37 public static function get_instance() {
38 if ( null === self::$instance ) {
39 self::$instance = new self();
40 }
41
42 return self::$instance;
43 }
44
45 /**
46 * Class constructor.
47 *
48 * @since 1.1.0
49 */
50 public function __construct() {
51 $this->register_step_post_type();
52
53 add_action( 'wp_ajax_sellkit_steps_get_products', [ $this, 'get_products' ] );
54 add_action( 'wp_ajax_sellkit_steps_get_products_data', [ $this, 'get_products_data' ] );
55 add_action( 'sellkit_funnels_after_create_new_step', [ $this, 'after_create_step_callback' ], 10 );
56 add_action( 'sellkit_funnels_after_edit_step_name_slug', [ $this, 'edit_step_name_and_slug' ], 10 );
57 add_action( 'sellkit_funnels_after_delete_step_post', [ $this, 'delete_related_page' ], 10 );
58 add_action( 'sellkit_funnels_update_steps', [ $this, 'save_steps_data' ], 10, 2 );
59 add_filter( 'template_include', [ $this, 'load_page_template' ], 10 );
60 add_action( 'wp_ajax_sellkit_steps_get_coupons', [ $this, 'get_coupons' ] );
61 add_action( 'wp_loaded', [ $this, 'sellkit_steps_flush_rules' ] );
62 add_action( 'sellkit_after_toggle_status_sellkit-funnels', [ $this, 'update_steps_post_status' ], 10, 2 );
63 add_action( 'sellkit_funnels_after_delete_posts_sellkit-funnels', [ $this, 'remove_steps_pages' ], 10, 1 );
64 }
65
66 /**
67 * Removes steps pages.
68 *
69 * @since 1.5.4
70 * @return void
71 * @param array $post Posts.
72 */
73 public function remove_steps_pages( $post ) {
74 $nodes = get_post_meta( $post['ID'], 'nodes' );
75
76 if ( empty( $nodes[0] ) ) {
77 return;
78 }
79
80 foreach ( $nodes[0] as $node ) {
81 wp_delete_post( $node['page_id'] );
82 }
83 }
84
85 /**
86 * Get default funnels.
87 *
88 * @since 1.1.0
89 */
90 public static function get_default_funnel_steps() {
91 return [
92 'sales-page' => esc_html__( 'Sales Page', 'sellkit' ),
93 'checkout' => esc_html__( 'Checkout', 'sellkit' ),
94 'thankyou' => esc_html__( 'Thank You', 'sellkit' ),
95 'decision' => esc_html__( 'Condition', 'sellkit' ),
96 'upsell' => esc_html__( 'Upsell', 'sellkit' ),
97 'downsell' => esc_html__( 'Downsell', 'sellkit' ),
98 'optin-confirmation' => esc_html__( 'Opt-in Confirmation' ),
99 'optin' => esc_html__( 'Opt-in', 'sellkit' ),
100 ];
101 }
102
103 /**
104 * Save_steps_data.
105 *
106 * @since 1.1.0
107 * @param array $steps Steps.
108 * @param int $funnel_id Funnel Id.
109 */
110 public static function save_steps_data( $steps, $funnel_id ) {
111 foreach ( $steps as $step_number => $step ) {
112 $step['funnel_id'] = $funnel_id;
113 $step['number'] = $step_number;
114
115 if ( array_key_exists( 'page_id', $step ) ) {
116 update_post_meta( $step['page_id'], 'step_data', $step );
117 }
118 }
119 }
120
121 /**
122 * Get all coupons.
123 *
124 * @since 1.1.0
125 */
126 public static function get_coupons() {
127 check_ajax_referer( 'sellkit', 'nonce' );
128
129 $input_value = sellkit_htmlspecialchars( INPUT_GET, 'input_value' );
130
131 $filtered_products = [];
132 $args = [
133 'post_type' => 'shop_coupon',
134 'post_status' => 'any',
135 's' => $input_value,
136 ];
137
138 $query = new WP_Query( $args );
139
140 if ( $query->have_posts() ) {
141 while ( $query->have_posts() ) {
142 $query->the_post();
143 $filtered_products[] = [
144 'label' => htmlspecialchars_decode( get_the_title() ),
145 'value' => get_the_ID(),
146 ];
147 }
148 }
149
150 wp_send_json_success( $filtered_products );
151 }
152
153 /**
154 * Get products.
155 *
156 * @since 1.1.0
157 */
158 public static function get_products() {
159 check_ajax_referer( 'sellkit', 'nonce' );
160
161 $input_value = sellkit_htmlspecialchars( INPUT_GET, 'input_value' );
162
163 if ( ! sellkit()->has_valid_dependencies() ) {
164 wp_send_json_error( __( 'Please install and activate WooCommerce.', 'sellkit' ) );
165 }
166
167 $filtered_products = [];
168
169 $args = [
170 'status' => 'any',
171 'orderby' => 'name',
172 'order' => 'ASC',
173 's' => $input_value,
174 'posts_per_page' => -1,
175 ];
176
177 $all_products = wc_get_products( $args );
178
179 foreach ( $all_products as $product ) {
180 if ( $product->get_type() !== 'variable' && $product->get_type() !== 'external' ) {
181 $filtered_products[] = [
182 'label' => $product->get_title(),
183 'value' => $product->get_id(),
184 ];
185 }
186
187 if ( $product->get_type() === 'variable' ) {
188 foreach ( $product->get_available_variations() as $available_variation ) {
189 $attributes = $available_variation['attributes'];
190 $attributes = implode( ', ', array_values( $attributes ) );
191
192 $filtered_products[] = [
193 'label' => "{$product->get_title()} - {$attributes} (#{$available_variation['variation_id']})",
194 'value' => $available_variation['variation_id'],
195 ];
196 }
197 }
198 }
199
200 wp_send_json_success( $filtered_products );
201 }
202
203 /**
204 * Get products.
205 *
206 * @since 1.1.0
207 */
208 public static function get_products_data() {
209 check_ajax_referer( 'sellkit', 'nonce' );
210
211 $filtered_products = [];
212 $products_id = filter_input( INPUT_GET, 'products_id', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
213
214 if ( ! sellkit()->has_valid_dependencies() ) {
215 wp_send_json_error( __( 'Please install and activate WooCommerce.', 'sellkit' ) );
216 }
217
218 if ( empty( $products_id ) ) {
219 wp_send_json_error( __( 'Please send some product ids' ) );
220 }
221
222 $products_id = is_array( $products_id ) ? $products_id : [ $products_id ];
223
224 foreach ( $products_id as $product_id ) {
225 $product = wc_get_product( $product_id );
226
227 if ( empty( $product ) ) {
228 continue;
229 }
230
231 $data = $product->get_data();
232
233 if ( 'trash' === $data['status'] ) {
234 continue;
235 }
236
237 $attributes = $product->get_attributes();
238 $label = $product->get_title();
239
240 if ( $product->get_type() === 'variation' ) {
241 $attributes = implode( ', ', array_values( $attributes ) );
242 $label = "{$product->get_title()} - $attributes (#{$product->get_id()})";
243 }
244
245 $filtered_products[] = [
246 'label' => $label,
247 'id' => $product->get_id(),
248 'title' => $label,
249 'thumbnail' => $product->get_image(),
250 'regular_price' => $product->get_regular_price( 'edit' ),
251 'sale_price' => ( $product->is_on_sale() ) ? $product->get_sale_price() : false,
252 ];
253 }
254
255 wp_send_json_success( $filtered_products );
256 }
257
258 /**
259 * Filter step data before saving.
260 *
261 * @since 1.1.0
262 * @param string $post_id Post id.
263 * @SuppressWarnings(PHPMD.NPathComplexity)
264 */
265 public function after_create_step_callback( $post_id ) {
266 $sub_action_data = sellkit_post( 'sub_action_data' );
267 $origin_node = isset( $sub_action_data['origin_node'] ) ? $sub_action_data['origin_node'] : '';
268 $target_node = ! empty( $sub_action_data['target_node'] ) ? $sub_action_data['target_node'] : null;
269 $target_index = isset( $sub_action_data['target_index'] ) ? $sub_action_data['target_index'] : 0;
270 $nodes = get_post_meta( $post_id, 'nodes', true );
271 $last_node = end( $nodes );
272
273 array_pop( $nodes );
274
275 $new_node_id = wp_insert_post( [
276 'post_type' => self::SELLKIT_STEP_POST_TYPE,
277 'post_title' => $last_node['title'],
278 'post_name' => sanitize_title( $last_node['title'] ),
279 'post_status' => 'publish',
280 ] );
281
282 $current_target = [ 'nodeId' => $target_node ];
283 $last_node['page_id'] = $new_node_id;
284 $last_node['slug'] = get_post_field( 'post_name', $new_node_id );
285 $last_node['targets'] = ! is_null( $target_node ) ? [ $current_target ] : [];
286
287 if (
288 'decision' === $last_node['type']['key'] && ! is_null( $target_node ) ||
289 'upsell' === $last_node['type']['key'] && ! is_null( $target_node ) ||
290 'downsell' === $last_node['type']['key'] && ! is_null( $target_node )
291 ) {
292 $last_node['targets'] = [
293 $current_target,
294 $current_target
295 ];
296 }
297
298 // Adding current target info the the step.
299 $last_node['current_target_index'] = $target_index;
300
301 if ( 'last-node' === $origin_node ) {
302 $first_end_path_node_key = self::get_first_end_path_node_key( $nodes );
303 $last_node['origin_node'] = 'last-node';
304
305 if ( $first_end_path_node_key ) {
306 unset( $nodes[ $first_end_path_node_key ]['origin_node'] );
307 }
308 }
309
310 $node_data = $last_node;
311 $node_data['funnel_id'] = $post_id;
312 $node_data['number'] = count( $nodes );
313
314 update_post_meta( $new_node_id, 'step_data', $node_data );// it should be updated.
315
316 $old_node_keys = array_keys( $nodes );
317 $nodes[ end( $old_node_keys ) + 1 ] = $last_node;
318 $keys = array_keys( $nodes );
319
320 if (
321 'last-node' !== $origin_node &&
322 'decision' !== $nodes[ $origin_node ]['type']['key'] &&
323 'upsell' !== $nodes[ $origin_node ]['type']['key'] &&
324 'downsell' !== $nodes[ $origin_node ]['type']['key']
325 ) {
326 $nodes[ $origin_node ]['targets'] = [ [ 'nodeId' => strval( end( $keys ) ) ] ];
327 }
328
329 if (
330 'last-node' !== $origin_node &&
331 (
332 'decision' === $nodes[ $origin_node ]['type']['key'] ||
333 'upsell' === $nodes[ $origin_node ]['type']['key'] ||
334 'downsell' === $nodes[ $origin_node ]['type']['key']
335 )
336 ) {
337 if ( is_array( $nodes[ $origin_node ]['targets'] ) && empty( $nodes[ $origin_node ]['targets'][0] ) ) {
338 $nodes[ $origin_node ]['targets'][0] = ! empty( $target_node ) ? [ 'nodeId' => $target_node ] : null;
339 }
340
341 if ( is_array( $nodes[ $origin_node ]['targets'] ) && empty( $nodes[ $origin_node ]['targets'][1] ) ) {
342 $nodes[ $origin_node ]['targets'][1] = ! empty( $target_node ) ? [ 'nodeId' => $target_node ] : null;
343 }
344
345 $nodes[ $origin_node ]['targets'][ (int) $target_index ] = [ 'nodeId' => strval( end( $old_node_keys ) + 1 ) ];
346 }
347
348 if ( '' === $origin_node && null === $target_node ) {
349 update_post_meta( $post_id, 'nodes', [ 1 => $last_node ] );
350 return;
351 }
352
353 update_post_meta( $post_id, 'nodes', $nodes );
354 }
355
356 /**
357 * Gets last node origin node
358 *
359 * @param array $nodes Nodes.
360 * @since 1.5.0
361 */
362 public static function get_first_end_path_node_key( $nodes ) {
363 foreach ( $nodes as $key => $node ) {
364 if ( ! empty( $node['origin_node'] ) && 'last-node' === $node['origin_node'] ) {
365 return $key;
366 }
367 }
368
369 return false;
370 }
371
372 /**
373 * Loading page templates.
374 *
375 * @since 1.1.0
376 * @param string $template Template name.
377 */
378 public function load_page_template( $template ) {
379
380 global $post;
381
382 if ( 'string' !== gettype( $template ) || ! is_object( $post ) || self::SELLKIT_STEP_POST_TYPE !== $post->post_type ) {
383 return $template;
384 }
385
386 if ( has_blocks( $post ) ) {
387 return $template;
388 }
389
390 remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' );
391 add_filter( 'next_post_rel_link', '__return_empty_string' );
392 add_filter( 'previous_post_rel_link', '__return_empty_string' );
393
394 $file = sellkit()->plugin_dir() . 'includes/templates/canvas.php';
395
396 if ( ! file_exists( $template ) ) {
397 return $template;
398 }
399
400 return $file;
401 }
402
403 /**
404 * Register step post type.
405 *
406 * @since 1.1.0
407 */
408 public function register_step_post_type() {
409 $labels = array(
410 'name' => esc_html_x( 'Steps', 'flow step general name', 'sellkit' ),
411 'singular_name' => esc_html_x( 'Step', 'flow step singular name', 'sellkit' ),
412 'search_items' => esc_html__( 'Search Steps', 'sellkit' ),
413 'all_items' => esc_html__( 'All Steps', 'sellkit' ),
414 'edit_item' => esc_html__( 'Edit Step', 'sellkit' ),
415 'view_item' => esc_html__( 'View Step', 'sellkit' ),
416 'add_new' => esc_html__( 'Add New', 'sellkit' ),
417 'update_item' => esc_html__( 'Update Step', 'sellkit' ),
418 'add_new_item' => esc_html__( 'Add New', 'sellkit' ),
419 'new_item_name' => esc_html__( 'New Step Name', 'sellkit' ),
420 );
421
422 $args = array(
423 'labels' => $labels,
424 'public' => true,
425 'query_var' => true,
426 'can_export' => true,
427 'exclude_from_search' => true,
428 'show_ui' => true,
429 'show_in_menu' => false,
430 'show_in_admin_bar' => true,
431 'show_in_rest' => true,
432 'publicly_queryable' => true,
433 'supports' => [ 'title', 'editor', 'elementor', 'revisions' ],
434 'capability_type' => 'post',
435 'capabilities' => [
436 'create_posts' => 'do_not_allow', // Prior to Wordpress 4.5, this was false.
437 ],
438 'map_meta_cap' => true,
439 );
440
441 register_post_type( self::SELLKIT_STEP_POST_TYPE, $args );
442 }
443
444 /**
445 * Edit step name or slug.
446 *
447 * @since 1.1.0
448 * @param string $funnel_id Funnel Id.
449 */
450 public function edit_step_name_and_slug( $funnel_id ) {
451 $sub_action_data = sellkit_post( 'sub_action_data' );
452 $title = ! empty( $sub_action_data['title'] ) ? $sub_action_data['title'] : '';
453 $slug = ! empty( $sub_action_data['slug'] ) ? $sub_action_data['slug'] : sanitize_title( $title );
454 $page_id = ! empty( $sub_action_data['page_id'] ) ? $sub_action_data['page_id'] : '';
455 $selected_step = $sub_action_data['selected_step'];
456
457 if ( empty( $page_id ) ) {
458 return;
459 }
460
461 wp_update_post( [
462 'ID' => $page_id,
463 'post_title' => $title,
464 'post_name' => $slug,
465 ] );
466
467 if ( get_post_field( 'post_name', $page_id ) === $slug ) {
468 return;
469 }
470
471 $post_data = get_post_meta( $funnel_id, 'nodes', true );
472 $post_data[ $selected_step ]['slug'] = get_post_field( 'post_name', $page_id );
473
474 update_post_meta( $funnel_id, 'nodes', $post_data );
475 }
476
477 /**
478 * Delete step page id.
479 *
480 * @since 1.1.0
481 */
482 public function delete_related_page() {
483 $sub_action_data = sellkit_post( 'sub_action_data' );
484
485 if ( empty( $sub_action_data['page_id'] ) ) {
486 return;
487 }
488
489 wp_delete_post( $sub_action_data['page_id'], true );
490 }
491
492 /**
493 * Flash rules.
494 *
495 * @since 1.1.0
496 */
497 public function sellkit_steps_flush_rules() {
498 $rules = get_option( 'rewrite_rules' );
499
500 if ( ! isset( $rules['sellkit_step/[^/]+/([^/]+)/?$'] ) ) {
501 flush_rewrite_rules();
502 }
503 }
504
505 /**
506 * Make publish steps page.
507 *
508 * @since 1.1.0
509 * @param string $funnel_id Funnel Id.
510 * @param string $new_status Status.
511 */
512 public function update_steps_post_status( $funnel_id, $new_status ) {
513 $steps = get_post_meta( $funnel_id, 'sellkit_steps', true );
514
515 if ( ! is_array( $steps ) ) {
516 return;
517 }
518
519 foreach ( $steps as $step ) {
520 if ( $step['page_id'] === $funnel_id ) {
521 continue;
522 }
523
524 wp_update_post(array(
525 'ID' => $step['page_id'],
526 'post_status' => $new_status,
527 ) );
528 }
529 }
530 }
531
532 Sellkit_Admin_Steps::get_instance();
533