PluginProbe
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant / 1.11.0
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant v1.11.0
2.3.2 2.3.1 2.3.0 2.2.8 2.2.7 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.11.0 1.11.1 1.11.2 1.6 1.7 1.8 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 1.9.10 1.9.11 All 60 releases
merchant / admin / classes / class-merchant-admin-options.php

class-merchant-admin-options.php in Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant 1.11.0, at admin/classes/class-merchant-admin-options.php

3,066 lines 118.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Merchant_Admin_Options Class.
4 */
5
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit; // Exit if accessed directly
8 }
9
10 if ( ! class_exists( 'Merchant_Admin_Options' ) ) {
11 class Merchant_Admin_Options {
12
13 /**
14 * The single class instance.
15 */
16 private static $instance = null;
17
18 /**
19 * Instance.
20 */
21 public static function instance() {
22 if ( is_null( self::$instance ) ) {
23 self::$instance = new self();
24 }
25
26 return self::$instance;
27 }
28
29 /**
30 * Constructor.
31 */
32 public function __construct() {
33 // Enqueue hooks.
34 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
35
36 // Ajax callbacks.
37 add_action( 'wp_ajax_merchant_create_page_control', array( $this, 'create_page_control_ajax_callback' ) );
38 add_action( 'wp_ajax_merchant_admin_options_select_ajax', array( $this, 'select_content_ajax' ) );
39 add_action( 'wp_ajax_merchant_admin_products_search', array( $this, 'products_search' ) );
40 add_action( 'wp_ajax_merchant_get_review_images', array( $this, 'get_review_images' ) );
41 add_action( 'wp_ajax_merchant_search_reviews', array( $this, 'search_reviews' ) );
42 add_action( 'wp_ajax_merchant_load_more_reviews', array( $this, 'load_more_reviews' ) );
43
44
45 add_action( 'clean_user_cache', array( $this, 'clear_customer_choices_cache' ), 10, 2 );
46
47 // Delete module data
48 add_action( 'admin_init', array( $this, 'delete_module_data' ) );
49 }
50
51 /**
52 * Enqueue scripts.
53 */
54 public function enqueue_scripts() {
55 if (
56 isset( $_GET['page'] ) && 'merchant' === $_GET['page'] // phpcs:ignore WordPress.Security.NonceVerification.Recommended
57 && isset( $_GET['module'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
58 ) {
59 wp_enqueue_script( 'merchant-select2', MERCHANT_URI . 'assets/vendor/select2/select2.full.min.js', array( 'jquery' ), '4.0.13', true );
60 wp_enqueue_style( 'merchant-select2', MERCHANT_URI . 'assets/vendor/select2/select2.min.css', array(), '4.0.13', 'all' );
61
62 wp_localize_script( 'merchant-select2', 'merchant_admin_options', array(
63 'ajaxurl' => admin_url( 'admin-ajax.php' ),
64 'ajaxnonce' => wp_create_nonce( 'merchant_admin_options' ),
65 'product_delete_confirmation_message' => esc_html__( 'Are you sure you want to remove this product?', 'merchant' ),
66 ) );
67
68 wp_enqueue_style('date-picker', MERCHANT_URI . 'assets/vendor/air-datepicker/air-datepicker.css', array(), MERCHANT_VERSION, 'all' );
69 wp_enqueue_script('date-picker', MERCHANT_URI . 'assets/vendor/air-datepicker/air-datepicker.js', array( 'jquery' ), MERCHANT_VERSION, true );
70 wp_localize_script( 'date-picker', 'merchant_datepicker_locale', array(
71 wp_json_encode(
72 array(
73 'days' => array(
74 esc_html__( 'Sunday', 'merchant' ),
75 esc_html__( 'Monday', 'merchant' ),
76 esc_html__( 'Tuesday', 'merchant' ),
77 esc_html__( 'Wednesday', 'merchant' ),
78 esc_html__( 'Thursday', 'merchant' ),
79 esc_html__( 'Friday', 'merchant' ),
80 esc_html__( 'Saturday', 'merchant' ),
81 ),
82 'daysShort' => array(
83 esc_html__( 'Sun', 'merchant' ),
84 esc_html__( 'Mon', 'merchant' ),
85 esc_html__( 'Tue', 'merchant' ),
86 esc_html__( 'Wed', 'merchant' ),
87 esc_html__( 'Thu', 'merchant' ),
88 esc_html__( 'Fri', 'merchant' ),
89 esc_html__( 'Sat', 'merchant' ),
90 ),
91 'daysMin' => array(
92 esc_html__( 'Su', 'merchant' ),
93 esc_html__( 'Mo', 'merchant' ),
94 esc_html__( 'Tu', 'merchant' ),
95 esc_html__( 'We', 'merchant' ),
96 esc_html__( 'Th', 'merchant' ),
97 esc_html__( 'Fr', 'merchant' ),
98 esc_html__( 'Sa', 'merchant' ),
99 ),
100 'months' => array(
101 esc_html__( 'January', 'merchant' ),
102 esc_html__( 'February', 'merchant' ),
103 esc_html__( 'March', 'merchant' ),
104 esc_html__( 'April', 'merchant' ),
105 esc_html__( 'May', 'merchant' ),
106 esc_html__( 'June', 'merchant' ),
107 esc_html__( 'July', 'merchant' ),
108 esc_html__( 'August', 'merchant' ),
109 esc_html__( 'September', 'merchant' ),
110 esc_html__( 'October', 'merchant' ),
111 esc_html__( 'November', 'merchant' ),
112 esc_html__( 'December', 'merchant' ),
113 ),
114 'monthsShort' => array(
115 esc_html__( 'Jan', 'merchant' ),
116 esc_html__( 'Feb', 'merchant' ),
117 esc_html__( 'Mar', 'merchant' ),
118 esc_html__( 'Apr', 'merchant' ),
119 esc_html__( 'May', 'merchant' ),
120 esc_html__( 'Jun', 'merchant' ),
121 esc_html__( 'Jul', 'merchant' ),
122 esc_html__( 'Aug', 'merchant' ),
123 esc_html__( 'Sep', 'merchant' ),
124 esc_html__( 'Oct', 'merchant' ),
125 esc_html__( 'Nov', 'merchant' ),
126 esc_html__( 'Dec', 'merchant' ),
127 ),
128 'clear' => esc_html__( 'Clear', 'merchant' ),
129 )
130 ),
131 ) );
132 }
133 }
134
135 /**
136 * Ajax callbacks.
137 */
138 public function create_page_control_ajax_callback() {
139 check_ajax_referer( 'customize-create-page-control-nonce', 'nonce' );
140
141 // Check current user capabilities
142 if ( ! current_user_can( 'manage_options' ) ) {
143 wp_send_json_error( 'You are not allowed to do this.' );
144 }
145
146 $page_title = isset( $_POST['page_title'] ) ? sanitize_text_field( $_POST['page_title'] ) : '';
147 $page_meta_key = isset( $_POST['page_meta_key'] ) ? sanitize_text_field( $_POST['page_meta_key'] ) : '';
148 $page_meta_value = isset( $_POST['page_meta_value'] ) ? sanitize_text_field( $_POST['page_meta_value'] ) : '';
149 $option_name = isset( $_POST['option_name'] ) ? sanitize_text_field( $_POST['option_name'] ) : '';
150
151 $meta_input = array();
152 if ( $page_meta_key && $page_meta_value ) {
153 $meta_input = array(
154 $page_meta_key => $page_meta_value,
155 );
156 }
157
158 $postarr = array(
159 'post_type' => 'page',
160 'post_status' => 'publish',
161 'post_title' => $page_title,
162 'post_content' => '',
163 'meta_input' => $meta_input,
164 );
165
166 $page_id = wp_insert_post( $postarr );
167
168 if ( ! is_wp_error( $page_id ) ) {
169 if ( $option_name ) {
170 update_option( $option_name, $page_id );
171 }
172
173 wp_send_json( array(
174 'status' => 'success',
175 'page_id' => $page_id,
176 ) );
177 } else {
178 wp_send_json( array(
179 'status' => 'error',
180 ) );
181 }
182 }
183
184 /**
185 * Select content ajax callback.
186 *
187 */
188 public function select_content_ajax() {
189 $term = ( isset( $_GET['term'] ) ) ? sanitize_text_field( wp_unslash( $_GET['term'] ) ) : '';
190 $nonce = ( isset( $_GET['nonce'] ) ) ? sanitize_text_field( wp_unslash( $_GET['nonce'] ) ) : '';
191 $source = ( isset( $_GET['source'] ) ) ? sanitize_text_field( wp_unslash( $_GET['source'] ) ) : '';
192
193 // Check current user capabilities
194 if ( ! current_user_can( 'manage_options' ) ) {
195 wp_send_json_error( 'You are not allowed to do this.' );
196 }
197
198 if ( ! empty( $term ) && ! empty( $source ) && ! empty( $nonce ) && wp_verify_nonce( $nonce, 'merchant_admin_options' ) ) {
199 $options = array();
200
201 switch ( $source ) {
202 case 'post':
203 case 'product':
204 $query = new WP_Query( array(
205 's' => $term,
206 'post_type' => $source,
207 'post_status' => 'publish',
208 'posts_per_page' => 25,
209 'order' => 'DESC',
210 ) );
211
212 if ( ! empty( $query->posts ) ) {
213 foreach ( $query->posts as $post ) {
214 $options[] = array(
215 'id' => $post->ID,
216 'text' => $post->post_title,
217 );
218 }
219 }
220
221 break;
222
223 case 'user':
224 $query = new WP_User_Query( array(
225 'search' => '*' . $term . '*',
226 'search_columns' => array( 'user_login', 'user_nicename', 'user_email', 'user_url' ),
227 'number' => 25,
228 ) );
229
230 if ( ! empty( $query->results ) ) {
231 foreach ( $query->results as $user ) {
232 $options[] = array(
233 'id' => $user->ID,
234 'text' => $user->display_name,
235 );
236 }
237 }
238
239 break;
240 }
241
242 wp_send_json_success( $options );
243 } else {
244 wp_send_json_error();
245 }
246 }
247
248 /**
249 * Get option.
250 */
251 public static function get( $module, $setting, $default_val ) {
252 $options = get_option( 'merchant', array() );
253
254 $value = $default_val;
255
256 if ( isset( $options[ $module ][ $setting ] ) ) {
257 $value = $options[ $module ][ $setting ];
258 }
259
260 /**
261 * Hook: merchant_get_option filter.
262 * Fires after getting module option.
263 *
264 * @param mixed $value Option value.
265 * @param string $module Module ID.
266 * @param string $setting Setting ID.
267 * @param mixed $default_val Default value.
268 *
269 * @since 1.9.3
270 */
271 return apply_filters( 'merchant_get_option', $value, $module, $setting, $default_val );
272 }
273
274 /**
275 * Set option.
276 */
277 public static function set( $module, $setting, $value ) {
278 $options = get_option( 'merchant', array() );
279 $options[ $module ][ $setting ] = $value;
280 update_option( 'merchant', $options );
281 }
282
283 /**
284 * Delete option.
285 */
286 public static function delete( $module, $setting ) {
287 $options = get_option( 'merchant', array() );
288 unset( $options[ $module ][ $setting ] );
289 update_option( 'merchant', $options );
290 }
291
292 /**
293 * Get all options.
294 */
295 public static function get_all( $module ) {
296 $options = get_option( 'merchant', array() );
297 $value = array();
298
299 if ( isset( $options[ $module ] ) ) {
300 $value = $options[ $module ];
301 }
302
303 return $value;
304 }
305
306 /**
307 * Create options.
308 */
309 public static function create( $settings ) {
310 $module_id = ( isset( $_GET['module'] ) ) ? sanitize_text_field( wp_unslash( $_GET['module'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
311
312 /**
313 * Hook: merchant_module_settings
314 *
315 * @param array $settings Module settings.
316 * @param string $module_id Module ID.
317 *
318 * @since 1.0
319 */
320 $settings = apply_filters( 'merchant_module_settings', $settings, $module_id );
321
322 self::save_options( $settings );
323
324 $options = get_option( 'merchant', array() );
325 ?>
326 <div class="merchant-module-page-settings">
327 <div class="merchant-module-page-setting-box">
328 <?php
329 if ( ! empty( $settings['title'] ) ) : ?>
330 <div class="merchant-module-page-setting-title">
331 <?php
332 echo esc_html( $settings['title'] ); ?>
333 <?php
334 if ( ! empty( $settings['subtitle'] ) ) : ?>
335 <div class="merchant-module-page-setting-subtitle"><?php
336 echo esc_html( $settings['subtitle'] ); ?></div>
337 <?php
338 endif; ?>
339 </div>
340 <?php
341 endif; ?>
342 <div class="merchant-module-page-setting-fields">
343 <?php
344
345 if ( ! empty( $settings['fields'] ) ) {
346 foreach ( $settings['fields'] as $field ) {
347 $value = null;
348
349 if ( isset( $field['default'] ) ) {
350 $value = $field['default'];
351 }
352
353 if ( isset( $field['id'] ) && isset( $options[ $settings['module'] ] ) && isset( $options[ $settings['module'] ][ $field['id'] ] ) ) {
354 $value = $options[ $settings['module'] ][ $field['id'] ];
355 }
356
357 $current_module = Merchant_Admin_Modules::get_module_info( $settings['module'] );
358 $is_pro_module = ! merchant_is_pro_active() && isset( $current_module['pro'] ) && $current_module['pro'] === true;
359 $is_pro_field = ! merchant_is_pro_active() && isset( $field['pro'] ) && $field['pro'] === true;
360
361 if ( $is_pro_module || $is_pro_field ) {
362 self::disabled_field( $field, $value );
363 } else {
364 self::field( $field, $value, $module_id );
365 }
366 }
367 }
368 ?>
369 </div>
370 </div>
371 </div>
372 <?php
373 }
374
375 /**
376 * Save options.
377 */
378 public static function save_options( $settings ) {
379 $save = ( isset( $_POST['merchant_save'] ) ) ? sanitize_text_field( wp_unslash( $_POST['merchant_save'] ) ) : '';
380 $reset = ( isset( $_POST['merchant_reset'] ) ) ? sanitize_text_field( wp_unslash( $_POST['merchant_reset'] ) ) : '';
381 $nonce = ( isset( $_POST['merchant_nonce'] ) ) ? sanitize_text_field( wp_unslash( $_POST['merchant_nonce'] ) ) : '';
382
383 if ( ! wp_verify_nonce( $nonce, 'merchant_nonce' ) ) {
384 return;
385 }
386
387 if ( ! current_user_can( 'manage_options' ) ) {
388 return;
389 }
390
391 $options = get_option( 'merchant', array() );
392
393 if ( ! empty( $save ) ) {
394 if ( ! empty( $settings['fields'] ) ) {
395 foreach ( $settings['fields'] as $field ) {
396 if ( ! isset( $field['id'] ) ) {
397 continue;
398 }
399
400 if ( ! merchant_is_pro_active() && isset( $field['pro'] ) && $field['pro'] === true ) {
401 continue;
402 }
403
404 $value = null;
405
406 if ( isset( $_POST['merchant'] ) && isset( $_POST['merchant'][ $field['id'] ] ) ) {
407 if ( 'textarea_code' === $field['type'] ) {
408 $value = wp_kses( $_POST['merchant'][ $field['id'] ], merchant_kses_allowed_tags_for_code_snippets() );
409 } elseif ( 'textarea_multiline' === $field['type'] ) {
410 $value = sanitize_textarea_field( $_POST['merchant'][ $field['id'] ] );
411 } elseif ( 'reviews_selector' === $field['type'] ) {
412 $value = sanitize_textarea_field( $_POST['merchant'][ $field['id'] ] );
413 $value = explode( ',', $value );
414 } elseif ( is_array( $_POST['merchant'][ $field['id'] ] ) ) {
415 $value = array_filter( map_deep( wp_unslash( $_POST['merchant'][ $field['id'] ] ), 'sanitize_textarea_field' ) );
416 } else {
417 $value = sanitize_text_field( wp_unslash( $_POST['merchant'][ $field['id'] ] ) );
418 }
419 }
420
421 $options[ $settings['module'] ][ $field['id'] ] = self::sanitize( $field, $value );
422 }
423 }
424 } elseif ( ! empty( $reset ) ) {
425 $options[ $settings['module'] ] = array();
426 }
427
428 update_option( 'merchant', $options );
429
430 /**
431 * Hook: merchant_options_saved, fired after saving module options.
432 *
433 * @param string $module module ID.
434 * @param array $options module options.
435 *
436 * @since 1.9.3
437 */
438 do_action( 'merchant_options_saved', $settings['module'], $options[ $settings['module'] ] );
439
440 /**
441 * Hook: merchant_options_saved, fired after saving specific module options.
442 *
443 * @param array $options module options.
444 *
445 * @since 1.9.3
446 */
447 do_action( "merchant_options_saved_{$settings['module']}", $options[ $settings['module'] ] );
448 }
449
450 /**
451 * Sanitize options.
452 */
453 public static function sanitize( $field, $value ) {
454 // Callback for custom sanitize methods.
455 if ( ! empty( $field['sanitize'] ) && is_callable( $field['sanitize'] ) ) {
456 return call_user_func( $field['sanitize'], $value );
457 }
458
459 switch ( $field['type'] ) {
460 case 'text':
461 case 'color':
462 case 'number':
463 case 'select_size_chart':
464 $value = sanitize_text_field( $value );
465 break;
466
467 case 'textarea_multiline':
468 case 'textarea':
469 $value = sanitize_textarea_field( $value );
470 break;
471
472 case 'textarea_code':
473 $value = $value;
474 break;
475
476 case 'checkbox_multiple':
477 if ( is_array( $value ) && ! empty( $value ) ) {
478 $value = array_filter( array_map( 'sanitize_text_field', $value ) );
479 } else {
480 $value = array();
481 }
482 break;
483
484 case 'checkbox':
485 case 'switcher':
486 $value = ( '1' === $value ) ? 1 : 0;
487 break;
488
489 case 'range':
490 case 'number':
491 $value = absint( $value );
492 break;
493 case 'select_ajax':
494 if ( is_array( $value ) && ! empty( $value ) ) {
495 $value = array_filter( array_map( 'sanitize_text_field', $value ) );
496 } elseif ( is_string( $value ) ) {
497 $value = sanitize_text_field( $value );
498 } else {
499 $value = isset( $field['multiple'] ) && $field['multiple'] === false ? '' : array();
500 }
501 break;
502 case 'radio':
503 case 'radio_alt':
504 case 'select':
505 case 'choices':
506 case 'buttons':
507 case 'buttons_alt':
508 case 'buttons_content':
509 $value = ( in_array( $value, array_keys( $field['options'] ), true ) ) ? sanitize_key( $value ) : '';
510 break;
511
512 case 'reviews_selector':
513 if ( ! is_array( $value ) ) {
514 $value = explode( ',', $value );
515 }
516 $value = array_map( 'absint', $value );
517 break;
518
519 case 'hook_select':
520 $value = is_array( $value ) ? $value : array();
521
522 foreach ( $value as $key => $val ) {
523 if ( $key === 'hook_name' ) {
524 $value[ $key ] = in_array( $val, array_keys( $field['options'] ), true ) ? sanitize_key( $val ) : '';
525 } else {
526 $value[ $key ] = (int) $val;
527 }
528 }
529
530 break;
531
532 case 'code-editor':
533 $value = wp_kses_post( $value );
534 break;
535
536 case 'sortable':
537 $values = json_decode( $value );
538 $value = array();
539
540 foreach ( $values as $val ) {
541 if ( in_array( $val, array_keys( $field['options'] ), true ) ) {
542 $value[] = sanitize_key( $val );
543 }
544 }
545 break;
546 case 'dimensions':
547 $values = is_array( $value ) ? $value : array();
548
549 foreach ( $values as $key => $val ) {
550 if ( $key === 'unit' ) {
551 $value[ $key ] = sanitize_key( $value[ $key ] );
552 } else {
553 $value[ $key ] = absint( $value[ $key ] );
554 }
555 }
556 break;
557 case 'responsive_dimensions':
558 $values = is_array( $value ) ? $value : array();
559
560 foreach ( $values as $device => $device_fields ) {
561 foreach ( $device_fields as $device_field => $device_field_val ) {
562 if ( $device_field === 'unit' ) {
563 $value[ $device ][ $device_field ] = sanitize_key( $device_field_val );
564 } else {
565 $value[ $device ][ $device_field ] = absint( $device_field_val );
566 }
567 }
568 }
569 break;
570
571 case 'sortable_repeater':
572 $values = json_decode( $value );
573 $value = array_map( 'sanitize_text_field', $values );
574 break;
575 case 'flexible_content':
576 $value = ! is_array( $value ) || empty( $value ) ? array() : $value;
577
578 $value = array_map( function ( $sub_fields ) use ( $field ) {
579 foreach ( $sub_fields as $sub_field => $value ) {
580 if ( $sub_field === 'layout' ) {
581 $sub_fields[ $sub_field ] = sanitize_text_field( $value );
582 } else {
583 $layout_field = array_filter( $field['layouts'][ $sub_fields['layout'] ]['fields'], function ( $layout_field ) use ( $sub_field ) {
584 return $layout_field['id'] === $sub_field;
585 } );
586
587
588 $sub_fields[ $sub_field ] = self::sanitize( reset( $layout_field ), $value );
589 }
590 }
591
592 return $sub_fields;
593 }, $value );
594
595 break;
596 }
597
598 return $value;
599 }
600
601 /**
602 * Field
603 */
604 public static function field( $settings, $value, $module_id = '') {
605
606 if ( ! empty( $settings['type'] ) ) {
607 $type = $settings['type'];
608
609 $id = ( ! empty( $settings['id'] ) ) ? $settings['id'] : '';
610 $class = ( ! empty( $settings['class'] ) ) ? ' ' . $settings['class'] : '';
611 $condition = ( ! empty( $settings['condition'] ) ) ? $settings['condition'] : array();
612 $conditions = ( ! empty( $settings['conditions'] ) ) ? $settings['conditions'] : ''; //Docs here: https://github.com/athemes/Merchant/pull/133
613 $default = ( ! empty( $settings['default'] ) ) ? $settings['default'] : null;
614 $is_upsell = ! merchant_is_pro_active() && isset( $settings['pro'] ) && $settings['pro'] === true;
615
616 if ( ! $value && ( 0 !== $value && '0' !== $value ) ) {
617 if ( $type === 'checkbox_multiple' ) {
618 $value = is_array( $value ) ? $value : (array) $default;
619 } elseif ( $type === 'text' && ! empty( $module_id ) ) {
620 $value = Merchant_Option::get( $module_id, $id );
621 } else {
622 $value = $default;
623 }
624 }
625
626 $wrapper_classes = array( 'merchant-module-page-setting-field' );
627 $wrapper_classes[] = 'merchant-module-page-setting-field-' . $type;
628
629 if ( ! empty( $class ) ) {
630 $wrapper_classes[] = $class;
631 }
632
633 /**
634 * Hook 'merchant_admin_module_field_wrapper_classes'
635 *
636 * @since 1.9.3
637 */
638 $wrapper_classes = apply_filters( 'merchant_admin_module_field_wrapper_classes', $wrapper_classes, $settings, $value, $module_id );
639
640 echo '<div class="'. esc_attr( implode( ' ', $wrapper_classes ) ) .'" data-id="'
641 . esc_attr( $id ) . '" data-type="' . esc_attr( $type ) . '" data-condition="' . esc_attr( wp_json_encode( $condition ) ) .
642 '" data-conditions="' . ( $conditions ? esc_attr( wp_json_encode( $conditions ) ) : "" ) . '">';
643 if ( ! empty( $settings['title'] ) ) {
644 ?>
645 <div class="merchant-module-page-setting-field-title<?php echo esc_attr( $is_upsell ? ' merchant-module-page-setting-field-title__has-upsell' : '' ); ?>">
646 <?php echo esc_html( $settings['title'] ); ?>
647
648 <?php if ( $is_upsell ) : ?>
649 <a href="<?php echo esc_url( 'https://athemes.com/merchant-upgrade?utm_source=plugin_dashboard&utm_medium=merchant_dashboard&utm_campaign=Merchant' ); ?>" class="merchant-module-pro-upsell" target="_blank">
650 <span class="merchant-pro-badge merchant-pro-tooltip" data-tooltip-message="<?php echo esc_attr__( 'This option is only available on Merchant Pro', 'merchant' ); ?>">
651 <svg width="28" height="16" viewBox="0 0 28 16" fill="none" xmlns="http://www.w3.org/2000/svg">
652 <path d="M7.41309 8.90723H5.58203V7.85254H7.41309C7.71257 7.85254 7.95508 7.80371 8.14062 7.70605C8.32943 7.60514 8.46777 7.46842 8.55566 7.2959C8.64355 7.12012 8.6875 6.91992 8.6875 6.69531C8.6875 6.47721 8.64355 6.27376 8.55566 6.08496C8.46777 5.89616 8.32943 5.74316 8.14062 5.62598C7.95508 5.50879 7.71257 5.4502 7.41309 5.4502H6.02148V11.5H4.67871V4.39062H7.41309C7.96647 4.39062 8.43848 4.48991 8.8291 4.68848C9.22298 4.88379 9.52246 5.1556 9.72754 5.50391C9.93587 5.84896 10.04 6.24284 10.04 6.68555C10.04 7.14453 9.93587 7.54004 9.72754 7.87207C9.52246 8.2041 9.22298 8.45964 8.8291 8.63867C8.43848 8.81771 7.96647 8.90723 7.41309 8.90723ZM11.0947 4.39062H13.6777C14.2181 4.39062 14.682 4.47201 15.0693 4.63477C15.4567 4.79753 15.7546 5.03841 15.9629 5.35742C16.1712 5.67643 16.2754 6.06868 16.2754 6.53418C16.2754 6.90202 16.2103 7.22103 16.0801 7.49121C15.9499 7.76139 15.766 7.98763 15.5283 8.16992C15.2939 8.35221 15.0173 8.49544 14.6982 8.59961L14.2783 8.81445H11.998L11.9883 7.75488H13.6924C13.9691 7.75488 14.1986 7.70605 14.3809 7.6084C14.5632 7.51074 14.6999 7.37565 14.791 7.20312C14.8854 7.0306 14.9326 6.83366 14.9326 6.6123C14.9326 6.37467 14.887 6.1696 14.7959 5.99707C14.7048 5.82129 14.5664 5.6862 14.3809 5.5918C14.1953 5.4974 13.9609 5.4502 13.6777 5.4502H12.4375V11.5H11.0947V4.39062ZM15.1084 11.5L13.4629 8.31641L14.8838 8.31152L16.5488 11.4316V11.5H15.1084ZM23.209 7.76465V8.13086C23.209 8.66797 23.1374 9.15137 22.9941 9.58105C22.8509 10.0075 22.6475 10.3704 22.3838 10.6699C22.1201 10.9694 21.806 11.1989 21.4414 11.3584C21.0768 11.5179 20.6715 11.5977 20.2256 11.5977C19.7861 11.5977 19.3825 11.5179 19.0146 11.3584C18.6501 11.1989 18.3343 10.9694 18.0674 10.6699C17.8005 10.3704 17.5938 10.0075 17.4473 9.58105C17.3008 9.15137 17.2275 8.66797 17.2275 8.13086V7.76465C17.2275 7.22428 17.3008 6.74089 17.4473 6.31445C17.5938 5.88802 17.7988 5.52507 18.0625 5.22559C18.3262 4.92285 18.6403 4.69173 19.0049 4.53223C19.3727 4.37272 19.7764 4.29297 20.2158 4.29297C20.6618 4.29297 21.0671 4.37272 21.4316 4.53223C21.7962 4.69173 22.1104 4.92285 22.374 5.22559C22.641 5.52507 22.846 5.88802 22.9893 6.31445C23.1357 6.74089 23.209 7.22428 23.209 7.76465ZM21.8516 8.13086V7.75488C21.8516 7.36751 21.8158 7.02734 21.7441 6.73438C21.6725 6.43815 21.5667 6.18913 21.4268 5.9873C21.2868 5.78548 21.1143 5.63411 20.9092 5.5332C20.7041 5.42904 20.473 5.37695 20.2158 5.37695C19.9554 5.37695 19.7243 5.42904 19.5225 5.5332C19.3239 5.63411 19.1546 5.78548 19.0146 5.9873C18.8747 6.18913 18.7673 6.43815 18.6924 6.73438C18.6208 7.02734 18.585 7.36751 18.585 7.75488V8.13086C18.585 8.51497 18.6208 8.85514 18.6924 9.15137C18.7673 9.44759 18.8747 9.69824 19.0146 9.90332C19.1579 10.1051 19.3304 10.2581 19.5322 10.3623C19.734 10.4665 19.9652 10.5186 20.2256 10.5186C20.486 10.5186 20.7171 10.4665 20.9189 10.3623C21.1208 10.2581 21.29 10.1051 21.4268 9.90332C21.5667 9.69824 21.6725 9.44759 21.7441 9.15137C21.8158 8.85514 21.8516 8.51497 21.8516 8.13086Z" fill="#3858E9"/>
653 <rect x="0.5" y="1" width="27" height="14" rx="1.5" stroke="#3858E9"/>
654 </svg>
655 </span>
656 </a>
657 <?php endif; ?>
658 </div>
659 <?php
660 }
661
662 echo '<div class="merchant-module-page-setting-field-inner merchant-field-' . esc_attr( $id ) . '">';
663 if ( method_exists( 'Merchant_Admin_Options', $type ) ) {
664 call_user_func( array( 'Merchant_Admin_Options', $type ), $settings, $value, $module_id );
665 } else {
666 esc_html_e( 'Field not found!', 'merchant' );
667 }
668 echo '</div>';
669
670 $hidden_desc = $settings['hidden_desc'] ?? '';
671
672 /**
673 * Hook 'merchant_admin_module_field_hidden_description'
674 *
675 * @since 1.9.3
676 */
677 $hidden_desc = apply_filters( 'merchant_admin_module_field_hidden_description', $hidden_desc, $settings, $value, $module_id );
678
679 $desc = ( ! empty( $settings['desc'] ) ) ? $settings['desc'] : '';
680
681 /**
682 * Hook 'merchant_admin_module_field_description'
683 *
684 * @since 1.9.3
685 */
686 $desc = apply_filters( 'merchant_admin_module_field_description', $desc, $settings, $value, $module_id );
687
688 if ( ! empty( $desc ) ) {
689 $hidden_desc_html = '';
690 if ( ! empty( $hidden_desc ) ) {
691 $hidden_desc_html = '<div class="merchant-module-page-setting-field-hidden-desc-trigger" data-show-text="' . esc_html__( 'Show more', 'merchant' ) . '" data-hidden-text="' . esc_html__( 'Show less', 'merchant' ) . '"><span>' . esc_html__( 'Show more', 'merchant' ) . '</span>';
692 $hidden_desc_html .= '<img src="' . esc_url( MERCHANT_URI . '/assets/images/arrow-down.svg' ) . '" alt="Merchant" />';
693 $hidden_desc_html .= '</div>';
694 }
695
696 printf( '<div class="merchant-module-page-setting-field-desc' . esc_attr( $hidden_desc ? ' merchant-module-page-setting-field-desc-has-hidden-desc' : '' ) .'">%s%s</div>', wp_kses_post( $desc ), wp_kses_post( $hidden_desc_html ) );
697 }
698
699 if ( ! empty( $hidden_desc ) ) {
700 printf( '<div class="merchant-module-page-setting-field-hidden-desc">%s</div>', wp_kses_post( nl2br( $hidden_desc ) ) );
701 }
702
703 echo '</div>';
704 }
705 }
706
707 /**
708 * Disabled field
709 *
710 * @param array $settings
711 * @param mixed $value
712 *
713 * @return void
714 */
715 public static function disabled_field( $settings, $value, $module_id = '') {
716 static::replace_field(
717 $settings,
718 $value,
719 array(
720 '<input ',
721 '<select ',
722 'merchant-module-page-setting-field-inner',
723 ),
724 array(
725 '<input disabled ',
726 '<select disabled ',
727 'merchant-module-page-setting-field-inner disabled',
728 ),
729 $module_id
730 );
731 }
732
733 /**
734 * Modified field.
735 *
736 * @param $settings
737 * @param $value
738 * @param $search
739 * @param $replace
740 *
741 * @return void
742 */
743 public static function replace_field( $settings, $value, $search, $replace, $module_id = '') {
744 ob_start();
745 self::field( $settings, $value, $module_id );
746 $field_html = ob_get_clean();
747
748 // Replace attributes in the field
749 $field = str_replace( $search, $replace, $field_html );
750
751 // Process specific field types
752 if ( isset( $settings['type'] ) && $settings['type'] === 'hook_select' ) {
753 $field = self::update_field_attributes( $field, array(
754 'select' => '[hook_name]',
755 'input' => '[hook_priority]',
756 ) );
757 }
758
759 echo wp_kses( $field, merchant_kses_allowed_tags( array( 'all' ) ) );
760 }
761
762 /**
763 * Update attributes for specific tags in the field HTML.
764 *
765 * @param string $field_html The field's HTML.
766 * @param array $tag_updates An associative array where keys are tag names
767 * and values are strings to append to the `name` attribute.
768 *
769 * @return string Updated field HTML.
770 */
771 private static function update_field_attributes( $field, $updates ) {
772 if ( ! class_exists( 'WP_HTML_Tag_Processor' ) ) {
773 return $field;
774 }
775
776 foreach ( $updates as $tag => $append ) {
777 $processor = new WP_HTML_Tag_Processor( $field ); // Reset processor for each tag type
778
779 while ( $processor->next_tag( $tag ) ) {
780 $current_name = $processor->get_attribute( 'name' );
781
782 // Append the specified string if not already present
783 if ( $current_name && ! str_ends_with( $current_name, $append ) ) {
784 $processor->set_attribute( 'name', $current_name . $append );
785 }
786 }
787
788 // Update the field with the latest processed HTML
789 $field = $processor->get_updated_html();
790 }
791
792 return $field;
793 }
794
795 /**
796 * Field: Text
797 */
798 public static function text( $settings, $value, $module_id = '' ) {
799 ?>
800 <input type="text" name="merchant[<?php
801 echo esc_attr( $settings['id'] ); ?>]" value="<?php
802 echo esc_attr( $value ); ?>" placeholder="<?php
803 echo esc_attr( $settings['placeholder'] ?? '' ); ?>"/>
804 <?php
805 }
806
807 /**
808 * Field: Text (readonly)
809 */
810 public static function text_readonly( $settings, $value, $module_id = '' ) {
811 ?>
812 <input type="text" value="<?php
813 echo esc_attr( $value ); ?>" readonly/>
814 <?php
815 }
816
817 /**
818 * Field: Number
819 */
820 public static function number( $settings, $value, $module_id = '' ) {
821 ?>
822 <input type="number" name="merchant[<?php
823 echo esc_attr( $settings['id'] ); ?>]" value="<?php
824 echo esc_attr( $value ); ?>"<?php
825 echo isset( $settings['step'] ) ? ' step="' . esc_attr( $settings['step'] ) . '"' : '';
826 echo isset( $settings['max'] ) ? ' max="' . esc_attr( $settings['max'] ) . '"' : '';
827 echo isset( $settings['min'] ) ? ' min="' . esc_attr( $settings['min'] ) . '"' : '' ?>
828 placeholder="<?php echo esc_attr( $settings['placeholder'] ?? '' ); ?>"/>
829 <?php
830 }
831
832 /**
833 * Field: Textarea
834 */
835 public static function textarea( $settings, $value, $module_id = '' ) {
836 $value = ( $value ) ? $value : '';
837 ?>
838 <textarea name="merchant[<?php
839 echo esc_attr( $settings['id'] ); ?>]"><?php
840 echo wp_kses_post( $value ); ?></textarea>
841 <?php
842 }
843
844 /**
845 * Field: Textarea
846 */
847 public static function textarea_multiline( $settings, $value, $module_id = '' ) {
848 $value = ( $value ) ? $value : '';
849 ?>
850 <textarea name="merchant[<?php
851 echo esc_attr( $settings['id'] ); ?>]"><?php
852 echo wp_kses_post( $value ); ?></textarea>
853 <?php
854 }
855
856 /**
857 * Field: Textarea Code Snippet.
858 */
859 public static function textarea_code( $settings, $value, $module_id = '' ) {
860 $value = ( $value ) ? $value : '';
861 ?>
862 <textarea name="merchant[<?php
863 echo esc_attr( $settings['id'] ); ?>]"><?php
864 echo wp_kses( $value, merchant_kses_allowed_tags_for_code_snippets() ); ?></textarea>
865 <?php
866 }
867
868 /**
869 * Field: Checkbox
870 */
871 public static function checkbox( $settings, $value, $module_id = '' ) {
872 ?>
873 <div>
874 <label>
875 <input type="checkbox" name="merchant[<?php
876 echo esc_attr( $settings['id'] ); ?>]" value="1" <?php
877 checked( $value, 1, true ); ?> />
878 <?php
879 if ( ! empty( $settings['label'] ) ) : ?>
880 <span><?php
881 echo esc_html( $settings['label'] ); ?></span>
882 <?php
883 endif; ?>
884 </label>
885 </div>
886 <?php
887 }
888
889 /**
890 * Field: Checkbox multiple
891 */
892 public static function checkbox_multiple( $settings, $value ) {
893 if ( ! empty( $settings['options'] ) ) : ?>
894 <?php
895 foreach ( $settings['options'] as $key => $option ) : ?>
896 <label>
897 <input
898 type="checkbox" name="merchant[<?php echo esc_attr( $settings['id'] ); ?>][]"
899 value="<?php echo esc_attr( $key ); ?>"
900 <?php checked( in_array( $key, $value, true ), true ); ?>
901 />
902 <span><?php echo esc_html( $option ); ?></span>
903 </label>
904 <?php
905 endforeach; ?>
906 <?php
907 endif;
908 }
909
910 /**
911 * Field: Switcher
912 */
913 public static function switcher( $settings, $value, $module_id = '' ) {
914 ?>
915 <div class="merchant-toggle-switch">
916 <input type="checkbox" id="<?php
917 echo esc_attr( $settings['id'] ); ?>" name="merchant[<?php
918 echo esc_attr( $settings['id'] ); ?>]" value="1" <?php
919 checked( $value, 1, true ); ?>
920 class="toggle-switch-checkbox"/>
921 <label class="toggle-switch-label" for="<?php
922 echo esc_attr( $settings['id'] ); ?>">
923 <span class="toggle-switch-inner"></span>
924 <span class="toggle-switch-switch"></span>
925 </label>
926 <?php
927 if ( ! empty( $settings['label'] ) ) : ?>
928 <span><?php
929 echo esc_html( $settings['label'] ); ?></span>
930 <?php
931 endif; ?>
932 </div>
933 <?php
934 }
935
936 /**
937 * Field: Radio
938 */
939 public static function radio( $settings, $value, $module_id = '' ) {
940 ?>
941 <?php
942 if ( ! empty( $settings['options'] ) ) : ?>
943 <?php
944 foreach ( $settings['options'] as $key => $option ) : ?>
945 <label>
946 <input type="radio" name="merchant[<?php
947 echo esc_attr( $settings['id'] ); ?>]" value="<?php
948 echo esc_attr( $key ); ?>" <?php
949 checked( $value, $key, true ); ?>/>
950 <span><?php
951 echo esc_html( $option ); ?></span>
952 </label>
953 <?php
954 endforeach; ?>
955 <?php
956 endif; ?>
957 <?php
958 }
959
960 /**
961 * Field: Image picker
962 */
963 public static function image_picker( $settings, $value, $module_id = '' ) {
964 ?>
965 <div class="merchant-image-picker">
966 <?php
967 if ( ! empty( $settings['options'] ) ) : ?>
968 <?php
969 foreach ( $settings['options'] as $key => $option ) : ?>
970 <label>
971 <input type="radio" name="merchant[<?php
972 echo esc_attr( $settings['id'] ); ?>]" value="<?php
973 echo esc_attr( $key ); ?>" <?php
974 checked( $value, $key, true ); ?>/>
975 <?php
976 if ( isset( $option['image'] ) ) { ?>
977 <img src="<?php
978 echo esc_url( $option['image'] ) ?>" alt="">
979 <?php
980 } ?>
981 <?php
982 if ( isset( $option['title'] ) ) { ?>
983 <span class="tool-tip-text"><?php
984 echo esc_html( $option['title'] ) ?></span>
985 <?php
986 } ?>
987 </label>
988 <?php
989 endforeach; ?>
990 <?php
991 endif; ?>
992 </div>
993 <?php
994 }
995
996 /**
997 * Reviews selector field.
998 *
999 * @param $settings Array field settings
1000 * @param $value Array field value
1001 * @param $module_id string module ID
1002 *
1003 * @return void
1004 */
1005 public static function reviews_selector( $settings, $value, $module_id = '' ) {
1006 if ( ! is_array( $value ) ) {
1007 $value = array();
1008 }
1009 ?>
1010 <div class="merchant-reviews-selector" data-id="<?php
1011 echo esc_attr( $settings['id'] ); ?>">
1012 <div class="selected-reviews">
1013 <div class="product-reviews"><?php // keep this (no space) between the div and php tag to avoid white space issues with css
1014 foreach ( $value as $review ) {
1015 echo wp_kses( self::get_review( $review ), merchant_kses_allowed_tags( array( 'div' ) ) );
1016 } ?></div>
1017 </div>
1018 <button type="button" class="merchant-btn-control popup-trigger"><?php esc_html_e('Select Reviews', 'merchant'); ?></button>
1019 <div class="overlay"></div>
1020 <div class="selector-popup">
1021 <div class="popup-content">
1022 <div class="popup-header">
1023 <label>
1024 <span>
1025 <?php
1026 esc_html_e( 'Search & filter', 'merchant' ); ?>
1027 </span>
1028 <input type="text" name="products_search" class="products-search" placeholder="<?php echo esc_attr__( 'Search for a product', 'merchant' ); ?>">
1029 </label>
1030
1031 <span class="close" title="<?php esc_attr_e( 'Close', 'merchant' ); ?>">&times;</span>
1032 </div>
1033 <div class="popup-body merchant-reviews-selector-body">
1034 <div class="products-search-results">
1035 <?php
1036 echo wp_kses(self::products_selector_search_results(), merchant_kses_allowed_tags(array( 'div' ))); ?>
1037 </div>
1038 </div>
1039 <div class="loader"></div>
1040 </div>
1041 </div>
1042 <input type="hidden" class="review-saved-ids" name="merchant[<?php echo esc_attr( $settings['id'] ); ?>]" value="<?php
1043 echo esc_attr( implode( ',', $value ) ) ?>">
1044 </div>
1045 <?php
1046 }
1047
1048 /**
1049 * Get search results for the reviews selector.
1050 *
1051 * @return void
1052 */
1053 public function search_reviews() {
1054 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'merchant_admin_options' ) ) {
1055 wp_send_json_error( array( 'message' => esc_html__( 'Nonce verification failed', 'merchant' ) ) );
1056 }
1057
1058 $term = '';
1059 if ( isset( $_POST['search'] ) && ! empty( $_POST['search'] ) ) {
1060 $term = trim( sanitize_text_field( $_POST['search'] ) );
1061 }
1062 wp_send_json_success( self::products_selector_search_results( $term ) );
1063 }
1064
1065 /**
1066 * Load more reviews for a product.
1067 * Used in the reviews ajax requests.
1068 *
1069 * @return void
1070 */
1071 public function load_more_reviews() {
1072 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'merchant_admin_options' ) ) {
1073 wp_send_json_error( array( 'message' => esc_html__( 'Nonce verification failed', 'merchant' ) ) );
1074 }
1075
1076 $product_id = isset( $_POST['product_id'] ) ? absint( $_POST['product_id'] ) : 0;
1077 $offset = isset( $_POST['offset'] ) ? absint( $_POST['offset'] ) : 0;
1078 if( 0 === $product_id ) {
1079 wp_send_json_error( array( 'message' => esc_html__( 'Invalid product ID', 'merchant' ) ) );
1080 }
1081
1082 wp_send_json_success( self::get_rendered_product_reviews( wc_get_product( $product_id ), $offset ) );
1083 }
1084
1085 /**
1086 * Search for products and display the results.
1087 *
1088 * @param $term string Search term.
1089 * @param $exclude array IDs of products to exclude from the search results.
1090 *
1091 * @return string
1092 */
1093 private static function products_selector_search_results( $term = '', $exclude = array() ) {
1094 $args = array(
1095 'post_type' => 'product',
1096 'post_status' => 'any',
1097 'posts_per_page' => 20,
1098 'post__not_in' => $exclude,
1099 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
1100 'meta_key' => '_wc_review_count', // Meta key for review count
1101 'orderby' => 'meta_value_num', // Sort by numeric value of review count
1102 'order' => 'DESC', // Order by descending
1103 );
1104
1105 if ( $term ) {
1106 $args['s'] = $term;
1107 }
1108
1109 $query = new WC_Product_Query( $args );
1110 $products = $query->get_products();
1111
1112 $response = '';
1113
1114 foreach ( $products as $product ) {
1115 $reviews_count = $product->get_review_count();
1116 $reviews_text = _n( 'Review', 'Reviews', $reviews_count, 'merchant' );
1117 $product_item_has_reviews = $reviews_count > 0 ? ' product-item-has-reviews' : '';
1118
1119 $response .= '<div class="product-item' . $product_item_has_reviews . '" data-id="' . $product->get_id() . '">';
1120 $response .= '<div class="header">';
1121 $response .= '<div class="product-image"><a href="' . get_edit_post_link( $product->get_id() ) . '" target="_blank">'
1122 . get_the_post_thumbnail( $product->get_id(),
1123 'thumbnail' ) . '</a></div>';
1124 $response .= '<div class="product-title"><a href="' . get_edit_post_link( $product->get_id() ) . '" target="_blank">' . $product->get_name()
1125 . '</a></div>';
1126 $response .= '<div class="spacer"></div>';
1127 $response .= '<div class="rating-stars">' . wc_get_rating_html( $product->get_average_rating(), $reviews_count ) . '</div>';
1128 if ( $reviews_count > 0 ) {
1129 $response .= '<div class="selected-reviews-count"><span class="counter">0</span>
1130 <div class="tooltip">' . esc_attr__( 'Number of selected reviews', 'merchant' ) . '</div></div>';
1131 }
1132 $response .= '<div class="product-status product-status-' . esc_attr( $product->get_status() ) . '">' . esc_html( $product->get_status() )
1133 . '</div>';
1134 $response .= '<div class="product-reviews-count">' . $reviews_count . ' ' . $reviews_text . '</div>';
1135 $response .= '<div class="product-expander"><svg width="12" height="13" viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.5 5.07422L6 8.57422L2.5 5.07422" stroke="#a6a4a4" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></div>';
1136 $response .= '</div>'; // .header
1137 if ( $reviews_count > 0 ) {
1138 $reviews = self::get_rendered_product_reviews( $product );
1139 $rendered_reviews = $reviews['reviews'];
1140 $rendered_reviews .= $reviews['load_more'];
1141 $response .= '<div class="product-reviews" data-id="' . esc_attr( $product->get_id() ) . '">' . $rendered_reviews . '</div>';
1142 }
1143 $response .= '</div>';
1144 }
1145
1146 return $response;
1147 }
1148
1149 /**
1150 * Get rendered product reviews.
1151 *
1152 * @param $product WC_Product Product object.
1153 * @param $offset int Offset for the reviews query.
1154 *
1155 * @return string[] Array with 'reviews' and 'load_more' keys.
1156 */
1157 private static function get_rendered_product_reviews( $product, $offset = 0 ) {
1158 $response = '';
1159 $load_more = '';
1160 $reviews_count = $product->get_review_count();
1161 if ( $reviews_count === 0 ) {
1162 return array(
1163 'reviews' => '',
1164 'load_more' => '',
1165 );
1166 }
1167
1168 /**
1169 * Filter the number of reviews per page.
1170 *
1171 * @since 1.10.4
1172 */
1173 $reviews_needed = apply_filters( 'merchant_reviews_per_page', 10 );
1174 $reviews = get_comments( array(
1175 'post_id' => $product->get_id(),
1176 'status' => array( 'approve', 'spam', 'hold' ),
1177 'type' => 'review',
1178 'number' => $reviews_needed,
1179 'offset' => $offset,
1180 'fields' => 'ids',
1181 ) );
1182
1183
1184 if ( ! empty( $reviews ) ) {
1185 $response .= '<div class="reviews-wrapper">';
1186 foreach ( $reviews as $review ) {
1187 $response .= self::get_review( $review );
1188 }
1189 $response .= '</div>';
1190 }
1191
1192 // compare the reviews count and offset to determine if we need to show the load more button
1193 if ( $reviews_count > $reviews_needed && $offset + $reviews_needed < $reviews_count ) {
1194 $load_more .= '<div class="product-reviews-load-more">';
1195 $load_more .= '<button type="button">' . esc_html__( 'Load more', 'merchant' ) . '</button>';
1196 $load_more .= '</div>';
1197 }
1198
1199 return array(
1200 'reviews' => $response,
1201 'load_more' => $load_more,
1202 );
1203 }
1204
1205 /**
1206 * Get a single review.
1207 *
1208 * @param $review_id int Review ID.
1209 *
1210 * @return string Review HTML.
1211 */
1212 private static function get_review( $review_id ) {
1213 $response = '';
1214 $review = get_comment( $review_id );
1215 $product = wc_get_product( $review->comment_post_ID );
1216 if ( $product ) {
1217 $response .= '<div class="product-review" data-id="' . esc_attr( $review->comment_ID ) . '" data-product-id="' . esc_attr( $product->get_id() ) . '">';
1218 $response .= '<div class="product-review-add-checkbox"><input type="checkbox" class="review-checkbox" title="' . esc_html__( 'Add this review', 'merchant' )
1219 . '"></div>';
1220 $response .= '<div class="product-review-author">' . esc_html( $review->comment_author ) . '</div>';
1221 $response .= '<div class="product-review-product-image"><a href="' . get_edit_post_link( $product->get_id() ) . '" target="_blank">'
1222 . $product->get_image( 'thumbnail' ) . '</a></div>';
1223 $response .= '<div class="product-review-product-name"><a href="' . get_edit_post_link( $product->get_id() ) . '" target="_blank">'
1224 . esc_html( $product->get_name() ) . '</a></div>';
1225 $rating = get_comment_meta( $review->comment_ID, 'rating', true );
1226 $response .= '<div class="product-review-rating">' . wc_get_rating_html( $rating ) . '</div>';
1227 $response .= '<div class="product-review-status"><div class="status status-' . esc_attr( wp_get_comment_status( $review->comment_ID ) ) . '">' . esc_html(
1228 wp_get_comment_status(
1229 $review->comment_ID ) ) . '</div></div>';
1230 $response .= '<div class="product-review-content">' . esc_html( wp_trim_words( $review->comment_content, 8, '...' ) ) . '</div>';
1231 $response .= '<div class="spacer"></div>';
1232 $response .= '<div class="product-review-photos"><div class="tooltip">' . esc_attr__( 'View customer\'s uploaded review photos.', 'merchant' ) . '</div>' . self::get_review_main_photo( $review->comment_ID ) . '</div>';
1233 $response .= '<div class="product-review-date">' . esc_html( get_comment_date( 'j/n/Y', $review->comment_ID ) ) . '</div>';
1234 $response .= '<div class="product-review-edit"><a href="' . esc_url( get_edit_comment_link( $review->comment_ID ) ) . '" target="_blank">' . esc_html__( 'Edit',
1235 'merchant' ) . '</a></div>';
1236 $response .= '<button class="product-review-delete">
1237 <svg width="80px" height="80px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M6 7V18C6 19.1046 6.89543 20 8 20H16C17.1046 20 18 19.1046 18 18V7M6 7H5M6 7H8M18 7H19M18 7H16M10 11V16M14 11V16M8 7V5C8 3.89543 8.89543 3 10 3H14C15.1046 3 16 3.89543 16 5V7M8 7H16" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> </g></svg>
1238 </button>';
1239 $response .= '<button class="product-review-move">
1240 <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M12 3V21M12 3L9 6M12 3L15 6M12 21L15 18M12 21L9 18M3 12H21M3 12L6 15M3 12L6 9M21 12L18 9M21 12L18 15" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> </g></svg>
1241 </button>';
1242 $response .= '</div>';
1243 }
1244 /**
1245 * Filter the single review item.
1246 *
1247 * @since 1.10.4
1248 */
1249 return apply_filters( 'merchant_single_review_item_rendered', $response, $review );
1250 }
1251
1252 /**
1253 * Get the main photo for a review.
1254 *
1255 * @param $comment_id int Review ID.
1256 *
1257 * @return string Review main photo HTML.
1258 */
1259 private static function get_review_main_photo( $comment_id ) {
1260 $photos_ids = get_comment_meta( $comment_id, 'review_images', true );
1261 if ( empty( $photos_ids ) ) {
1262 return '';
1263 }
1264 $response = '';
1265 $first_image = wp_get_attachment_image_url( $photos_ids[0], array( 60, 60 ) );
1266 $counter = '';
1267 if ( count( $photos_ids ) > 1 ) {
1268 $counter = '<span class="count">' . count( $photos_ids ) . '</span>';
1269 }
1270 $response .= '<div class="review-photo"><img src="' . esc_url( $first_image ) . '" alt="">' . $counter . '</div>';
1271
1272 return $response;
1273 }
1274
1275 /**
1276 * Get review images for gallery ajax request.
1277 *
1278 * @return void
1279 */
1280 public function get_review_images() {
1281 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'merchant_admin_options' ) ) {
1282 wp_send_json_error( array( 'message' => esc_html__( 'Nonce verification failed', 'merchant' ) ) );
1283 }
1284 $review_id = 0;
1285 if ( ! isset( $_POST['review_id'] ) || ! is_numeric( $_POST['review_id'] ) ) {
1286 wp_send_json_error( array( 'message' => esc_html__( 'Invalid review ID', 'merchant' ) ) );
1287 } else {
1288 $review_id = absint( $_POST['review_id'] );
1289 }
1290 $photos_ids = get_comment_meta( $review_id, 'review_images', true );
1291 if ( empty( $photos_ids ) ) {
1292 wp_send_json_error( array( 'message' => esc_html__( 'No images found', 'merchant' ) ) );
1293 }
1294 $images = array_map( static function ( $image_id ) {
1295 return wp_get_attachment_image_url( $image_id, 'full' );
1296 }, $photos_ids );
1297 $response = '<div class="review-photos-popup images-count-' . count( $images ) . '" data-images-count="' . count( $images ) . '">';
1298 $response .= '<div class="overlay"></div>';
1299 foreach ( $images as $image ) {
1300 $response .= '<div class="review-photo"><a href="' . esc_url( $image ) . '" target="_blank"><img src="' . esc_url( $image ) . '" alt=""></a></div>';
1301 }
1302 $response .= '</div>';
1303
1304 wp_send_json_success( $response );
1305 }
1306
1307 /**
1308 * Field: Radio Alt
1309 */
1310 public static function radio_alt( $settings, $value, $module_id = '' ) {
1311 if ( ! empty( $settings['options'] ) ) : ?>
1312 <?php
1313 foreach ( $settings['options'] as $key => $option ) : ?>
1314 <div>
1315 <label>
1316 <input type="radio" name="merchant[<?php
1317 echo esc_attr( $settings['id'] ); ?>]" value="<?php
1318 echo esc_attr( $key ); ?>" <?php
1319 checked( $value, $key, true ); ?>/>
1320 <span><?php
1321 echo esc_html( $option['title'] ); ?></span>
1322 </label>
1323 <p><?php
1324 echo esc_html( $option['desc'] ); ?></p>
1325 </div>
1326 <?php
1327 endforeach; ?>
1328 <?php
1329 endif; ?>
1330 <?php
1331 }
1332
1333 /**
1334 * Field: Choices
1335 */
1336 public static function choices( $settings, $value, $module_id = '' ) {
1337 ?>
1338 <div class="merchant-choices merchant-choices-<?php
1339 echo esc_attr( $settings['id'] ) ?>">
1340 <?php
1341 if ( ! empty( $settings['options'] ) ) : ?>
1342 <?php
1343 foreach ( $settings['options'] as $key => $option ) : ?>
1344 <label>
1345 <input type="radio" name="merchant[<?php
1346 echo esc_attr( $settings['id'] ); ?>]" value="<?php
1347 echo esc_attr( $key ); ?>" <?php
1348 checked( $value, $key, true ); ?>/>
1349 <?php
1350 if ( ! empty( $option['svg'] ) ) : ?>
1351 <span class="merchant-svg">
1352 <?php
1353 echo wp_kses( Merchant_SVG_Icons::get_svg_icon( $option['svg'] ), merchant_kses_allowed_tags( array(), false ) ); ?>
1354
1355 <?php
1356 if ( ! empty( $option['label'] ) ) : ?>
1357 <span class="merchant-tooltip"><?php
1358 echo esc_html( $option['label'] ); ?></span>
1359 <?php
1360 endif; ?>
1361 </span>
1362 <?php
1363 else : ?>
1364 <figure>
1365 <?php if ( ! empty( $option['image'] ) ) :
1366 $title = $option['title'] ?? '';
1367 ?>
1368 <img src="<?php echo esc_url( sprintf( $option['image'], MERCHANT_URI . 'assets/images' ) ); ?>" alt="<?php echo esc_attr( $title ); ?>" title="<?php echo esc_attr( $title ); ?>"/>
1369 <?php
1370 else : ?>
1371 <img src="<?php
1372 echo esc_url( sprintf( $option, MERCHANT_URI . 'assets/images' ) ); ?>"/>
1373 <?php
1374 endif; ?>
1375 <?php
1376 if ( ! empty( $option['label'] ) ) : ?>
1377 <span class="merchant-tooltip"><?php
1378 echo esc_html( $option['label'] ); ?></span>
1379 <?php
1380 endif; ?>
1381 </figure>
1382 <?php
1383 endif; ?>
1384 </label>
1385 <?php
1386 endforeach; ?>
1387 <?php
1388 endif; ?>
1389 </div>
1390 <?php
1391 }
1392
1393 /**
1394 * Field: Select
1395 */
1396 public static function select( $settings, $value, $module_id = '' ) {
1397 ?>
1398 <?php
1399 if ( ! empty( $settings['options'] ) ) : ?>
1400 <select name="merchant[<?php
1401 echo esc_attr( $settings['id'] ); ?>]">
1402 <?php
1403 foreach ( $settings['options'] as $key => $option ) : ?>
1404 <option value="<?php
1405 echo esc_attr( $key ); ?>" <?php
1406 selected( $value, $key, true ); ?>><?php
1407 echo esc_html( $option ); ?></option>
1408 <?php
1409 endforeach; ?>
1410 </select>
1411 <?php
1412 endif; ?>
1413 <?php
1414 }
1415
1416 /**
1417 * Field: Hook Select
1418 */
1419 public static function hook_select( $settings, $value, $module_id = '' ) {
1420 $hook_name = isset( $value['hook_name'] ) ? $value['hook_name'] : '';
1421 $hook_priority = isset( $value['hook_priority'] ) ? $value['hook_priority'] : '';
1422
1423 ?>
1424 <div class="merchant-module-page-setting-field-hook_select-wrapper">
1425 <div class="merchant-module-page-setting-field-select">
1426 <select name="merchant[<?php
1427 echo esc_attr( $settings['id'] ); ?>][hook_name]">
1428 <?php
1429 if ( ! empty( $settings['options'] ) ) : ?>
1430 <?php
1431 foreach ( $settings['options'] as $key => $option ) : ?>
1432 <option value="<?php
1433 echo esc_attr( $key ); ?>" <?php
1434 selected( $hook_name, $key, true ); ?>><?php
1435 echo esc_html( $option ); ?></option>
1436 <?php
1437 endforeach; ?>
1438 <?php
1439 endif; ?>
1440 </select>
1441 </div>
1442 <?php
1443
1444 if ( isset( $settings['order'] ) && true === $settings['order'] ) {
1445 ?>
1446 <div class="merchant-module-page-setting-field-number">
1447 <input type="number" name="merchant[<?php
1448 echo esc_attr( $settings['id'] ); ?>][hook_priority]" value="<?php
1449 echo esc_attr( $hook_priority ); ?>"/>
1450 </div>
1451 <?php
1452 } ?>
1453 </div>
1454
1455 <?php
1456 }
1457
1458 /**
1459 * Field: Select ajax
1460 *
1461 * @param $settings
1462 * @param $value
1463 *
1464 * @return void
1465 */
1466 public static function select_ajax( $settings, $value, $module_id = '' ) {
1467 $settings = wp_parse_args( $settings, array(
1468 'source' => 'post',
1469 ) );
1470
1471 $ids = ( is_array( $value ) && ! empty( $value ) ) ? $value : (array) $value;
1472
1473 if ( isset( $settings['multiple'] ) ) {
1474 $multiple = $settings['multiple'] === true ? 'multiple' : '';
1475 } else {
1476 $multiple = 'multiple';
1477 }
1478
1479 $placeholder = $settings['placeholder'] ?? '';
1480 ?>
1481 <select
1482 name="merchant[<?php echo esc_attr( $settings['id'] ); ?>]<?php echo esc_attr( $multiple ? '[]' : '' ); ?>"
1483 <?php echo esc_attr( $multiple ); ?>
1484 data-source="<?php echo esc_attr( $settings['source'] ); ?>"
1485 data-placeholder="<?php echo esc_attr( $placeholder ); ?>">
1486 <?php
1487 if ( ! empty( $ids ) ) {
1488 foreach ( $ids as $id ) {
1489 switch ( $settings['source'] ) {
1490 case 'post':
1491 case 'product':
1492 $post = get_post( $id );
1493
1494 if ( ! empty( $post ) ) {
1495 echo '<option value="' . esc_attr( $post->ID ) . '" selected>' . esc_html( $post->post_title ) . '</option>';
1496 }
1497 break;
1498 case 'user':
1499 $user = get_user_by( 'ID', $id );
1500 if( $user ) {
1501 echo '<option value="' . esc_attr( $user->ID ) . '" selected>' . esc_html( $user->display_name ) . '</option>';
1502 }
1503 break;
1504 case 'options':
1505 }
1506 }
1507 }
1508 if ( $settings['source'] === 'options' ) {
1509 $value = ! empty( $value ) ? $value : '';
1510
1511 foreach ( $settings['options'] as $option ) {
1512 if ( isset( $option['options'] ) ) {
1513 echo '<optgroup label="' . esc_attr( $option['text'] ) . '">';
1514
1515 foreach ( $option['options'] as $child_option ) {
1516 $selected_value = is_array( $value ) ? in_array( $child_option['id'], $value, true ) : $child_option['id'];
1517 echo '<option value="' . esc_attr( $child_option['id'] ) . '" ' . selected( $selected_value, is_array( $value ) ? true : $value, false ) . '>' . esc_html( $child_option['text'] ) . '</option>';
1518 }
1519
1520 echo '</optgroup>';
1521 } else {
1522 $selected_value = is_array( $value ) ? in_array( $option['id'], $value, true ) : $option['id'];
1523 echo '<option value="' . esc_attr( $option['id'] ) . '" ' . selected( $selected_value, is_array( $value ) ? true : $value, false ) . '>' . esc_html( $option['text'] ) . '</option>';
1524 }
1525 }
1526 } ?>
1527 </select>
1528 <?php
1529 }
1530
1531 /**
1532 * Field: Info Block
1533 *
1534 * @return void
1535 */
1536 public static function info_block( $settings, $value, $module_id = '' ) {
1537 ?>
1538 <div class="merchant-info-block">
1539 <i class="dashicons dashicons-info"></i>
1540 <p><?php
1541 echo ! empty( $settings['description'] ) ? esc_html( $settings['description'] ) : ''; ?><?php
1542 if ( ! empty( $settings['button_text'] ) && ! empty( $settings['button_link'] ) ) {
1543 printf( '<a href="%s" target="_blank">%s</a>', esc_url( $settings['button_link'] ), esc_html( $settings['button_text'] ) );
1544 } ?></p>
1545 </div>
1546 <?php
1547 }
1548
1549 /**
1550 * Field: Products Selector
1551 *
1552 * @param $settings
1553 * @param $value
1554 *
1555 * @return void
1556 */
1557 public static function products_selector( $settings, $value, $module_id = '' ) {
1558 if ( ! class_exists( 'WooCommerce' ) ) {
1559 echo '<p class="merchant-notice">' . esc_html__( 'WooCommerce is not installed or activated.', 'merchant' ) . '</p>';
1560
1561 return;
1562 }
1563
1564 $ids = $value ? explode( ',', $value ) : array();
1565
1566 if ( isset( $settings['multiple'] ) ) {
1567 $multiple = $settings['multiple'] === true ? 'multiple' : 'false';
1568 } else {
1569 $multiple = 'multiple';
1570 }
1571
1572 if ( ! isset( $settings['allowed_types'] ) ) {
1573 $settings['allowed_types'] = array( 'all' );
1574 }
1575 ?>
1576
1577 <div class="merchant-products-search-container" data-multiple="<?php
1578 echo esc_attr( $multiple ); ?>">
1579 <div class="merchant-search-area">
1580 <input type="text" name="merchant-search-field" data-allowed-types="<?php
1581 echo esc_attr( implode( ',', $settings['allowed_types'] ) ) ?>" placeholder="<?php
1582 esc_attr_e( 'Search products', 'merchant' ); ?>" class="merchant-search-field">
1583 <span class="merchant-searching"><?php
1584 esc_html_e( 'Searching...', 'merchant' ); ?></span>
1585 <img src="<?php echo esc_url( MERCHANT_URI . 'assets/images/admin/products-search-icon.svg' ); ?>" class="merchant-search-icon"
1586 alt="<?php esc_attr_e( 'Search icon', 'merchant' ); ?>">
1587 <div class="merchant-selections-products-preview"></div>
1588 </div>
1589 <div class="merchant-selected-products-preview">
1590 <ul>
1591 <?php
1592 if ( ! empty( $ids ) ) {
1593 foreach ( $ids as $product_id ) {
1594 $product = wc_get_product( $product_id );
1595 if ( $product ) {
1596 self::product_data_li( $product );
1597 }
1598 }
1599 }
1600 ?>
1601 </ul>
1602 </div>
1603 <input type="hidden" name="merchant[<?php
1604 echo esc_attr( $settings['id'] ); ?>]" class="merchant-selected-products" value="<?php
1605 echo esc_attr( $value ) ?>">
1606 </div>
1607 <?php
1608 }
1609
1610 public static function product_data_li( $product, $search = false, $hierarchy = false ) {
1611 $product_id = $product->get_id();
1612 $product_sku = $product->get_sku();
1613 $product_name = $product->get_name();
1614 $edit_link = get_edit_post_link( $product_id );
1615 if ( $product->is_type( 'variation' ) ) {
1616 $edit_link = get_edit_post_link( $product->get_parent_id() );
1617 }
1618
1619 /**
1620 * Filter product image.
1621 *
1622 * @since 1.9.1
1623 */
1624 $product_image = apply_filters(
1625 'merchant_product_item_product_image',
1626 '<span class="img">' . $product->get_image( array( 30, 30 ) ) . '</span>',
1627 $product
1628 );
1629
1630 $price = $product->get_price();
1631 $key = $product_id . '_' . $product_sku;
1632
1633 /**
1634 * Filter the product bundle item product info.
1635 *
1636 * @param string $product_info The product bundle item product info.
1637 * @param WC_Product $product The product object.
1638 *
1639 * @since 1.9.0
1640 */
1641 $product_info = apply_filters( 'merchant_pro_product_bundle_item_product_info', $product->get_type() . '<br/>#' . $product_id, $product );
1642 if ( $search ) {
1643 $remove_btn = '<span class="remove hint--left" aria-label="' . esc_html__( 'Add', 'merchant' ) . '">+</span>';
1644 } else {
1645 $remove_btn = '<span class="remove hint--left" aria-label="' . esc_html__( 'Remove', 'merchant' ) . '">×</span>';
1646 }
1647
1648 $item_class = 'product-item';
1649 if( $hierarchy && $product->is_type( 'variation' ) ) {
1650 $item_class .= ' hierarchy-style';
1651 }
1652
1653 echo '<li class="' . esc_attr( $item_class ) . '" data-key="' . esc_attr( $key ) . '" data-name="' . esc_attr( $product_name ) . '" data-sku="'
1654 . esc_attr( $product_sku ) . '" data-id="' . esc_attr( $product_id ) . '" data-price="' . esc_attr( $price ) . '">'
1655 . wp_kses( $product_image, array(
1656 'span' => array(
1657 'class' => true,
1658 ),
1659 'img' => array(
1660 'src' => true,
1661 'alt' => true,
1662 'decoding' => true,
1663 'srcset' => true,
1664 'loading' => true,
1665 'sizes' => true,
1666 'class' => true,
1667 'width' => true,
1668 'height' => true,
1669 ),
1670 ) ) . '<span class="data">'
1671 . '<span class="name">' . esc_html( $product_name ) . '</span><span class="info">' . wp_kses( $product->get_price_html(), array(
1672 'span' => array(
1673 'class' => true,
1674 ),
1675 'del' => array(
1676 'aria-hidden' => true,
1677 ),
1678 'ins' => array(),
1679 'bdi' => array(),
1680
1681 ) ) . '</span> ' . ( $product->is_sold_individually()
1682 ? '<span class="info">' . esc_html__( 'sold individually', 'merchant' ) . '</span> ' : '' ) . '</span>'
1683 . '<span class="type"><a href="' . esc_url( $edit_link ) . '" target="_blank">' . wp_kses_post( $product_info ) . '</a></span> ' . wp_kses( $remove_btn, array(
1684 'span' => array(
1685 'class' => true,
1686 'aria-label' => true,
1687 ),
1688 ) );
1689
1690 echo '</li>';
1691 }
1692
1693 public function products_search() {
1694 check_ajax_referer( 'merchant_admin_options', 'nonce' );
1695
1696 if ( ! isset( $_POST['keyword'] ) || empty( $_POST['keyword'] ) ) {
1697 exit();
1698 }
1699
1700 $types = array( 'simple', 'variable' ); // limit search to product types
1701
1702 if ( isset( $_POST['product_types'] ) || ! empty( $_POST['product_types'] ) ) {
1703 $types = explode( ',', sanitize_text_field( $_POST['product_types'] ) );
1704 }
1705
1706 $hierarchy = false;
1707 if (
1708 in_array( 'all', $types, true )
1709 || ( in_array( 'variation', $types, true ) && in_array( 'variable', $types, true ) )
1710 ) {
1711 $hierarchy = true;
1712 }
1713
1714 $added_ids = isset( $_POST['ids'] ) ? explode( ',', sanitize_text_field( $_POST['ids'] ) ) : array();
1715 $keyword = sanitize_text_field( $_POST['keyword'] );
1716 if ( is_numeric( $keyword ) ) {
1717 // search by id
1718 $query_args = array(
1719 'p' => absint( $keyword ),
1720 'post_type' => 'product',
1721 );
1722 } else {
1723 $query_args = array(
1724 'post_type' => 'product',
1725 'post_status' => array( 'publish', 'private' ),
1726 's' => $keyword,
1727 'posts_per_page' => 10,
1728 );
1729
1730 if ( ! empty( $types ) && ! in_array( 'all', $types, true ) ) {
1731 $product_types = $types;
1732
1733 if ( in_array( 'variation', $types, true ) ) {
1734 $product_types[] = 'variable';
1735 }
1736
1737 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
1738 $query_args['tax_query'] = array(
1739 array(
1740 'taxonomy' => 'product_type',
1741 'field' => 'slug',
1742 'terms' => $product_types,
1743 ),
1744 );
1745 }
1746
1747 $query_args['post__not_in'] = array_map( 'absint', $added_ids );
1748 }
1749
1750 // Filter by category slugs.
1751 $categories = array_map( 'sanitize_text_field', $_POST['categories'] ?? array() );
1752 if ( is_array( $categories ) && ! empty( $categories ) ) {
1753 $query_args['tax_query'][] = array(
1754 'taxonomy' => 'product_cat',
1755 'field' => 'slug',
1756 'terms' => $categories,
1757 'operator' => 'IN',
1758 );
1759 }
1760
1761 $query = new WP_Query( $query_args );
1762
1763 if ( $query->have_posts() ) {
1764 echo '<ul>';
1765
1766 while ( $query->have_posts() ) {
1767 $query->the_post();
1768 $_product = wc_get_product( get_the_ID() );
1769
1770 if ( ! $_product ) {
1771 continue;
1772 }
1773
1774 if (
1775 ! $_product->is_type( 'variable' )
1776 || in_array( 'variable', $types, true )
1777 || in_array( 'all', $types, true )
1778 ) {
1779 self::product_data_li( $_product, array( 'qty' => 1 ), true, $hierarchy );
1780 }
1781
1782 if (
1783 $_product->is_type( 'variable' )
1784 && (
1785 empty( $types )
1786 || in_array( 'all', $types, true )
1787 || in_array( 'variation', $types, true )
1788 )
1789 ) {
1790 // show all children
1791 $children = $_product->get_children();
1792
1793 if ( is_array( $children ) && count( $children ) > 0 ) {
1794 foreach ( $children as $child ) {
1795 $child_product = wc_get_product( $child );
1796 /**
1797 * @var WC_Product_Variation $child_product
1798 */
1799 if ( ! $this->are_variation_attributes_set( $child_product ) ) {
1800 // Don't display variations that don't have all attributes set.
1801 continue;
1802 }
1803 self::product_data_li( $child_product, true, $hierarchy );
1804 }
1805 }
1806 }
1807 }
1808
1809 echo '</ul>';
1810 wp_reset_postdata();
1811 } else {
1812 // translators: %s is the search keyword
1813 echo wp_kses( '<ul><span>' . sprintf( esc_html__( 'No results found for "%s"', 'merchant' ), $keyword ) . '</span></ul>', array(
1814 'ul' => array(),
1815 'span' => array(),
1816 ) );
1817 }
1818
1819 exit;
1820 }
1821
1822 /**
1823 * Check if all variation attributes are set.
1824 *
1825 * @param $variation WC_Product_Variation variation product object
1826 *
1827 * @return bool
1828 */
1829 public function are_variation_attributes_set( $variation ) {
1830 // Check if the product is a variation and has attributes
1831 if ( $variation && $variation->is_type( 'variation' ) ) {
1832 // Get the variation attributes
1833 $variation_attributes = $variation->get_variation_attributes();
1834
1835 // Check if all variation attributes are set
1836 foreach ( $variation_attributes as $attribute => $value ) {
1837 if ( empty( $value ) ) {
1838 return false; // At least one attribute is not set
1839 }
1840 }
1841
1842 return true; // All variation attributes are set
1843 }
1844
1845 return false; // Not a valid variation product
1846 }
1847
1848 /**
1849 * Field: Select Size Chart
1850 */
1851 public static function select_size_chart( $settings, $value, $module_id = '' ) {
1852 $options = array(
1853 '' => esc_html__( 'Default', 'merchant' ),
1854 );
1855
1856 $posts = get_posts( array(
1857 'post_type' => 'merchant_size_chart',
1858 'posts_per_page' => - 1,
1859 'post_status' => 'publish',
1860 ) );
1861
1862 if ( ! is_wp_error( $posts ) && ! empty( $posts ) ) {
1863 foreach ( $posts as $_post ) {
1864 $options[ $_post->ID ] = $_post->post_title;
1865 }
1866 }
1867
1868 ?>
1869 <?php
1870 if ( ! empty( $options ) ) : ?>
1871 <select name="merchant[<?php
1872 echo esc_attr( $settings['id'] ); ?>]">
1873 <?php
1874 foreach ( $options as $key => $option ) : ?>
1875 <option value="<?php
1876 echo esc_attr( $key ); ?>" <?php
1877 selected( $value, $key, true ); ?>><?php
1878 echo esc_html( $option ); ?></option>
1879 <?php
1880 endforeach; ?>
1881 </select>
1882 <?php
1883 endif; ?>
1884 <?php
1885 }
1886
1887 /**
1888 * Field: Buttons
1889 */
1890 public static function buttons( $settings, $value, $module_id = '' ) {
1891 ?>
1892 <div class="merchant-buttons">
1893 <?php
1894 if ( ! empty( $settings['options'] ) ) : ?>
1895 <?php
1896 foreach ( $settings['options'] as $key => $option ) : ?>
1897 <label class="merchant-button-<?php
1898 echo esc_attr( $key ); ?>"">
1899 <input type="radio" name="merchant[<?php
1900 echo esc_attr( $settings['id'] ); ?>]" value="<?php
1901 echo esc_attr( $key ); ?>" <?php
1902 checked( $value, $key, true ); ?>/>
1903 <span><?php
1904 echo esc_html( $option ); ?></span>
1905 </label>
1906 <?php
1907 endforeach; ?>
1908 <?php
1909 endif; ?>
1910 </div>
1911 <?php
1912 }
1913
1914 /**
1915 * Field: Buttons Alt
1916 */
1917 public static function buttons_alt( $settings, $value, $module_id = '' ) {
1918 ?>
1919 <div class="merchant-buttons">
1920 <?php
1921 if ( ! empty( $settings['options'] ) ) : ?>
1922 <?php
1923 foreach ( $settings['options'] as $key => $option ) : ?>
1924 <label class="merchant-button-<?php
1925 echo esc_attr( $key ); ?>">
1926 <input type="radio" name="merchant[<?php
1927 echo esc_attr( $settings['id'] ); ?>]" value="<?php
1928 echo esc_attr( $key ); ?>" <?php
1929 checked( $value, $key, true ); ?>/>
1930 <span><?php
1931 echo esc_html( $option ); ?></span>
1932 </label>
1933 <?php
1934 endforeach; ?>
1935 <?php
1936 endif; ?>
1937 </div>
1938 <?php
1939 }
1940
1941 /**
1942 * Field: Buttons Content
1943 */
1944 public static function buttons_content( $settings, $value, $module_id = '' ) {
1945 ?>
1946 <div class="merchant-buttons-content">
1947 <?php
1948 if ( ! empty( $settings['options'] ) ) : ?>
1949 <?php
1950 foreach ( $settings['options'] as $key => $option ) : ?>
1951 <label class="merchant-button-content-<?php echo esc_attr( $key ); ?>">
1952 <input
1953 type="radio"
1954 name="merchant[<?php echo esc_attr( $settings['id'] ); ?>]"
1955 value="<?php echo esc_attr( $key ); ?>"
1956 <?php checked( $value, $key, true ); ?>
1957 />
1958
1959 <span class="merchant-buttons-inner-content">
1960 <?php if ( $option['icon'] ) : ?>
1961 <img src="<?php echo esc_url( $option['icon'] ); ?>" alt="">
1962 <?php endif; ?>
1963 <span>
1964 <span><?php echo esc_html( $option['title'] ?? '' ); ?></span>
1965 <span><?php echo esc_html( $option['desc'] ?? '' ); ?></span>
1966 </span>
1967 </span>
1968 </label>
1969 <?php
1970 endforeach; ?>
1971 <?php
1972 endif; ?>
1973 </div>
1974 <?php
1975 }
1976
1977 /**
1978 * Field: Range
1979 */
1980 public static function range( $settings, $value, $module_id = '' ) {
1981 $settings = wp_parse_args( $settings, array(
1982 'min' => '',
1983 'max' => '',
1984 'step' => '',
1985 'unit' => '',
1986 ) );
1987
1988 ?>
1989 <div class="merchant-range">
1990 <input type="range" class="merchant-range-input" name="" min="<?php
1991 echo esc_attr( $settings['min'] ); ?>" max="<?php
1992 echo esc_attr( $settings['max'] ); ?>"
1993 step="<?php
1994 echo esc_attr( $settings['step'] ); ?>" value="<?php
1995 echo esc_attr( $value ); ?>"/>
1996 <input type="number" class="merchant-range-number-input" name="merchant[<?php
1997 echo esc_attr( $settings['id'] ); ?>]" min="<?php
1998 echo esc_attr( $settings['min'] ); ?>"
1999 max="<?php
2000 echo esc_attr( $settings['max'] ); ?>" step="<?php
2001 echo esc_attr( $settings['step'] ); ?>" value="<?php
2002 echo esc_attr( $value ); ?>"/>
2003 <?php
2004 if ( ! empty( $settings['unit'] ) ) : ?>
2005 <span class="merchant-range-unit"><?php
2006 echo esc_html( $settings['unit'] ); ?></span>
2007 <?php
2008 endif; ?>
2009 </div>
2010 <?php
2011 }
2012
2013 /**
2014 * Field: Color
2015 */
2016 public static function color( $settings, $value, $module_id = '' ) {
2017 $settings = wp_parse_args( $settings, array(
2018 'default' => '#212121',
2019 ) );
2020
2021 ?>
2022 <div class="merchant-color">
2023 <div class="merchant-color-picker" data-default-color="<?php
2024 echo esc_attr( $settings['default'] ); ?>" style="background-color: <?php
2025 echo esc_attr( $value ); ?>;"></div>
2026 <input type="text" class="merchant-color-input" name="merchant[<?php
2027 echo esc_attr( $settings['id'] ); ?>]" value="<?php
2028 echo esc_attr( $value ); ?>"/>
2029 </div>
2030 <?php
2031 }
2032
2033 /**
2034 * Field: Gallery
2035 */
2036 public static function gallery( $settings, $value, $module_id = '' ) {
2037 $settings = wp_parse_args( $settings, array(
2038 'label' => esc_html__( 'Select Images', 'merchant' ),
2039 ) );
2040
2041 $images = ( ! empty( $value ) ) ? explode( ',', $value ) : array();
2042
2043
2044 echo '<div class="merchant-gallery-images">';
2045
2046 if ( ! empty( $images ) ) :
2047
2048 foreach ( $images as $image_id ) :
2049
2050 $image = wp_get_attachment_image_src( $image_id, 'thumbnail' );
2051
2052 if ( ! empty( $image ) && ! empty( $image[0] ) ) :
2053
2054 printf( '<div class="merchant-gallery-image" data-item-id="%s">', esc_attr( $image_id ) );
2055
2056 echo '<i class="merchant-gallery-remove dashicons dashicons-no-alt"></i>';
2057
2058 printf( '<img src="%s" />', esc_url( $image[0] ) );
2059
2060 echo '</div>';
2061
2062 endif;
2063
2064 endforeach;
2065
2066 endif;
2067
2068 echo '</div>';
2069
2070 ?>
2071 <a href="#" class="merchant-gallery-button"><?php
2072 echo esc_html( $settings['label'] ); ?></a>
2073 <input type="hidden" class="merchant-gallery-input" name="merchant[<?php
2074 echo esc_attr( $settings['id'] ); ?>]" value="<?php
2075 echo esc_attr( $value ); ?>"/>
2076 <?php
2077 }
2078
2079 /**
2080 * Field: Upload
2081 */
2082 public static function upload( $settings, $value, $module_id = '' ) {
2083 $settings = wp_parse_args( $settings, array(
2084 'label' => esc_html__( 'Select Image', 'merchant' ),
2085 ) );
2086
2087 echo '<div class="merchant-upload-wrapper">';
2088 if ( ! empty( $value ) ) :
2089 $image = wp_get_attachment_image_src( $value, 'thumbnail' );
2090 if ( ! empty( $image ) && ! empty( $image[0] ) ) :
2091 echo '<div class="merchant-upload-image">';
2092 echo '<i class="merchant-upload-remove dashicons dashicons-no-alt"></i>';
2093 printf( '<img src="%s" />', esc_url( $image[0] ) );
2094 echo '</div>';
2095 endif;
2096 endif;
2097 echo '</div>';
2098
2099 $drag_drop = $settings['drag_drop'] ?? false;
2100 ?>
2101 <div class="merchant-upload-button-wrapper<?php echo esc_attr( $drag_drop ? ' merchant-upload-button-drag-drop' : '' ); ?>">
2102 <?php if ( $drag_drop ) : ?>
2103 <img src="<?php echo esc_url( esc_url( MERCHANT_URI . '/assets/images/upload-icon.svg' ) ); ?>" alt="<?php echo esc_attr__( 'Upload image', 'merchant' ); ?>"/>
2104 <?php endif; ?>
2105 <a href="#" class="merchant-upload-button"><?php echo esc_html( $settings['label'] ?? '' ); ?></a>
2106 </div>
2107 <input type="hidden" class="merchant-upload-input" name="merchant[<?php echo esc_attr( $settings['id'] ); ?>]" value="<?php echo esc_attr( $value ); ?>"/>
2108 <?php
2109 }
2110
2111 /**
2112 * Field: Warning
2113 */
2114 public static function warning( $settings ) {
2115 echo wp_kses_post( $settings['content'] );
2116 }
2117
2118 /**
2119 * Field: Warning
2120 */
2121 public static function info( $settings ) {
2122 echo wp_kses_post( $settings['content'] );
2123 }
2124
2125 /**
2126 * Field: Divider
2127 */
2128 public static function divider() {
2129 }
2130
2131 /**
2132 * Field: Content
2133 */
2134 public static function content( $settings ) {
2135 echo wp_kses_post( $settings['content'] );
2136 }
2137
2138 /**
2139 * Field: Sortable
2140 */
2141 public static function sortable( $settings, $value, $module_id = '' ) {
2142 ?>
2143 <div class="merchant-sortable">
2144 <ul class="merchant-sortable-list ui-sortable">
2145 <?php
2146 foreach ( $value as $option_key ) :
2147 $option_val = $settings['options'][ $option_key ];
2148
2149 if ( in_array( $option_key, $value, true ) ) :
2150 ?>
2151 <li class="merchant-sortable-item" data-value="<?php
2152 echo esc_attr( $option_key ); ?>">
2153 <i class='dashicons dashicons-menu'></i>
2154 <i class="dashicons dashicons-visibility visibility"></i>
2155 <?php
2156 echo esc_html( $option_val ); ?>
2157 </li>
2158 <?php
2159 endif; ?>
2160 <?php
2161 endforeach; ?>
2162
2163 <?php
2164 foreach ( $settings['options'] as $option_key => $option_val ) :
2165 if ( ! in_array( $option_key, $value, true ) ) :
2166 $invisible = ! in_array( $option_key, $value, true ) ? ' invisible' : '';
2167
2168 ?>
2169 <li class="merchant-sortable-item<?php
2170 echo esc_attr( $invisible ); ?>" data-value="<?php
2171 echo esc_attr( $option_key ); ?>">
2172 <i class='dashicons dashicons-menu'></i>
2173 <i class="dashicons dashicons-visibility visibility"></i>
2174 <?php
2175 echo esc_html( $option_val ); ?>
2176 </li>
2177 <?php
2178 endif; ?>
2179 <?php
2180 endforeach; ?>
2181 </ul>
2182
2183 <input class="merchant-sortable-input" type="hidden" name="merchant[<?php
2184 echo esc_attr( $settings['id'] ); ?>]" value="<?php
2185 echo esc_attr( wp_json_encode( $value ) ); ?>"/>
2186 </div>
2187 <?php
2188 }
2189
2190 /**
2191 * Field: Sortable Repeater.
2192 */
2193 public static function sortable_repeater( $settings, $value, $module_id = '' ) {
2194 ?>
2195 <div class="merchant-sortable-repeater-control<?php
2196 echo isset( $settings['sorting'] ) && false === $settings['sorting'] ? ' disable-sorting' : ''; ?>">
2197 <div class="merchant-sortable-repeater sortable regular-field">
2198 <div class="repeater">
2199 <input type="text" value="" class="repeater-input"/><span class="dashicons dashicons-menu"></span><a class="customize-control-sortable-repeater-delete"
2200 href="#"><span
2201 class="dashicons dashicons-no-alt"></span></a>
2202 </div>
2203 </div>
2204 <button class="button customize-control-sortable-repeater-add" type="button"><?php
2205 echo esc_html( $settings['button_label'] ); ?></button>
2206 <input class="merchant-sortable-repeater-input" type="hidden" name="merchant[<?php
2207 echo esc_attr( $settings['id'] ); ?>]" value="<?php
2208 echo esc_attr( wp_json_encode( $value ) ); ?>"/>
2209 </div>
2210 <?php
2211 }
2212
2213 public static function wc_coupons( $settings, $value ) {
2214 $coupons = get_posts( array(
2215 'posts_per_page' => - 1,
2216 'orderby' => 'title',
2217 'order' => 'asc',
2218 'post_type' => 'shop_coupon',
2219 'post_status' => 'publish',
2220 ) );
2221 if ( $coupons ) {
2222 ?>
2223 <div class="merchant-wc-coupon-selector merchant-<?php
2224 echo esc_attr( $settings['id'] ) ?>">
2225 <?php
2226 echo '<select name="merchant[' . esc_attr( $settings['id'] ) . ']">';
2227 echo '<option value="">' . esc_html__( 'Select a coupon', 'merchant' ) . '</option>';
2228
2229 foreach ( $coupons as $coupon ) {
2230 $coupon_code = strtolower( $coupon->post_title );
2231
2232 echo '<option value="' . esc_attr( $coupon_code ) . '"' . selected( esc_attr( $coupon_code ), $value, false ) . '>';
2233 echo esc_html( $coupon_code );
2234 echo '</option>';
2235 }
2236
2237 echo '</select>';
2238 echo '<a href="' . esc_url( admin_url( 'edit.php?post_type=shop_coupon' ) ) . '" target="_blank" style="margin-left: 10px;">' . esc_html__( 'Manage coupons',
2239 'merchant' ) . '</a>';
2240 ?>
2241 </div>
2242 <?php
2243 } else {
2244 echo '<div style="color: red;">';
2245 echo wp_kses_post(
2246 sprintf(
2247 /* Translators: 1. Coupon admin url 2. Link target attribute value */
2248 __( 'No coupons found! <a href="%1$s" target="%2$s">Create a new coupon</a>', 'merchant' ),
2249 admin_url( 'post-new.php?post_type=shop_coupon' ),
2250 '_blank'
2251 )
2252 );
2253 echo '</div>';
2254 echo '<input type="hidden" name="merchant[' . esc_attr( $settings['id'] ) . ']" value="" />';
2255 }
2256 }
2257
2258 /**
2259 * Field: Date Time.
2260 *
2261 * @param $settings array field settings.
2262 * @param $value string field value.
2263 * @param $module_id string module id.
2264 *
2265 * @return void
2266 */
2267 public static function date_time( $settings, $value, $module_id = '' ) {
2268 // All options are documented here: https://air-datepicker.com/docs
2269 $options = array(
2270 'dateFormat' => 'MM-dd-yyyy',
2271 'timepicker' => true,
2272 'timeFormat' => 'hh:mm AA',
2273 'minDate' => 'today',
2274 'timeZone' => wp_timezone_string(),
2275 );
2276 if ( isset( $settings['options'] ) ) {
2277 $settings['options'] = wp_parse_args( $settings['options'], $options );
2278 } else {
2279 $settings['options'] = $options;
2280 }
2281 ?>
2282 <div class="merchant-datetime-field" data-options="<?php echo esc_attr( wp_json_encode( $settings['options'] ) ); ?>">
2283 <input type="text" name="merchant[<?php
2284 echo esc_attr( $settings['id'] ); ?>]" value="<?php
2285 echo esc_attr( $value ); ?>" placeholder="<?php
2286 echo isset( $settings['placeholder'] ) ? esc_attr( $settings['placeholder'] ) : ''; ?>"/>
2287 </div>
2288 <?php
2289 }
2290
2291 /**
2292 * Field: Flexible Content.
2293 *
2294 * @param array $settings
2295 * @param mixed $value
2296 *
2297 * @return void
2298 */
2299 public static function flexible_content( $settings, $value, $module_id = '' ) {
2300 $values = ( is_array( $value ) && ! empty( $value ) ) ? $value : array();
2301 $empty = empty( $values ) ? 'empty' : '';
2302
2303 $settings = wp_parse_args( $settings, array(
2304 'sorting' => false,
2305 'style' => 'default',
2306 'accordion' => false,
2307 ) );
2308
2309 $classes = array(
2310 'merchant-flexible-content-control',
2311 "{$settings['style']}-style",
2312 );
2313
2314 $has_sorting = (bool) ( $settings['sorting'] ?? true );
2315 if ( ! $has_sorting ) {
2316 $classes[] = 'disable-sorting';
2317 }
2318
2319 $has_accordion = (bool) ( $settings['accordion'] ?? false );
2320 if ( $has_accordion ) {
2321 $classes[] = 'has-accordion';
2322 }
2323
2324 $has_duplicate = (bool) ( $settings['duplicate'] ?? false );
2325 if ( $has_duplicate ) {
2326 $classes[] = 'has-duplicate';
2327 }
2328 ?>
2329 <div class="<?php
2330 echo esc_attr( implode( ' ', $classes ) ); ?>"
2331 data-id="<?php
2332 echo esc_attr( $settings['id'] ) ?>">
2333
2334 <div class="layouts" data-id="<?php echo esc_attr( $settings['id'] ) ?>">
2335 <?php foreach ( $settings['layouts'] as $layout_type => $layout ) : ?>
2336 <div class="layout" data-type="<?php echo esc_attr( $layout_type ) ?>">
2337 <div class="layout__inner">
2338 <?php if ( $has_sorting ) : ?>
2339 <span class="customize-control-flexible-content-move">
2340 <svg xmlns="http://www.w3.org/2000/svg" width="10" height="14" viewBox="0 0 10 14" fill="none">
2341 <path d="M1.75 0.5C1.19772 0.5 0.75 0.947715 0.75 1.5V2.5C0.75 3.05228 1.19772 3.5 1.75 3.5H2.75C3.30228 3.5 3.75 3.05228 3.75 2.5V1.5C3.75 0.947715 3.30228 0.5 2.75 0.5H1.75Z" fill="#4A4A4A"/>
2342 <path d="M1.75 5.5C1.19772 5.5 0.75 5.94772 0.75 6.5V7.5C0.75 8.05228 1.19772 8.5 1.75 8.5H2.75C3.30228 8.5 3.75 8.05228 3.75 7.5V6.5C3.75 5.94772 3.30228 5.5 2.75 5.5H1.75Z" fill="#4A4A4A"/>
2343 <path d="M0.75 11.5C0.75 10.9477 1.19772 10.5 1.75 10.5H2.75C3.30228 10.5 3.75 10.9477 3.75 11.5V12.5C3.75 13.0523 3.30228 13.5 2.75 13.5H1.75C1.19772 13.5 0.75 13.0523 0.75 12.5V11.5Z" fill="#4A4A4A"/>
2344 <path d="M7.25 0.5C6.69772 0.5 6.25 0.947715 6.25 1.5V2.5C6.25 3.05228 6.69772 3.5 7.25 3.5H8.25C8.80228 3.5 9.25 3.05228 9.25 2.5V1.5C9.25 0.947715 8.80228 0.5 8.25 0.5H7.25Z" fill="#4A4A4A"/>
2345 <path d="M6.25 6.5C6.25 5.94772 6.69772 5.5 7.25 5.5H8.25C8.80228 5.5 9.25 5.94772 9.25 6.5V7.5C9.25 8.05228 8.80228 8.5 8.25 8.5H7.25C6.69772 8.5 6.25 8.05228 6.25 7.5V6.5Z" fill="#4A4A4A"/>
2346 <path d="M7.25 10.5C6.69772 10.5 6.25 10.9477 6.25 11.5V12.5C6.25 13.0523 6.69772 13.5 7.25 13.5H8.25C8.80228 13.5 9.25 13.0523 9.25 12.5V11.5C9.25 10.9477 8.80228 10.5 8.25 10.5H7.25Z" fill="#4A4A4A"/>
2347 </svg>
2348 </span>
2349 <?php endif; ?>
2350 <div class="layout-header">
2351 <div class="layout-count">1</div>
2352 <div class="layout-title"<?php
2353 if ( isset( $layout['title-field'] ) && ! empty( $layout['title-field'] ) ) {
2354 echo ' data-title-field="' . esc_attr( $layout['title-field'] ) . '"';
2355 } ?>>
2356 <?php
2357 echo esc_html( $layout['title'] ) ?>
2358 </div>
2359 <div class="layout-toggle">
2360 <?php if ( $has_accordion ) : ?>
2361 <span class="customize-control-flexible-content-accordion">
2362 <svg xmlns="http://www.w3.org/2000/svg" width="10" height="7" viewBox="0 0 10 7" fill="none">
2363 <path fill-rule="evenodd" clip-rule="evenodd" d="M0.71967 0.732854C1.01256 0.43996 1.48744 0.43996 1.78033 0.732854L5.25 4.20252L8.71967 0.732854C9.01256 0.43996 9.48744 0.43996 9.78033 0.732854C10.0732 1.02575 10.0732 1.50062 9.78033 1.79351L5.78033 5.79351C5.48744 6.08641 5.01256 6.08641 4.71967 5.79351L0.71967 1.79351C0.426777 1.50062 0.426777 1.02575 0.71967 0.732854Z" fill="#4A4A4A"/>
2364 </svg>
2365 </span>
2366 <?php endif; ?>
2367 </div>
2368 </div>
2369 <div class="layout-body">
2370 <?php
2371 foreach ( $layout['fields'] as $sub_field ) :
2372 $classes = array( 'layout-field' );
2373
2374 if ( isset( $sub_field['classes'] ) ) {
2375 $classes = array_merge( $classes, $sub_field['classes'] );
2376 } ?>
2377 <div class="<?php
2378 echo esc_attr( implode( ' ', $classes ) ); ?>">
2379 <?php
2380 if ( 'fields_group' === $sub_field['type'] ) {
2381 static::fields_group( $sub_field, $value, $module_id, true, array(
2382 'id' => $settings['id'],
2383 'option_key' => 0,
2384 'value' => $value,
2385 ) );
2386 } else {
2387 static::replace_field(
2388 $sub_field,
2389 $sub_field['default'] ?? '',
2390 array(
2391 "name=\"merchant[{$sub_field['id']}]",
2392 'merchant-module-page-setting-field-upload',
2393 'merchant-module-page-setting-field-select_ajax',
2394 ),
2395 array(
2396 "data-name=\"merchant[{$settings['id']}][0][{$sub_field['id']}]",
2397 'merchant-module-page-setting-field-upload template',
2398 'merchant-module-page-setting-field-select_ajax template',
2399 ),
2400 $module_id
2401 );
2402 }
2403 ?>
2404 </div>
2405 <?php
2406 endforeach; ?>
2407 <input type="hidden" data-name="merchant[<?php
2408 echo esc_attr( $settings['id'] ) ?>][0][layout]" value="<?php
2409 echo esc_attr( $layout_type ) ?>">
2410 </div>
2411 <?php self::print_flexible_layout_actions( $settings, $layout_type ); ?>
2412 </div>
2413 </div>
2414 <?php
2415 endforeach; ?>
2416 </div>
2417
2418 <div class="merchant-flexible-content <?php echo esc_attr( $empty ); ?> sortable">
2419 <?php
2420 foreach ( $values as $option_key => $option ) : ?>
2421 <div class="layout" data-type="<?php echo esc_attr( $option['layout'] ) ?>">
2422 <div class="layout__inner">
2423 <?php if ( $has_sorting ) : ?>
2424 <span class="customize-control-flexible-content-move">
2425 <svg xmlns="http://www.w3.org/2000/svg" width="10" height="14" viewBox="0 0 10 14" fill="none">
2426 <path d="M1.75 0.5C1.19772 0.5 0.75 0.947715 0.75 1.5V2.5C0.75 3.05228 1.19772 3.5 1.75 3.5H2.75C3.30228 3.5 3.75 3.05228 3.75 2.5V1.5C3.75 0.947715 3.30228 0.5 2.75 0.5H1.75Z" fill="#4A4A4A"/>
2427 <path d="M1.75 5.5C1.19772 5.5 0.75 5.94772 0.75 6.5V7.5C0.75 8.05228 1.19772 8.5 1.75 8.5H2.75C3.30228 8.5 3.75 8.05228 3.75 7.5V6.5C3.75 5.94772 3.30228 5.5 2.75 5.5H1.75Z" fill="#4A4A4A"/>
2428 <path d="M0.75 11.5C0.75 10.9477 1.19772 10.5 1.75 10.5H2.75C3.30228 10.5 3.75 10.9477 3.75 11.5V12.5C3.75 13.0523 3.30228 13.5 2.75 13.5H1.75C1.19772 13.5 0.75 13.0523 0.75 12.5V11.5Z" fill="#4A4A4A"/>
2429 <path d="M7.25 0.5C6.69772 0.5 6.25 0.947715 6.25 1.5V2.5C6.25 3.05228 6.69772 3.5 7.25 3.5H8.25C8.80228 3.5 9.25 3.05228 9.25 2.5V1.5C9.25 0.947715 8.80228 0.5 8.25 0.5H7.25Z" fill="#4A4A4A"/>
2430 <path d="M6.25 6.5C6.25 5.94772 6.69772 5.5 7.25 5.5H8.25C8.80228 5.5 9.25 5.94772 9.25 6.5V7.5C9.25 8.05228 8.80228 8.5 8.25 8.5H7.25C6.69772 8.5 6.25 8.05228 6.25 7.5V6.5Z" fill="#4A4A4A"/>
2431 <path d="M7.25 10.5C6.69772 10.5 6.25 10.9477 6.25 11.5V12.5C6.25 13.0523 6.69772 13.5 7.25 13.5H8.25C8.80228 13.5 9.25 13.0523 9.25 12.5V11.5C9.25 10.9477 8.80228 10.5 8.25 10.5H7.25Z" fill="#4A4A4A"/>
2432 </svg>
2433 </span>
2434 <?php endif; ?>
2435 <div class="layout-header">
2436 <div class="layout-count"><?php
2437 echo absint( $option_key + 1 ) ?></div>
2438 <div class="layout-title"<?php
2439 if ( isset( $layout['title-field'] ) && ! empty( $layout['title-field'] ) ) {
2440 echo ' data-title-field="' . esc_attr( $layout['title-field'] ) . '"';
2441 } ?>>
2442 <?php
2443 echo isset( $settings['layouts'][ $option['layout'] ]['title'] ) ? esc_html( $settings['layouts'][ $option['layout'] ]['title'] ) : '' ?>
2444 </div>
2445 <div class="layout-toggle">
2446 <?php if ( $has_accordion ) : ?>
2447 <span class="customize-control-flexible-content-accordion">
2448 <svg xmlns="http://www.w3.org/2000/svg" width="10" height="7" viewBox="0 0 10 7" fill="none">
2449 <path fill-rule="evenodd" clip-rule="evenodd" d="M0.71967 0.732854C1.01256 0.43996 1.48744 0.43996 1.78033 0.732854L5.25 4.20252L8.71967 0.732854C9.01256 0.43996 9.48744 0.43996 9.78033 0.732854C10.0732 1.02575 10.0732 1.50062 9.78033 1.79351L5.78033 5.79351C5.48744 6.08641 5.01256 6.08641 4.71967 5.79351L0.71967 1.79351C0.426777 1.50062 0.426777 1.02575 0.71967 0.732854Z" fill="#4A4A4A"/>
2450 </svg>
2451 </span>
2452 <?php endif; ?>
2453 </div>
2454 </div>
2455 <div class="layout-body">
2456 <?php
2457 foreach ( $settings['layouts'][ $option['layout'] ]['fields'] as $sub_field ) :
2458 $classes = array( 'layout-field' );
2459 if ( isset( $sub_field['classes'] ) ) {
2460 $classes = array_merge( $classes, $sub_field['classes'] );
2461 } ?>
2462 <div class="<?php
2463 echo esc_attr( implode( ' ', $classes ) ) ?>">
2464 <?php
2465 $value = null;
2466 if ( isset( $option[ $sub_field['id'] ] ) ) {
2467 $value = $option[ $sub_field['id'] ];
2468 } elseif ( isset( $sub_field['type'] ) && $sub_field['type'] === 'switcher' ) {
2469 $value = is_null( $value ) ? 0 : 1;
2470 } elseif ( isset( $sub_field['default'] ) ) {
2471 $value = $sub_field['default'];
2472 }
2473
2474 if ( 'fields_group' === $sub_field['type'] ) {
2475 static::fields_group( $sub_field, $value, $module_id, true, array(
2476 'id' => $settings['id'],
2477 'option_key' => $option_key,
2478 'value' => $option,
2479 ) );
2480 } else {
2481 static::replace_field(
2482 $sub_field,
2483 $value,
2484 "name=\"merchant[{$sub_field['id']}]",
2485 "name=\"merchant[{$settings['id']}][{$option_key}][{$sub_field['id']}]",
2486 $module_id
2487 );
2488 }
2489 ?>
2490 </div>
2491 <?php
2492 endforeach; ?>
2493 <input type="hidden" name="merchant[<?php
2494 echo esc_attr( $settings['id'] ) ?>][<?php
2495 echo absint( $option_key ) ?>][layout]"
2496 value="<?php
2497 echo esc_attr( $option['layout'] ) ?>">
2498 </div>
2499 <?php self::print_flexible_layout_actions( $settings, $layout_type ); ?>
2500 </div>
2501 </div>
2502 <?php
2503 endforeach; ?>
2504 </div>
2505
2506 <div class="customize-control-flexible-content-add-wrapper">
2507 <div class="customize-control-flexible-content-add-list">
2508 <?php
2509 foreach ( $settings['layouts'] as $layout_type => $layout ) : ?>
2510 <a href="#"
2511 class="customize-control-flexible-content-add"
2512 data-id="<?php
2513 echo esc_attr( $settings['id'] ) ?>"
2514 data-layout="<?php
2515 echo esc_attr( $layout_type ) ?>">
2516 <?php
2517 echo esc_attr( $layout['title'] ) ?>
2518 </a>
2519 <?php
2520 endforeach; ?>
2521 </div>
2522 <button class="button customize-control-flexible-content-add-button" type="button"><?php
2523 echo esc_html( $settings['button_label'] ); ?></button>
2524 </div>
2525 </div>
2526 <?php
2527 }
2528
2529 /**
2530 * Field: Group.
2531 *
2532 * @param $settings array field settings.
2533 * @param $value mixed field value.
2534 * @param $module_id string module id.
2535 * @param $inside_flexible boolean is inside flexible content.
2536 * @param $args array extra arguments to be passed if inside flexible content.
2537 *
2538 * @return void
2539 */
2540 public static function fields_group( $settings, $value, $module_id = '', $inside_flexible = false, $args = array() ) {
2541 $control_field_status = ! empty( $settings['display_status'] ) && $settings['display_status'] === true;
2542 $accordion = ! empty( $settings['accordion'] ) && $settings['accordion'] === true;
2543 $state = ! empty( $settings['state'] ) && $settings['state'] === 'open';
2544 ?>
2545 <div class="merchant-group-field<?php
2546 echo $accordion ? ' has-accordion' : '';
2547 echo $control_field_status ? ' has-flag' : '';
2548 echo $state ? ' open' : '';
2549 echo ' merchant-group-field-' . esc_attr( $settings['id'] ) ?>" data-id="<?php
2550 echo esc_attr( $settings['id'] ) ?>">
2551 <div class="title-area<?php
2552 echo $accordion ? ' accordion-style' : '' ?>">
2553 <?php
2554 if ( ! empty( $settings['title'] ) ) {
2555 printf( '<div class="merchant-module-page-setting-field-title">%s<span class="field-status hidden"></span></div>', esc_html( $settings['title'] ) );
2556 }
2557 if ( ! empty( $settings['sub-desc'] ) ) {
2558 printf( '<div class="merchant-module-page-setting-field-desc">%s</div>', wp_kses_post( $settings['sub-desc'] ) );
2559 }
2560 if ( $accordion ) {
2561 ?>
2562 <span class="accordion-icon dashicons dashicons-arrow-down-alt2"></span>
2563 <?php
2564 }
2565 ?>
2566 </div>
2567 <div class="merchant-group-fields-container">
2568 <?php
2569 if ( $control_field_status ) {
2570 /**
2571 * Field: Status.
2572 *
2573 * @since 1.9.12
2574 */
2575 $status = apply_filters(
2576 'merchant_group_status_field',
2577 array(
2578 'id' => $settings['id'] . '_status',
2579 'type' => 'select',
2580 'title' => esc_html__( 'Status', 'merchant' ),
2581 'options' => array(
2582 'inactive' => esc_html__( 'Inactive', 'merchant' ),
2583 'active' => esc_html__( 'Active', 'merchant' ),
2584 ),
2585 'default' => isset( $settings['default'] ) ? $settings['default'] : 'active',
2586 ),
2587 $settings,
2588 $value,
2589 $module_id
2590 );
2591 if ( $inside_flexible ) {
2592 static::replace_field(
2593 $status,
2594 isset( $args['value'][$settings['id']][ $status['id'] ] ) ? $args['value'][$settings['id']][ $status['id'] ] : $status['default'] ?? '',
2595 "name=\"merchant[{$status['id']}]",
2596 "name=\"merchant[{$args['id']}][{$args['option_key']}][{$settings['id']}][{$status['id']}]\" data-name=\"merchant[{$args['id']}][0][{$settings['id']}][{$status['id']}]",
2597 $module_id
2598 );
2599 } else {
2600 static::replace_field(
2601 $status,
2602 isset( $value[ $status['id'] ] ) ? $value[ $status['id'] ] : '',
2603 "name=\"merchant[{$status['id']}]",
2604 "name=\"merchant[{$settings['id']}][{$status['id']}]",
2605 $module_id
2606 );
2607 }
2608 }
2609
2610 foreach ( $settings['fields'] as $field ) {
2611 if ( $inside_flexible ) {
2612 static::replace_field(
2613 $field,
2614 $args['value'][ $settings['id'] ][ $field['id'] ] ?? ( $field['default'] ?? '' ),
2615 "name=\"merchant[{$field['id']}]",
2616 "name=\"merchant[{$args['id']}][{$args['option_key']}][{$settings['id']}][{$field['id']}]\" data-name=\"merchant[{$args['id']}][0][{$settings['id']}][{$field['id']}]",
2617 $module_id
2618 );
2619 } else {
2620 static::replace_field(
2621 $field,
2622 isset( $value[ $field['id'] ] ) ? $value[ $field['id'] ] : ( $field['default'] ?? '' ),
2623 "name=\"merchant[{$field['id']}]",
2624 "name=\"merchant[{$settings['id']}][{$field['id']}]",
2625 $module_id
2626 );
2627 }
2628 } ?>
2629 </div>
2630 </div>
2631 <?php
2632 }
2633
2634 /**
2635 * Field: Create Page.
2636 */
2637 public static function create_page( $settings, $value, $module_id = '' ) {
2638 $page_id = get_option( $settings['option_name'] );
2639
2640 echo '<div class="merchant-create-page-control">';
2641
2642 if ( $page_id && post_exists( get_the_title( $page_id ) ) && 'publish' === get_post_status( $page_id ) ) {
2643 echo wp_kses_post(
2644 sprintf( /* translators: 1: link to edit page */
2645 __( '<p class="merchant-module-page-setting-field-desc mrc-mt-0">Your page is created!</p><p class="merchant-module-page-setting-field-desc">Click <a href="%1$s" target="_blank">here</a> if you want to edit the page.</p><p class="merchant-module-page-setting-field-desc mrc-mb-0">To display the page in your theme header area, assign the page to the primary menu by clicking <a href="%2$s" target="_blank">here</a></p>',
2646 'merchant' ),
2647 get_admin_url() . 'post.php?post=' . $page_id . '&action=edit',
2648 get_admin_url() . 'nav-menus.php'
2649 )
2650 );
2651 } else {
2652 echo '<div class="merchant-create-page-control-create-message">';
2653 echo wp_kses_post(
2654 '<p class="merchant-module-page-setting-field-desc mrc-mt-0">'.
2655 sprintf( /* translators: 1: page name */
2656 __( 'It looks like you haven\'t created a <strong>%1$s</strong> page yet. Click the below button to create the page.',
2657 'merchant' ),
2658 $settings['page_title']
2659 ). '</p>'
2660 );
2661 echo '</div>';
2662 echo '<div class="merchant-create-page-control-success-message" style="display: none;">';
2663 echo wp_kses_post(
2664 sprintf( /* translators: 1: link to edit page */
2665 __( '<p class="merchant-module-page-setting-field-desc">Page created with success!</p><p class="merchant-module-page-setting-field-desc">Click <a href="%1$s" target="_blank">here</a> if you want to edit the page.</p><p class="merchant-module-page-setting-field-desc mrc-mb-0">To display the page in your theme header area, assign the page to the primary menu by clicking <a href="%2$s" target="_blank">here</a></p>',
2666 'merchant' ),
2667 get_admin_url() . 'post.php?post=&action=edit',
2668 get_admin_url() . 'nav-menus.php'
2669 )
2670 );
2671 echo '</div>';
2672 echo wp_kses_post(
2673 sprintf( /* translators: 1: page title, 2: page meta key, 3: page meta value, 4: option name, 5: nonce, 6: loading text, 7: success text */
2674 __( '<a href="#" class="merchant-create-page-control-button button-tertiary" data-page-title="%2$s" data-page-meta-key="%3$s" data-page-meta-value="%4$s" data-option-name="%5$s" data-nonce="%6$s" data-creating-text="%7$s" data-created-text="%8$s">%1$s</a>',
2675 'merchant' ),
2676 __( 'Create Page', 'merchant' ),
2677 $settings['page_title'],
2678 $settings['page_meta_key'],
2679 $settings['page_meta_value'],
2680 $settings['option_name'],
2681 wp_create_nonce( 'customize-create-page-control-nonce' ),
2682 __( 'Creating...', 'merchant' ),
2683 __( 'Created!', 'merchant' )
2684 )
2685 );
2686 }
2687
2688 echo '</div>';
2689 }
2690
2691 public static function dimensions( $settings, $value, $module_id = '' ) {
2692 $settings = wp_parse_args( $settings, array(
2693 'units' => array(
2694 'px' => 'px',
2695 'rem' => 'rem',
2696 'em' => 'em',
2697 'vw' => 'vw',
2698 'vh' => 'vh',
2699 '%' => '%',
2700 ),
2701 'dimensions' => array(
2702 'top',
2703 'right',
2704 'bottom',
2705 'left',
2706 ),
2707 ) );
2708 $default_value = array(
2709 'unit' => reset( $settings['units'] ),
2710 );
2711 foreach ( $settings['dimensions'] as $dimension ) {
2712 $default_value[ $dimension ] = 0;
2713 }
2714 $value = wp_parse_args( $value, $default_value );
2715 ?>
2716 <div class="merchant-module-page-settings-unit">
2717 <select name="merchant[<?php
2718 echo esc_attr( $settings['id'] ); ?>][unit]">
2719 <?php
2720 foreach ( $settings['units'] as $key => $option ) : ?>
2721 <option value="<?php
2722 echo esc_attr( $key ); ?>" <?php
2723 selected( $value['unit'], $key, true ); ?>><?php
2724 echo esc_html( $option ); ?></option>
2725 <?php
2726 endforeach; ?>
2727 </select>
2728 </div>
2729 <div class="merchant-module-page-settings-dimensions">
2730 <?php
2731 foreach ( $settings['dimensions'] as $dimension ) : ?>
2732 <div>
2733 <input id="merchant-<?php
2734 echo esc_attr( $settings['id'] ); ?>-<?php
2735 echo esc_attr( $dimension ) ?>"
2736 type="number"
2737 name="merchant[<?php
2738 echo esc_attr( $settings['id'] ); ?>][<?php
2739 echo esc_attr( $dimension ) ?>]"
2740 value="<?php
2741 echo esc_attr( $value[ $dimension ] ); ?>"/>
2742 <label for="merchant-<?php
2743 echo esc_attr( $settings['id'] ); ?>-<?php
2744 echo esc_attr( $dimension ) ?>">
2745 <?php
2746 echo esc_html( ucfirst( $dimension ) ); ?>
2747 </label>
2748 </div>
2749 <?php
2750 endforeach; ?>
2751 </div>
2752 <?php
2753 }
2754
2755 public static function responsive_dimensions( $settings, $value, $module_id = '' ) {
2756 $settings = wp_parse_args( $settings, array(
2757 'units' => array(
2758 'px' => 'px',
2759 'rem' => 'rem',
2760 'em' => 'em',
2761 'vw' => 'vw',
2762 'vh' => 'vh',
2763 '%' => '%',
2764 ),
2765 'dimensions' => array(
2766 'top',
2767 'right',
2768 'bottom',
2769 'left',
2770 ),
2771 'devices' => array(
2772 'desktop' => 'dashicons-desktop',
2773 'tablet' => 'dashicons-tablet',
2774 'mobile' => 'dashicons-smartphone',
2775 ),
2776 ) );
2777 $default_values = array();
2778 foreach ( $settings['devices'] as $device => $icon ) {
2779 $default_values[ $device ]['unit'] = reset( $settings['units'] );
2780
2781 foreach ( $settings['dimensions'] as $dimension ) {
2782 $default_values[ $device ][ $dimension ] = 0;
2783 }
2784 }
2785 $value = wp_parse_args( $value, $default_values );
2786 ?>
2787 <div class="merchant-module-page-settings-responsive">
2788 <ul class="merchant-module-page-settings-devices">
2789 <?php
2790 foreach ( $settings['devices'] as $device => $icon ) : ?>
2791 <li class="<?php
2792 echo esc_attr( $device ); ?>">
2793 <button type="button" class="preview-<?php
2794 echo esc_attr( $device ); ?> <?php
2795 echo $device === key( $settings['devices'] ) ? 'active' : '' ?>"
2796 data-device="<?php
2797 echo esc_attr( $device ); ?>">
2798 <i class="dashicons <?php
2799 echo esc_attr( $icon ); ?>"></i>
2800 </button>
2801 </li>
2802 <?php
2803 endforeach; ?>
2804 </ul>
2805 <?php
2806 foreach ( $settings['devices'] as $device => $icon ) : ?>
2807 <div class="merchant-module-page-settings-device-container <?php
2808 echo $device === key( $settings['devices'] ) ? 'active' : '' ?>" data-device="<?php
2809 echo esc_attr( $device ); ?>">
2810 <div class="merchant-module-page-settings-unit">
2811 <select name="merchant[<?php
2812 echo esc_attr( $settings['id'] ); ?>][<?php
2813 echo esc_attr( $device ) ?>][unit]">
2814 <?php
2815 foreach ( $settings['units'] as $key => $option ) : ?>
2816 <option value="<?php
2817 echo esc_attr( $key ); ?>" <?php
2818 selected( $value[ esc_attr( $device ) ]['unit'], $key, true ); ?>><?php
2819 echo esc_html( $option ); ?></option>
2820 <?php
2821 endforeach; ?>
2822 </select>
2823 </div>
2824 <div class="merchant-module-page-settings-dimensions">
2825 <?php
2826 foreach ( $settings['dimensions'] as $dimension ) : ?>
2827 <div>
2828 <input id="merchant-<?php
2829 echo esc_attr( $settings['id'] ); ?>-<?php
2830 echo esc_attr( $device ) ?>-<?php
2831 echo esc_attr( $dimension ) ?>"
2832 type="number"
2833 name="merchant[<?php
2834 echo esc_attr( $settings['id'] ); ?>][<?php
2835 echo esc_attr( $device ) ?>][<?php
2836 echo esc_attr( $dimension ) ?>]"
2837 value="<?php
2838 echo esc_attr( $value[ $device ][ $dimension ] ); ?>"/>
2839 <label for="merchant-<?php
2840 echo esc_attr( $settings['id'] ); ?>-<?php
2841 echo esc_attr( $device ) ?>-<?php
2842 echo esc_attr( $dimension ) ?>">
2843 <?php
2844 echo esc_html( ucfirst( $dimension ) ); ?>
2845 </label>
2846 </div>
2847 <?php
2848 endforeach; ?>
2849 </div>
2850 </div>
2851 <?php
2852 endforeach; ?>
2853 </div>
2854
2855 <?php
2856 }
2857
2858 /**
2859 * Field: Custom callback.
2860 */
2861 public static function custom_callback( $settings, $value, $module_id = '' ) {
2862 if ( ! empty( $settings['class_name'] ) && ! empty( $settings['callback_name'] ) ) {
2863 return call_user_func( array( $settings['class_name'], $settings['callback_name'] ), $settings, $value );
2864 }
2865
2866 if ( ! empty( $settings['callback_name'] ) ) {
2867 return call_user_func( $settings['callback_name'], $settings, $value );
2868 }
2869
2870 return false;
2871 }
2872
2873 private static function print_flexible_layout_actions( $settings, $layout_type ) {
2874 $has_duplicate = ! empty( $settings['duplicate'] );
2875 $has_accordion = ! empty( $settings['accordion'] );
2876 ?>
2877 <div class="layout-actions<?php echo $has_accordion ? esc_attr( ' layout-actions-has_accordion' ) : ' layout-actions-no_accordion'; ?>">
2878 <a href="#" class="layout-actions__toggle">
2879 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="4" viewBox="0 0 14 4" fill="none">
2880 <path d="M1.5 0.763184C2.32843 0.763183 3 1.43476 3 2.26318C3 3.09161 2.32843 3.76318 1.5 3.76318C0.671573 3.76318 -2.93554e-08 3.09161 -6.55671e-08 2.26318C-1.01779e-07 1.43476 0.671573 0.763184 1.5 0.763184Z" fill="#4A4A4A"/>
2881 <path d="M7 0.763183C7.82843 0.763183 8.5 1.43476 8.5 2.26318C8.5 3.09161 7.82843 3.76318 7 3.76318C6.17157 3.76318 5.5 3.09161 5.5 2.26318C5.5 1.43476 6.17157 0.763183 7 0.763183Z" fill="#4A4A4A"/>
2882 <path d="M12.5 0.763183C13.3284 0.763183 14 1.43476 14 2.26318C14 3.09161 13.3284 3.76318 12.5 3.76318C11.6716 3.76318 11 3.09161 11 2.26318C11 1.43476 11.6716 0.763183 12.5 0.763183Z" fill="#4A4A4A"/>
2883 </svg>
2884 </a>
2885 <div class="layout-actions__inner" style="display: none;">
2886 <?php if ( $has_duplicate ) : ?>
2887 <a
2888 href="#"
2889 class="customize-control-flexible-content-duplicate"
2890 data-id="<?php echo esc_attr( $settings['id'] ); ?>"
2891 data-layout="<?php echo esc_attr( $layout_type ); ?>"
2892 title="<?php echo esc_attr__( 'Duplicate', 'merchant' ); ?>">
2893 <svg xmlns="http://www.w3.org/2000/svg" width="19" height="19" viewBox="0 0 19 22" fill="none">
2894 <path fill-rule="evenodd" clip-rule="evenodd" d="M2 1.76318H13C13.2761 1.76318 13.5 1.98704 13.5 2.26318V13.2632C13.5 13.5393 13.2761 13.7632 13 13.7632H2C1.72386 13.7632 1.5 13.5393 1.5 13.2632V2.26318C1.5 1.98704 1.72386 1.76318 2 1.76318ZM0 2.26318C0 1.15861 0.895431 0.263184 2 0.263184H13C14.1046 0.263184 15 1.15861 15 2.26318V13.2632C15 14.3678 14.1046 15.2632 13 15.2632H2C0.89543 15.2632 0 14.3678 0 13.2632V2.26318ZM17 5.26318V16.0132C17 16.7035 16.4404 17.2632 15.75 17.2632H3V18.7632H15.75C17.2688 18.7632 18.5 17.532 18.5 16.0132V5.26318H17Z" fill="#757575"/>
2895 </svg>
2896 <span><?php echo esc_html__( 'Duplicate', 'merchant' ); ?></span>
2897 </a>
2898 <?php endif; ?>
2899
2900 <a class="customize-control-flexible-content-delete" href="#">
2901 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="17" viewBox="0 0 14 17" fill="none">
2902 <path fill-rule="evenodd" clip-rule="evenodd" d="M7 0.263184C5.18578 0.263184 3.67247 1.55151 3.32502 3.26318H0V4.76318H1.2699L2.08782 13.7602C2.21659 15.1767 3.40421 16.2613 4.82652 16.2613H9.17366C10.596 16.2613 11.7836 15.1767 11.9124 13.7602L12.7303 4.76318H14V3.26318H10.675C10.3275 1.5515 8.81422 0.263184 7 0.263184ZM7 1.76318C6.02034 1.76318 5.18691 2.38929 4.87803 3.26318H9.12197C8.81309 2.38929 7.97966 1.76318 7 1.76318ZM11.2241 4.76318H2.77609L3.58166 13.6244C3.64019 14.2683 4.18002 14.7613 4.82652 14.7613H9.17366C9.82017 14.7613 10.36 14.2683 10.4185 13.6244L11.2241 4.76318Z" fill="#757575"/>
2903 </svg>
2904 <span><?php echo esc_html__( 'Delete', 'merchant' ); ?></span>
2905 </a>
2906 </div>
2907 </div>
2908 <?php
2909 }
2910
2911 /**
2912 * Get category choices for select2
2913 *
2914 * @return array
2915 */
2916 public static function get_category_select2_choices() {
2917 $choices = array();
2918 $categories = merchant_get_product_categories();
2919
2920 if ( is_array( $categories ) && ! empty( $categories ) ) {
2921 $choices = self::build_category_select2_choices( $categories );
2922 }
2923
2924 return $choices;
2925 }
2926
2927 /**
2928 * Build Select2 choices for hierarchical categories.
2929 *
2930 * @param $categories
2931 * @param $level
2932 *
2933 * @return array
2934 */
2935 private static function build_category_select2_choices( $categories, $level = 0 ) {
2936 $choices = array();
2937
2938 foreach ( $categories as $cat ) {
2939 $indent = str_repeat( '&nbsp;', $level * 4 ); // Use non-breaking spaces for indentation
2940
2941 $choices[] = array(
2942 'id' => esc_attr( $cat['slug'] ?? '' ),
2943 'text' => esc_html( $indent . ( $cat['name'] ?? '' ) ),
2944 );
2945
2946 if ( ! empty( $cat['children'] ) ) {
2947 $choices = array_merge( $choices, self::build_category_select2_choices( $cat['children'], $level + 1 ) );
2948 }
2949 }
2950
2951 return $choices;
2952 }
2953
2954 /**
2955 * Get Tag choices for select2
2956 *
2957 * @return array
2958 */
2959 public static function get_tag_select2_choices() {
2960 $choices = array();
2961 $tags = merchant_get_product_tags();
2962
2963 if ( is_array( $tags ) && ! empty( $tags ) ) {
2964 foreach ( $tags as $slug => $name ) {
2965 $choices[] = array(
2966 'id' => esc_attr( $slug ),
2967 'text' => esc_html( $name ),
2968 );
2969 }
2970 }
2971
2972 return $choices;
2973 }
2974
2975 /**
2976 * Get User Roles choices for select2
2977 *
2978 * @return array
2979 */
2980 public static function get_user_roles_select2_choices() {
2981 $choices = array();
2982 $user_roles = get_editable_roles();
2983
2984 if ( ! empty( $user_roles ) ) {
2985 foreach ( $user_roles as $role_id => $role_data ) {
2986 $choices[] = array(
2987 'id' => $role_id,
2988 'text' => $role_data['name'],
2989 );
2990 }
2991 }
2992
2993 return $choices;
2994 }
2995
2996 /**
2997 * Get Customers choices for select2.
2998 *
2999 * @return array
3000 */
3001 public static function get_customers_select2_choices() {
3002 $cache_key = 'customers_select2_choices';
3003 $choices = get_transient( $cache_key );
3004
3005 if ( ! empty( $choices ) && is_array( $choices ) ) {
3006 return $choices;
3007 }
3008
3009 // Get users with the 'customer' role
3010 $customer_users = get_users(
3011 array(
3012 'role' => 'customer',
3013 'fields' => array( 'ID', 'display_name' ),
3014 )
3015 );
3016
3017 $choices = array();
3018 if ( ! empty( $customer_users ) ) {
3019 foreach ( $customer_users as $user ) {
3020 $choices[] = array(
3021 'id' => $user->ID,
3022 'text' => $user->display_name,
3023 );
3024 }
3025 }
3026
3027 // Cache the choices with no expiration. Will be cleared using `clear_customer_choices_cache`
3028 set_transient( $cache_key, $choices );
3029
3030 return $choices;
3031 }
3032
3033 /**
3034 * Clear customers cache when a customer is created/deleted/updated.
3035 *
3036 * @param $user_id
3037 * @param $user
3038 *
3039 * @return void
3040 */
3041 public function clear_customer_choices_cache( $user_id, $user ) {
3042 $user_roles = $user->roles ?? array();
3043 if ( in_array( 'customer', $user_roles, true ) ) {
3044 delete_transient( 'customers_select2_choices' );
3045 }
3046 }
3047
3048 /**
3049 * Delete module data.
3050 *
3051 * @return void
3052 */
3053 public function delete_module_data() {
3054 $options = get_option( 'merchant', array() );
3055
3056 // Code Snippets module was removed, so delete data from DB as well.
3057 if ( isset( $options['code-snippets'] ) ) {
3058 unset( $options['code-snippets'] );
3059 update_option( 'merchant', $options );
3060 }
3061 }
3062 }
3063
3064 Merchant_Admin_Options::instance();
3065 }
3066