PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / 2.7.0
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster v2.7.0
2.7.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 All 43 releases
← All changes | includes/admin/class-steps.php +193 -20 1.2.92.7.0 View file →
@@ -5,8 +5,10 @@
5 5 /**
6 6 * Steps class.
7 7 *
8 8 * @since 1.1.0
9 + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
10 + * @SuppressWarnings(PHPMD.CyclomaticComplexity)
9 11 */
10 12 class Sellkit_Admin_Steps {
11 13
12 14 /**
@@ -57,11 +59,35 @@
57 59 add_filter( 'template_include', [ $this, 'load_page_template' ], 10 );
58 60 add_action( 'wp_ajax_sellkit_steps_get_coupons', [ $this, 'get_coupons' ] );
59 61 add_action( 'wp_loaded', [ $this, 'sellkit_steps_flush_rules' ] );
60 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 + add_action( 'wp_loaded', [ $this, 'sellkit_set_sellkit_translation_mode' ] );
61 66 }
62 67
63 68 /**
69 + * Removes steps pages.
70 + *
71 + * @since 1.5.4
72 + * @return void
73 + * @param array $post Posts.
74 + */
75 + public function remove_steps_pages( $post ) {
76 + $nodes = get_post_meta( $post['ID'], 'nodes' );
77 +
78 + if ( empty( $nodes[0] ) ) {
79 + return;
80 + }
81 +
82 + foreach ( $nodes[0] as $node ) {
83 + if ( isset( $node['page_id'] ) ) {
84 + wp_delete_post( $node['page_id'] );
85 + }
86 + }
87 + }
88 +
89 + /**
64 90 * Get default funnels.
65 91 *
66 92 * @since 1.1.0
67 93 */
@@ -69,9 +95,13 @@
69 95 return [
70 96 'sales-page' => esc_html__( 'Sales Page', 'sellkit' ),
71 97 'checkout' => esc_html__( 'Checkout', 'sellkit' ),
72 98 'thankyou' => esc_html__( 'Thank You', 'sellkit' ),
99 + 'decision' => esc_html__( 'Condition', 'sellkit' ),
73 100 'upsell' => esc_html__( 'Upsell', 'sellkit' ),
101 + 'downsell' => esc_html__( 'Downsell', 'sellkit' ),
102 + 'optin-confirmation' => esc_html__( 'Opt-in Confirmation', 'sellkit' ),
103 + 'optin' => esc_html__( 'Opt-in', 'sellkit' ),
74 104 ];
75 105 }
76 106
77 107 /**
@@ -85,9 +115,17 @@
85 115 foreach ( $steps as $step_number => $step ) {
86 116 $step['funnel_id'] = $funnel_id;
87 117 $step['number'] = $step_number;
88 118
89 - update_post_meta( $step['page_id'], 'step_data', $step );
119 + if ( array_key_exists( 'page_id', $step ) ) {
120 + $translated_step = apply_filters( 'wpml_object_id', $step['page_id'], 'sellkit_step', true ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
121 +
122 + if ( $step['page_id'] !== $translated_step ) {
123 + update_post_meta( $translated_step, 'step_data', $step );
124 + }
125 +
126 + update_post_meta( $step['page_id'], 'step_data', $step );
127 + }
90 128 }
91 129 }
92 130
93 131 /**
@@ -131,8 +169,12 @@
131 169 check_ajax_referer( 'sellkit', 'nonce' );
132 170
133 171 $input_value = sellkit_htmlspecialchars( INPUT_GET, 'input_value' );
134 172
173 + if ( ! sellkit()->has_valid_dependencies() ) {
174 + wp_send_json_error( __( 'Please install and activate WooCommerce.', 'sellkit' ) );
175 + }
176 +
135 177 $filtered_products = [];
136 178
137 179 $args = [
138 180 'status' => 'any',
@@ -144,9 +186,9 @@
144 186
145 187 $all_products = wc_get_products( $args );
146 188
147 189 foreach ( $all_products as $product ) {
148 - if ( $product->get_type() !== 'variable' ) {
190 + if ( $product->get_type() !== 'variable' && $product->get_type() !== 'external' ) {
149 191 $filtered_products[] = [
150 192 'label' => $product->get_title(),
151 193 'value' => $product->get_id(),
152 194 ];
@@ -178,10 +220,14 @@
178 220
179 221 $filtered_products = [];
180 222 $products_id = filter_input( INPUT_GET, 'products_id', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
181 223
224 + if ( ! sellkit()->has_valid_dependencies() ) {
225 + wp_send_json_error( __( 'Please install and activate WooCommerce.', 'sellkit' ) );
226 + }
227 +
182 228 if ( empty( $products_id ) ) {
183 - wp_send_json_error( __( 'Please send some product ids' ) );
229 + wp_send_json_error( __( 'Please send some product ids', 'sellkit' ) );
184 230 }
185 231
186 232 $products_id = is_array( $products_id ) ? $products_id : [ $products_id ];
187 233
@@ -188,12 +234,17 @@
188 234 foreach ( $products_id as $product_id ) {
189 235 $product = wc_get_product( $product_id );
190 236
191 237 if ( empty( $product ) ) {
192 - wp_send_json_error( __( 'Product does not exist', 'sellkit' ) );
193 238 continue;
194 239 }
195 240
241 + $data = $product->get_data();
242 +
243 + if ( 'trash' === $data['status'] ) {
244 + continue;
245 + }
246 +
196 247 $attributes = $product->get_attributes();
197 248 $label = $product->get_title();
198 249
199 250 if ( $product->get_type() === 'variation' ) {
@@ -218,37 +269,118 @@
218 269 * Filter step data before saving.
219 270 *
220 271 * @since 1.1.0
221 272 * @param string $post_id Post id.
273 + * @SuppressWarnings(PHPMD.NPathComplexity)
222 274 */
223 275 public function after_create_step_callback( $post_id ) {
224 - $steps = get_post_meta( $post_id, 'sellkit_steps', true );
225 - $last_step = end( $steps );
276 + $sub_action_data = sellkit_post( 'sub_action_data' );
277 + $origin_node = isset( $sub_action_data['origin_node'] ) ? $sub_action_data['origin_node'] : '';
278 + $target_node = ! empty( $sub_action_data['target_node'] ) ? $sub_action_data['target_node'] : null;
279 + $target_index = isset( $sub_action_data['target_index'] ) ? $sub_action_data['target_index'] : 0;
280 + $nodes = get_post_meta( $post_id, 'nodes', true );
281 + $last_node = end( $nodes );
226 282
227 - array_pop( $steps );
283 + array_pop( $nodes );
228 284
229 - $new_step_id = wp_insert_post( [
285 + $new_node_id = wp_insert_post( [
230 286 'post_type' => self::SELLKIT_STEP_POST_TYPE,
231 - 'post_title' => $last_step['title'],
232 - 'post_name' => sanitize_title( $last_step['title'] ),
287 + 'post_title' => $last_node['title'],
288 + 'post_name' => sanitize_title( $last_node['title'] ),
233 289 'post_status' => 'publish',
234 290 ] );
235 291
236 - $last_step['page_id'] = $new_step_id;
237 - $last_step['slug'] = get_post_field( 'post_name', $new_step_id );
292 + $current_target = [ 'nodeId' => $target_node ];
293 + $last_node['page_id'] = $new_node_id;
294 + $last_node['slug'] = get_post_field( 'post_name', $new_node_id );
295 + $last_node['targets'] = ! is_null( $target_node ) ? [ $current_target ] : [];
238 296
239 - $step_data = $last_step;
240 - $step_data['funnel_id'] = $post_id;
241 - $step_data['number'] = count( $steps );
297 + if (
298 + 'decision' === $last_node['type']['key'] && ! is_null( $target_node ) ||
299 + 'upsell' === $last_node['type']['key'] && ! is_null( $target_node ) ||
300 + 'downsell' === $last_node['type']['key'] && ! is_null( $target_node )
301 + ) {
302 + $last_node['targets'] = [
303 + $current_target,
304 + $current_target
305 + ];
306 + }
242 307
243 - update_post_meta( $new_step_id, 'step_data', $step_data );
308 + // Adding current target info the the step.
309 + $last_node['current_target_index'] = $target_index;
244 310
245 - $new_steps = array_merge( $steps, [ $last_step ] );
311 + if ( 'last-node' === $origin_node ) {
312 + $first_end_path_node_key = self::get_first_end_path_node_key( $nodes );
313 + $last_node['origin_node'] = 'last-node';
246 314
247 - update_post_meta( $post_id, 'sellkit_steps', $new_steps );
315 + if ( $first_end_path_node_key ) {
316 + unset( $nodes[ $first_end_path_node_key ]['origin_node'] );
317 + }
318 + }
319 +
320 + $node_data = $last_node;
321 + $node_data['funnel_id'] = $post_id;
322 + $node_data['number'] = count( $nodes );
323 +
324 + update_post_meta( $new_node_id, 'step_data', $node_data );// it should be updated.
325 +
326 + $old_node_keys = array_keys( $nodes );
327 + $nodes[ end( $old_node_keys ) + 1 ] = $last_node;
328 + $keys = array_keys( $nodes );
329 +
330 + if (
331 + 'last-node' !== $origin_node && ! empty( $nodes[ $origin_node ] ) &&
332 + 'decision' !== $nodes[ $origin_node ]['type']['key'] &&
333 + 'upsell' !== $nodes[ $origin_node ]['type']['key'] &&
334 + 'downsell' !== $nodes[ $origin_node ]['type']['key']
335 + ) {
336 + $nodes[ $origin_node ]['targets'] = [ [ 'nodeId' => strval( end( $keys ) ) ] ];
337 + }
338 +
339 + if (
340 + 'last-node' !== $origin_node && isset( $nodes[ $origin_node ]['type'] ) &&
341 + (
342 + 'decision' === $nodes[ $origin_node ]['type']['key'] ||
343 + 'upsell' === $nodes[ $origin_node ]['type']['key'] ||
344 + 'downsell' === $nodes[ $origin_node ]['type']['key']
345 + )
346 + ) {
347 + if ( is_array( $nodes[ $origin_node ]['targets'] ) && empty( $nodes[ $origin_node ]['targets'][0] ) ) {
348 + $nodes[ $origin_node ]['targets'][0] = ! empty( $target_node ) ? [ 'nodeId' => $target_node ] : null;
349 + }
350 +
351 + if ( is_array( $nodes[ $origin_node ]['targets'] ) && empty( $nodes[ $origin_node ]['targets'][1] ) ) {
352 + $nodes[ $origin_node ]['targets'][1] = ! empty( $target_node ) ? [ 'nodeId' => $target_node ] : null;
353 + }
354 +
355 + $nodes[ $origin_node ]['targets'][ (int) $target_index ] = [ 'nodeId' => strval( end( $old_node_keys ) + 1 ) ];
356 + }
357 +
358 + if ( '' === $origin_node && null === $target_node ) {
359 + update_post_meta( $post_id, 'nodes', [ 1 => $last_node ] );
360 + return;
361 + }
362 +
363 + update_post_meta( $post_id, 'nodes', $nodes );
248 364 }
249 365
250 366 /**
367 + * Gets last node origin node
368 + *
369 + * @param array $nodes Nodes.
370 + * @since 1.5.0
371 + */
372 + public static function get_first_end_path_node_key( $nodes ) {
373 + foreach ( $nodes as $key => $node ) {
374 + if ( ! empty( $node['origin_node'] ) && 'last-node' === $node['origin_node'] ) {
375 + return $key;
376 + }
377 + }
378 +
379 + return false;
380 + }
381 +
382 + /**
251 383 * Loading page templates.
252 384 *
253 385 * @since 1.1.0
254 386 * @param string $template Template name.
@@ -260,8 +392,12 @@
260 392 if ( 'string' !== gettype( $template ) || ! is_object( $post ) || self::SELLKIT_STEP_POST_TYPE !== $post->post_type ) {
261 393 return $template;
262 394 }
263 395
396 + if ( has_blocks( $post ) ) {
397 + return $template;
398 + }
399 +
264 400 remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' );
265 401 add_filter( 'next_post_rel_link', '__return_empty_string' );
266 402 add_filter( 'previous_post_rel_link', '__return_empty_string' );
267 403
@@ -341,12 +477,12 @@
341 477 if ( get_post_field( 'post_name', $page_id ) === $slug ) {
342 478 return;
343 479 }
344 480
345 - $post_data = get_post_meta( $funnel_id, 'sellkit_steps', true );
481 + $post_data = get_post_meta( $funnel_id, 'nodes', true );
346 482 $post_data[ $selected_step ]['slug'] = get_post_field( 'post_name', $page_id );
347 483
348 - update_post_meta( $funnel_id, 'sellkit_steps', $post_data );
484 + update_post_meta( $funnel_id, 'nodes', $post_data );
349 485 }
350 486
351 487 /**
352 488 * Delete step page id.
@@ -385,8 +521,12 @@
385 521 */
386 522 public function update_steps_post_status( $funnel_id, $new_status ) {
387 523 $steps = get_post_meta( $funnel_id, 'sellkit_steps', true );
388 524
525 + if ( ! is_array( $steps ) ) {
526 + return;
527 + }
528 +
389 529 foreach ( $steps as $step ) {
390 530 if ( $step['page_id'] === $funnel_id ) {
391 531 continue;
392 532 }
@@ -394,8 +534,41 @@
394 534 wp_update_post(array(
395 535 'ID' => $step['page_id'],
396 536 'post_status' => $new_status,
397 537 ) );
538 + }
539 + }
540 +
541 + /**
542 + * Make SellKit custom post types translatable by default and set step_data custom field to "Copy".
543 + *
544 + * @since 2.3.2
545 + */
546 + public function sellkit_set_sellkit_translation_mode() {
547 + if ( function_exists( 'wpml_load_settings_helper' ) ) {
548 + global $sitepress;
549 +
550 + if ( $sitepress instanceof SitePress ) {
551 + $settings_helper = wpml_load_settings_helper();
552 +
553 + $post_types = [
554 + 'sellkit_step',
555 + 'sellkit-discount',
556 + 'sellkit-coupon',
557 + 'sellkit-alert',
558 + ];
559 +
560 + foreach ( $post_types as $post_type ) {
561 + $settings_helper->set_post_type_translatable( $post_type );
562 + $settings_helper->set_post_type_translation_unlocked_option( $post_type, true );
563 +
564 + if ( class_exists( '\WPML\Settings\PostType\Automatic' ) ) {
565 + \WPML\Settings\PostType\Automatic::set( $post_type, true );
566 + }
567 + }
568 +
569 + do_action( 'wpml_save_custom_field_translation_option', 'step_data', WPML_COPY_CUSTOM_FIELD ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML API.
570 + }
398 571 }
399 572 }
400 573 }
401 574