| @@ -5,8 +5,10 @@ | ||
| 5 | 5 | /** |
| 6 | 6 | * Forms class. |
| 7 | 7 | * |
| 8 | 8 | * @since 1.1.0 |
| 9 | + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) | |
| 10 | + * @SuppressWarnings(PHPMD.NPathComplexity) | |
| 9 | 11 | */ |
| 10 | 12 | class Sellkit_Admin_Posts { |
| 11 | 13 | |
| 12 | 14 | /** |
| @@ -25,8 +27,16 @@ | ||
| 25 | 27 | */ |
| 26 | 28 | public $sub_action = ''; |
| 27 | 29 | |
| 28 | 30 | /** |
| 31 | + * Page. | |
| 32 | + * | |
| 33 | + * @since 1.7.4 | |
| 34 | + * @var string | |
| 35 | + */ | |
| 36 | + public $page; | |
| 37 | + | |
| 38 | + /** | |
| 29 | 39 | * Get a class instance. |
| 30 | 40 | * |
| 31 | 41 | * @since 1.1.0 |
| 32 | 42 | * |
| @@ -78,8 +88,9 @@ | ||
| 78 | 88 | public function get_post() { |
| 79 | 89 | check_ajax_referer( 'sellkit', 'nonce' ); |
| 80 | 90 | |
| 81 | 91 | $post_id = sellkit_htmlspecialchars( INPUT_GET, 'post_id' ); |
| 92 | + $page = sellkit_htmlspecialchars( INPUT_GET, 'page' ); | |
| 82 | 93 | |
| 83 | 94 | $post_data = get_post_meta( $post_id ); |
| 84 | 95 | |
| 85 | 96 | // Get post meta (without key) returns array by default, Now converts them to singles. |
| @@ -98,16 +109,168 @@ | ||
| 98 | 109 | if ( ! empty( $post_data['sellkit_steps'] ) ) { |
| 99 | 110 | $post_data['sellkit_steps'] = unserialize( $post_data['sellkit_steps'] ); // phpcs:ignore WordPress.PHP |
| 100 | 111 | } |
| 101 | 112 | |
| 113 | + if ( ! empty( $post_data['nodes'] ) ) { | |
| 114 | + delete_post_meta( $post_id, 'sellkit_steps' ); | |
| 115 | + unset( $post_data['sellkit_steps'] ); | |
| 116 | + | |
| 117 | + $new_node = $this->check_product_status( $post_data['nodes'] ); | |
| 118 | + | |
| 119 | + $post_data['nodes'] = $new_node; | |
| 120 | + } | |
| 121 | + | |
| 122 | + $post_data['data_is_converted'] = false; | |
| 123 | + | |
| 124 | + if ( empty( $post_data['nodes'] ) && ! empty( $post_data['sellkit_steps'] ) ) { | |
| 125 | + $nodes = self::convert_steps_to_nodes( $post_data['sellkit_steps'] ); | |
| 126 | + | |
| 127 | + update_post_meta( $post_id, 'nodes', $nodes ); | |
| 128 | + delete_post_meta( $post_id, 'sellkit_steps' ); | |
| 129 | + unset( $post_data['sellkit_steps'] ); | |
| 130 | + | |
| 131 | + $post_data['nodes'] = $nodes; | |
| 132 | + $post_data['data_is_converted'] = true; | |
| 133 | + } | |
| 134 | + | |
| 102 | 135 | $post_data['sellkit_post_title'] = html_entity_decode( get_the_title( $post_id ) ); |
| 103 | 136 | $post_data['sellkit_post_slug'] = get_post_field( 'post_name', $post_id ); |
| 104 | 137 | $post_data['sellkit_post_status'] = get_post_status( $post_id ); |
| 105 | 138 | |
| 139 | + // Unset sales-page when user exploring global checkout page. | |
| 140 | + if ( 'checkout' === $page && ! empty( $post_data['nodes'] ) ) { | |
| 141 | + foreach ( $post_data['nodes'] as $key => $step ) { | |
| 142 | + if ( is_array( $step['type'] ) && 'sales-page' === $step['type']['key'] ) { | |
| 143 | + unset( $post_data['nodes']->$key ); | |
| 144 | + } | |
| 145 | + | |
| 146 | + if ( is_object( $step['type'] ) && 'sales-page' === $step['type']->key ) { | |
| 147 | + unset( $post_data['nodes']->$key ); | |
| 148 | + } | |
| 149 | + } | |
| 150 | + } | |
| 151 | + | |
| 106 | 152 | wp_send_json_success( [ 'post_data' => $post_data ] ); |
| 107 | 153 | } |
| 108 | 154 | |
| 109 | 155 | /** |
| 156 | + * Converts steps to nodes. | |
| 157 | + * | |
| 158 | + * @since 1.5.0 | |
| 159 | + * @param array $steps Steps. | |
| 160 | + */ | |
| 161 | + public static function convert_steps_to_nodes( $steps ) { | |
| 162 | + $nodes = []; | |
| 163 | + $final_steps = $steps; | |
| 164 | + | |
| 165 | + $first_key = array_key_first( $steps ); | |
| 166 | + | |
| 167 | + if ( empty( $steps[ $first_key ]['targets'] ) ) { | |
| 168 | + $final_steps = self::add_decision_node_before_upsells( $steps ); | |
| 169 | + } | |
| 170 | + | |
| 171 | + foreach ( $final_steps as $key => $step ) { | |
| 172 | + $nodes[ $key ] = $step; | |
| 173 | + | |
| 174 | + if ( ! empty( $step['type']->key ) && 'decision' !== $step['type']->key ) { | |
| 175 | + $nodes[ $key ]['targets'] = count( $final_steps ) > $key + 1 ? [ [ 'nodeId' => strval( $key + 1 ) ] ] : []; | |
| 176 | + } | |
| 177 | + | |
| 178 | + if ( ! empty( $step['type']->key ) && 'upsell' === $step['type']->key && count( $final_steps ) > $key + 1 ) { | |
| 179 | + $nodes[ $key ]['targets'] = [ | |
| 180 | + [ | |
| 181 | + 'nodeId' => strval( $key + 1 ) | |
| 182 | + ], | |
| 183 | + [ | |
| 184 | + 'nodeId' => strval( $key + 1 ) | |
| 185 | + ] | |
| 186 | + ]; | |
| 187 | + } | |
| 188 | + | |
| 189 | + if ( ! empty( $step['type']->key ) && 'decision' === $step['type']->key ) { | |
| 190 | + if ( is_object( $step['targets'][0] ) ) { | |
| 191 | + $step['targets'][0] = (array) $step['targets'][0]; | |
| 192 | + } | |
| 193 | + | |
| 194 | + if ( is_object( $step['targets'][1] ) ) { | |
| 195 | + $step['targets'][1] = (array) $step['targets'][1]; | |
| 196 | + } | |
| 197 | + | |
| 198 | + if ( empty( $final_steps[ $step['targets'][0]['nodeId'] ] ) ) { | |
| 199 | + $nodes[ $key ]['targets'][0] = ''; | |
| 200 | + } | |
| 201 | + | |
| 202 | + if ( empty( $final_steps[ $step['targets'][1]['nodeId'] ] ) ) { | |
| 203 | + $nodes[ $key ]['targets'][1] = ''; | |
| 204 | + } | |
| 205 | + } | |
| 206 | + | |
| 207 | + $nodes[ $key ]['jsx'] = $step['title']; | |
| 208 | + } | |
| 209 | + | |
| 210 | + return (object) $nodes; | |
| 211 | + } | |
| 212 | + | |
| 213 | + /** | |
| 214 | + * Adds decision node before upsells. | |
| 215 | + * | |
| 216 | + * @since 1.5.0 | |
| 217 | + * @param array $steps Steps. | |
| 218 | + */ | |
| 219 | + public static function add_decision_node_before_upsells( $steps ) { | |
| 220 | + $final_steps = []; | |
| 221 | + $upsell_num = 0; | |
| 222 | + | |
| 223 | + foreach ( $steps as $key => $step ) { | |
| 224 | + $step['type'] = (object) $step['type']; | |
| 225 | + $step['data'] = ! empty( $step['data'] ) ? (object) $step['data'] : null; | |
| 226 | + | |
| 227 | + if ( ! empty( $step['type']->key ) && 'upsell' === $step['type']->key ) { | |
| 228 | + $new_node_id = wp_insert_post( [ | |
| 229 | + 'post_type' => Sellkit_Admin_Steps::SELLKIT_STEP_POST_TYPE, | |
| 230 | + 'post_title' => __( 'Decision', 'sellkit' ), | |
| 231 | + 'post_name' => sanitize_title( __( 'Decision', 'sellkit' ) ), | |
| 232 | + 'post_status' => 'publish', | |
| 233 | + ] ); | |
| 234 | + | |
| 235 | + $type_object = new stdClass(); | |
| 236 | + $type_object->title = esc_html__( 'Decision', 'sellkit' ); | |
| 237 | + $type_object->key = 'decision'; | |
| 238 | + | |
| 239 | + $decision_node = [ | |
| 240 | + 'title' => esc_html__( 'Decision', 'sellkit' ), | |
| 241 | + 'type' => $type_object, | |
| 242 | + 'current_target_index' => 0, | |
| 243 | + 'page_id' => $new_node_id, | |
| 244 | + 'slug' => get_post_field( 'post_name', $new_node_id ), | |
| 245 | + 'funnel_id' => $step['funnel_id'], | |
| 246 | + 'targets' => [ | |
| 247 | + [ | |
| 248 | + 'nodeId' => strval( $key + 1 + $upsell_num ) | |
| 249 | + ], | |
| 250 | + [ | |
| 251 | + 'nodeId' => strval( $key + 2 + $upsell_num ) | |
| 252 | + ] | |
| 253 | + ], | |
| 254 | + 'data' => [ | |
| 255 | + 'conditions' => ! empty( $step['data']->conditions ) ? $step['data']->conditions : [] | |
| 256 | + ] | |
| 257 | + ]; | |
| 258 | + | |
| 259 | + update_post_meta( $new_node_id, 'step_data', $decision_node ); | |
| 260 | + | |
| 261 | + $final_steps[] = $decision_node; | |
| 262 | + | |
| 263 | + $upsell_num++; | |
| 264 | + } | |
| 265 | + | |
| 266 | + $final_steps[] = $step; | |
| 267 | + } | |
| 268 | + | |
| 269 | + return $final_steps; | |
| 270 | + } | |
| 271 | + | |
| 272 | + /** | |
| 110 | 273 | * Save post. |
| 111 | 274 | * |
| 112 | 275 | * @since 1.1.0 |
| 113 | 276 | */ |
| @@ -117,8 +280,9 @@ | ||
| 117 | 280 | $fields = sellkit_post( 'fields' ); |
| 118 | 281 | $post_id = sellkit_htmlspecialchars( INPUT_POST, 'post_id' ); |
| 119 | 282 | $post_type = sellkit_htmlspecialchars( INPUT_POST, 'post_type' ); |
| 120 | 283 | $this->sub_action = sellkit_htmlspecialchars( INPUT_POST, 'sub_action' ); |
| 284 | + $this->page = sellkit_htmlspecialchars( INPUT_POST, 'page' ); | |
| 121 | 285 | |
| 122 | 286 | $sanitized_fields = $this->sanitize_fields( $fields ); |
| 123 | 287 | |
| 124 | 288 | $this->validate_fields( $sanitized_fields ); |
| @@ -154,11 +318,15 @@ | ||
| 154 | 318 | * @SuppressWarnings(PHPMD.NPathComplexity) |
| 155 | 319 | */ |
| 156 | 320 | private function handle_save_post( $data, $post_type, $post_id = false ) { |
| 157 | 321 | $title = sanitize_text_field( $data['sellkit_post_title'] ); |
| 158 | - $slug = sanitize_text_field( $data['sellkit_post_slug'] ); | |
| 322 | + $slug = ! empty( $data['sellkit_post_slug'] ) ? sanitize_text_field( $data['sellkit_post_slug'] ) : ''; | |
| 159 | 323 | $post_status = 'publish' === $data['sellkit_post_status'] ? 'publish' : 'draft'; |
| 160 | 324 | |
| 325 | + if ( isset( $data['sellkit_global_checkout_header_footer_templates'] ) && 'checkout' === $this->page ) { | |
| 326 | + unset( $data['sellkit_global_checkout_header_footer_templates'] ); | |
| 327 | + } | |
| 328 | + | |
| 161 | 329 | $post_args = [ |
| 162 | 330 | 'post_title' => $title, |
| 163 | 331 | 'post_name' => $slug ?: sanitize_title( $title ), |
| 164 | 332 | 'post_type' => $post_type, |
| @@ -186,9 +354,9 @@ | ||
| 186 | 354 | } |
| 187 | 355 | |
| 188 | 356 | update_post_meta( $new_post_id, $name, $value ); |
| 189 | 357 | |
| 190 | - if ( 'sellkit_steps' === $name ) { | |
| 358 | + if ( 'sellkit_steps' === $name || 'nodes' === $name ) { | |
| 191 | 359 | do_action( 'sellkit_funnels_update_steps', $value, $new_post_id ); |
| 192 | 360 | } |
| 193 | 361 | } |
| 194 | 362 | |
| @@ -201,8 +369,16 @@ | ||
| 201 | 369 | if ( empty( $data['sellkit_steps'] ) ) { |
| 202 | 370 | delete_post_meta( $new_post_id, 'sellkit_steps' ); |
| 203 | 371 | } |
| 204 | 372 | |
| 373 | + if ( empty( $data['nodes'] ) ) { | |
| 374 | + delete_post_meta( $new_post_id, 'nodes' ); | |
| 375 | + } | |
| 376 | + | |
| 377 | + if ( 'checkout' === $this->page ) { | |
| 378 | + update_option( 'sellkit_global_checkout_id', $new_post_id ); | |
| 379 | + } | |
| 380 | + | |
| 205 | 381 | return $new_post_id; |
| 206 | 382 | } |
| 207 | 383 | |
| 208 | 384 | /** |
| @@ -257,8 +433,59 @@ | ||
| 257 | 433 | return; |
| 258 | 434 | } |
| 259 | 435 | |
| 260 | 436 | wp_send_json_error( $errors ); |
| 437 | + } | |
| 438 | + | |
| 439 | + /** | |
| 440 | + * Check product status | |
| 441 | + * | |
| 442 | + * @param mixed $nodes List of node data. | |
| 443 | + * @since 1.7.9 | |
| 444 | + * @return array | |
| 445 | + */ | |
| 446 | + public function check_product_status( $nodes ) { | |
| 447 | + $nodes = (object) unserialize( $nodes ); // phpcs:ignore WordPress.PHP | |
| 448 | + $new_node = []; | |
| 449 | + | |
| 450 | + if ( ! class_exists( 'WooCommerce' ) ) { | |
| 451 | + return $new_node; | |
| 452 | + } | |
| 453 | + | |
| 454 | + foreach ( $nodes as $index => $node ) { | |
| 455 | + if ( ! empty( $node['page_id'] ) ) { | |
| 456 | + $node['link'] = get_the_permalink( $node['page_id'] ); | |
| 457 | + } | |
| 458 | + | |
| 459 | + if ( 'checkout' !== $node['type']['key'] ) { | |
| 460 | + $new_node[ $index ] = $node; | |
| 461 | + continue; | |
| 462 | + } | |
| 463 | + | |
| 464 | + if ( ! empty( $node['data']['products'] ) && ! empty( $node['data']['products']['list'] ) ) { | |
| 465 | + foreach ( $node['data']['products']['list'] as $key => $product ) { | |
| 466 | + $product_obj = wc_get_product( $key ); | |
| 467 | + | |
| 468 | + if ( empty( $product_obj ) ) { | |
| 469 | + unset( $node['data']['products']['list'][ $key ] ); | |
| 470 | + } | |
| 471 | + | |
| 472 | + $data = ! empty( $product_obj ) ? $product_obj->get_data() : []; | |
| 473 | + | |
| 474 | + if ( ! empty( $data['status'] ) && 'trash' === $data['status'] ) { | |
| 475 | + unset( $node['data']['products']['list'][ $key ] ); | |
| 476 | + } | |
| 477 | + } | |
| 478 | + } | |
| 479 | + | |
| 480 | + $new_node[ $index ] = $node; | |
| 481 | + } | |
| 482 | + | |
| 483 | + if ( empty( $new_node ) ) { | |
| 484 | + return $node; | |
| 485 | + } | |
| 486 | + | |
| 487 | + return $new_node; | |
| 261 | 488 | } |
| 262 | 489 | } |
| 263 | 490 | |
| 264 | 491 | Sellkit_Admin_Posts::get_instance(); |