PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / 1.7.2
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster v1.7.2
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 1.7.2, at includes/admin/class-steps.php

519 lines 14.2 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__( 'Decision', '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 $attributes = $product->get_attributes();
232 $label = $product->get_title();
233
234 if ( $product->get_type() === 'variation' ) {
235 $attributes = implode( ', ', array_values( $attributes ) );
236 $label = "{$product->get_title()} - $attributes (#{$product->get_id()})";
237 }
238
239 $filtered_products[] = [
240 'label' => $label,
241 'id' => $product->get_id(),
242 'title' => $label,
243 'thumbnail' => $product->get_image(),
244 'regular_price' => $product->get_regular_price( 'edit' ),
245 'sale_price' => ( $product->is_on_sale() ) ? $product->get_sale_price() : false,
246 ];
247 }
248
249 wp_send_json_success( $filtered_products );
250 }
251
252 /**
253 * Filter step data before saving.
254 *
255 * @since 1.1.0
256 * @param string $post_id Post id.
257 * @SuppressWarnings(PHPMD.NPathComplexity)
258 */
259 public function after_create_step_callback( $post_id ) {
260 $sub_action_data = sellkit_post( 'sub_action_data' );
261 $origin_node = isset( $sub_action_data['origin_node'] ) ? $sub_action_data['origin_node'] : '';
262 $target_node = ! empty( $sub_action_data['target_node'] ) ? $sub_action_data['target_node'] : null;
263 $target_index = isset( $sub_action_data['target_index'] ) ? $sub_action_data['target_index'] : 0;
264 $nodes = get_post_meta( $post_id, 'nodes', true );
265 $last_node = end( $nodes );
266
267 array_pop( $nodes );
268
269 $new_node_id = wp_insert_post( [
270 'post_type' => self::SELLKIT_STEP_POST_TYPE,
271 'post_title' => $last_node['title'],
272 'post_name' => sanitize_title( $last_node['title'] ),
273 'post_status' => 'publish',
274 ] );
275
276 $current_target = [ 'nodeId' => $target_node ];
277 $last_node['page_id'] = $new_node_id;
278 $last_node['slug'] = get_post_field( 'post_name', $new_node_id );
279 $last_node['targets'] = ! is_null( $target_node ) ? [ $current_target ] : [];
280
281 if (
282 'decision' === $last_node['type']['key'] && ! is_null( $target_node ) ||
283 'upsell' === $last_node['type']['key'] && ! is_null( $target_node ) ||
284 'downsell' === $last_node['type']['key'] && ! is_null( $target_node )
285 ) {
286 $last_node['targets'] = [
287 $current_target,
288 $current_target
289 ];
290 }
291
292 // Adding current target info the the step.
293 $last_node['current_target_index'] = $target_index;
294
295 if ( 'last-node' === $origin_node ) {
296 $first_end_path_node_key = self::get_first_end_path_node_key( $nodes );
297 $last_node['origin_node'] = 'last-node';
298
299 if ( $first_end_path_node_key ) {
300 unset( $nodes[ $first_end_path_node_key ]['origin_node'] );
301 }
302 }
303
304 $node_data = $last_node;
305 $node_data['funnel_id'] = $post_id;
306 $node_data['number'] = count( $nodes );
307
308 update_post_meta( $new_node_id, 'step_data', $node_data );// it should be updated.
309
310 $old_node_keys = array_keys( $nodes );
311 $nodes[ end( $old_node_keys ) + 1 ] = $last_node;
312 $keys = array_keys( $nodes );
313
314 if (
315 'last-node' !== $origin_node &&
316 'decision' !== $nodes[ $origin_node ]['type']['key'] &&
317 'upsell' !== $nodes[ $origin_node ]['type']['key'] &&
318 'downsell' !== $nodes[ $origin_node ]['type']['key']
319 ) {
320 $nodes[ $origin_node ]['targets'] = [ [ 'nodeId' => strval( end( $keys ) ) ] ];
321 }
322
323 if (
324 'last-node' !== $origin_node &&
325 (
326 'decision' === $nodes[ $origin_node ]['type']['key'] ||
327 'upsell' === $nodes[ $origin_node ]['type']['key'] ||
328 'downsell' === $nodes[ $origin_node ]['type']['key']
329 )
330 ) {
331 if ( is_array( $nodes[ $origin_node ]['targets'] ) && empty( $nodes[ $origin_node ]['targets'][0] ) ) {
332 $nodes[ $origin_node ]['targets'][0] = ! empty( $target_node ) ? [ 'nodeId' => $target_node ] : null;
333 }
334
335 if ( is_array( $nodes[ $origin_node ]['targets'] ) && empty( $nodes[ $origin_node ]['targets'][1] ) ) {
336 $nodes[ $origin_node ]['targets'][1] = ! empty( $target_node ) ? [ 'nodeId' => $target_node ] : null;
337 }
338
339 $nodes[ $origin_node ]['targets'][ (int) $target_index ] = [ 'nodeId' => strval( end( $old_node_keys ) + 1 ) ];
340 }
341
342 if ( '' === $origin_node && null === $target_node ) {
343 update_post_meta( $post_id, 'nodes', [ 1 => $last_node ] );
344 return;
345 }
346
347 update_post_meta( $post_id, 'nodes', $nodes );
348 }
349
350 /**
351 * Gets last node origin node
352 *
353 * @param array $nodes Nodes.
354 * @since 1.5.0
355 */
356 public static function get_first_end_path_node_key( $nodes ) {
357 foreach ( $nodes as $key => $node ) {
358 if ( ! empty( $node['origin_node'] ) && 'last-node' === $node['origin_node'] ) {
359 return $key;
360 }
361 }
362
363 return false;
364 }
365
366 /**
367 * Loading page templates.
368 *
369 * @since 1.1.0
370 * @param string $template Template name.
371 */
372 public function load_page_template( $template ) {
373
374 global $post;
375
376 if ( 'string' !== gettype( $template ) || ! is_object( $post ) || self::SELLKIT_STEP_POST_TYPE !== $post->post_type ) {
377 return $template;
378 }
379
380 remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' );
381 add_filter( 'next_post_rel_link', '__return_empty_string' );
382 add_filter( 'previous_post_rel_link', '__return_empty_string' );
383
384 $file = sellkit()->plugin_dir() . 'includes/templates/canvas.php';
385
386 if ( ! file_exists( $template ) ) {
387 return $template;
388 }
389
390 return $file;
391 }
392
393 /**
394 * Register step post type.
395 *
396 * @since 1.1.0
397 */
398 public function register_step_post_type() {
399 $labels = array(
400 'name' => esc_html_x( 'Steps', 'flow step general name', 'sellkit' ),
401 'singular_name' => esc_html_x( 'Step', 'flow step singular name', 'sellkit' ),
402 'search_items' => esc_html__( 'Search Steps', 'sellkit' ),
403 'all_items' => esc_html__( 'All Steps', 'sellkit' ),
404 'edit_item' => esc_html__( 'Edit Step', 'sellkit' ),
405 'view_item' => esc_html__( 'View Step', 'sellkit' ),
406 'add_new' => esc_html__( 'Add New', 'sellkit' ),
407 'update_item' => esc_html__( 'Update Step', 'sellkit' ),
408 'add_new_item' => esc_html__( 'Add New', 'sellkit' ),
409 'new_item_name' => esc_html__( 'New Step Name', 'sellkit' ),
410 );
411
412 $args = array(
413 'labels' => $labels,
414 'public' => true,
415 'query_var' => true,
416 'can_export' => true,
417 'exclude_from_search' => true,
418 'show_ui' => true,
419 'show_in_menu' => false,
420 'show_in_admin_bar' => true,
421 'show_in_rest' => true,
422 'publicly_queryable' => true,
423 'supports' => [ 'title', 'editor', 'elementor', 'revisions' ],
424 'capability_type' => 'post',
425 'capabilities' => [
426 'create_posts' => 'do_not_allow', // Prior to Wordpress 4.5, this was false.
427 ],
428 'map_meta_cap' => true,
429 );
430
431 register_post_type( self::SELLKIT_STEP_POST_TYPE, $args );
432 }
433
434 /**
435 * Edit step name or slug.
436 *
437 * @since 1.1.0
438 * @param string $funnel_id Funnel Id.
439 */
440 public function edit_step_name_and_slug( $funnel_id ) {
441 $sub_action_data = sellkit_post( 'sub_action_data' );
442 $title = ! empty( $sub_action_data['title'] ) ? $sub_action_data['title'] : '';
443 $slug = ! empty( $sub_action_data['slug'] ) ? $sub_action_data['slug'] : sanitize_title( $title );
444 $page_id = ! empty( $sub_action_data['page_id'] ) ? $sub_action_data['page_id'] : '';
445 $selected_step = $sub_action_data['selected_step'];
446
447 if ( empty( $page_id ) ) {
448 return;
449 }
450
451 wp_update_post( [
452 'ID' => $page_id,
453 'post_title' => $title,
454 'post_name' => $slug,
455 ] );
456
457 if ( get_post_field( 'post_name', $page_id ) === $slug ) {
458 return;
459 }
460
461 $post_data = get_post_meta( $funnel_id, 'nodes', true );
462 $post_data[ $selected_step ]['slug'] = get_post_field( 'post_name', $page_id );
463
464 update_post_meta( $funnel_id, 'nodes', $post_data );
465 }
466
467 /**
468 * Delete step page id.
469 *
470 * @since 1.1.0
471 */
472 public function delete_related_page() {
473 $sub_action_data = sellkit_post( 'sub_action_data' );
474
475 if ( empty( $sub_action_data['page_id'] ) ) {
476 return;
477 }
478
479 wp_delete_post( $sub_action_data['page_id'], true );
480 }
481
482 /**
483 * Flash rules.
484 *
485 * @since 1.1.0
486 */
487 public function sellkit_steps_flush_rules() {
488 $rules = get_option( 'rewrite_rules' );
489
490 if ( ! isset( $rules['sellkit_step/[^/]+/([^/]+)/?$'] ) ) {
491 flush_rewrite_rules();
492 }
493 }
494
495 /**
496 * Make publish steps page.
497 *
498 * @since 1.1.0
499 * @param string $funnel_id Funnel Id.
500 * @param string $new_status Status.
501 */
502 public function update_steps_post_status( $funnel_id, $new_status ) {
503 $steps = get_post_meta( $funnel_id, 'sellkit_steps', true );
504
505 foreach ( $steps as $step ) {
506 if ( $step['page_id'] === $funnel_id ) {
507 continue;
508 }
509
510 wp_update_post(array(
511 'ID' => $step['page_id'],
512 'post_status' => $new_status,
513 ) );
514 }
515 }
516 }
517
518 Sellkit_Admin_Steps::get_instance();
519