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

517 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 update_post_meta( $step['page_id'], 'step_data', $step );
116 }
117 }
118
119 /**
120 * Get all coupons.
121 *
122 * @since 1.1.0
123 */
124 public static function get_coupons() {
125 check_ajax_referer( 'sellkit', 'nonce' );
126
127 $input_value = sellkit_htmlspecialchars( INPUT_GET, 'input_value' );
128
129 $filtered_products = [];
130 $args = [
131 'post_type' => 'shop_coupon',
132 'post_status' => 'any',
133 's' => $input_value,
134 ];
135
136 $query = new WP_Query( $args );
137
138 if ( $query->have_posts() ) {
139 while ( $query->have_posts() ) {
140 $query->the_post();
141 $filtered_products[] = [
142 'label' => htmlspecialchars_decode( get_the_title() ),
143 'value' => get_the_ID(),
144 ];
145 }
146 }
147
148 wp_send_json_success( $filtered_products );
149 }
150
151 /**
152 * Get products.
153 *
154 * @since 1.1.0
155 */
156 public static function get_products() {
157 check_ajax_referer( 'sellkit', 'nonce' );
158
159 $input_value = sellkit_htmlspecialchars( INPUT_GET, 'input_value' );
160
161 if ( ! sellkit()->has_valid_dependencies() ) {
162 wp_send_json_error( __( 'Please install and activate WooCommerce.', 'sellkit' ) );
163 }
164
165 $filtered_products = [];
166
167 $args = [
168 'status' => 'any',
169 'orderby' => 'name',
170 'order' => 'ASC',
171 's' => $input_value,
172 'posts_per_page' => -1,
173 ];
174
175 $all_products = wc_get_products( $args );
176
177 foreach ( $all_products as $product ) {
178 if ( $product->get_type() !== 'variable' && $product->get_type() !== 'external' ) {
179 $filtered_products[] = [
180 'label' => $product->get_title(),
181 'value' => $product->get_id(),
182 ];
183 }
184
185 if ( $product->get_type() === 'variable' ) {
186 foreach ( $product->get_available_variations() as $available_variation ) {
187 $attributes = $available_variation['attributes'];
188 $attributes = implode( ', ', array_values( $attributes ) );
189
190 $filtered_products[] = [
191 'label' => "{$product->get_title()} - {$attributes} (#{$available_variation['variation_id']})",
192 'value' => $available_variation['variation_id'],
193 ];
194 }
195 }
196 }
197
198 wp_send_json_success( $filtered_products );
199 }
200
201 /**
202 * Get products.
203 *
204 * @since 1.1.0
205 */
206 public static function get_products_data() {
207 check_ajax_referer( 'sellkit', 'nonce' );
208
209 $filtered_products = [];
210 $products_id = filter_input( INPUT_GET, 'products_id', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
211
212 if ( ! sellkit()->has_valid_dependencies() ) {
213 wp_send_json_error( __( 'Please install and activate WooCommerce.', 'sellkit' ) );
214 }
215
216 if ( empty( $products_id ) ) {
217 wp_send_json_error( __( 'Please send some product ids' ) );
218 }
219
220 $products_id = is_array( $products_id ) ? $products_id : [ $products_id ];
221
222 foreach ( $products_id as $product_id ) {
223 $product = wc_get_product( $product_id );
224
225 if ( empty( $product ) ) {
226 continue;
227 }
228
229 $attributes = $product->get_attributes();
230 $label = $product->get_title();
231
232 if ( $product->get_type() === 'variation' ) {
233 $attributes = implode( ', ', array_values( $attributes ) );
234 $label = "{$product->get_title()} - $attributes (#{$product->get_id()})";
235 }
236
237 $filtered_products[] = [
238 'label' => $label,
239 'id' => $product->get_id(),
240 'title' => $label,
241 'thumbnail' => $product->get_image(),
242 'regular_price' => $product->get_regular_price( 'edit' ),
243 'sale_price' => ( $product->is_on_sale() ) ? $product->get_sale_price() : false,
244 ];
245 }
246
247 wp_send_json_success( $filtered_products );
248 }
249
250 /**
251 * Filter step data before saving.
252 *
253 * @since 1.1.0
254 * @param string $post_id Post id.
255 * @SuppressWarnings(PHPMD.NPathComplexity)
256 */
257 public function after_create_step_callback( $post_id ) {
258 $sub_action_data = sellkit_post( 'sub_action_data' );
259 $origin_node = isset( $sub_action_data['origin_node'] ) ? $sub_action_data['origin_node'] : '';
260 $target_node = ! empty( $sub_action_data['target_node'] ) ? $sub_action_data['target_node'] : null;
261 $target_index = isset( $sub_action_data['target_index'] ) ? $sub_action_data['target_index'] : 0;
262 $nodes = get_post_meta( $post_id, 'nodes', true );
263 $last_node = end( $nodes );
264
265 array_pop( $nodes );
266
267 $new_node_id = wp_insert_post( [
268 'post_type' => self::SELLKIT_STEP_POST_TYPE,
269 'post_title' => $last_node['title'],
270 'post_name' => sanitize_title( $last_node['title'] ),
271 'post_status' => 'publish',
272 ] );
273
274 $current_target = [ 'nodeId' => $target_node ];
275 $last_node['page_id'] = $new_node_id;
276 $last_node['slug'] = get_post_field( 'post_name', $new_node_id );
277 $last_node['targets'] = ! is_null( $target_node ) ? [ $current_target ] : [];
278
279 if (
280 'decision' === $last_node['type']['key'] && ! is_null( $target_node ) ||
281 'upsell' === $last_node['type']['key'] && ! is_null( $target_node ) ||
282 'downsell' === $last_node['type']['key'] && ! is_null( $target_node )
283 ) {
284 $last_node['targets'] = [
285 $current_target,
286 $current_target
287 ];
288 }
289
290 // Adding current target info the the step.
291 $last_node['current_target_index'] = $target_index;
292
293 if ( 'last-node' === $origin_node ) {
294 $first_end_path_node_key = self::get_first_end_path_node_key( $nodes );
295 $last_node['origin_node'] = 'last-node';
296
297 if ( $first_end_path_node_key ) {
298 unset( $nodes[ $first_end_path_node_key ]['origin_node'] );
299 }
300 }
301
302 $node_data = $last_node;
303 $node_data['funnel_id'] = $post_id;
304 $node_data['number'] = count( $nodes );
305
306 update_post_meta( $new_node_id, 'step_data', $node_data );// it should be updated.
307
308 $old_node_keys = array_keys( $nodes );
309 $nodes[ end( $old_node_keys ) + 1 ] = $last_node;
310 $keys = array_keys( $nodes );
311
312 if (
313 'last-node' !== $origin_node &&
314 'decision' !== $nodes[ $origin_node ]['type']['key'] &&
315 'upsell' !== $nodes[ $origin_node ]['type']['key'] &&
316 'downsell' !== $nodes[ $origin_node ]['type']['key']
317 ) {
318 $nodes[ $origin_node ]['targets'] = [ [ 'nodeId' => strval( end( $keys ) ) ] ];
319 }
320
321 if (
322 'last-node' !== $origin_node &&
323 (
324 'decision' === $nodes[ $origin_node ]['type']['key'] ||
325 'upsell' === $nodes[ $origin_node ]['type']['key'] ||
326 'downsell' === $nodes[ $origin_node ]['type']['key']
327 )
328 ) {
329 if ( is_array( $nodes[ $origin_node ]['targets'] ) && empty( $nodes[ $origin_node ]['targets'][0] ) ) {
330 $nodes[ $origin_node ]['targets'][0] = ! empty( $target_node ) ? [ 'nodeId' => $target_node ] : null;
331 }
332
333 if ( is_array( $nodes[ $origin_node ]['targets'] ) && empty( $nodes[ $origin_node ]['targets'][1] ) ) {
334 $nodes[ $origin_node ]['targets'][1] = ! empty( $target_node ) ? [ 'nodeId' => $target_node ] : null;
335 }
336
337 $nodes[ $origin_node ]['targets'][ (int) $target_index ] = [ 'nodeId' => strval( end( $old_node_keys ) + 1 ) ];
338 }
339
340 if ( '' === $origin_node && null === $target_node ) {
341 update_post_meta( $post_id, 'nodes', [ 1 => $last_node ] );
342 return;
343 }
344
345 update_post_meta( $post_id, 'nodes', $nodes );
346 }
347
348 /**
349 * Gets last node origin node
350 *
351 * @param array $nodes Nodes.
352 * @since 1.5.0
353 */
354 public static function get_first_end_path_node_key( $nodes ) {
355 foreach ( $nodes as $key => $node ) {
356 if ( ! empty( $node['origin_node'] ) && 'last-node' === $node['origin_node'] ) {
357 return $key;
358 }
359 }
360
361 return false;
362 }
363
364 /**
365 * Loading page templates.
366 *
367 * @since 1.1.0
368 * @param string $template Template name.
369 */
370 public function load_page_template( $template ) {
371
372 global $post;
373
374 if ( 'string' !== gettype( $template ) || ! is_object( $post ) || self::SELLKIT_STEP_POST_TYPE !== $post->post_type ) {
375 return $template;
376 }
377
378 remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' );
379 add_filter( 'next_post_rel_link', '__return_empty_string' );
380 add_filter( 'previous_post_rel_link', '__return_empty_string' );
381
382 $file = sellkit()->plugin_dir() . 'includes/templates/canvas.php';
383
384 if ( ! file_exists( $template ) ) {
385 return $template;
386 }
387
388 return $file;
389 }
390
391 /**
392 * Register step post type.
393 *
394 * @since 1.1.0
395 */
396 public function register_step_post_type() {
397 $labels = array(
398 'name' => esc_html_x( 'Steps', 'flow step general name', 'sellkit' ),
399 'singular_name' => esc_html_x( 'Step', 'flow step singular name', 'sellkit' ),
400 'search_items' => esc_html__( 'Search Steps', 'sellkit' ),
401 'all_items' => esc_html__( 'All Steps', 'sellkit' ),
402 'edit_item' => esc_html__( 'Edit Step', 'sellkit' ),
403 'view_item' => esc_html__( 'View Step', 'sellkit' ),
404 'add_new' => esc_html__( 'Add New', 'sellkit' ),
405 'update_item' => esc_html__( 'Update Step', 'sellkit' ),
406 'add_new_item' => esc_html__( 'Add New', 'sellkit' ),
407 'new_item_name' => esc_html__( 'New Step Name', 'sellkit' ),
408 );
409
410 $args = array(
411 'labels' => $labels,
412 'public' => true,
413 'query_var' => true,
414 'can_export' => true,
415 'exclude_from_search' => true,
416 'show_ui' => true,
417 'show_in_menu' => false,
418 'show_in_admin_bar' => true,
419 'show_in_rest' => true,
420 'publicly_queryable' => true,
421 'supports' => [ 'title', 'editor', 'elementor', 'revisions' ],
422 'capability_type' => 'post',
423 'capabilities' => [
424 'create_posts' => 'do_not_allow', // Prior to Wordpress 4.5, this was false.
425 ],
426 'map_meta_cap' => true,
427 );
428
429 register_post_type( self::SELLKIT_STEP_POST_TYPE, $args );
430 }
431
432 /**
433 * Edit step name or slug.
434 *
435 * @since 1.1.0
436 * @param string $funnel_id Funnel Id.
437 */
438 public function edit_step_name_and_slug( $funnel_id ) {
439 $sub_action_data = sellkit_post( 'sub_action_data' );
440 $title = ! empty( $sub_action_data['title'] ) ? $sub_action_data['title'] : '';
441 $slug = ! empty( $sub_action_data['slug'] ) ? $sub_action_data['slug'] : sanitize_title( $title );
442 $page_id = ! empty( $sub_action_data['page_id'] ) ? $sub_action_data['page_id'] : '';
443 $selected_step = $sub_action_data['selected_step'];
444
445 if ( empty( $page_id ) ) {
446 return;
447 }
448
449 wp_update_post( [
450 'ID' => $page_id,
451 'post_title' => $title,
452 'post_name' => $slug,
453 ] );
454
455 if ( get_post_field( 'post_name', $page_id ) === $slug ) {
456 return;
457 }
458
459 $post_data = get_post_meta( $funnel_id, 'nodes', true );
460 $post_data[ $selected_step ]['slug'] = get_post_field( 'post_name', $page_id );
461
462 update_post_meta( $funnel_id, 'nodes', $post_data );
463 }
464
465 /**
466 * Delete step page id.
467 *
468 * @since 1.1.0
469 */
470 public function delete_related_page() {
471 $sub_action_data = sellkit_post( 'sub_action_data' );
472
473 if ( empty( $sub_action_data['page_id'] ) ) {
474 return;
475 }
476
477 wp_delete_post( $sub_action_data['page_id'], true );
478 }
479
480 /**
481 * Flash rules.
482 *
483 * @since 1.1.0
484 */
485 public function sellkit_steps_flush_rules() {
486 $rules = get_option( 'rewrite_rules' );
487
488 if ( ! isset( $rules['sellkit_step/[^/]+/([^/]+)/?$'] ) ) {
489 flush_rewrite_rules();
490 }
491 }
492
493 /**
494 * Make publish steps page.
495 *
496 * @since 1.1.0
497 * @param string $funnel_id Funnel Id.
498 * @param string $new_status Status.
499 */
500 public function update_steps_post_status( $funnel_id, $new_status ) {
501 $steps = get_post_meta( $funnel_id, 'sellkit_steps', true );
502
503 foreach ( $steps as $step ) {
504 if ( $step['page_id'] === $funnel_id ) {
505 continue;
506 }
507
508 wp_update_post(array(
509 'ID' => $step['page_id'],
510 'post_status' => $new_status,
511 ) );
512 }
513 }
514 }
515
516 Sellkit_Admin_Steps::get_instance();
517