PluginProbe
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant / 2.3.2
Product Labels, Quick View, Buy Now, Pre-Orders, Frequently Bought Together & More for WooCommerce – Merchant v2.3.2
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
← All changes | admin/classes/class-merchant-admin-options.php +254 -2866 1.11.02.3.2 View file →
@@ -1,7 +1,15 @@
1 1 <?php
2 2 /**
3 - * Merchant_Admin_Options Class.
3 + * Merchant Admin Options.
4 + *
5 + * Thin orchestrator that manages module settings. Delegates rendering
6 + * to {@see Merchant_Settings_Renderer}, saving to {@see Merchant_Settings_Saver},
7 + * asset enqueueing to {@see Merchant_Admin_Assets}, and Select2 data
8 + * to {@see Merchant_Select2_Choices}.
9 + *
10 + * @package Merchant
11 + * @since 1.0
4 12 */
5 13
6 14 if ( ! defined( 'ABSPATH' ) ) {
7 15 exit; // Exit if accessed directly
@@ -7,17 +15,36 @@
7 15 exit; // Exit if accessed directly
8 16 }
9 17
10 18 if ( ! class_exists( 'Merchant_Admin_Options' ) ) {
19 + /**
20 + * Merchant_Admin_Options
21 + *
22 + * Orchestrates the Merchant admin settings panel. Core responsibilities:
23 + * - CRUD operations for module settings (`get`, `set`, `delete`, `get_all`)
24 + * - Wiring admin hooks (assets, cache, cleanup, Pro activation)
25 + * - Backward-compatible facades that delegate to extracted classes
26 + *
27 + * This class is a singleton; use {@see Merchant_Admin_Options::instance()} to retrieve it.
28 + *
29 + * @since 1.0
30 + */
11 31 class Merchant_Admin_Options {
12 32
13 33 /**
14 34 * The single class instance.
35 + *
36 + * @since 1.0
37 + * @var Merchant_Admin_Options|null
15 38 */
16 39 private static $instance = null;
17 40
18 41 /**
19 - * Instance.
42 + * Get the singleton instance.
43 + *
44 + * @since 1.0
45 + *
46 + * @return Merchant_Admin_Options
20 47 */
21 48 public static function instance() {
22 49 if ( is_null( self::$instance ) ) {
23 50 self::$instance = new self();
@@ -27,228 +54,44 @@
27 54 }
28 55
29 56 /**
30 57 * Constructor.
58 + *
59 + * Registers admin hooks for script enqueueing, cache clearance,
60 + * legacy data cleanup, and Pro plugin activation.
61 + *
62 + * @since 1.0
31 63 */
32 64 public function __construct() {
33 - // Enqueue hooks.
34 - add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
65 + $assets = new Merchant_Admin_Assets();
66 + $choices = new Merchant_Select2_Choices();
35 67
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' ) );
68 + add_action( 'admin_enqueue_scripts', array( $assets, 'enqueue_scripts' ) );
69 + add_action( 'clean_user_cache', array( $choices, 'clear_customer_choices_cache' ), 10, 2 );
43 70
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' ) );
71 + add_action( 'admin_init', array( $this, 'delete_module_data' ) );
72 + add_action( 'admin_init', array( $this, 'activate_pro_plugin' ) );
49 73 }
50 74
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' );
75 + // ──────────────────────────────────────────────────────
76 + // CRUD — core orchestrator responsibility.
77 + // ──────────────────────────────────────────────────────
61 78
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 79 /**
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.
80 + * Get a single module setting value.
186 81 *
82 + * Retrieves the value of a specific setting for a given module
83 + * from the `merchant` option. Falls back to `$default_val` if
84 + * the setting does not exist.
85 + *
86 + * @since 1.0
87 + *
88 + * @param string $module The module ID (e.g. 'buy-x-get-y').
89 + * @param string $setting The setting key within the module.
90 + * @param mixed $default_val The default value if the setting is not found.
91 + *
92 + * @return mixed The setting value, filtered via `merchant_get_option`.
187 93 */
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 94 public static function get( $module, $setting, $default_val ) {
252 95 $options = get_option( 'merchant', array() );
253 96
254 97 $value = $default_val;
@@ -264,17 +107,25 @@
264 107 * @param mixed $value Option value.
265 108 * @param string $module Module ID.
266 109 * @param string $setting Setting ID.
267 110 * @param mixed $default_val Default value.
268 - *
269 - * @since 1.9.3
111 + *
112 + * @since 1.9.3
270 113 */
271 114 return apply_filters( 'merchant_get_option', $value, $module, $setting, $default_val );
272 115 }
273 116
274 - /**
275 - * Set option.
276 - */
117 + /**
118 + * Set a single module setting value.
119 + *
120 + * @since 1.0
121 + *
122 + * @param string $module The module ID.
123 + * @param string $setting The setting key.
124 + * @param mixed $value The value to store.
125 + *
126 + * @return void
127 + */
277 128 public static function set( $module, $setting, $value ) {
278 129 $options = get_option( 'merchant', array() );
279 130 $options[ $module ][ $setting ] = $value;
280 131 update_option( 'merchant', $options );
@@ -279,19 +130,32 @@
279 130 $options[ $module ][ $setting ] = $value;
280 131 update_option( 'merchant', $options );
281 132 }
282 133
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 - }
134 + /**
135 + * Delete a single module setting.
136 + *
137 + * @since 1.0
138 + *
139 + * @param string $module The module ID.
140 + * @param string $setting The setting key to remove.
141 + *
142 + * @return void
143 + */
144 + public static function delete( $module, $setting ) {
145 + $options = get_option( 'merchant', array() );
146 + unset( $options[ $module ][ $setting ] );
147 + update_option( 'merchant', $options );
148 + }
291 149
292 150 /**
293 - * Get all options.
151 + * Get all settings for a module.
152 + *
153 + * @since 1.0
154 + *
155 + * @param string $module The module ID.
156 + *
157 + * @return array All settings for the module.
294 158 */
295 159 public static function get_all( $module ) {
296 160 $options = get_option( 'merchant', array() );
297 161 $value = array();
@@ -302,2764 +166,288 @@
302 166
303 167 return $value;
304 168 }
305 169
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
170 + // ──────────────────────────────────────────────────────
171 + // Delegation facades — backward compatibility.
172 + // ──────────────────────────────────────────────────────
311 173
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 174 /**
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
175 + * Create and render a module settings panel.
709 176 *
710 - * @param array $settings
711 - * @param mixed $value
177 + * @since 1.0
178 + * @see Merchant_Settings_Renderer::create()
712 179 *
180 + * @param array $settings Module settings configuration.
181 + *
713 182 * @return void
714 183 */
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 - );
184 + public static function create( $settings ) {
185 + Merchant_Settings_Renderer::create( $settings );
731 186 }
732 187
733 188 /**
734 - * Modified field.
189 + * Render a single field.
735 190 *
736 - * @param $settings
737 - * @param $value
738 - * @param $search
739 - * @param $replace
191 + * @since 1.0
192 + * @see Merchant_Settings_Renderer::field()
740 193 *
194 + * @param array $settings The field configuration array.
195 + * @param mixed $value The current saved value.
196 + * @param string $module_id The module ID.
197 + *
741 198 * @return void
742 199 */
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' ) ) );
200 + public static function field( $settings, $value, $module_id = '' ) {
201 + Merchant_Settings_Renderer::field( $settings, $value, $module_id );
760 202 }
761 203
762 204 /**
763 - * Update attributes for specific tags in the field HTML.
205 + * Render a disabled (pro-gated) field.
764 206 *
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.
207 + * @since 1.0
208 + * @see Merchant_Settings_Renderer::disabled_field()
768 209 *
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
210 + * @param array $settings Field settings.
211 + * @param mixed $value Field value.
212 + * @param string $module_id Module ID.
1002 213 *
1003 214 * @return void
1004 215 */
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
216 + public static function disabled_field( $settings, $value, $module_id = '' ) {
217 + Merchant_Settings_Renderer::disabled_field( $settings, $value, $module_id );
1046 218 }
1047 219
1048 220 /**
1049 - * Get search results for the reviews selector.
1050 - *
221 + * Save module options from a form submission.
222 + *
223 + * @since 1.0
224 + * @see Merchant_Settings_Saver::save_options()
225 + *
226 + * @param array $settings Module settings configuration.
227 + *
1051 228 * @return void
1052 229 */
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 ) );
230 + public static function save_options( $settings ) {
231 + Merchant_Settings_Saver::save_options( $settings );
1063 232 }
1064 233
1065 234 /**
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.
235 + * Sanitize options.
1087 236 *
1088 - * @param $term string Search term.
1089 - * @param $exclude array IDs of products to exclude from the search results.
237 + * @since 1.9.3
238 + * @see Merchant_Settings_Saver::sanitize()
1090 239 *
1091 - * @return string
240 + * @param array $field The field configuration.
241 + * @param mixed $value The raw submitted value.
242 + *
243 + * @return mixed The sanitized value.
1092 244 */
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;
245 + public static function sanitize( $field, $value ) {
246 + return Merchant_Settings_Saver::sanitize( $field, $value );
1147 247 }
1148 248
1149 249 /**
1150 - * Get rendered product reviews.
1151 - *
1152 - * @param $product WC_Product Product object.
1153 - * @param $offset int Offset for the reviews query.
250 + * Get product category choices for Select2.
1154 251 *
1155 - * @return string[] Array with 'reviews' and 'load_more' keys.
252 + * @since 1.9.3
253 + * @see Merchant_Select2_Choices::get_category_select2_choices()
254 + *
255 + * @return array Formatted category choices.
1156 256 */
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 - );
257 + public static function get_category_select2_choices() {
258 + return Merchant_Select2_Choices::get_category_select2_choices();
1203 259 }
1204 260
1205 261 /**
1206 - * Get a single review.
1207 - *
1208 - * @param $review_id int Review ID.
262 + * Get product tag choices for Select2.
1209 263 *
1210 - * @return string Review HTML.
264 + * @since 1.9.3
265 + * @see Merchant_Select2_Choices::get_tag_select2_choices()
266 + *
267 + * @return array Formatted tag choices.
1211 268 */
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 - }
269 + public static function get_tag_select2_choices() {
270 + return Merchant_Select2_Choices::get_tag_select2_choices();
271 + }
1251 272
1252 273 /**
1253 - * Get the main photo for a review.
274 + * Get product brand choices for Select2.
1254 275 *
1255 - * @param $comment_id int Review ID.
276 + * @since 1.9.3
277 + * @see Merchant_Select2_Choices::get_brand_select2_choices()
1256 278 *
1257 - * @return string Review main photo HTML.
279 + * @return array Formatted brand choices.
1258 280 */
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;
281 + public static function get_brand_select2_choices() {
282 + return Merchant_Select2_Choices::get_brand_select2_choices();
1273 283 }
1274 284
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 285 /**
1308 - * Field: Radio Alt
286 + * Get user role choices for Select2.
287 + *
288 + * @since 1.9.3
289 + * @see Merchant_Select2_Choices::get_user_roles_select2_choices()
290 + *
291 + * @return array Formatted role choices.
1309 292 */
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
293 + public static function get_user_roles_select2_choices() {
294 + return Merchant_Select2_Choices::get_user_roles_select2_choices();
1331 295 }
1332 296
1333 297 /**
1334 - * Field: Choices
298 + * Get weekday choices, Monday first, keyed by day name.
299 + *
300 + * @since 2.3.2
301 + * @see Merchant_Select2_Choices::get_weekday_choices()
302 + *
303 + * @return array Day name => translated label.
1335 304 */
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
305 + public static function get_weekday_choices() {
306 + return Merchant_Select2_Choices::get_weekday_choices();
1391 307 }
1392 308
1393 309 /**
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
310 + * Get shipping zone choices for Select2.
1460 311 *
1461 - * @param $settings
1462 - * @param $value
312 + * @since 2.3.2
313 + * @see Merchant_Select2_Choices::get_shipping_zone_select2_choices()
1463 314 *
1464 - * @return void
315 + * @return array Formatted zone choices.
1465 316 */
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
317 + public static function get_shipping_zone_select2_choices() {
318 + return Merchant_Select2_Choices::get_shipping_zone_select2_choices();
1529 319 }
1530 320
1531 321 /**
1532 - * Field: Info Block
322 + * Get registered shipping method choices for Select2.
1533 323 *
1534 - * @return void
324 + * @since 2.3.2
325 + * @see Merchant_Select2_Choices::get_shipping_method_select2_choices()
326 + *
327 + * @return array Formatted method choices.
1535 328 */
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
329 + public static function get_shipping_method_select2_choices() {
330 + return Merchant_Select2_Choices::get_shipping_method_select2_choices();
1547 331 }
1548 332
1549 333 /**
1550 - * Field: Products Selector
334 + * Get enabled payment gateway choices for Select2.
1551 335 *
1552 - * @param $settings
1553 - * @param $value
336 + * @since 2.3.2
337 + * @see Merchant_Select2_Choices::get_payment_gateway_select2_choices()
1554 338 *
1555 - * @return void
339 + * @return array Formatted gateway choices.
1556 340 */
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
341 + public static function get_payment_gateway_select2_choices() {
342 + return Merchant_Select2_Choices::get_payment_gateway_select2_choices();
1608 343 }
1609 344
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 345 /**
1823 - * Check if all variation attributes are set.
346 + * Get customer user choices for Select2.
1824 347 *
1825 - * @param $variation WC_Product_Variation variation product object
348 + * @since 1.9.3
349 + * @see Merchant_Select2_Choices::get_customers_select2_choices()
1826 350 *
1827 - * @return bool
351 + * @return array Formatted customer choices.
1828 352 */
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
353 + public static function get_customers_select2_choices() {
354 + return Merchant_Select2_Choices::get_customers_select2_choices();
1846 355 }
1847 356
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 - );
357 + // ──────────────────────────────────────────────────────
358 + // Admin hooks — small one-off actions.
359 + // ──────────────────────────────────────────────────────
1855 360
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 361 /**
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.
362 + * Delete stale module data from the database.
2264 363 *
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.
364 + * Cleans up data for removed modules (e.g., the deprecated
365 + * `code-snippets` module). Hooked to `admin_init`.
2293 366 *
2294 - * @param array $settings
2295 - * @param mixed $value
367 + * @since 1.9.3
2296 368 *
2297 369 * @return void
2298 370 */
2299 - public static function flexible_content( $settings, $value, $module_id = '' ) {
2300 - $values = ( is_array( $value ) && ! empty( $value ) ) ? $value : array();
2301 - $empty = empty( $values ) ? 'empty' : '';
371 + public function delete_module_data() {
372 + $options = get_option( 'merchant', array() );
2302 373
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';
374 + if ( isset( $options['code-snippets'] ) ) {
375 + unset( $options['code-snippets'] );
376 + update_option( 'merchant', $options );
2317 377 }
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 378 }
2528 379
2529 380 /**
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.
381 + * Activate the Merchant Pro plugin.
2537 382 *
383 + * Handles the `merchant_activate_pro` admin action: verifies
384 + * nonce and capabilities, activates the Pro plugin, and
385 + * redirects back to the originating Merchant page.
386 + *
387 + * @since 1.0
388 + *
2538 389 * @return void
2539 390 */
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 - );
391 + public function activate_pro_plugin() {
392 + if ( ! isset( $_GET['action'] ) || 'merchant_activate_pro' !== $_GET['action'] ) {
393 + return;
2686 394 }
2687 395
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;
396 + if (
397 + ! isset( $_GET['nonce'] )
398 + || ! wp_verify_nonce(
399 + sanitize_text_field( wp_unslash( $_GET['nonce'] ) ),
400 + 'merchant_activate_pro'
401 + )
402 + ) {
403 + return;
2713 404 }
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 405
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 - }
406 + if ( ! current_user_can( 'activate_plugins' ) ) {
407 + return;
2784 408 }
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 409
2855 - <?php
2856 - }
410 + $result = activate_plugin( 'merchant-pro/merchant-pro.php' );
2857 411
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 );
412 + if ( is_wp_error( $result ) ) {
413 + wp_die( esc_html( $result->get_error_message() ) );
2864 414 }
2865 415
2866 - if ( ! empty( $settings['callback_name'] ) ) {
2867 - return call_user_func( $settings['callback_name'], $settings, $value );
2868 - }
2869 -
2870 - return false;
416 + wp_safe_redirect( $this->get_safe_merchant_redirect_url() );
417 + exit;
2871 418 }
2872 419
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 420 /**
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
421 + * Get a safe redirect URL pointing to a Merchant admin page.
2932 422 *
2933 - * @return array
423 + * Validates the HTTP referer is a Merchant page. Falls back
424 + * to the main Merchant dashboard if the referer is invalid
425 + * or points to a non-Merchant page.
426 + *
427 + * @since 1.9.3
428 + *
429 + * @return string Safe redirect URL.
2934 430 */
2935 - private static function build_category_select2_choices( $categories, $level = 0 ) {
2936 - $choices = array();
431 + private function get_safe_merchant_redirect_url() {
432 + $fallback_url = admin_url( 'admin.php?page=merchant' );
433 + $referer = wp_get_referer();
434 + $redirect_to = wp_validate_redirect( $referer, $fallback_url );
2937 435
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 - }
436 + if ( $redirect_to === $fallback_url ) {
437 + return $fallback_url;
2949 438 }
2950 439
2951 - return $choices;
2952 - }
440 + $parsed_url = wp_parse_url( $redirect_to );
441 + $query_params = array();
442 + parse_str( $parsed_url['query'] ?? '', $query_params );
2953 443
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 - }
444 + if ( empty( $query_params['page'] ) || strpos( $query_params['page'], 'merchant' ) !== 0 ) {
445 + return $fallback_url;
2970 446 }
2971 447
2972 - return $choices;
448 + return $redirect_to;
2973 449 }
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 450 }
3063 451
3064 452 Merchant_Admin_Options::instance();
3065 453 }