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 +202 -22 1.1.02.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 /**
@@ -97,9 +135,9 @@
97 135 */
98 136 public static function get_coupons() {
99 137 check_ajax_referer( 'sellkit', 'nonce' );
100 138
101 - $input_value = filter_input( INPUT_GET, 'input_value', FILTER_SANITIZE_STRING );
139 + $input_value = sellkit_htmlspecialchars( INPUT_GET, 'input_value' );
102 140
103 141 $filtered_products = [];
104 142 $args = [
105 143 'post_type' => 'shop_coupon',
@@ -129,10 +167,14 @@
129 167 */
130 168 public static function get_products() {
131 169 check_ajax_referer( 'sellkit', 'nonce' );
132 170
133 - $input_value = filter_input( INPUT_GET, 'input_value', FILTER_SANITIZE_STRING );
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,16 +220,31 @@
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 234 foreach ( $products_id as $product_id ) {
189 - $product = wc_get_product( $product_id );
235 + $product = wc_get_product( $product_id );
236 +
237 + if ( empty( $product ) ) {
238 + continue;
239 + }
240 +
241 + $data = $product->get_data();
242 +
243 + if ( 'trash' === $data['status'] ) {
244 + continue;
245 + }
246 +
190 247 $attributes = $product->get_attributes();
191 248 $label = $product->get_title();
192 249
193 250 if ( $product->get_type() === 'variation' ) {
@@ -200,8 +257,9 @@
200 257 'id' => $product->get_id(),
201 258 'title' => $label,
202 259 'thumbnail' => $product->get_image(),
203 260 'regular_price' => $product->get_regular_price( 'edit' ),
261 + 'sale_price' => ( $product->is_on_sale() ) ? $product->get_sale_price() : false,
204 262 ];
205 263 }
206 264
207 265 wp_send_json_success( $filtered_products );
@@ -211,37 +269,118 @@
211 269 * Filter step data before saving.
212 270 *
213 271 * @since 1.1.0
214 272 * @param string $post_id Post id.
273 + * @SuppressWarnings(PHPMD.NPathComplexity)
215 274 */
216 275 public function after_create_step_callback( $post_id ) {
217 - $steps = get_post_meta( $post_id, 'sellkit_steps', true );
218 - $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 );
219 282
220 - array_pop( $steps );
283 + array_pop( $nodes );
221 284
222 - $new_step_id = wp_insert_post( [
285 + $new_node_id = wp_insert_post( [
223 286 'post_type' => self::SELLKIT_STEP_POST_TYPE,
224 - 'post_title' => $last_step['title'],
225 - 'post_name' => sanitize_title( $last_step['title'] ),
287 + 'post_title' => $last_node['title'],
288 + 'post_name' => sanitize_title( $last_node['title'] ),
226 289 'post_status' => 'publish',
227 290 ] );
228 291
229 - $last_step['page_id'] = $new_step_id;
230 - $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 ] : [];
231 296
232 - $step_data = $last_step;
233 - $step_data['funnel_id'] = $post_id;
234 - $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 + }
235 307
236 - 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;
237 310
238 - $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';
239 314
240 - 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 );
241 364 }
242 365
243 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 + /**
244 383 * Loading page templates.
245 384 *
246 385 * @since 1.1.0
247 386 * @param string $template Template name.
@@ -253,8 +392,12 @@
253 392 if ( 'string' !== gettype( $template ) || ! is_object( $post ) || self::SELLKIT_STEP_POST_TYPE !== $post->post_type ) {
254 393 return $template;
255 394 }
256 395
396 + if ( has_blocks( $post ) ) {
397 + return $template;
398 + }
399 +
257 400 remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' );
258 401 add_filter( 'next_post_rel_link', '__return_empty_string' );
259 402 add_filter( 'previous_post_rel_link', '__return_empty_string' );
260 403
@@ -334,12 +477,12 @@
334 477 if ( get_post_field( 'post_name', $page_id ) === $slug ) {
335 478 return;
336 479 }
337 480
338 - $post_data = get_post_meta( $funnel_id, 'sellkit_steps', true );
481 + $post_data = get_post_meta( $funnel_id, 'nodes', true );
339 482 $post_data[ $selected_step ]['slug'] = get_post_field( 'post_name', $page_id );
340 483
341 - update_post_meta( $funnel_id, 'sellkit_steps', $post_data );
484 + update_post_meta( $funnel_id, 'nodes', $post_data );
342 485 }
343 486
344 487 /**
345 488 * Delete step page id.
@@ -378,8 +521,12 @@
378 521 */
379 522 public function update_steps_post_status( $funnel_id, $new_status ) {
380 523 $steps = get_post_meta( $funnel_id, 'sellkit_steps', true );
381 524
525 + if ( ! is_array( $steps ) ) {
526 + return;
527 + }
528 +
382 529 foreach ( $steps as $step ) {
383 530 if ( $step['page_id'] === $funnel_id ) {
384 531 continue;
385 532 }
@@ -387,8 +534,41 @@
387 534 wp_update_post(array(
388 535 'ID' => $step['page_id'],
389 536 'post_status' => $new_status,
390 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 + }
391 571 }
392 572 }
393 573 }
394 574