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 +262 -2484 1.10.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,221 +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' ) );
68 + add_action( 'admin_enqueue_scripts', array( $assets, 'enqueue_scripts' ) );
69 + add_action( 'clean_user_cache', array( $choices, 'clear_customer_choices_cache' ), 10, 2 );
40 70
41 - add_action( 'clean_user_cache', array( $this, 'clear_customer_choices_cache' ), 10, 2 );
71 + add_action( 'admin_init', array( $this, 'delete_module_data' ) );
72 + add_action( 'admin_init', array( $this, 'activate_pro_plugin' ) );
42 73 }
43 74
44 - /**
45 - * Enqueue scripts.
46 - */
47 - public function enqueue_scripts() {
48 - if (
49 - isset( $_GET['page'] ) && 'merchant' === $_GET['page'] // phpcs:ignore WordPress.Security.NonceVerification.Recommended
50 - && isset( $_GET['module'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
51 - ) {
52 - wp_enqueue_script( 'merchant-select2', MERCHANT_URI . 'assets/vendor/select2/select2.full.min.js', array( 'jquery' ), '4.0.13', true );
53 - 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 + // ──────────────────────────────────────────────────────
54 78
55 - wp_localize_script( 'merchant-select2', 'merchant_admin_options', array(
56 - 'ajaxurl' => admin_url( 'admin-ajax.php' ),
57 - 'ajaxnonce' => wp_create_nonce( 'merchant_admin_options' ),
58 - 'product_delete_confirmation_message' => esc_html__( 'Are you sure you want to remove this product?', 'merchant' ),
59 - ) );
60 -
61 - wp_enqueue_style('date-picker', MERCHANT_URI . 'assets/vendor/air-datepicker/air-datepicker.css', array(), MERCHANT_VERSION, 'all' );
62 - wp_enqueue_script('date-picker', MERCHANT_URI . 'assets/vendor/air-datepicker/air-datepicker.js', array( 'jquery' ), MERCHANT_VERSION, true );
63 - wp_localize_script( 'date-picker', 'merchant_datepicker_locale', array(
64 - wp_json_encode(
65 - array(
66 - 'days' => array(
67 - esc_html__( 'Sunday', 'merchant' ),
68 - esc_html__( 'Monday', 'merchant' ),
69 - esc_html__( 'Tuesday', 'merchant' ),
70 - esc_html__( 'Wednesday', 'merchant' ),
71 - esc_html__( 'Thursday', 'merchant' ),
72 - esc_html__( 'Friday', 'merchant' ),
73 - esc_html__( 'Saturday', 'merchant' ),
74 - ),
75 - 'daysShort' => array(
76 - esc_html__( 'Sun', 'merchant' ),
77 - esc_html__( 'Mon', 'merchant' ),
78 - esc_html__( 'Tue', 'merchant' ),
79 - esc_html__( 'Wed', 'merchant' ),
80 - esc_html__( 'Thu', 'merchant' ),
81 - esc_html__( 'Fri', 'merchant' ),
82 - esc_html__( 'Sat', 'merchant' ),
83 - ),
84 - 'daysMin' => array(
85 - esc_html__( 'Su', 'merchant' ),
86 - esc_html__( 'Mo', 'merchant' ),
87 - esc_html__( 'Tu', 'merchant' ),
88 - esc_html__( 'We', 'merchant' ),
89 - esc_html__( 'Th', 'merchant' ),
90 - esc_html__( 'Fr', 'merchant' ),
91 - esc_html__( 'Sa', 'merchant' ),
92 - ),
93 - 'months' => array(
94 - esc_html__( 'January', 'merchant' ),
95 - esc_html__( 'February', 'merchant' ),
96 - esc_html__( 'March', 'merchant' ),
97 - esc_html__( 'April', 'merchant' ),
98 - esc_html__( 'May', 'merchant' ),
99 - esc_html__( 'June', 'merchant' ),
100 - esc_html__( 'July', 'merchant' ),
101 - esc_html__( 'August', 'merchant' ),
102 - esc_html__( 'September', 'merchant' ),
103 - esc_html__( 'October', 'merchant' ),
104 - esc_html__( 'November', 'merchant' ),
105 - esc_html__( 'December', 'merchant' ),
106 - ),
107 - 'monthsShort' => array(
108 - esc_html__( 'Jan', 'merchant' ),
109 - esc_html__( 'Feb', 'merchant' ),
110 - esc_html__( 'Mar', 'merchant' ),
111 - esc_html__( 'Apr', 'merchant' ),
112 - esc_html__( 'May', 'merchant' ),
113 - esc_html__( 'Jun', 'merchant' ),
114 - esc_html__( 'Jul', 'merchant' ),
115 - esc_html__( 'Aug', 'merchant' ),
116 - esc_html__( 'Sep', 'merchant' ),
117 - esc_html__( 'Oct', 'merchant' ),
118 - esc_html__( 'Nov', 'merchant' ),
119 - esc_html__( 'Dec', 'merchant' ),
120 - ),
121 - 'clear' => esc_html__( 'Clear', 'merchant' ),
122 - )
123 - ),
124 - ) );
125 - }
126 - }
127 -
128 79 /**
129 - * Ajax callbacks.
130 - */
131 - public function create_page_control_ajax_callback() {
132 - check_ajax_referer( 'customize-create-page-control-nonce', 'nonce' );
133 -
134 - // Check current user capabilities
135 - if ( ! current_user_can( 'manage_options' ) ) {
136 - wp_send_json_error( 'You are not allowed to do this.' );
137 - }
138 -
139 - $page_title = isset( $_POST['page_title'] ) ? sanitize_text_field( $_POST['page_title'] ) : '';
140 - $page_meta_key = isset( $_POST['page_meta_key'] ) ? sanitize_text_field( $_POST['page_meta_key'] ) : '';
141 - $page_meta_value = isset( $_POST['page_meta_value'] ) ? sanitize_text_field( $_POST['page_meta_value'] ) : '';
142 - $option_name = isset( $_POST['option_name'] ) ? sanitize_text_field( $_POST['option_name'] ) : '';
143 -
144 - $meta_input = array();
145 - if ( $page_meta_key && $page_meta_value ) {
146 - $meta_input = array(
147 - $page_meta_key => $page_meta_value,
148 - );
149 - }
150 -
151 - $postarr = array(
152 - 'post_type' => 'page',
153 - 'post_status' => 'publish',
154 - 'post_title' => $page_title,
155 - 'post_content' => '',
156 - 'meta_input' => $meta_input,
157 - );
158 -
159 - $page_id = wp_insert_post( $postarr );
160 -
161 - if ( ! is_wp_error( $page_id ) ) {
162 - if ( $option_name ) {
163 - update_option( $option_name, $page_id );
164 - }
165 -
166 - wp_send_json( array(
167 - 'status' => 'success',
168 - 'page_id' => $page_id,
169 - ) );
170 - } else {
171 - wp_send_json( array(
172 - 'status' => 'error',
173 - ) );
174 - }
175 - }
176 -
177 - /**
178 - * Select content ajax callback.
80 + * Get a single module setting value.
179 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`.
180 93 */
181 - public function select_content_ajax() {
182 - $term = ( isset( $_GET['term'] ) ) ? sanitize_text_field( wp_unslash( $_GET['term'] ) ) : '';
183 - $nonce = ( isset( $_GET['nonce'] ) ) ? sanitize_text_field( wp_unslash( $_GET['nonce'] ) ) : '';
184 - $source = ( isset( $_GET['source'] ) ) ? sanitize_text_field( wp_unslash( $_GET['source'] ) ) : '';
185 -
186 - // Check current user capabilities
187 - if ( ! current_user_can( 'manage_options' ) ) {
188 - wp_send_json_error( 'You are not allowed to do this.' );
189 - }
190 -
191 - if ( ! empty( $term ) && ! empty( $source ) && ! empty( $nonce ) && wp_verify_nonce( $nonce, 'merchant_admin_options' ) ) {
192 - $options = array();
193 -
194 - switch ( $source ) {
195 - case 'post':
196 - case 'product':
197 - $query = new WP_Query( array(
198 - 's' => $term,
199 - 'post_type' => $source,
200 - 'post_status' => 'publish',
201 - 'posts_per_page' => 25,
202 - 'order' => 'DESC',
203 - ) );
204 -
205 - if ( ! empty( $query->posts ) ) {
206 - foreach ( $query->posts as $post ) {
207 - $options[] = array(
208 - 'id' => $post->ID,
209 - 'text' => $post->post_title,
210 - );
211 - }
212 - }
213 -
214 - break;
215 -
216 - case 'user':
217 - $query = new WP_User_Query( array(
218 - 'search' => '*' . $term . '*',
219 - 'search_columns' => array( 'user_login', 'user_nicename', 'user_email', 'user_url' ),
220 - 'number' => 25,
221 - ) );
222 -
223 - if ( ! empty( $query->results ) ) {
224 - foreach ( $query->results as $user ) {
225 - $options[] = array(
226 - 'id' => $user->ID,
227 - 'text' => $user->display_name,
228 - );
229 - }
230 - }
231 -
232 - break;
233 - }
234 -
235 - wp_send_json_success( $options );
236 - } else {
237 - wp_send_json_error();
238 - }
239 - }
240 -
241 - /**
242 - * Get option.
243 - */
244 94 public static function get( $module, $setting, $default_val ) {
245 95 $options = get_option( 'merchant', array() );
246 96
247 97 $value = $default_val;
@@ -257,17 +107,25 @@
257 107 * @param mixed $value Option value.
258 108 * @param string $module Module ID.
259 109 * @param string $setting Setting ID.
260 110 * @param mixed $default_val Default value.
261 - *
262 - * @since 1.9.3
111 + *
112 + * @since 1.9.3
263 113 */
264 114 return apply_filters( 'merchant_get_option', $value, $module, $setting, $default_val );
265 115 }
266 116
267 - /**
268 - * Set option.
269 - */
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 + */
270 128 public static function set( $module, $setting, $value ) {
271 129 $options = get_option( 'merchant', array() );
272 130 $options[ $module ][ $setting ] = $value;
273 131 update_option( 'merchant', $options );
@@ -272,19 +130,32 @@
272 130 $options[ $module ][ $setting ] = $value;
273 131 update_option( 'merchant', $options );
274 132 }
275 133
276 - /**
277 - * Delete option.
278 - */
279 - public static function delete( $module, $setting ) {
280 - $options = get_option( 'merchant', array() );
281 - unset( $options[ $module ][ $setting ] );
282 - update_option( 'merchant', $options );
283 - }
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 + }
284 149
285 150 /**
286 - * 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.
287 158 */
288 159 public static function get_all( $module ) {
289 160 $options = get_option( 'merchant', array() );
290 161 $value = array();
@@ -295,2381 +166,288 @@
295 166
296 167 return $value;
297 168 }
298 169
299 - /**
300 - * Create options.
301 - */
302 - public static function create( $settings ) {
303 - $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 + // ──────────────────────────────────────────────────────
304 173
305 - /**
306 - * Hook: merchant_module_settings
307 - *
308 - * @param array $settings Module settings.
309 - * @param string $module_id Module ID.
310 - *
311 - * @since 1.0
312 - */
313 - $settings = apply_filters( 'merchant_module_settings', $settings, $module_id );
314 -
315 - self::save_options( $settings );
316 -
317 - $options = get_option( 'merchant', array() );
318 - ?>
319 - <div class="merchant-module-page-settings">
320 - <div class="merchant-module-page-setting-box">
321 - <?php
322 - if ( ! empty( $settings['title'] ) ) : ?>
323 - <div class="merchant-module-page-setting-title">
324 - <?php
325 - echo esc_html( $settings['title'] ); ?>
326 - <?php
327 - if ( ! empty( $settings['subtitle'] ) ) : ?>
328 - <div class="merchant-module-page-setting-subtitle"><?php
329 - echo esc_html( $settings['subtitle'] ); ?></div>
330 - <?php
331 - endif; ?>
332 - </div>
333 - <?php
334 - endif; ?>
335 - <div class="merchant-module-page-setting-fields">
336 - <?php
337 -
338 - if ( ! empty( $settings['fields'] ) ) {
339 - foreach ( $settings['fields'] as $field ) {
340 - $value = null;
341 -
342 - if ( isset( $field['default'] ) ) {
343 - $value = $field['default'];
344 - }
345 -
346 - if ( isset( $field['id'] ) && isset( $options[ $settings['module'] ] ) && isset( $options[ $settings['module'] ][ $field['id'] ] ) ) {
347 - $value = $options[ $settings['module'] ][ $field['id'] ];
348 - }
349 -
350 - $current_module = Merchant_Admin_Modules::get_module_info( $settings['module'] );
351 - $is_pro_module = ! merchant_is_pro_active() && isset( $current_module['pro'] ) && $current_module['pro'] === true;
352 - $is_pro_field = ! merchant_is_pro_active() && isset( $field['pro'] ) && $field['pro'] === true;
353 -
354 - if ( $is_pro_module || $is_pro_field ) {
355 - self::disabled_field( $field, $value );
356 - } else {
357 - self::field( $field, $value, $module_id );
358 - }
359 - }
360 - }
361 - ?>
362 - </div>
363 - </div>
364 - </div>
365 - <?php
366 - }
367 -
368 174 /**
369 - * Save options.
370 - */
371 - public static function save_options( $settings ) {
372 - $save = ( isset( $_POST['merchant_save'] ) ) ? sanitize_text_field( wp_unslash( $_POST['merchant_save'] ) ) : '';
373 - $reset = ( isset( $_POST['merchant_reset'] ) ) ? sanitize_text_field( wp_unslash( $_POST['merchant_reset'] ) ) : '';
374 - $nonce = ( isset( $_POST['merchant_nonce'] ) ) ? sanitize_text_field( wp_unslash( $_POST['merchant_nonce'] ) ) : '';
375 -
376 - if ( ! wp_verify_nonce( $nonce, 'merchant_nonce' ) ) {
377 - return;
378 - }
379 -
380 - if ( ! current_user_can( 'manage_options' ) ) {
381 - return;
382 - }
383 -
384 - $options = get_option( 'merchant', array() );
385 -
386 - if ( ! empty( $save ) ) {
387 - if ( ! empty( $settings['fields'] ) ) {
388 - foreach ( $settings['fields'] as $field ) {
389 - if ( ! isset( $field['id'] ) ) {
390 - continue;
391 - }
392 -
393 - if ( ! merchant_is_pro_active() && isset( $field['pro'] ) && $field['pro'] === true ) {
394 - continue;
395 - }
396 -
397 - $value = null;
398 -
399 - if ( isset( $_POST['merchant'] ) && isset( $_POST['merchant'][ $field['id'] ] ) ) {
400 - if ( 'textarea_code' === $field['type'] ) {
401 - $value = wp_kses( $_POST['merchant'][ $field['id'] ], merchant_kses_allowed_tags_for_code_snippets() );
402 - } elseif ( 'textarea_multiline' === $field['type'] ) {
403 - $value = sanitize_textarea_field( $_POST['merchant'][ $field['id'] ] );
404 - } elseif ( is_array( $_POST['merchant'][ $field['id'] ] ) ) {
405 - $value = array_filter( map_deep( wp_unslash( $_POST['merchant'][ $field['id'] ] ), 'sanitize_text_field' ) );
406 - } else {
407 - $value = sanitize_text_field( wp_unslash( $_POST['merchant'][ $field['id'] ] ) );
408 - }
409 - }
410 -
411 - $options[ $settings['module'] ][ $field['id'] ] = self::sanitize( $field, $value );
412 - }
413 - }
414 - } elseif ( ! empty( $reset ) ) {
415 - $options[ $settings['module'] ] = array();
416 - }
417 -
418 - update_option( 'merchant', $options );
419 -
420 - /**
421 - * Hook: merchant_options_saved, fired after saving module options.
422 - *
423 - * @param string $module module ID.
424 - * @param array $options module options.
425 - *
426 - * @since 1.9.3
427 - */
428 - do_action( 'merchant_options_saved', $settings['module'], $options[ $settings['module'] ] );
429 -
430 - /**
431 - * Hook: merchant_options_saved, fired after saving specific module options.
432 - *
433 - * @param array $options module options.
434 - *
435 - * @since 1.9.3
436 - */
437 - do_action( "merchant_options_saved_{$settings['module']}", $options[ $settings['module'] ] );
438 - }
439 -
440 - /**
441 - * Sanitize options.
442 - */
443 - public static function sanitize( $field, $value ) {
444 - // Callback for custom sanitize methods.
445 - if ( ! empty( $field['sanitize'] ) && is_callable( $field['sanitize'] ) ) {
446 - return call_user_func( $field['sanitize'], $value );
447 - }
448 -
449 - switch ( $field['type'] ) {
450 - case 'text':
451 - case 'color':
452 - case 'number':
453 - case 'select_size_chart':
454 - $value = sanitize_text_field( $value );
455 - break;
456 -
457 - case 'textarea':
458 - $value = sanitize_textarea_field( $value );
459 - break;
460 -
461 - case 'textarea_code':
462 - $value = $value;
463 - break;
464 -
465 - case 'checkbox_multiple':
466 - if ( is_array( $value ) && ! empty( $value ) ) {
467 - $value = array_filter( array_map( 'sanitize_text_field', $value ) );
468 - } else {
469 - $value = array();
470 - }
471 - break;
472 -
473 - case 'checkbox':
474 - case 'switcher':
475 - $value = ( '1' === $value ) ? 1 : 0;
476 - break;
477 -
478 - case 'range':
479 - case 'number':
480 - $value = absint( $value );
481 - break;
482 - case 'select_ajax':
483 - if ( is_array( $value ) && ! empty( $value ) ) {
484 - $value = array_filter( array_map( 'sanitize_text_field', $value ) );
485 - } elseif ( is_string( $value ) ) {
486 - $value = sanitize_text_field( $value );
487 - } else {
488 - $value = isset( $field['multiple'] ) && $field['multiple'] === false ? '' : array();
489 - }
490 - break;
491 - case 'radio':
492 - case 'radio_alt':
493 - case 'select':
494 - case 'choices':
495 - case 'buttons':
496 - case 'buttons_alt':
497 - case 'buttons_content':
498 - $value = ( in_array( $value, array_keys( $field['options'] ), true ) ) ? sanitize_key( $value ) : '';
499 - break;
500 -
501 - case 'hook_select':
502 - $value = is_array( $value ) ? $value : array();
503 -
504 - foreach ( $value as $key => $val ) {
505 - if ( $key === 'hook_name' ) {
506 - $value[ $key ] = in_array( $val, array_keys( $field['options'] ), true ) ? sanitize_key( $val ) : '';
507 - } else {
508 - $value[ $key ] = (int) $val;
509 - }
510 - }
511 -
512 - break;
513 -
514 - case 'code-editor':
515 - $value = wp_kses_post( $value );
516 - break;
517 -
518 - case 'sortable':
519 - $values = json_decode( $value );
520 - $value = array();
521 -
522 - foreach ( $values as $val ) {
523 - if ( in_array( $val, array_keys( $field['options'] ), true ) ) {
524 - $value[] = sanitize_key( $val );
525 - }
526 - }
527 - break;
528 - case 'dimensions':
529 - $values = is_array( $value ) ? $value : array();
530 -
531 - foreach ( $values as $key => $val ) {
532 - if ( $key === 'unit' ) {
533 - $value[ $key ] = sanitize_key( $value[ $key ] );
534 - } else {
535 - $value[ $key ] = absint( $value[ $key ] );
536 - }
537 - }
538 - break;
539 - case 'responsive_dimensions':
540 - $values = is_array( $value ) ? $value : array();
541 -
542 - foreach ( $values as $device => $device_fields ) {
543 - foreach ( $device_fields as $device_field => $device_field_val ) {
544 - if ( $device_field === 'unit' ) {
545 - $value[ $device ][ $device_field ] = sanitize_key( $device_field_val );
546 - } else {
547 - $value[ $device ][ $device_field ] = absint( $device_field_val );
548 - }
549 - }
550 - }
551 - break;
552 -
553 - case 'sortable_repeater':
554 - $values = json_decode( $value );
555 - $value = array_map( 'sanitize_text_field', $values );
556 - break;
557 - case 'flexible_content':
558 - $value = ! is_array( $value ) || empty( $value ) ? array() : $value;
559 -
560 - $value = array_map( function ( $sub_fields ) use ( $field ) {
561 - foreach ( $sub_fields as $sub_field => $value ) {
562 - if ( $sub_field === 'layout' ) {
563 - $sub_fields[ $sub_field ] = sanitize_text_field( $value );
564 - } else {
565 - $layout_field = array_filter( $field['layouts'][ $sub_fields['layout'] ]['fields'], function ( $layout_field ) use ( $sub_field ) {
566 - return $layout_field['id'] === $sub_field;
567 - } );
568 -
569 -
570 - $sub_fields[ $sub_field ] = self::sanitize( reset( $layout_field ), $value );
571 - }
572 - }
573 -
574 - return $sub_fields;
575 - }, $value );
576 -
577 - break;
578 - }
579 -
580 - return $value;
581 - }
582 -
583 - /**
584 - * Field
585 - */
586 - public static function field( $settings, $value, $module_id = '') {
587 -
588 - if ( ! empty( $settings['type'] ) ) {
589 - $type = $settings['type'];
590 -
591 - $id = ( ! empty( $settings['id'] ) ) ? $settings['id'] : '';
592 - $class = ( ! empty( $settings['class'] ) ) ? ' ' . $settings['class'] : '';
593 - $condition = ( ! empty( $settings['condition'] ) ) ? $settings['condition'] : array();
594 - $conditions = ( ! empty( $settings['conditions'] ) ) ? $settings['conditions'] : ''; //Docs here: https://github.com/athemes/Merchant/pull/133
595 - $default = ( ! empty( $settings['default'] ) ) ? $settings['default'] : null;
596 - $is_upsell = ! merchant_is_pro_active() && isset( $settings['pro'] ) && $settings['pro'] === true;
597 -
598 - if ( ! $value && ( 0 !== $value && '0' !== $value ) ) {
599 - if ( $type === 'checkbox_multiple' ) {
600 - $value = is_array( $value ) ? $value : (array) $default;
601 - } else {
602 - $value = $default;
603 - }
604 - }
605 -
606 - $wrapper_classes = array( 'merchant-module-page-setting-field' );
607 - $wrapper_classes[] = 'merchant-module-page-setting-field-' . $type;
608 -
609 - if ( ! empty( $class ) ) {
610 - $wrapper_classes[] = $class;
611 - }
612 -
613 - /**
614 - * Hook 'merchant_admin_module_field_wrapper_classes'
615 - *
616 - * @since 1.9.3
617 - */
618 - $wrapper_classes = apply_filters( 'merchant_admin_module_field_wrapper_classes', $wrapper_classes, $settings, $value, $module_id );
619 -
620 - echo '<div class="'. esc_attr( implode( ' ', $wrapper_classes ) ) .'" data-id="'
621 - . esc_attr( $id ) . '" data-type="' . esc_attr( $type ) . '" data-condition="' . esc_attr( wp_json_encode( $condition ) ) .
622 - '" data-conditions="' . ( $conditions ? esc_attr( wp_json_encode( $conditions ) ) : "" ) . '">';
623 - if ( ! empty( $settings['title'] ) ) {
624 - ?>
625 - <div class="merchant-module-page-setting-field-title<?php echo esc_attr( $is_upsell ? ' merchant-module-page-setting-field-title__has-upsell' : '' ); ?>">
626 - <?php echo esc_html( $settings['title'] ); ?>
627 -
628 - <?php if ( $is_upsell ) : ?>
629 - <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">
630 - <span class="merchant-pro-badge merchant-pro-tooltip" data-tooltip-message="<?php echo esc_attr__( 'This option is only available on Merchant Pro', 'merchant' ); ?>">
631 - <svg width="28" height="16" viewBox="0 0 28 16" fill="none" xmlns="http://www.w3.org/2000/svg">
632 - <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"/>
633 - <rect x="0.5" y="1" width="27" height="14" rx="1.5" stroke="#3858E9"/>
634 - </svg>
635 - </span>
636 - </a>
637 - <?php endif; ?>
638 - </div>
639 - <?php
640 - }
641 -
642 - echo '<div class="merchant-module-page-setting-field-inner merchant-field-' . esc_attr( $id ) . '">';
643 - if ( method_exists( 'Merchant_Admin_Options', $type ) ) {
644 - call_user_func( array( 'Merchant_Admin_Options', $type ), $settings, $value, $module_id );
645 - } else {
646 - esc_html_e( 'Field not found!', 'merchant' );
647 - }
648 - echo '</div>';
649 -
650 - $hidden_desc = $settings['hidden_desc'] ?? '';
651 -
652 - /**
653 - * Hook 'merchant_admin_module_field_hidden_description'
654 - *
655 - * @since 1.9.3
656 - */
657 - $hidden_desc = apply_filters( 'merchant_admin_module_field_hidden_description', $hidden_desc, $settings, $value, $module_id );
658 -
659 - $desc = ( ! empty( $settings['desc'] ) ) ? $settings['desc'] : '';
660 -
661 - /**
662 - * Hook 'merchant_admin_module_field_description'
663 - *
664 - * @since 1.9.3
665 - */
666 - $desc = apply_filters( 'merchant_admin_module_field_description', $desc, $settings, $value, $module_id );
667 -
668 - if ( ! empty( $desc ) ) {
669 - $hidden_desc_html = '';
670 - if ( ! empty( $hidden_desc ) ) {
671 - $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>';
672 - $hidden_desc_html .= '<img src="' . esc_url( MERCHANT_URI . '/assets/images/arrow-down.svg' ) . '" alt="Merchant" />';
673 - $hidden_desc_html .= '</div>';
674 - }
675 -
676 - 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 ) );
677 - }
678 -
679 - if ( ! empty( $hidden_desc ) ) {
680 - printf( '<div class="merchant-module-page-setting-field-hidden-desc">%s</div>', wp_kses_post( nl2br( $hidden_desc ) ) );
681 - }
682 -
683 - echo '</div>';
684 - }
685 - }
686 -
687 - /**
688 - * Disabled field
175 + * Create and render a module settings panel.
689 176 *
690 - * @param array $settings
691 - * @param mixed $value
177 + * @since 1.0
178 + * @see Merchant_Settings_Renderer::create()
692 179 *
180 + * @param array $settings Module settings configuration.
181 + *
693 182 * @return void
694 183 */
695 - public static function disabled_field( $settings, $value, $module_id = '') {
696 - static::replace_field(
697 - $settings,
698 - $value,
699 - array(
700 - '<input ',
701 - '<select ',
702 - 'merchant-module-page-setting-field-inner',
703 - ),
704 - array(
705 - '<input disabled ',
706 - '<select disabled ',
707 - 'merchant-module-page-setting-field-inner disabled',
708 - ),
709 - $module_id
710 - );
184 + public static function create( $settings ) {
185 + Merchant_Settings_Renderer::create( $settings );
711 186 }
712 187
713 188 /**
714 - * Modified field.
189 + * Render a single field.
715 190 *
716 - * @param $settings
717 - * @param $value
718 - * @param $search
719 - * @param $replace
191 + * @since 1.0
192 + * @see Merchant_Settings_Renderer::field()
720 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 + *
721 198 * @return void
722 199 */
723 - public static function replace_field( $settings, $value, $search, $replace, $module_id = '') {
724 - ob_start();
725 - self::field( $settings, $value, $module_id );
726 - $field = ob_get_clean();
727 -
728 - echo wp_kses( str_replace( $search, $replace, $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 );
729 202 }
730 203
731 204 /**
732 - * Field: Text
733 - */
734 - public static function text( $settings, $value, $module_id = '' ) {
735 - ?>
736 - <input type="text" name="merchant[<?php
737 - echo esc_attr( $settings['id'] ); ?>]" value="<?php
738 - echo esc_attr( $value ); ?>" placeholder="<?php
739 - echo esc_attr( $settings['placeholder'] ?? '' ); ?>"/>
740 - <?php
741 - }
742 -
743 - /**
744 - * Field: Text (readonly)
745 - */
746 - public static function text_readonly( $settings, $value, $module_id = '' ) {
747 - ?>
748 - <input type="text" value="<?php
749 - echo esc_attr( $value ); ?>" readonly/>
750 - <?php
751 - }
752 -
753 - /**
754 - * Field: Number
755 - */
756 - public static function number( $settings, $value, $module_id = '' ) {
757 - ?>
758 - <input type="number" name="merchant[<?php
759 - echo esc_attr( $settings['id'] ); ?>]" value="<?php
760 - echo esc_attr( $value ); ?>"<?php
761 - echo isset( $settings['step'] ) ? ' step="' . esc_attr( $settings['step'] ) . '"' : '';
762 - echo isset( $settings['max'] ) ? ' max="' . esc_attr( $settings['max'] ) . '"' : '';
763 - echo isset( $settings['min'] ) ? ' min="' . esc_attr( $settings['min'] ) . '"' : '' ?>
764 - placeholder="<?php echo esc_attr( $settings['placeholder'] ?? '' ); ?>"/>
765 - <?php
766 - }
767 -
768 - /**
769 - * Field: Textarea
770 - */
771 - public static function textarea( $settings, $value, $module_id = '' ) {
772 - $value = ( $value ) ? $value : '';
773 - ?>
774 - <textarea name="merchant[<?php
775 - echo esc_attr( $settings['id'] ); ?>]"><?php
776 - echo wp_kses_post( $value ); ?></textarea>
777 - <?php
778 - }
779 -
780 - /**
781 - * Field: Textarea
782 - */
783 - public static function textarea_multiline( $settings, $value, $module_id = '' ) {
784 - $value = ( $value ) ? $value : '';
785 - ?>
786 - <textarea name="merchant[<?php
787 - echo esc_attr( $settings['id'] ); ?>]"><?php
788 - echo wp_kses_post( $value ); ?></textarea>
789 - <?php
790 - }
791 -
792 - /**
793 - * Field: Textarea Code Snippet.
794 - */
795 - public static function textarea_code( $settings, $value, $module_id = '' ) {
796 - $value = ( $value ) ? $value : '';
797 - ?>
798 - <textarea name="merchant[<?php
799 - echo esc_attr( $settings['id'] ); ?>]"><?php
800 - echo wp_kses( $value, merchant_kses_allowed_tags_for_code_snippets() ); ?></textarea>
801 - <?php
802 - }
803 -
804 - /**
805 - * Field: Checkbox
806 - */
807 - public static function checkbox( $settings, $value, $module_id = '' ) {
808 - ?>
809 - <div>
810 - <label>
811 - <input type="checkbox" name="merchant[<?php
812 - echo esc_attr( $settings['id'] ); ?>]" value="1" <?php
813 - checked( $value, 1, true ); ?> />
814 - <?php
815 - if ( ! empty( $settings['label'] ) ) : ?>
816 - <span><?php
817 - echo esc_html( $settings['label'] ); ?></span>
818 - <?php
819 - endif; ?>
820 - </label>
821 - </div>
822 - <?php
823 - }
824 -
825 - /**
826 - * Field: Checkbox multiple
827 - */
828 - public static function checkbox_multiple( $settings, $value ) {
829 - if ( ! empty( $settings['options'] ) ) : ?>
830 - <?php
831 - foreach ( $settings['options'] as $key => $option ) : ?>
832 - <label>
833 - <input
834 - type="checkbox" name="merchant[<?php echo esc_attr( $settings['id'] ); ?>][]"
835 - value="<?php echo esc_attr( $key ); ?>"
836 - <?php checked( in_array( $key, $value, true ), true ); ?>
837 - />
838 - <span><?php echo esc_html( $option ); ?></span>
839 - </label>
840 - <?php
841 - endforeach; ?>
842 - <?php
843 - endif;
844 - }
845 -
846 - /**
847 - * Field: Switcher
848 - */
849 - public static function switcher( $settings, $value, $module_id = '' ) {
850 - ?>
851 - <div class="merchant-toggle-switch">
852 - <input type="checkbox" id="<?php
853 - echo esc_attr( $settings['id'] ); ?>" name="merchant[<?php
854 - echo esc_attr( $settings['id'] ); ?>]" value="1" <?php
855 - checked( $value, 1, true ); ?>
856 - class="toggle-switch-checkbox"/>
857 - <label class="toggle-switch-label" for="<?php
858 - echo esc_attr( $settings['id'] ); ?>">
859 - <span class="toggle-switch-inner"></span>
860 - <span class="toggle-switch-switch"></span>
861 - </label>
862 - <?php
863 - if ( ! empty( $settings['label'] ) ) : ?>
864 - <span><?php
865 - echo esc_html( $settings['label'] ); ?></span>
866 - <?php
867 - endif; ?>
868 - </div>
869 - <?php
870 - }
871 -
872 - /**
873 - * Field: Radio
874 - */
875 - public static function radio( $settings, $value, $module_id = '' ) {
876 - ?>
877 - <?php
878 - if ( ! empty( $settings['options'] ) ) : ?>
879 - <?php
880 - foreach ( $settings['options'] as $key => $option ) : ?>
881 - <label>
882 - <input type="radio" name="merchant[<?php
883 - echo esc_attr( $settings['id'] ); ?>]" value="<?php
884 - echo esc_attr( $key ); ?>" <?php
885 - checked( $value, $key, true ); ?>/>
886 - <span><?php
887 - echo esc_html( $option ); ?></span>
888 - </label>
889 - <?php
890 - endforeach; ?>
891 - <?php
892 - endif; ?>
893 - <?php
894 - }
895 -
896 - /**
897 - * Field: Image picker
898 - */
899 - public static function image_picker( $settings, $value, $module_id = '' ) {
900 - ?>
901 - <div class="merchant-image-picker">
902 - <?php
903 - if ( ! empty( $settings['options'] ) ) : ?>
904 - <?php
905 - foreach ( $settings['options'] as $key => $option ) : ?>
906 - <label>
907 - <input type="radio" name="merchant[<?php
908 - echo esc_attr( $settings['id'] ); ?>]" value="<?php
909 - echo esc_attr( $key ); ?>" <?php
910 - checked( $value, $key, true ); ?>/>
911 - <?php
912 - if ( isset( $option['image'] ) ) { ?>
913 - <img src="<?php
914 - echo esc_url( $option['image'] ) ?>" alt="">
915 - <?php
916 - } ?>
917 - <?php
918 - if ( isset( $option['title'] ) ) { ?>
919 - <span class="tool-tip-text"><?php
920 - echo esc_html( $option['title'] ) ?></span>
921 - <?php
922 - } ?>
923 - </label>
924 - <?php
925 - endforeach; ?>
926 - <?php
927 - endif; ?>
928 - </div>
929 - <?php
930 - }
931 -
932 - /**
933 - * Field: Radio Alt
934 - */
935 - public static function radio_alt( $settings, $value, $module_id = '' ) {
936 - ?>
937 - <?php
938 - if ( ! empty( $settings['options'] ) ) : ?>
939 - <?php
940 - foreach ( $settings['options'] as $key => $option ) : ?>
941 - <div>
942 - <label>
943 - <input type="radio" name="merchant[<?php
944 - echo esc_attr( $settings['id'] ); ?>]" value="<?php
945 - echo esc_attr( $key ); ?>" <?php
946 - checked( $value, $key, true ); ?>/>
947 - <span><?php
948 - echo esc_html( $option['title'] ); ?></span>
949 - </label>
950 - <p><?php
951 - echo esc_html( $option['desc'] ); ?></p>
952 - </div>
953 - <?php
954 - endforeach; ?>
955 - <?php
956 - endif; ?>
957 - <?php
958 - }
959 -
960 - /**
961 - * Field: Choices
962 - */
963 - public static function choices( $settings, $value, $module_id = '' ) {
964 - ?>
965 - <div class="merchant-choices merchant-choices-<?php
966 - echo esc_attr( $settings['id'] ) ?>">
967 - <?php
968 - if ( ! empty( $settings['options'] ) ) : ?>
969 - <?php
970 - foreach ( $settings['options'] as $key => $option ) : ?>
971 - <label>
972 - <input type="radio" name="merchant[<?php
973 - echo esc_attr( $settings['id'] ); ?>]" value="<?php
974 - echo esc_attr( $key ); ?>" <?php
975 - checked( $value, $key, true ); ?>/>
976 - <?php
977 - if ( ! empty( $option['svg'] ) ) : ?>
978 - <span class="merchant-svg">
979 - <?php
980 - echo wp_kses( Merchant_SVG_Icons::get_svg_icon( $option['svg'] ), merchant_kses_allowed_tags( array(), false ) ); ?>
981 -
982 - <?php
983 - if ( ! empty( $option['label'] ) ) : ?>
984 - <span class="merchant-tooltip"><?php
985 - echo esc_html( $option['label'] ); ?></span>
986 - <?php
987 - endif; ?>
988 - </span>
989 - <?php
990 - else : ?>
991 - <figure>
992 - <?php if ( ! empty( $option['image'] ) ) :
993 - $title = $option['title'] ?? '';
994 - ?>
995 - <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 ); ?>"/>
996 - <?php
997 - else : ?>
998 - <img src="<?php
999 - echo esc_url( sprintf( $option, MERCHANT_URI . 'assets/images' ) ); ?>"/>
1000 - <?php
1001 - endif; ?>
1002 - <?php
1003 - if ( ! empty( $option['label'] ) ) : ?>
1004 - <span class="merchant-tooltip"><?php
1005 - echo esc_html( $option['label'] ); ?></span>
1006 - <?php
1007 - endif; ?>
1008 - </figure>
1009 - <?php
1010 - endif; ?>
1011 - </label>
1012 - <?php
1013 - endforeach; ?>
1014 - <?php
1015 - endif; ?>
1016 - </div>
1017 - <?php
1018 - }
1019 -
1020 - /**
1021 - * Field: Select
1022 - */
1023 - public static function select( $settings, $value, $module_id = '' ) {
1024 - ?>
1025 - <?php
1026 - if ( ! empty( $settings['options'] ) ) : ?>
1027 - <select name="merchant[<?php
1028 - echo esc_attr( $settings['id'] ); ?>]">
1029 - <?php
1030 - foreach ( $settings['options'] as $key => $option ) : ?>
1031 - <option value="<?php
1032 - echo esc_attr( $key ); ?>" <?php
1033 - selected( $value, $key, true ); ?>><?php
1034 - echo esc_html( $option ); ?></option>
1035 - <?php
1036 - endforeach; ?>
1037 - </select>
1038 - <?php
1039 - endif; ?>
1040 - <?php
1041 - }
1042 -
1043 - /**
1044 - * Field: Hook Select
1045 - */
1046 - public static function hook_select( $settings, $value, $module_id = '' ) {
1047 - $hook_name = isset( $value['hook_name'] ) ? $value['hook_name'] : '';
1048 - $hook_priority = isset( $value['hook_priority'] ) ? $value['hook_priority'] : '';
1049 -
1050 - ?>
1051 - <div class="merchant-module-page-setting-field-hook_select-wrapper">
1052 - <div class="merchant-module-page-setting-field-select">
1053 - <select name="merchant[<?php
1054 - echo esc_attr( $settings['id'] ); ?>][hook_name]">
1055 - <?php
1056 - if ( ! empty( $settings['options'] ) ) : ?>
1057 - <?php
1058 - foreach ( $settings['options'] as $key => $option ) : ?>
1059 - <option value="<?php
1060 - echo esc_attr( $key ); ?>" <?php
1061 - selected( $hook_name, $key, true ); ?>><?php
1062 - echo esc_html( $option ); ?></option>
1063 - <?php
1064 - endforeach; ?>
1065 - <?php
1066 - endif; ?>
1067 - </select>
1068 - </div>
1069 - <?php
1070 -
1071 - if ( isset( $settings['order'] ) && true === $settings['order'] ) {
1072 - ?>
1073 - <div class="merchant-module-page-setting-field-number">
1074 - <input type="number" name="merchant[<?php
1075 - echo esc_attr( $settings['id'] ); ?>][hook_priority]" value="<?php
1076 - echo esc_attr( $hook_priority ); ?>"/>
1077 - </div>
1078 - <?php
1079 - } ?>
1080 - </div>
1081 -
1082 - <?php
1083 - }
1084 -
1085 - /**
1086 - * Field: Select ajax
205 + * Render a disabled (pro-gated) field.
1087 206 *
1088 - * @param $settings
1089 - * @param $value
207 + * @since 1.0
208 + * @see Merchant_Settings_Renderer::disabled_field()
1090 209 *
210 + * @param array $settings Field settings.
211 + * @param mixed $value Field value.
212 + * @param string $module_id Module ID.
213 + *
1091 214 * @return void
1092 215 */
1093 - public static function select_ajax( $settings, $value, $module_id = '' ) {
1094 - $settings = wp_parse_args( $settings, array(
1095 - 'source' => 'post',
1096 - ) );
1097 -
1098 - $ids = ( is_array( $value ) && ! empty( $value ) ) ? $value : (array) $value;
1099 -
1100 - if ( isset( $settings['multiple'] ) ) {
1101 - $multiple = $settings['multiple'] === true ? 'multiple' : '';
1102 - } else {
1103 - $multiple = 'multiple';
1104 - }
1105 -
1106 - $placeholder = $settings['placeholder'] ?? '';
1107 - ?>
1108 - <select
1109 - name="merchant[<?php echo esc_attr( $settings['id'] ); ?>]<?php echo esc_attr( $multiple ? '[]' : '' ); ?>"
1110 - <?php echo esc_attr( $multiple ); ?>
1111 - data-source="<?php echo esc_attr( $settings['source'] ); ?>"
1112 - data-placeholder="<?php echo esc_attr( $placeholder ); ?>">
1113 - <?php
1114 - if ( ! empty( $ids ) ) {
1115 - foreach ( $ids as $id ) {
1116 - switch ( $settings['source'] ) {
1117 - case 'post':
1118 - case 'product':
1119 - $post = get_post( $id );
1120 -
1121 - if ( ! empty( $post ) ) {
1122 - echo '<option value="' . esc_attr( $post->ID ) . '" selected>' . esc_html( $post->post_title ) . '</option>';
1123 - }
1124 - break;
1125 - case 'user':
1126 - $user = get_user_by( 'ID', $id );
1127 - if( $user ) {
1128 - echo '<option value="' . esc_attr( $user->ID ) . '" selected>' . esc_html( $user->display_name ) . '</option>';
1129 - }
1130 - break;
1131 - case 'options':
1132 - }
1133 - }
1134 - }
1135 - if ( $settings['source'] === 'options' ) {
1136 - $value = ! empty( $value ) ? $value : '';
1137 -
1138 - foreach ( $settings['options'] as $option ) {
1139 - if ( isset( $option['options'] ) ) {
1140 - echo '<optgroup label="' . esc_attr( $option['text'] ) . '">';
1141 -
1142 - foreach ( $option['options'] as $child_option ) {
1143 - $selected_value = is_array( $value ) ? in_array( $child_option['id'], $value, true ) : $child_option['id'];
1144 - echo '<option value="' . esc_attr( $child_option['id'] ) . '" ' . selected( $selected_value, is_array( $value ) ? true : $value, false ) . '>' . esc_html( $child_option['text'] ) . '</option>';
1145 - }
1146 -
1147 - echo '</optgroup>';
1148 - } else {
1149 - $selected_value = is_array( $value ) ? in_array( $option['id'], $value, true ) : $option['id'];
1150 - echo '<option value="' . esc_attr( $option['id'] ) . '" ' . selected( $selected_value, is_array( $value ) ? true : $value, false ) . '>' . esc_html( $option['text'] ) . '</option>';
1151 - }
1152 - }
1153 - } ?>
1154 - </select>
1155 - <?php
216 + public static function disabled_field( $settings, $value, $module_id = '' ) {
217 + Merchant_Settings_Renderer::disabled_field( $settings, $value, $module_id );
1156 218 }
1157 219
1158 220 /**
1159 - * Field: Info Block
221 + * Save module options from a form submission.
1160 222 *
223 + * @since 1.0
224 + * @see Merchant_Settings_Saver::save_options()
225 + *
226 + * @param array $settings Module settings configuration.
227 + *
1161 228 * @return void
1162 229 */
1163 - public static function info_block( $settings, $value, $module_id = '' ) {
1164 - ?>
1165 - <div class="merchant-info-block">
1166 - <i class="dashicons dashicons-info"></i>
1167 - <p><?php
1168 - echo ! empty( $settings['description'] ) ? esc_html( $settings['description'] ) : ''; ?><?php
1169 - if ( ! empty( $settings['button_text'] ) && ! empty( $settings['button_link'] ) ) {
1170 - printf( '<a href="%s" target="_blank">%s</a>', esc_url( $settings['button_link'] ), esc_html( $settings['button_text'] ) );
1171 - } ?></p>
1172 - </div>
1173 - <?php
230 + public static function save_options( $settings ) {
231 + Merchant_Settings_Saver::save_options( $settings );
1174 232 }
1175 233
1176 234 /**
1177 - * Field: Products Selector
235 + * Sanitize options.
1178 236 *
1179 - * @param $settings
1180 - * @param $value
237 + * @since 1.9.3
238 + * @see Merchant_Settings_Saver::sanitize()
1181 239 *
1182 - * @return void
240 + * @param array $field The field configuration.
241 + * @param mixed $value The raw submitted value.
242 + *
243 + * @return mixed The sanitized value.
1183 244 */
1184 - public static function products_selector( $settings, $value, $module_id = '' ) {
1185 - if ( ! class_exists( 'WooCommerce' ) ) {
1186 - echo '<p class="merchant-notice">' . esc_html__( 'WooCommerce is not installed or activated.', 'merchant' ) . '</p>';
1187 -
1188 - return;
1189 - }
1190 -
1191 - $ids = $value ? explode( ',', $value ) : array();
1192 -
1193 - if ( isset( $settings['multiple'] ) ) {
1194 - $multiple = $settings['multiple'] === true ? 'multiple' : 'false';
1195 - } else {
1196 - $multiple = 'multiple';
1197 - }
1198 -
1199 - if ( ! isset( $settings['allowed_types'] ) ) {
1200 - $settings['allowed_types'] = array( 'all' );
1201 - }
1202 - ?>
1203 -
1204 - <div class="merchant-products-search-container" data-multiple="<?php
1205 - echo esc_attr( $multiple ); ?>">
1206 - <div class="merchant-search-area">
1207 - <input type="text" name="merchant-search-field" data-allowed-types="<?php
1208 - echo esc_attr( implode( ',', $settings['allowed_types'] ) ) ?>" placeholder="<?php
1209 - esc_attr_e( 'Search products', 'merchant' ); ?>" class="merchant-search-field">
1210 - <span class="merchant-searching"><?php
1211 - esc_html_e( 'Searching...', 'merchant' ); ?></span>
1212 - <img src="<?php echo esc_url( MERCHANT_URI . 'assets/images/admin/products-search-icon.svg' ); ?>" class="merchant-search-icon"
1213 - alt="<?php esc_attr_e( 'Search icon', 'merchant' ); ?>">
1214 - <div class="merchant-selections-products-preview"></div>
1215 - </div>
1216 - <div class="merchant-selected-products-preview">
1217 - <ul>
1218 - <?php
1219 - if ( ! empty( $ids ) ) {
1220 - foreach ( $ids as $product_id ) {
1221 - $product = wc_get_product( $product_id );
1222 - if ( $product ) {
1223 - self::product_data_li( $product );
1224 - }
1225 - }
1226 - }
1227 - ?>
1228 - </ul>
1229 - </div>
1230 - <input type="hidden" name="merchant[<?php
1231 - echo esc_attr( $settings['id'] ); ?>]" class="merchant-selected-products" value="<?php
1232 - echo esc_attr( $value ) ?>">
1233 - </div>
1234 - <?php
245 + public static function sanitize( $field, $value ) {
246 + return Merchant_Settings_Saver::sanitize( $field, $value );
1235 247 }
1236 248
1237 - public static function product_data_li( $product, $search = false, $hierarchy = false ) {
1238 - $product_id = $product->get_id();
1239 - $product_sku = $product->get_sku();
1240 - $product_name = $product->get_name();
1241 - $edit_link = get_edit_post_link( $product_id );
1242 - if ( $product->is_type( 'variation' ) ) {
1243 - $edit_link = get_edit_post_link( $product->get_parent_id() );
1244 - }
1245 -
1246 - /**
1247 - * Filter product image.
1248 - *
1249 - * @since 1.9.1
1250 - */
1251 - $product_image = apply_filters(
1252 - 'merchant_product_item_product_image',
1253 - '<span class="img">' . $product->get_image( array( 30, 30 ) ) . '</span>',
1254 - $product
1255 - );
1256 -
1257 - $price = $product->get_price();
1258 - $key = $product_id . '_' . $product_sku;
1259 -
1260 - /**
1261 - * Filter the product bundle item product info.
1262 - *
1263 - * @param string $product_info The product bundle item product info.
1264 - * @param WC_Product $product The product object.
1265 - *
1266 - * @since 1.9.0
1267 - */
1268 - $product_info = apply_filters( 'merchant_pro_product_bundle_item_product_info', $product->get_type() . '<br/>#' . $product_id, $product );
1269 - if ( $search ) {
1270 - $remove_btn = '<span class="remove hint--left" aria-label="' . esc_html__( 'Add', 'merchant' ) . '">+</span>';
1271 - } else {
1272 - $remove_btn = '<span class="remove hint--left" aria-label="' . esc_html__( 'Remove', 'merchant' ) . '">×</span>';
1273 - }
1274 -
1275 - $item_class = 'product-item';
1276 - if( $hierarchy && $product->is_type( 'variation' ) ) {
1277 - $item_class .= ' hierarchy-style';
1278 - }
1279 -
1280 - echo '<li class="' . esc_attr( $item_class ) . '" data-key="' . esc_attr( $key ) . '" data-name="' . esc_attr( $product_name ) . '" data-sku="'
1281 - . esc_attr( $product_sku ) . '" data-id="' . esc_attr( $product_id ) . '" data-price="' . esc_attr( $price ) . '">'
1282 - . wp_kses( $product_image, array(
1283 - 'span' => array(
1284 - 'class' => true,
1285 - ),
1286 - 'img' => array(
1287 - 'src' => true,
1288 - 'alt' => true,
1289 - 'decoding' => true,
1290 - 'srcset' => true,
1291 - 'loading' => true,
1292 - 'sizes' => true,
1293 - 'class' => true,
1294 - 'width' => true,
1295 - 'height' => true,
1296 - ),
1297 - ) ) . '<span class="data">'
1298 - . '<span class="name">' . esc_html( $product_name ) . '</span><span class="info">' . wp_kses( $product->get_price_html(), array(
1299 - 'span' => array(
1300 - 'class' => true,
1301 - ),
1302 - 'del' => array(
1303 - 'aria-hidden' => true,
1304 - ),
1305 - 'ins' => array(),
1306 - 'bdi' => array(),
1307 -
1308 - ) ) . '</span> ' . ( $product->is_sold_individually()
1309 - ? '<span class="info">' . esc_html__( 'sold individually', 'merchant' ) . '</span> ' : '' ) . '</span>'
1310 - . '<span class="type"><a href="' . esc_url( $edit_link ) . '" target="_blank">' . wp_kses_post( $product_info ) . '</a></span> ' . wp_kses( $remove_btn, array(
1311 - 'span' => array(
1312 - 'class' => true,
1313 - 'aria-label' => true,
1314 - ),
1315 - ) );
1316 -
1317 - echo '</li>';
1318 - }
1319 -
1320 - public function products_search() {
1321 - check_ajax_referer( 'merchant_admin_options', 'nonce' );
1322 -
1323 - if ( ! isset( $_POST['keyword'] ) || empty( $_POST['keyword'] ) ) {
1324 - exit();
1325 - }
1326 -
1327 - $types = array( 'simple', 'variable' ); // limit search to product types
1328 -
1329 - if ( isset( $_POST['product_types'] ) || ! empty( $_POST['product_types'] ) ) {
1330 - $types = explode( ',', sanitize_text_field( $_POST['product_types'] ) );
1331 - }
1332 -
1333 - $hierarchy = false;
1334 - if (
1335 - in_array( 'all', $types, true )
1336 - || ( in_array( 'variation', $types, true ) && in_array( 'variable', $types, true ) )
1337 - ) {
1338 - $hierarchy = true;
1339 - }
1340 -
1341 - $added_ids = isset( $_POST['ids'] ) ? explode( ',', sanitize_text_field( $_POST['ids'] ) ) : array();
1342 - $keyword = sanitize_text_field( $_POST['keyword'] );
1343 - if ( is_numeric( $keyword ) ) {
1344 - // search by id
1345 - $query_args = array(
1346 - 'p' => absint( $keyword ),
1347 - 'post_type' => 'product',
1348 - );
1349 - } else {
1350 - $query_args = array(
1351 - 'post_type' => 'product',
1352 - 'post_status' => array( 'publish', 'private' ),
1353 - 's' => $keyword,
1354 - 'posts_per_page' => 10,
1355 - );
1356 -
1357 - if ( ! empty( $types ) && ! in_array( 'all', $types, true ) ) {
1358 - $product_types = $types;
1359 -
1360 - if ( in_array( 'variation', $types, true ) ) {
1361 - $product_types[] = 'variable';
1362 - }
1363 -
1364 - // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
1365 - $query_args['tax_query'] = array(
1366 - array(
1367 - 'taxonomy' => 'product_type',
1368 - 'field' => 'slug',
1369 - 'terms' => $product_types,
1370 - ),
1371 - );
1372 - }
1373 -
1374 - $query_args['post__not_in'] = array_map( 'absint', $added_ids );
1375 - }
1376 -
1377 - // Filter by category slugs.
1378 - $categories = array_map( 'sanitize_text_field', $_POST['categories'] ?? array() );
1379 - if ( is_array( $categories ) && ! empty( $categories ) ) {
1380 - $query_args['tax_query'][] = array(
1381 - 'taxonomy' => 'product_cat',
1382 - 'field' => 'slug',
1383 - 'terms' => $categories,
1384 - 'operator' => 'IN',
1385 - );
1386 - }
1387 -
1388 - $query = new WP_Query( $query_args );
1389 -
1390 - if ( $query->have_posts() ) {
1391 - echo '<ul>';
1392 -
1393 - while ( $query->have_posts() ) {
1394 - $query->the_post();
1395 - $_product = wc_get_product( get_the_ID() );
1396 -
1397 - if ( ! $_product ) {
1398 - continue;
1399 - }
1400 -
1401 - if (
1402 - ! $_product->is_type( 'variable' )
1403 - || in_array( 'variable', $types, true )
1404 - || in_array( 'all', $types, true )
1405 - ) {
1406 - self::product_data_li( $_product, array( 'qty' => 1 ), true, $hierarchy );
1407 - }
1408 -
1409 - if (
1410 - $_product->is_type( 'variable' )
1411 - && (
1412 - empty( $types )
1413 - || in_array( 'all', $types, true )
1414 - || in_array( 'variation', $types, true )
1415 - )
1416 - ) {
1417 - // show all children
1418 - $children = $_product->get_children();
1419 -
1420 - if ( is_array( $children ) && count( $children ) > 0 ) {
1421 - foreach ( $children as $child ) {
1422 - $child_product = wc_get_product( $child );
1423 - /**
1424 - * @var WC_Product_Variation $child_product
1425 - */
1426 - if ( ! $this->are_variation_attributes_set( $child_product ) ) {
1427 - // Don't display variations that don't have all attributes set.
1428 - continue;
1429 - }
1430 - self::product_data_li( $child_product, true, $hierarchy );
1431 - }
1432 - }
1433 - }
1434 - }
1435 -
1436 - echo '</ul>';
1437 - wp_reset_postdata();
1438 - } else {
1439 - // translators: %s is the search keyword
1440 - echo wp_kses( '<ul><span>' . sprintf( esc_html__( 'No results found for "%s"', 'merchant' ), $keyword ) . '</span></ul>', array(
1441 - 'ul' => array(),
1442 - 'span' => array(),
1443 - ) );
1444 - }
1445 -
1446 - exit;
1447 - }
1448 -
1449 249 /**
1450 - * Check if all variation attributes are set.
250 + * Get product category choices for Select2.
1451 251 *
1452 - * @param $variation WC_Product_Variation variation product object
252 + * @since 1.9.3
253 + * @see Merchant_Select2_Choices::get_category_select2_choices()
1453 254 *
1454 - * @return bool
255 + * @return array Formatted category choices.
1455 256 */
1456 - public function are_variation_attributes_set( $variation ) {
1457 - // Check if the product is a variation and has attributes
1458 - if ( $variation && $variation->is_type( 'variation' ) ) {
1459 - // Get the variation attributes
1460 - $variation_attributes = $variation->get_variation_attributes();
1461 -
1462 - // Check if all variation attributes are set
1463 - foreach ( $variation_attributes as $attribute => $value ) {
1464 - if ( empty( $value ) ) {
1465 - return false; // At least one attribute is not set
1466 - }
1467 - }
1468 -
1469 - return true; // All variation attributes are set
1470 - }
1471 -
1472 - return false; // Not a valid variation product
257 + public static function get_category_select2_choices() {
258 + return Merchant_Select2_Choices::get_category_select2_choices();
1473 259 }
1474 260
1475 261 /**
1476 - * Field: Select Size Chart
262 + * Get product tag choices for Select2.
263 + *
264 + * @since 1.9.3
265 + * @see Merchant_Select2_Choices::get_tag_select2_choices()
266 + *
267 + * @return array Formatted tag choices.
1477 268 */
1478 - public static function select_size_chart( $settings, $value, $module_id = '' ) {
1479 - $options = array(
1480 - '' => esc_html__( 'Default', 'merchant' ),
1481 - );
1482 -
1483 - $posts = get_posts( array(
1484 - 'post_type' => 'merchant_size_chart',
1485 - 'posts_per_page' => - 1,
1486 - 'post_status' => 'publish',
1487 - ) );
1488 -
1489 - if ( ! is_wp_error( $posts ) && ! empty( $posts ) ) {
1490 - foreach ( $posts as $_post ) {
1491 - $options[ $_post->ID ] = $_post->post_title;
1492 - }
1493 - }
1494 -
1495 - ?>
1496 - <?php
1497 - if ( ! empty( $options ) ) : ?>
1498 - <select name="merchant[<?php
1499 - echo esc_attr( $settings['id'] ); ?>]">
1500 - <?php
1501 - foreach ( $options as $key => $option ) : ?>
1502 - <option value="<?php
1503 - echo esc_attr( $key ); ?>" <?php
1504 - selected( $value, $key, true ); ?>><?php
1505 - echo esc_html( $option ); ?></option>
1506 - <?php
1507 - endforeach; ?>
1508 - </select>
1509 - <?php
1510 - endif; ?>
1511 - <?php
269 + public static function get_tag_select2_choices() {
270 + return Merchant_Select2_Choices::get_tag_select2_choices();
1512 271 }
1513 272
1514 273 /**
1515 - * Field: Buttons
274 + * Get product brand choices for Select2.
275 + *
276 + * @since 1.9.3
277 + * @see Merchant_Select2_Choices::get_brand_select2_choices()
278 + *
279 + * @return array Formatted brand choices.
1516 280 */
1517 - public static function buttons( $settings, $value, $module_id = '' ) {
1518 - ?>
1519 - <div class="merchant-buttons">
1520 - <?php
1521 - if ( ! empty( $settings['options'] ) ) : ?>
1522 - <?php
1523 - foreach ( $settings['options'] as $key => $option ) : ?>
1524 - <label class="merchant-button-<?php
1525 - echo esc_attr( $key ); ?>"">
1526 - <input type="radio" name="merchant[<?php
1527 - echo esc_attr( $settings['id'] ); ?>]" value="<?php
1528 - echo esc_attr( $key ); ?>" <?php
1529 - checked( $value, $key, true ); ?>/>
1530 - <span><?php
1531 - echo esc_html( $option ); ?></span>
1532 - </label>
1533 - <?php
1534 - endforeach; ?>
1535 - <?php
1536 - endif; ?>
1537 - </div>
1538 - <?php
281 + public static function get_brand_select2_choices() {
282 + return Merchant_Select2_Choices::get_brand_select2_choices();
1539 283 }
1540 284
1541 285 /**
1542 - * Field: Buttons 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.
1543 292 */
1544 - public static function buttons_alt( $settings, $value, $module_id = '' ) {
1545 - ?>
1546 - <div class="merchant-buttons">
1547 - <?php
1548 - if ( ! empty( $settings['options'] ) ) : ?>
1549 - <?php
1550 - foreach ( $settings['options'] as $key => $option ) : ?>
1551 - <label class="merchant-button-<?php
1552 - echo esc_attr( $key ); ?>">
1553 - <input type="radio" name="merchant[<?php
1554 - echo esc_attr( $settings['id'] ); ?>]" value="<?php
1555 - echo esc_attr( $key ); ?>" <?php
1556 - checked( $value, $key, true ); ?>/>
1557 - <span><?php
1558 - echo esc_html( $option ); ?></span>
1559 - </label>
1560 - <?php
1561 - endforeach; ?>
1562 - <?php
1563 - endif; ?>
1564 - </div>
1565 - <?php
293 + public static function get_user_roles_select2_choices() {
294 + return Merchant_Select2_Choices::get_user_roles_select2_choices();
1566 295 }
1567 296
1568 297 /**
1569 - * Field: Buttons Content
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.
1570 304 */
1571 - public static function buttons_content( $settings, $value, $module_id = '' ) {
1572 - ?>
1573 - <div class="merchant-buttons-content">
1574 - <?php
1575 - if ( ! empty( $settings['options'] ) ) : ?>
1576 - <?php
1577 - foreach ( $settings['options'] as $key => $option ) : ?>
1578 - <label class="merchant-button-content-<?php echo esc_attr( $key ); ?>">
1579 - <input
1580 - type="radio"
1581 - name="merchant[<?php echo esc_attr( $settings['id'] ); ?>]"
1582 - value="<?php echo esc_attr( $key ); ?>"
1583 - <?php checked( $value, $key, true ); ?>
1584 - />
1585 -
1586 - <span class="merchant-buttons-inner-content">
1587 - <?php if ( $option['icon'] ) : ?>
1588 - <img src="<?php echo esc_url( $option['icon'] ); ?>" alt="">
1589 - <?php endif; ?>
1590 - <span>
1591 - <span><?php echo esc_html( $option['title'] ?? '' ); ?></span>
1592 - <span><?php echo esc_html( $option['desc'] ?? '' ); ?></span>
1593 - </span>
1594 - </span>
1595 - </label>
1596 - <?php
1597 - endforeach; ?>
1598 - <?php
1599 - endif; ?>
1600 - </div>
1601 - <?php
305 + public static function get_weekday_choices() {
306 + return Merchant_Select2_Choices::get_weekday_choices();
1602 307 }
1603 308
1604 309 /**
1605 - * Field: Range
310 + * Get shipping zone choices for Select2.
311 + *
312 + * @since 2.3.2
313 + * @see Merchant_Select2_Choices::get_shipping_zone_select2_choices()
314 + *
315 + * @return array Formatted zone choices.
1606 316 */
1607 - public static function range( $settings, $value, $module_id = '' ) {
1608 - $settings = wp_parse_args( $settings, array(
1609 - 'min' => '',
1610 - 'max' => '',
1611 - 'step' => '',
1612 - 'unit' => '',
1613 - ) );
1614 -
1615 - ?>
1616 - <div class="merchant-range">
1617 - <input type="range" class="merchant-range-input" name="" min="<?php
1618 - echo esc_attr( $settings['min'] ); ?>" max="<?php
1619 - echo esc_attr( $settings['max'] ); ?>"
1620 - step="<?php
1621 - echo esc_attr( $settings['step'] ); ?>" value="<?php
1622 - echo esc_attr( $value ); ?>"/>
1623 - <input type="number" class="merchant-range-number-input" name="merchant[<?php
1624 - echo esc_attr( $settings['id'] ); ?>]" min="<?php
1625 - echo esc_attr( $settings['min'] ); ?>"
1626 - max="<?php
1627 - echo esc_attr( $settings['max'] ); ?>" step="<?php
1628 - echo esc_attr( $settings['step'] ); ?>" value="<?php
1629 - echo esc_attr( $value ); ?>"/>
1630 - <?php
1631 - if ( ! empty( $settings['unit'] ) ) : ?>
1632 - <span class="merchant-range-unit"><?php
1633 - echo esc_html( $settings['unit'] ); ?></span>
1634 - <?php
1635 - endif; ?>
1636 - </div>
1637 - <?php
317 + public static function get_shipping_zone_select2_choices() {
318 + return Merchant_Select2_Choices::get_shipping_zone_select2_choices();
1638 319 }
1639 320
1640 321 /**
1641 - * Field: Color
322 + * Get registered shipping method choices for Select2.
323 + *
324 + * @since 2.3.2
325 + * @see Merchant_Select2_Choices::get_shipping_method_select2_choices()
326 + *
327 + * @return array Formatted method choices.
1642 328 */
1643 - public static function color( $settings, $value, $module_id = '' ) {
1644 - $settings = wp_parse_args( $settings, array(
1645 - 'default' => '#212121',
1646 - ) );
1647 -
1648 - ?>
1649 - <div class="merchant-color">
1650 - <div class="merchant-color-picker" data-default-color="<?php
1651 - echo esc_attr( $settings['default'] ); ?>" style="background-color: <?php
1652 - echo esc_attr( $value ); ?>;"></div>
1653 - <input type="text" class="merchant-color-input" name="merchant[<?php
1654 - echo esc_attr( $settings['id'] ); ?>]" value="<?php
1655 - echo esc_attr( $value ); ?>"/>
1656 - </div>
1657 - <?php
329 + public static function get_shipping_method_select2_choices() {
330 + return Merchant_Select2_Choices::get_shipping_method_select2_choices();
1658 331 }
1659 332
1660 333 /**
1661 - * Field: Gallery
334 + * Get enabled payment gateway choices for Select2.
335 + *
336 + * @since 2.3.2
337 + * @see Merchant_Select2_Choices::get_payment_gateway_select2_choices()
338 + *
339 + * @return array Formatted gateway choices.
1662 340 */
1663 - public static function gallery( $settings, $value, $module_id = '' ) {
1664 - $settings = wp_parse_args( $settings, array(
1665 - 'label' => esc_html__( 'Select Images', 'merchant' ),
1666 - ) );
1667 -
1668 - $images = ( ! empty( $value ) ) ? explode( ',', $value ) : array();
1669 -
1670 -
1671 - echo '<div class="merchant-gallery-images">';
1672 -
1673 - if ( ! empty( $images ) ) :
1674 -
1675 - foreach ( $images as $image_id ) :
1676 -
1677 - $image = wp_get_attachment_image_src( $image_id, 'thumbnail' );
1678 -
1679 - if ( ! empty( $image ) && ! empty( $image[0] ) ) :
1680 -
1681 - printf( '<div class="merchant-gallery-image" data-item-id="%s">', esc_attr( $image_id ) );
1682 -
1683 - echo '<i class="merchant-gallery-remove dashicons dashicons-no-alt"></i>';
1684 -
1685 - printf( '<img src="%s" />', esc_url( $image[0] ) );
1686 -
1687 - echo '</div>';
1688 -
1689 - endif;
1690 -
1691 - endforeach;
1692 -
1693 - endif;
1694 -
1695 - echo '</div>';
1696 -
1697 - ?>
1698 - <a href="#" class="merchant-gallery-button"><?php
1699 - echo esc_html( $settings['label'] ); ?></a>
1700 - <input type="hidden" class="merchant-gallery-input" name="merchant[<?php
1701 - echo esc_attr( $settings['id'] ); ?>]" value="<?php
1702 - echo esc_attr( $value ); ?>"/>
1703 - <?php
341 + public static function get_payment_gateway_select2_choices() {
342 + return Merchant_Select2_Choices::get_payment_gateway_select2_choices();
1704 343 }
1705 344
1706 345 /**
1707 - * Field: Upload
346 + * Get customer user choices for Select2.
347 + *
348 + * @since 1.9.3
349 + * @see Merchant_Select2_Choices::get_customers_select2_choices()
350 + *
351 + * @return array Formatted customer choices.
1708 352 */
1709 - public static function upload( $settings, $value, $module_id = '' ) {
1710 - $settings = wp_parse_args( $settings, array(
1711 - 'label' => esc_html__( 'Select Image', 'merchant' ),
1712 - ) );
1713 -
1714 - echo '<div class="merchant-upload-wrapper">';
1715 - if ( ! empty( $value ) ) :
1716 - $image = wp_get_attachment_image_src( $value, 'thumbnail' );
1717 - if ( ! empty( $image ) && ! empty( $image[0] ) ) :
1718 - echo '<div class="merchant-upload-image">';
1719 - echo '<i class="merchant-upload-remove dashicons dashicons-no-alt"></i>';
1720 - printf( '<img src="%s" />', esc_url( $image[0] ) );
1721 - echo '</div>';
1722 - endif;
1723 - endif;
1724 - echo '</div>';
1725 -
1726 - $drag_drop = $settings['drag_drop'] ?? false;
1727 - ?>
1728 - <div class="merchant-upload-button-wrapper<?php echo esc_attr( $drag_drop ? ' merchant-upload-button-drag-drop' : '' ); ?>">
1729 - <?php if ( $drag_drop ) : ?>
1730 - <img src="<?php echo esc_url( esc_url( MERCHANT_URI . '/assets/images/upload-icon.svg' ) ); ?>" alt="<?php echo esc_attr__( 'Upload image', 'merchant' ); ?>"/>
1731 - <?php endif; ?>
1732 - <a href="#" class="merchant-upload-button"><?php echo esc_html( $settings['label'] ?? '' ); ?></a>
1733 - </div>
1734 - <input type="hidden" class="merchant-upload-input" name="merchant[<?php echo esc_attr( $settings['id'] ); ?>]" value="<?php echo esc_attr( $value ); ?>"/>
1735 - <?php
353 + public static function get_customers_select2_choices() {
354 + return Merchant_Select2_Choices::get_customers_select2_choices();
1736 355 }
1737 356
1738 - /**
1739 - * Field: Warning
1740 - */
1741 - public static function warning( $settings ) {
1742 - echo wp_kses_post( $settings['content'] );
1743 - }
357 + // ──────────────────────────────────────────────────────
358 + // Admin hooks — small one-off actions.
359 + // ──────────────────────────────────────────────────────
1744 360
1745 361 /**
1746 - * Field: Warning
1747 - */
1748 - public static function info( $settings ) {
1749 - echo wp_kses_post( $settings['content'] );
1750 - }
1751 -
1752 - /**
1753 - * Field: Divider
1754 - */
1755 - public static function divider() {
1756 - }
1757 -
1758 - /**
1759 - * Field: Content
1760 - */
1761 - public static function content( $settings ) {
1762 - echo wp_kses_post( $settings['content'] );
1763 - }
1764 -
1765 - /**
1766 - * Field: Sortable
1767 - */
1768 - public static function sortable( $settings, $value, $module_id = '' ) {
1769 - ?>
1770 - <div class="merchant-sortable">
1771 - <ul class="merchant-sortable-list ui-sortable">
1772 - <?php
1773 - foreach ( $value as $option_key ) :
1774 - $option_val = $settings['options'][ $option_key ];
1775 -
1776 - if ( in_array( $option_key, $value, true ) ) :
1777 - ?>
1778 - <li class="merchant-sortable-item" data-value="<?php
1779 - echo esc_attr( $option_key ); ?>">
1780 - <i class='dashicons dashicons-menu'></i>
1781 - <i class="dashicons dashicons-visibility visibility"></i>
1782 - <?php
1783 - echo esc_html( $option_val ); ?>
1784 - </li>
1785 - <?php
1786 - endif; ?>
1787 - <?php
1788 - endforeach; ?>
1789 -
1790 - <?php
1791 - foreach ( $settings['options'] as $option_key => $option_val ) :
1792 - if ( ! in_array( $option_key, $value, true ) ) :
1793 - $invisible = ! in_array( $option_key, $value, true ) ? ' invisible' : '';
1794 -
1795 - ?>
1796 - <li class="merchant-sortable-item<?php
1797 - echo esc_attr( $invisible ); ?>" data-value="<?php
1798 - echo esc_attr( $option_key ); ?>">
1799 - <i class='dashicons dashicons-menu'></i>
1800 - <i class="dashicons dashicons-visibility visibility"></i>
1801 - <?php
1802 - echo esc_html( $option_val ); ?>
1803 - </li>
1804 - <?php
1805 - endif; ?>
1806 - <?php
1807 - endforeach; ?>
1808 - </ul>
1809 -
1810 - <input class="merchant-sortable-input" type="hidden" name="merchant[<?php
1811 - echo esc_attr( $settings['id'] ); ?>]" value="<?php
1812 - echo esc_attr( wp_json_encode( $value ) ); ?>"/>
1813 - </div>
1814 - <?php
1815 - }
1816 -
1817 - /**
1818 - * Field: Sortable Repeater.
1819 - */
1820 - public static function sortable_repeater( $settings, $value, $module_id = '' ) {
1821 - ?>
1822 - <div class="merchant-sortable-repeater-control<?php
1823 - echo isset( $settings['sorting'] ) && false === $settings['sorting'] ? ' disable-sorting' : ''; ?>">
1824 - <div class="merchant-sortable-repeater sortable regular-field">
1825 - <div class="repeater">
1826 - <input type="text" value="" class="repeater-input"/><span class="dashicons dashicons-menu"></span><a class="customize-control-sortable-repeater-delete"
1827 - href="#"><span
1828 - class="dashicons dashicons-no-alt"></span></a>
1829 - </div>
1830 - </div>
1831 - <button class="button customize-control-sortable-repeater-add" type="button"><?php
1832 - echo esc_html( $settings['button_label'] ); ?></button>
1833 - <input class="merchant-sortable-repeater-input" type="hidden" name="merchant[<?php
1834 - echo esc_attr( $settings['id'] ); ?>]" value="<?php
1835 - echo esc_attr( wp_json_encode( $value ) ); ?>"/>
1836 - </div>
1837 - <?php
1838 - }
1839 -
1840 - public static function wc_coupons( $settings, $value ) {
1841 - $coupons = get_posts( array(
1842 - 'posts_per_page' => - 1,
1843 - 'orderby' => 'title',
1844 - 'order' => 'asc',
1845 - 'post_type' => 'shop_coupon',
1846 - 'post_status' => 'publish',
1847 - ) );
1848 - if ( $coupons ) {
1849 - ?>
1850 - <div class="merchant-wc-coupon-selector merchant-<?php
1851 - echo esc_attr( $settings['id'] ) ?>">
1852 - <?php
1853 - echo '<select name="merchant[' . esc_attr( $settings['id'] ) . ']">';
1854 - echo '<option value="">' . esc_html__( 'Select a coupon', 'merchant' ) . '</option>';
1855 -
1856 - foreach ( $coupons as $coupon ) {
1857 - $coupon_code = strtolower( $coupon->post_title );
1858 -
1859 - echo '<option value="' . esc_attr( $coupon_code ) . '"' . selected( esc_attr( $coupon_code ), $value, false ) . '>';
1860 - echo esc_html( $coupon_code );
1861 - echo '</option>';
1862 - }
1863 -
1864 - echo '</select>';
1865 - echo '<a href="' . esc_url( admin_url( 'edit.php?post_type=shop_coupon' ) ) . '" target="_blank" style="margin-left: 10px;">' . esc_html__( 'Manage coupons',
1866 - 'merchant' ) . '</a>';
1867 - ?>
1868 - </div>
1869 - <?php
1870 - } else {
1871 - echo '<div style="color: red;">';
1872 - echo wp_kses_post(
1873 - sprintf(
1874 - /* Translators: 1. Coupon admin url 2. Link target attribute value */
1875 - __( 'No coupons found! <a href="%1$s" target="%2$s">Create a new coupon</a>', 'merchant' ),
1876 - admin_url( 'post-new.php?post_type=shop_coupon' ),
1877 - '_blank'
1878 - )
1879 - );
1880 - echo '</div>';
1881 - echo '<input type="hidden" name="merchant[' . esc_attr( $settings['id'] ) . ']" value="" />';
1882 - }
1883 - }
1884 -
1885 - /**
1886 - * Field: Date Time.
1887 - *
1888 - * @param $settings array field settings.
1889 - * @param $value string field value.
1890 - * @param $module_id string module id.
362 + * Delete stale module data from the database.
1891 363 *
1892 - * @return void
1893 - */
1894 - public static function date_time( $settings, $value, $module_id = '' ) {
1895 - // All options are documented here: https://air-datepicker.com/docs
1896 - $options = array(
1897 - 'dateFormat' => 'MM-dd-yyyy',
1898 - 'timepicker' => true,
1899 - 'timeFormat' => 'hh:mm AA',
1900 - 'minDate' => 'today',
1901 - 'timeZone' => wp_timezone_string(),
1902 - );
1903 - if ( isset( $settings['options'] ) ) {
1904 - $settings['options'] = wp_parse_args( $settings['options'], $options );
1905 - } else {
1906 - $settings['options'] = $options;
1907 - }
1908 - ?>
1909 - <div class="merchant-datetime-field" data-options="<?php echo esc_attr( wp_json_encode( $settings['options'] ) ); ?>">
1910 - <input type="text" name="merchant[<?php
1911 - echo esc_attr( $settings['id'] ); ?>]" value="<?php
1912 - echo esc_attr( $value ); ?>" placeholder="<?php
1913 - echo isset( $settings['placeholder'] ) ? esc_attr( $settings['placeholder'] ) : ''; ?>"/>
1914 - </div>
1915 - <?php
1916 - }
1917 -
1918 - /**
1919 - * Field: Flexible Content.
364 + * Cleans up data for removed modules (e.g., the deprecated
365 + * `code-snippets` module). Hooked to `admin_init`.
1920 366 *
1921 - * @param array $settings
1922 - * @param mixed $value
367 + * @since 1.9.3
1923 368 *
1924 369 * @return void
1925 370 */
1926 - public static function flexible_content( $settings, $value, $module_id = '' ) {
1927 - $values = ( is_array( $value ) && ! empty( $value ) ) ? $value : array();
1928 - $empty = empty( $values ) ? 'empty' : '';
371 + public function delete_module_data() {
372 + $options = get_option( 'merchant', array() );
1929 373
1930 - $settings = wp_parse_args( $settings, array(
1931 - 'sorting' => false,
1932 - 'style' => 'default',
1933 - 'accordion' => false,
1934 - ) );
1935 -
1936 - $classes = array(
1937 - 'merchant-flexible-content-control',
1938 - "{$settings['style']}-style",
1939 - );
1940 -
1941 - $has_sorting = (bool) ( $settings['sorting'] ?? true );
1942 - if ( ! $has_sorting ) {
1943 - $classes[] = 'disable-sorting';
374 + if ( isset( $options['code-snippets'] ) ) {
375 + unset( $options['code-snippets'] );
376 + update_option( 'merchant', $options );
1944 377 }
1945 -
1946 - $has_accordion = (bool) ( $settings['accordion'] ?? false );
1947 - if ( $has_accordion ) {
1948 - $classes[] = 'has-accordion';
1949 - }
1950 -
1951 - $has_duplicate = (bool) ( $settings['duplicate'] ?? false );
1952 - if ( $has_duplicate ) {
1953 - $classes[] = 'has-duplicate';
1954 - }
1955 - ?>
1956 - <div class="<?php
1957 - echo esc_attr( implode( ' ', $classes ) ); ?>"
1958 - data-id="<?php
1959 - echo esc_attr( $settings['id'] ) ?>">
1960 -
1961 - <div class="layouts" data-id="<?php echo esc_attr( $settings['id'] ) ?>">
1962 - <?php foreach ( $settings['layouts'] as $layout_type => $layout ) : ?>
1963 - <div class="layout" data-type="<?php echo esc_attr( $layout_type ) ?>">
1964 - <div class="layout__inner">
1965 - <?php if ( $has_sorting ) : ?>
1966 - <span class="customize-control-flexible-content-move">
1967 - <svg xmlns="http://www.w3.org/2000/svg" width="10" height="14" viewBox="0 0 10 14" fill="none">
1968 - <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"/>
1969 - <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"/>
1970 - <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"/>
1971 - <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"/>
1972 - <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"/>
1973 - <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"/>
1974 - </svg>
1975 - </span>
1976 - <?php endif; ?>
1977 - <div class="layout-header">
1978 - <div class="layout-count">1</div>
1979 - <div class="layout-title"<?php
1980 - if ( isset( $layout['title-field'] ) && ! empty( $layout['title-field'] ) ) {
1981 - echo ' data-title-field="' . esc_attr( $layout['title-field'] ) . '"';
1982 - } ?>>
1983 - <?php
1984 - echo esc_html( $layout['title'] ) ?>
1985 - </div>
1986 - <div class="layout-toggle">
1987 - <?php if ( $has_accordion ) : ?>
1988 - <span class="customize-control-flexible-content-accordion">
1989 - <svg xmlns="http://www.w3.org/2000/svg" width="10" height="7" viewBox="0 0 10 7" fill="none">
1990 - <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"/>
1991 - </svg>
1992 - </span>
1993 - <?php endif; ?>
1994 - </div>
1995 - </div>
1996 - <div class="layout-body">
1997 - <?php
1998 - foreach ( $layout['fields'] as $sub_field ) :
1999 - $classes = array( 'layout-field' );
2000 -
2001 - if ( isset( $sub_field['classes'] ) ) {
2002 - $classes = array_merge( $classes, $sub_field['classes'] );
2003 - } ?>
2004 - <div class="<?php
2005 - echo esc_attr( implode( ' ', $classes ) ); ?>">
2006 - <?php
2007 - if ( 'fields_group' === $sub_field['type'] ) {
2008 - static::fields_group( $sub_field, $value, $module_id, true, array(
2009 - 'id' => $settings['id'],
2010 - 'option_key' => 0,
2011 - 'value' => $value,
2012 - ) );
2013 - } else {
2014 - static::replace_field(
2015 - $sub_field,
2016 - '',
2017 - array(
2018 - "name=\"merchant[{$sub_field['id']}]",
2019 - 'merchant-module-page-setting-field-upload',
2020 - 'merchant-module-page-setting-field-select_ajax',
2021 - ),
2022 - array(
2023 - "data-name=\"merchant[{$settings['id']}][0][{$sub_field['id']}]",
2024 - 'merchant-module-page-setting-field-upload template',
2025 - 'merchant-module-page-setting-field-select_ajax template',
2026 - ),
2027 - $module_id
2028 - );
2029 - }
2030 - ?>
2031 - </div>
2032 - <?php
2033 - endforeach; ?>
2034 - <input type="hidden" data-name="merchant[<?php
2035 - echo esc_attr( $settings['id'] ) ?>][0][layout]" value="<?php
2036 - echo esc_attr( $layout_type ) ?>">
2037 - </div>
2038 - <?php self::print_flexible_layout_actions( $settings, $layout_type ); ?>
2039 - </div>
2040 - </div>
2041 - <?php
2042 - endforeach; ?>
2043 - </div>
2044 -
2045 - <div class="merchant-flexible-content <?php echo esc_attr( $empty ); ?> sortable">
2046 - <?php
2047 - foreach ( $values as $option_key => $option ) : ?>
2048 - <div class="layout" data-type="<?php echo esc_attr( $option['layout'] ) ?>">
2049 - <div class="layout__inner">
2050 - <?php if ( $has_sorting ) : ?>
2051 - <span class="customize-control-flexible-content-move">
2052 - <svg xmlns="http://www.w3.org/2000/svg" width="10" height="14" viewBox="0 0 10 14" fill="none">
2053 - <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"/>
2054 - <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"/>
2055 - <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"/>
2056 - <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"/>
2057 - <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"/>
2058 - <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"/>
2059 - </svg>
2060 - </span>
2061 - <?php endif; ?>
2062 - <div class="layout-header">
2063 - <div class="layout-count"><?php
2064 - echo absint( $option_key + 1 ) ?></div>
2065 - <div class="layout-title"<?php
2066 - if ( isset( $layout['title-field'] ) && ! empty( $layout['title-field'] ) ) {
2067 - echo ' data-title-field="' . esc_attr( $layout['title-field'] ) . '"';
2068 - } ?>>
2069 - <?php
2070 - echo isset( $settings['layouts'][ $option['layout'] ]['title'] ) ? esc_html( $settings['layouts'][ $option['layout'] ]['title'] ) : '' ?>
2071 - </div>
2072 - <div class="layout-toggle">
2073 - <?php if ( $has_accordion ) : ?>
2074 - <span class="customize-control-flexible-content-accordion">
2075 - <svg xmlns="http://www.w3.org/2000/svg" width="10" height="7" viewBox="0 0 10 7" fill="none">
2076 - <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"/>
2077 - </svg>
2078 - </span>
2079 - <?php endif; ?>
2080 - </div>
2081 - </div>
2082 - <div class="layout-body">
2083 - <?php
2084 - foreach ( $settings['layouts'][ $option['layout'] ]['fields'] as $sub_field ) :
2085 - $classes = array( 'layout-field' );
2086 - if ( isset( $sub_field['classes'] ) ) {
2087 - $classes = array_merge( $classes, $sub_field['classes'] );
2088 - } ?>
2089 - <div class="<?php
2090 - echo esc_attr( implode( ' ', $classes ) ) ?>">
2091 - <?php
2092 - $value = null;
2093 - if ( isset( $option[ $sub_field['id'] ] ) ) {
2094 - $value = $option[ $sub_field['id'] ];
2095 - } elseif ( isset( $sub_field['default'] ) ) {
2096 - $value = $sub_field['default'];
2097 - }
2098 -
2099 - if ( 'fields_group' === $sub_field['type'] ) {
2100 - static::fields_group( $sub_field, $value, $module_id, true, array(
2101 - 'id' => $settings['id'],
2102 - 'option_key' => $option_key,
2103 - 'value' => $option,
2104 - ) );
2105 - } else {
2106 - static::replace_field(
2107 - $sub_field,
2108 - $value,
2109 - "name=\"merchant[{$sub_field['id']}]",
2110 - "name=\"merchant[{$settings['id']}][{$option_key}][{$sub_field['id']}]",
2111 - $module_id
2112 - );
2113 - }
2114 - ?>
2115 - </div>
2116 - <?php
2117 - endforeach; ?>
2118 - <input type="hidden" name="merchant[<?php
2119 - echo esc_attr( $settings['id'] ) ?>][<?php
2120 - echo absint( $option_key ) ?>][layout]"
2121 - value="<?php
2122 - echo esc_attr( $option['layout'] ) ?>">
2123 - </div>
2124 - <?php self::print_flexible_layout_actions( $settings, $layout_type ); ?>
2125 - </div>
2126 - </div>
2127 - <?php
2128 - endforeach; ?>
2129 - </div>
2130 -
2131 - <div class="customize-control-flexible-content-add-wrapper">
2132 - <div class="customize-control-flexible-content-add-list">
2133 - <?php
2134 - foreach ( $settings['layouts'] as $layout_type => $layout ) : ?>
2135 - <a href="#"
2136 - class="customize-control-flexible-content-add"
2137 - data-id="<?php
2138 - echo esc_attr( $settings['id'] ) ?>"
2139 - data-layout="<?php
2140 - echo esc_attr( $layout_type ) ?>">
2141 - <?php
2142 - echo esc_attr( $layout['title'] ) ?>
2143 - </a>
2144 - <?php
2145 - endforeach; ?>
2146 - </div>
2147 - <button class="button customize-control-flexible-content-add-button" type="button"><?php
2148 - echo esc_html( $settings['button_label'] ); ?></button>
2149 - </div>
2150 - </div>
2151 - <?php
2152 378 }
2153 379
2154 380 /**
2155 - * Field: Group.
2156 - *
2157 - * @param $settings array field settings.
2158 - * @param $value mixed field value.
2159 - * @param $module_id string module id.
2160 - * @param $inside_flexible boolean is inside flexible content.
2161 - * @param $args array extra arguments to be passed if inside flexible content.
381 + * Activate the Merchant Pro plugin.
2162 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 + *
2163 389 * @return void
2164 390 */
2165 - public static function fields_group( $settings, $value, $module_id = '', $inside_flexible = false, $args = array() ) {
2166 - $control_field_status = ! empty( $settings['display_status'] ) && $settings['display_status'] === true;
2167 - $accordion = ! empty( $settings['accordion'] ) && $settings['accordion'] === true;
2168 - $state = ! empty( $settings['state'] ) && $settings['state'] === 'open';
2169 - ?>
2170 - <div class="merchant-group-field<?php
2171 - echo $accordion ? ' has-accordion' : '';
2172 - echo $control_field_status ? ' has-flag' : '';
2173 - echo $state ? ' open' : '';
2174 - echo ' merchant-group-field-' . esc_attr( $settings['id'] ) ?>" data-id="<?php
2175 - echo esc_attr( $settings['id'] ) ?>">
2176 - <div class="title-area<?php
2177 - echo $accordion ? ' accordion-style' : '' ?>">
2178 - <?php
2179 - if ( ! empty( $settings['title'] ) ) {
2180 - printf( '<div class="merchant-module-page-setting-field-title">%s<span class="field-status hidden"></span></div>', esc_html( $settings['title'] ) );
2181 - }
2182 - if ( ! empty( $settings['sub-desc'] ) ) {
2183 - printf( '<div class="merchant-module-page-setting-field-desc">%s</div>', wp_kses_post( $settings['sub-desc'] ) );
2184 - }
2185 - if ( $accordion ) {
2186 - ?>
2187 - <span class="accordion-icon dashicons dashicons-arrow-down-alt2"></span>
2188 - <?php
2189 - }
2190 - ?>
2191 - </div>
2192 - <div class="merchant-group-fields-container">
2193 - <?php
2194 - if ( $control_field_status ) {
2195 - /**
2196 - * Field: Status.
2197 - *
2198 - * @since 1.9.12
2199 - */
2200 - $status = apply_filters(
2201 - 'merchant_group_status_field',
2202 - array(
2203 - 'id' => $settings['id'] . '_status',
2204 - 'type' => 'select',
2205 - 'title' => esc_html__( 'Status', 'merchant' ),
2206 - 'options' => array(
2207 - 'inactive' => esc_html__( 'Inactive', 'merchant' ),
2208 - 'active' => esc_html__( 'Active', 'merchant' ),
2209 - ),
2210 - 'default' => isset( $settings['default'] ) ? $settings['default'] : 'active',
2211 - ),
2212 - $settings,
2213 - $value,
2214 - $module_id
2215 - );
2216 - if ( $inside_flexible ) {
2217 - static::replace_field(
2218 - $status,
2219 - isset( $args['value'][$settings['id']][ $status['id'] ] ) ? $args['value'][$settings['id']][ $status['id'] ] : $status['default'] ?? '',
2220 - "name=\"merchant[{$status['id']}]",
2221 - "name=\"merchant[{$args['id']}][{$args['option_key']}][{$settings['id']}][{$status['id']}]\" data-name=\"merchant[{$args['id']}][0][{$settings['id']}][{$status['id']}]",
2222 - $module_id
2223 - );
2224 - } else {
2225 - static::replace_field(
2226 - $status,
2227 - isset( $value[ $status['id'] ] ) ? $value[ $status['id'] ] : '',
2228 - "name=\"merchant[{$status['id']}]",
2229 - "name=\"merchant[{$settings['id']}][{$status['id']}]",
2230 - $module_id
2231 - );
2232 - }
2233 - }
2234 -
2235 - foreach ( $settings['fields'] as $field ) {
2236 - if ( $inside_flexible ) {
2237 - static::replace_field(
2238 - $field,
2239 - isset( $args['value'][$settings['id']][ $field['id'] ] ) ? $args['value'][$settings['id']][ $field['id'] ] : $field['default'] ?? '',
2240 - "name=\"merchant[{$field['id']}]",
2241 - "name=\"merchant[{$args['id']}][{$args['option_key']}][{$settings['id']}][{$field['id']}]\" data-name=\"merchant[{$args['id']}][0][{$settings['id']}][{$field['id']}]",
2242 - $module_id
2243 - );
2244 - } else {
2245 - static::replace_field(
2246 - $field,
2247 - isset( $value[ $field['id'] ] ) ? $value[ $field['id'] ] : '',
2248 - "name=\"merchant[{$field['id']}]",
2249 - "name=\"merchant[{$settings['id']}][{$field['id']}]",
2250 - $module_id
2251 - );
2252 - }
2253 - } ?>
2254 - </div>
2255 - </div>
2256 - <?php
2257 - }
2258 -
2259 - /**
2260 - * Field: Create Page.
2261 - */
2262 - public static function create_page( $settings, $value, $module_id = '' ) {
2263 - $page_id = get_option( $settings['option_name'] );
2264 -
2265 - echo '<div class="merchant-create-page-control">';
2266 -
2267 - if ( $page_id && post_exists( get_the_title( $page_id ) ) && 'publish' === get_post_status( $page_id ) ) {
2268 - echo wp_kses_post(
2269 - sprintf( /* translators: 1: link to edit page */
2270 - __( '<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>',
2271 - 'merchant' ),
2272 - get_admin_url() . 'post.php?post=' . $page_id . '&action=edit',
2273 - get_admin_url() . 'nav-menus.php'
2274 - )
2275 - );
2276 - } else {
2277 - echo '<div class="merchant-create-page-control-create-message">';
2278 - echo wp_kses_post(
2279 - '<p class="merchant-module-page-setting-field-desc mrc-mt-0">'.
2280 - sprintf( /* translators: 1: page name */
2281 - __( 'It looks like you haven\'t created a <strong>%1$s</strong> page yet. Click the below button to create the page.',
2282 - 'merchant' ),
2283 - $settings['page_title']
2284 - ). '</p>'
2285 - );
2286 - echo '</div>';
2287 - echo '<div class="merchant-create-page-control-success-message" style="display: none;">';
2288 - echo wp_kses_post(
2289 - sprintf( /* translators: 1: link to edit page */
2290 - __( '<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>',
2291 - 'merchant' ),
2292 - get_admin_url() . 'post.php?post=&action=edit',
2293 - get_admin_url() . 'nav-menus.php'
2294 - )
2295 - );
2296 - echo '</div>';
2297 - echo wp_kses_post(
2298 - sprintf( /* translators: 1: page title, 2: page meta key, 3: page meta value, 4: option name, 5: nonce, 6: loading text, 7: success text */
2299 - __( '<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>',
2300 - 'merchant' ),
2301 - __( 'Create Page', 'merchant' ),
2302 - $settings['page_title'],
2303 - $settings['page_meta_key'],
2304 - $settings['page_meta_value'],
2305 - $settings['option_name'],
2306 - wp_create_nonce( 'customize-create-page-control-nonce' ),
2307 - __( 'Creating...', 'merchant' ),
2308 - __( 'Created!', 'merchant' )
2309 - )
2310 - );
391 + public function activate_pro_plugin() {
392 + if ( ! isset( $_GET['action'] ) || 'merchant_activate_pro' !== $_GET['action'] ) {
393 + return;
2311 394 }
2312 395
2313 - echo '</div>';
2314 - }
2315 -
2316 - public static function dimensions( $settings, $value, $module_id = '' ) {
2317 - $settings = wp_parse_args( $settings, array(
2318 - 'units' => array(
2319 - 'px' => 'px',
2320 - 'rem' => 'rem',
2321 - 'em' => 'em',
2322 - 'vw' => 'vw',
2323 - 'vh' => 'vh',
2324 - '%' => '%',
2325 - ),
2326 - 'dimensions' => array(
2327 - 'top',
2328 - 'right',
2329 - 'bottom',
2330 - 'left',
2331 - ),
2332 - ) );
2333 - $default_value = array(
2334 - 'unit' => reset( $settings['units'] ),
2335 - );
2336 - foreach ( $settings['dimensions'] as $dimension ) {
2337 - $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;
2338 404 }
2339 - $value = wp_parse_args( $value, $default_value );
2340 - ?>
2341 - <div class="merchant-module-page-settings-unit">
2342 - <select name="merchant[<?php
2343 - echo esc_attr( $settings['id'] ); ?>][unit]">
2344 - <?php
2345 - foreach ( $settings['units'] as $key => $option ) : ?>
2346 - <option value="<?php
2347 - echo esc_attr( $key ); ?>" <?php
2348 - selected( $value['unit'], $key, true ); ?>><?php
2349 - echo esc_html( $option ); ?></option>
2350 - <?php
2351 - endforeach; ?>
2352 - </select>
2353 - </div>
2354 - <div class="merchant-module-page-settings-dimensions">
2355 - <?php
2356 - foreach ( $settings['dimensions'] as $dimension ) : ?>
2357 - <div>
2358 - <input id="merchant-<?php
2359 - echo esc_attr( $settings['id'] ); ?>-<?php
2360 - echo esc_attr( $dimension ) ?>"
2361 - type="number"
2362 - name="merchant[<?php
2363 - echo esc_attr( $settings['id'] ); ?>][<?php
2364 - echo esc_attr( $dimension ) ?>]"
2365 - value="<?php
2366 - echo esc_attr( $value[ $dimension ] ); ?>"/>
2367 - <label for="merchant-<?php
2368 - echo esc_attr( $settings['id'] ); ?>-<?php
2369 - echo esc_attr( $dimension ) ?>">
2370 - <?php
2371 - echo esc_html( ucfirst( $dimension ) ); ?>
2372 - </label>
2373 - </div>
2374 - <?php
2375 - endforeach; ?>
2376 - </div>
2377 - <?php
2378 - }
2379 405
2380 - public static function responsive_dimensions( $settings, $value, $module_id = '' ) {
2381 - $settings = wp_parse_args( $settings, array(
2382 - 'units' => array(
2383 - 'px' => 'px',
2384 - 'rem' => 'rem',
2385 - 'em' => 'em',
2386 - 'vw' => 'vw',
2387 - 'vh' => 'vh',
2388 - '%' => '%',
2389 - ),
2390 - 'dimensions' => array(
2391 - 'top',
2392 - 'right',
2393 - 'bottom',
2394 - 'left',
2395 - ),
2396 - 'devices' => array(
2397 - 'desktop' => 'dashicons-desktop',
2398 - 'tablet' => 'dashicons-tablet',
2399 - 'mobile' => 'dashicons-smartphone',
2400 - ),
2401 - ) );
2402 - $default_values = array();
2403 - foreach ( $settings['devices'] as $device => $icon ) {
2404 - $default_values[ $device ]['unit'] = reset( $settings['units'] );
2405 -
2406 - foreach ( $settings['dimensions'] as $dimension ) {
2407 - $default_values[ $device ][ $dimension ] = 0;
2408 - }
406 + if ( ! current_user_can( 'activate_plugins' ) ) {
407 + return;
2409 408 }
2410 - $value = wp_parse_args( $value, $default_values );
2411 - ?>
2412 - <div class="merchant-module-page-settings-responsive">
2413 - <ul class="merchant-module-page-settings-devices">
2414 - <?php
2415 - foreach ( $settings['devices'] as $device => $icon ) : ?>
2416 - <li class="<?php
2417 - echo esc_attr( $device ); ?>">
2418 - <button type="button" class="preview-<?php
2419 - echo esc_attr( $device ); ?> <?php
2420 - echo $device === key( $settings['devices'] ) ? 'active' : '' ?>"
2421 - data-device="<?php
2422 - echo esc_attr( $device ); ?>">
2423 - <i class="dashicons <?php
2424 - echo esc_attr( $icon ); ?>"></i>
2425 - </button>
2426 - </li>
2427 - <?php
2428 - endforeach; ?>
2429 - </ul>
2430 - <?php
2431 - foreach ( $settings['devices'] as $device => $icon ) : ?>
2432 - <div class="merchant-module-page-settings-device-container <?php
2433 - echo $device === key( $settings['devices'] ) ? 'active' : '' ?>" data-device="<?php
2434 - echo esc_attr( $device ); ?>">
2435 - <div class="merchant-module-page-settings-unit">
2436 - <select name="merchant[<?php
2437 - echo esc_attr( $settings['id'] ); ?>][<?php
2438 - echo esc_attr( $device ) ?>][unit]">
2439 - <?php
2440 - foreach ( $settings['units'] as $key => $option ) : ?>
2441 - <option value="<?php
2442 - echo esc_attr( $key ); ?>" <?php
2443 - selected( $value[ esc_attr( $device ) ]['unit'], $key, true ); ?>><?php
2444 - echo esc_html( $option ); ?></option>
2445 - <?php
2446 - endforeach; ?>
2447 - </select>
2448 - </div>
2449 - <div class="merchant-module-page-settings-dimensions">
2450 - <?php
2451 - foreach ( $settings['dimensions'] as $dimension ) : ?>
2452 - <div>
2453 - <input id="merchant-<?php
2454 - echo esc_attr( $settings['id'] ); ?>-<?php
2455 - echo esc_attr( $device ) ?>-<?php
2456 - echo esc_attr( $dimension ) ?>"
2457 - type="number"
2458 - name="merchant[<?php
2459 - echo esc_attr( $settings['id'] ); ?>][<?php
2460 - echo esc_attr( $device ) ?>][<?php
2461 - echo esc_attr( $dimension ) ?>]"
2462 - value="<?php
2463 - echo esc_attr( $value[ $device ][ $dimension ] ); ?>"/>
2464 - <label for="merchant-<?php
2465 - echo esc_attr( $settings['id'] ); ?>-<?php
2466 - echo esc_attr( $device ) ?>-<?php
2467 - echo esc_attr( $dimension ) ?>">
2468 - <?php
2469 - echo esc_html( ucfirst( $dimension ) ); ?>
2470 - </label>
2471 - </div>
2472 - <?php
2473 - endforeach; ?>
2474 - </div>
2475 - </div>
2476 - <?php
2477 - endforeach; ?>
2478 - </div>
2479 409
2480 - <?php
2481 - }
410 + $result = activate_plugin( 'merchant-pro/merchant-pro.php' );
2482 411
2483 - /**
2484 - * Field: Custom callback.
2485 - */
2486 - public static function custom_callback( $settings, $value, $module_id = '' ) {
2487 - if ( ! empty( $settings['class_name'] ) && ! empty( $settings['callback_name'] ) ) {
2488 - 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() ) );
2489 414 }
2490 415
2491 - if ( ! empty( $settings['callback_name'] ) ) {
2492 - return call_user_func( $settings['callback_name'], $settings, $value );
2493 - }
2494 -
2495 - return false;
416 + wp_safe_redirect( $this->get_safe_merchant_redirect_url() );
417 + exit;
2496 418 }
2497 419
2498 - private static function print_flexible_layout_actions( $settings, $layout_type ) {
2499 - $has_duplicate = ! empty( $settings['duplicate'] );
2500 - $has_accordion = ! empty( $settings['accordion'] );
2501 - ?>
2502 - <div class="layout-actions<?php echo $has_accordion ? esc_attr( ' layout-actions-has_accordion' ) : ' layout-actions-no_accordion'; ?>">
2503 - <a href="#" class="layout-actions__toggle">
2504 - <svg xmlns="http://www.w3.org/2000/svg" width="14" height="4" viewBox="0 0 14 4" fill="none">
2505 - <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"/>
2506 - <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"/>
2507 - <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"/>
2508 - </svg>
2509 - </a>
2510 - <div class="layout-actions__inner" style="display: none;">
2511 - <?php if ( $has_duplicate ) : ?>
2512 - <a
2513 - href="#"
2514 - class="customize-control-flexible-content-duplicate"
2515 - data-id="<?php echo esc_attr( $settings['id'] ); ?>"
2516 - data-layout="<?php echo esc_attr( $layout_type ); ?>"
2517 - title="<?php echo esc_attr__( 'Duplicate', 'merchant' ); ?>">
2518 - <svg xmlns="http://www.w3.org/2000/svg" width="19" height="19" viewBox="0 0 19 22" fill="none">
2519 - <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"/>
2520 - </svg>
2521 - <span><?php echo esc_html__( 'Duplicate', 'merchant' ); ?></span>
2522 - </a>
2523 - <?php endif; ?>
2524 -
2525 - <a class="customize-control-flexible-content-delete" href="#">
2526 - <svg xmlns="http://www.w3.org/2000/svg" width="14" height="17" viewBox="0 0 14 17" fill="none">
2527 - <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"/>
2528 - </svg>
2529 - <span><?php echo esc_html__( 'Delete', 'merchant' ); ?></span>
2530 - </a>
2531 - </div>
2532 - </div>
2533 - <?php
2534 - }
2535 -
2536 420 /**
2537 - * Get category choices for select2
2538 - *
2539 - * @return array
2540 - */
2541 - public static function get_category_select2_choices() {
2542 - $choices = array();
2543 - $categories = merchant_get_product_categories();
2544 -
2545 - if ( is_array( $categories ) && ! empty( $categories ) ) {
2546 - $choices = self::build_category_select2_choices( $categories );
2547 - }
2548 -
2549 - return $choices;
2550 - }
2551 -
2552 - /**
2553 - * Build Select2 choices for hierarchical categories.
2554 - *
2555 - * @param $categories
2556 - * @param $level
421 + * Get a safe redirect URL pointing to a Merchant admin page.
2557 422 *
2558 - * @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.
2559 430 */
2560 - private static function build_category_select2_choices( $categories, $level = 0 ) {
2561 - $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 );
2562 435
2563 - foreach ( $categories as $cat ) {
2564 - $indent = str_repeat( '&nbsp;', $level * 4 ); // Use non-breaking spaces for indentation
2565 -
2566 - $choices[] = array(
2567 - 'id' => esc_attr( $cat['slug'] ?? '' ),
2568 - 'text' => esc_html( $indent . ( $cat['name'] ?? '' ) ),
2569 - );
2570 -
2571 - if ( ! empty( $cat['children'] ) ) {
2572 - $choices = array_merge( $choices, self::build_category_select2_choices( $cat['children'], $level + 1 ) );
2573 - }
436 + if ( $redirect_to === $fallback_url ) {
437 + return $fallback_url;
2574 438 }
2575 439
2576 - return $choices;
2577 - }
440 + $parsed_url = wp_parse_url( $redirect_to );
441 + $query_params = array();
442 + parse_str( $parsed_url['query'] ?? '', $query_params );
2578 443
2579 - /**
2580 - * Get Tag choices for select2
2581 - *
2582 - * @return array
2583 - */
2584 - public static function get_tag_select2_choices() {
2585 - $choices = array();
2586 - $tags = merchant_get_product_tags();
2587 -
2588 - if ( is_array( $tags ) && ! empty( $tags ) ) {
2589 - foreach ( $tags as $slug => $name ) {
2590 - $choices[] = array(
2591 - 'id' => esc_attr( $slug ),
2592 - 'text' => esc_html( $name ),
2593 - );
2594 - }
444 + if ( empty( $query_params['page'] ) || strpos( $query_params['page'], 'merchant' ) !== 0 ) {
445 + return $fallback_url;
2595 446 }
2596 447
2597 - return $choices;
448 + return $redirect_to;
2598 449 }
2599 -
2600 - /**
2601 - * Get User Roles choices for select2
2602 - *
2603 - * @return array
2604 - */
2605 - public static function get_user_roles_select2_choices() {
2606 - $choices = array();
2607 - $user_roles = get_editable_roles();
2608 -
2609 - if ( ! empty( $user_roles ) ) {
2610 - foreach ( $user_roles as $role_id => $role_data ) {
2611 - $choices[] = array(
2612 - 'id' => $role_id,
2613 - 'text' => $role_data['name'],
2614 - );
2615 - }
2616 - }
2617 -
2618 - return $choices;
2619 - }
2620 -
2621 - /**
2622 - * Get Customers choices for select2.
2623 - *
2624 - * @return array
2625 - */
2626 - public static function get_customers_select2_choices() {
2627 - $cache_key = 'customers_select2_choices';
2628 - $choices = get_transient( $cache_key );
2629 -
2630 - if ( ! empty( $choices ) && is_array( $choices ) ) {
2631 - return $choices;
2632 - }
2633 -
2634 - // Get users with the 'customer' role
2635 - $customer_users = get_users(
2636 - array(
2637 - 'role' => 'customer',
2638 - 'fields' => array( 'ID', 'display_name' ),
2639 - )
2640 - );
2641 -
2642 - $choices = array();
2643 - if ( ! empty( $customer_users ) ) {
2644 - foreach ( $customer_users as $user ) {
2645 - $choices[] = array(
2646 - 'id' => $user->ID,
2647 - 'text' => $user->display_name,
2648 - );
2649 - }
2650 - }
2651 -
2652 - // Cache the choices with no expiration. Will be cleared using `clear_customer_choices_cache`
2653 - set_transient( $cache_key, $choices );
2654 -
2655 - return $choices;
2656 - }
2657 -
2658 - /**
2659 - * Clear customers cache when a customer is created/deleted/updated.
2660 - *
2661 - * @param $user_id
2662 - * @param $user
2663 - *
2664 - * @return void
2665 - */
2666 - public function clear_customer_choices_cache( $user_id, $user ) {
2667 - $user_roles = $user->roles ?? array();
2668 - if ( in_array( 'customer', $user_roles, true ) ) {
2669 - delete_transient( 'customers_select2_choices' );
2670 - }
2671 - }
2672 450 }
2673 451
2674 452 Merchant_Admin_Options::instance();
2675 453 }