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