PluginProbe
Orderable – Restaurant & Food Ordering System / 1.17.1
Orderable – Restaurant & Food Ordering System v1.17.1
0.1.4 0.1.5 0.2.0 1.0.0 1.1.0 1.1.1 1.10.0 1.10.1 1.11.0 1.12.0 1.12.1 1.12.2 1.13.0 1.14.0 1.15.0 1.16.0 1.17.0 1.17.1 1.18.0 1.19.0 1.19.1 1.19.2 1.2.0 1.20.0 1.20.1 All 44 releases
orderable / inc / class-settings.php

class-settings.php in Orderable – Restaurant & Food Ordering System 1.17.1, at inc/class-settings.php

1,094 lines 39.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Settings methods.
4 *
5 * @package Orderable/Classes
6 */
7
8 defined( 'ABSPATH' ) || exit;
9
10 /**
11 * Settings class.
12 */
13 class Orderable_Settings {
14 /**
15 * Settings framework instance.
16 *
17 * @var null|Orderable_Settings_Framework
18 */
19 public static $settings_framework = null;
20
21 /**
22 * Settings.
23 *
24 * @var array
25 */
26 public static $settings = array();
27
28 /**
29 * Option group.
30 *
31 * @var string
32 */
33 protected static $option_group = 'orderable';
34
35 /**
36 * Run.
37 */
38 public static function run() {
39 add_action( 'admin_init', array( __CLASS__, 'maybe_opt_in' ) );
40 add_action( 'init', array( __CLASS__, 'init_settings' ) );
41 add_filter( 'plugin_action_links_' . ORDERABLE_BASENAME, array( __CLASS__, 'plugin_settings_link' ) );
42 add_action( 'init', array( __CLASS__, 'init_onboarding' ) );
43 add_action( 'admin_menu', array( __CLASS__, 'add_settings_page' ), 1 );
44 add_filter( 'orderable_default_settings', array( __CLASS__, 'default_settings' ) );
45 add_filter( 'wpsf_register_settings_orderable', array( __CLASS__, 'register_settings' ) );
46 add_filter( 'wpsf_register_settings_orderable', array( __CLASS__, 'reorder_settings_tabs' ), 200 );
47 add_filter( 'iconic_onboard_save_orderable_result', array( __CLASS__, 'save_onboard_settings' ), 10, 2 );
48 add_filter( 'wpsf_title_orderable', array( __CLASS__, 'settings_logo' ) );
49 add_action( 'wpsf_after_title_orderable', array( __CLASS__, 'settings_header' ) );
50
51 // Add category fields.
52 add_action( 'product_cat_add_form_fields', array( __CLASS__, 'add_category_fields' ), 20 );
53 add_action( 'product_cat_edit_form_fields', array( __CLASS__, 'edit_category_fields' ), 20 );
54 add_action( 'created_term', array( __CLASS__, 'save_category_fields' ), 10, 3 );
55 add_action( 'edit_term', array( __CLASS__, 'save_category_fields' ), 10, 3 );
56
57 // Admin Pointers.
58 add_action( 'orderable_admin_assets_enqueued', array( __CLASS__, 'admin_pointers' ) );
59 add_filter( 'orderable_admin_script_deps', array( __CLASS__, 'admin_script_deps' ) );
60 }
61
62 /**
63 * Init settings.
64 */
65 public static function init_settings() {
66 require_once ORDERABLE_VENDOR_PATH . 'wp-settings-framework/wp-settings-framework.php';
67
68 self::$settings_framework = new Orderable_Settings_Framework( null, self::$option_group );
69 self::$settings = self::$settings_framework->get_settings();
70 }
71
72 /**
73 * Add settings page link to plugin listing.
74 *
75 * @param array $links
76 *
77 * @return array
78 */
79 public static function plugin_settings_link( $links = array() ) {
80 $settings_url = admin_url( 'admin.php?page=orderable-settings' );
81 $links[] = sprintf( '<a href="%s">%s</a>', esc_url( $settings_url ), esc_html( __( 'Settings', 'orderable' ) ) );
82
83 return $links;
84 }
85
86 /**
87 * Add settings page.
88 */
89 public static function add_settings_page() {
90 $icon = self::get_orderable_svg_icon();
91
92 add_menu_page( 'Orderable', 'Orderable', 'manage_options', 'orderable', null, $icon, 54 );
93
94 self::$settings_framework->add_settings_page(
95 array(
96 'parent_slug' => 'orderable',
97 'page_title' => __( 'Orderable Settings', 'orderable' ),
98 'menu_title' => __( 'Settings', 'orderable' ),
99 'capability' => 'manage_options',
100 )
101 );
102
103 remove_submenu_page( 'orderable', 'orderable' );
104 }
105
106 /**
107 * Add default settings.
108 *
109 * @param array $default_settings
110 *
111 * @return array
112 */
113 public static function default_settings( $default_settings = array() ) {
114 $default_settings['style_style_color'] = '#000000';
115 $default_settings['style_style_buttons'] = 'rounded';
116 $default_settings['style_products_title_size'] = 20;
117 $default_settings['style_products_price_size'] = 18;
118
119 return $default_settings;
120 }
121
122 /**
123 * Register settings.
124 *
125 * @param array $settings
126 *
127 * @return array
128 */
129 public static function register_settings( $settings = array() ) {
130 $settings = array(
131 'tabs' => array(),
132 'sections' => array(),
133 );
134
135 $settings['tabs'] = array(
136 array(
137 'id' => 'dashboard',
138 'title' => __( 'Dashboard', 'orderable' ),
139 'priority' => 0,
140 ),
141 array(
142 'id' => 'style',
143 'title' => __( 'Style', 'orderable' ),
144 'priority' => 100,
145 ),
146 );
147
148 $settings['sections']['dashboard_getting_started'] = array(
149 'tab_id' => 'dashboard',
150 'section_id' => 'getting_started',
151 'section_title' => __( 'Getting Started', 'orderable' ),
152 'section_description' => sprintf( __( 'Below you will find a playlist of useful "getting started" videos for Orderable. If you need any help getting things set up, please <a href="%s" target="_blank">reach out to our support team</a>.', 'orderable' ), 'https://my.orderable.com/support/?utm_source=orderable&utm_medium=plugin&utm_campaign=getting-started' ),
153 'section_order' => 0,
154 'fields' => array(
155 'playlist' => array(
156 'id' => 'playlist',
157 'title' => '',
158 'type' => 'custom',
159 'output' => wp_oembed_get( 'https://youtube.com/playlist?list=PLUUdHDDAkhAV8-k86JRjB34Xtbp1y6kXh' ),
160 ),
161 ),
162 );
163
164 $settings['sections']['style'] = array(
165 'tab_id' => 'style',
166 'section_id' => 'style',
167 'section_title' => __( 'General', 'orderable' ),
168 'section_description' => '',
169 'section_order' => 10,
170 'fields' => array(
171 'color' => array(
172 'id' => 'color',
173 'title' => __( 'Brand Color', 'orderable' ),
174 'subtitle' => __( 'Select an accent color to be used for buttons and other elements.', 'orderable' ),
175 'type' => 'color',
176 'default' => self::get_setting_default( 'style_style_color' ),
177 ),
178 'buttons' => array(
179 'id' => 'buttons',
180 'title' => __( 'Button Style', 'orderable' ),
181 'subtitle' => __( 'Choose a button style which suits your theme.', 'orderable' ),
182 'type' => 'select',
183 'default' => self::get_setting_default( 'style_style_buttons' ),
184 'choices' => array(
185 'rounded' => __( 'Rounded', 'orderable' ),
186 'square' => __( 'Square', 'orderable' ),
187 ),
188 ),
189 ),
190 );
191
192 $settings['sections']['products_style'] = array(
193 'tab_id' => 'style',
194 'section_id' => 'products',
195 'section_title' => __( 'Product Cards', 'orderable' ),
196 'section_description' => '',
197 'section_order' => 15,
198 'fields' => array(
199 'title_size' => array(
200 'id' => 'title_size',
201 'title' => __( 'Product Title Size (px)', 'orderable' ),
202 'subtitle' => __( 'Set the product title font size in pixels.', 'orderable' ),
203 'type' => 'number',
204 'default' => self::get_setting_default( 'style_products_title_size' ),
205 ),
206 'price_size' => array(
207 'id' => 'price_size',
208 'title' => __( 'Product Price Size (px)', 'orderable' ),
209 'subtitle' => __( 'Set the product price font size in pixels.', 'orderable' ),
210 'type' => 'number',
211 'default' => self::get_setting_default( 'style_products_price_size' ),
212 ),
213 ),
214 );
215
216 return $settings;
217 }
218
219 /**
220 * Reorder settings tabs.
221 *
222 * @param array $settings
223 *
224 * @return array
225 */
226 public static function reorder_settings_tabs( $settings = array() ) {
227 usort(
228 $settings['tabs'],
229 function ( $a, $b ) {
230 $a['priority'] = empty( $a['priority'] ) ? 0 : absint( $a['priority'] );
231 $b['priority'] = empty( $b['priority'] ) ? 0 : absint( $b['priority'] );
232
233 return $a['priority'] - $b['priority'];
234 }
235 );
236
237 return $settings;
238 }
239
240 /**
241 * Get setting default.
242 *
243 * @param string $setting_name
244 *
245 * @return bool|mixed
246 */
247 public static function get_setting_default( $setting_name = '' ) {
248 if ( empty( $setting_name ) ) {
249 return false;
250 }
251
252 $defaults = apply_filters( 'orderable_default_settings', array() );
253
254 if ( ! isset( $defaults[ $setting_name ] ) ) {
255 return false;
256 }
257
258 return $defaults[ $setting_name ];
259 }
260
261 /**
262 * Get setting.
263 *
264 * This method is accessible before the settings are assign to self::$settings.
265 *
266 * @param string $setting_name
267 *
268 * @return bool|mixed
269 */
270 public static function get_setting( $setting_name = '' ) {
271 if ( empty( $setting_name ) ) {
272 return false;
273 }
274
275 /**
276 * Filter the orderable settings.
277 *
278 * @since 1.8.0
279 * @hook orderable_settings
280 * @param false|array $settings The orderable settings.
281 * @param string $setting_name The setting name to be retrieved.
282 * @return false|array New value
283 */
284 $settings = apply_filters( 'orderable_settings', false, $setting_name );
285
286 if ( isset( $settings[ $setting_name ] ) ) {
287 return $settings[ $setting_name ];
288 }
289
290 $settings = get_option( self::$option_group . '_settings' );
291
292 if ( isset( $settings[ $setting_name ] ) ) {
293 return $settings[ $setting_name ];
294 }
295
296 return self::get_setting_default( $setting_name );
297 }
298
299 /**
300 * @param string $suffix
301 *
302 * @return bool
303 */
304 public static function is_settings_page( $suffix = '' ) {
305 if ( ! is_admin() ) {
306 return false;
307 }
308
309 // Allow bypass from other modules.
310 if ( apply_filters( 'orderable_is_settings_page', false ) ) {
311 return true;
312 }
313
314 $valid_pages = apply_filters(
315 'orderable_valid_admin_pages',
316 array(
317 self::$option_group . '-settings' . $suffix,
318 )
319 );
320 $page = empty( $_GET['page'] ) ? '' : sanitize_text_field( wp_unslash( $_GET['page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
321
322 if ( ! in_array( $page, $valid_pages, true ) ) {
323 return false;
324 }
325
326 return true;
327 }
328
329 /**
330 * Init onboarding.
331 */
332 public static function init_onboarding() {
333 if ( ! self::is_settings_page() || defined( 'WC_DOING_AJAX' ) ) {
334 if ( empty( $_REQUEST['plugin_slug'] ) || 'orderable' !== $_REQUEST['plugin_slug'] ) {
335 return;
336 }
337 }
338
339 include_once ORDERABLE_INC_PATH . 'vendor/iconic-onboard/class-iconic-onboard.php';
340
341 $slides = apply_filters(
342 'orderable_onboarding_slides',
343 array(
344 'welcome' => array(
345 'header_image' => ORDERABLE_URL . '/assets/img/onboarding/orderable-onboarding-header.jpg',
346 'title' => 'Welcome',
347 'description' => 'Thank you for choosing Orderable to add local ordering to your website. This short setup wizard will guide you though configuring Orderable.',
348 'button_text' => 'Begin <span class="dashicons dashicons-arrow-right-alt2"></span>',
349 'button_icon' => '',
350 ),
351 'requirements' => array(
352 'title' => 'Requirements',
353 'description' => 'Orderable requires WooCommerce for the order checkout, management, and reporting. In this step we will install WooCommerce for you. This might take a couple of minutes.',
354 'button_text' => 'Install &amp; Activate WooCommerce',
355 'wait' => 'install_plugin',
356 'json_data' => array(
357 'wait_text' => __( 'Installing...', 'orderable' ),
358 'plugin_data' => array(
359 'name' => __( 'WooCommerce', 'orderable' ),
360 'repo-slug' => 'woocommerce',
361 'file' => 'woocommerce.php',
362 ),
363 ),
364 ),
365 'business' => array(
366 'title' => 'Business Info',
367 'description' => 'Orderable needs some basic business information that will be used when orders are placed.',
368 'button_text' => 'Continue <span class="dashicons dashicons-arrow-right-alt2"></span>',
369 'fields' => array(
370 'name' => array(
371 'id' => 'business_name',
372 'title' => __( 'Business Name', 'orderable' ),
373 'desc' => '',
374 'type' => 'text',
375 'default' => get_bloginfo( 'name' ),
376 ),
377 'address' => array(
378 'id' => 'business_address',
379 'title' => __( 'Address line 1', 'orderable' ),
380 'desc' => '',
381 'type' => 'text',
382 ),
383 'address_2' => array(
384 'id' => 'business_address_2',
385 'title' => __( 'Address line 2', 'orderable' ),
386 'desc' => '',
387 'type' => 'text',
388 ),
389 'city' => array(
390 'id' => 'business_city',
391 'title' => __( 'City', 'orderable' ),
392 'desc' => '',
393 'type' => 'text',
394 ),
395 'default_country' => array(
396 'id' => 'default_country',
397 'title' => __( 'Country / State', 'orderable' ),
398 'desc' => '',
399 'type' => 'select',
400 'choices' => array(),
401 ),
402 'postcode' => array(
403 'id' => 'business_postcode',
404 'title' => __( 'Postcode / ZIP', 'orderable' ),
405 'desc' => '',
406 'type' => 'text',
407 ),
408 'email' => array(
409 'id' => 'business_email',
410 'title' => __( 'Business Email', 'orderable' ),
411 'desc' => '',
412 'type' => 'text',
413 'default' => get_option( 'admin_email' ),
414 ),
415 array(
416 'id' => 'opt_in',
417 'title' => '',
418 'desc' => __( 'Please keep me up to date via email on new Orderable training and features', 'orderable' ),
419 'type' => 'checkbox',
420 'default' => 1,
421 ),
422 ),
423 ),
424 'location' => array(
425 'title' => 'Location Info',
426 'description' => 'Help us set up your ordering system. You can refine these details further after completing the onboarding process.',
427 'button_text' => "Continue <span class='dashicons dashicons-arrow-right-alt2'></span>",
428 'fields' => array(
429 array(
430 'id' => 'services',
431 'title' => __( 'Which services do you offer?', 'orderable' ),
432 'desc' => '',
433 'type' => 'checkboxes',
434 'choices' => array(
435 'flat_rate' => __( 'Delivery', 'orderable' ),
436 'local_pickup' => __( 'Pickup', 'orderable' ),
437 ),
438 ),
439 array(
440 'id' => 'days',
441 'title' => __( 'Which days of the week are you open?', 'orderable' ),
442 'desc' => '',
443 'type' => 'checkboxes',
444 'choices' => array(
445 1 => __( 'Monday', 'orderable' ),
446 2 => __( 'Tuesday', 'orderable' ),
447 3 => __( 'Wednesday', 'orderable' ),
448 4 => __( 'Thursday', 'orderable' ),
449 5 => __( 'Friday', 'orderable' ),
450 6 => __( 'Saturday', 'orderable' ),
451 0 => __( 'Sunday', 'orderable' ),
452 ),
453 ),
454 array(
455 'id' => 'open_hours',
456 'title' => __( 'What are your normal opening hours?', 'orderable' ),
457 'desc' => '',
458 'type' => 'custom',
459 'default' => self::get_open_hours_fields(),
460 ),
461 ),
462 ),
463 'done' => array(
464 'title' => 'All Done',
465 'description' => "Congratulations, You Did It! Orderable is ready to use on your website. You've successfully completed the setup process and all that is left for you to do is create/customize your products.",
466 'button_text' => "Save and Finish <span class='dashicons dashicons-yes'></span>",
467 ),
468 )
469 );
470
471 if ( function_exists( 'WC' ) ) {
472 unset( $slides['requirements'] );
473
474 $base = wc_get_base_location();
475 $countries_states = self::get_countries_states();
476 $default = '';
477
478 if ( isset( $base['country'] ) && isset( $countries_states[ 'country:' . $base['country'] ] ) ) {
479 $default = 'country:' . $base['country'];
480 }
481
482 if ( isset( $base['country'] ) && isset( $base['state'] ) && isset( $countries_states[ $base['country'] ] ) ) {
483 $state = 'state:' . $base['country'] . ':' . $base['state'];
484 if ( isset( $countries_states[ $base['country'] ]['values'][ $state ] ) ) {
485 $default = $state;
486 }
487 }
488
489 $slides['business']['fields']['default_country']['choices'] = $countries_states;
490 $slides['business']['fields']['default_country']['default'] = $default;
491 $slides['business']['fields']['address']['default'] = WC()->countries->get_base_address();
492 $slides['business']['fields']['address_2']['default'] = WC()->countries->get_base_address_2();
493 $slides['business']['fields']['city']['default'] = WC()->countries->get_base_city();
494 $slides['business']['fields']['postcode']['default'] = WC()->countries->get_base_postcode();
495 }
496
497 Orderable_Onboard::run(
498 array(
499 'version' => ORDERABLE_VERSION,
500 'plugin_slug' => 'orderable',
501 'plugin_url' => ORDERABLE_URL,
502 'plugin_path' => ORDERABLE_PATH,
503 'slides' => $slides,
504 )
505 );
506 }
507
508 /**
509 * Save the onboarding data in database.
510 *
511 * @param array $result Was the submission a success?
512 * @param array $fields Submitted fields.
513 *
514 * @return bool|array $result
515 */
516 public static function save_onboard_settings( $result, $fields ) {
517 if ( empty( $fields['orderable_settings'] ) ) {
518 return false;
519 }
520
521 // Get existing/default settings.
522 $settings = get_option( 'orderable_settings', array() );
523
524 // Assign opening hours.
525 $from = array(
526 'hour' => sanitize_text_field( $fields['orderable_settings']['iconic_onboard_open_hours']['from']['hour'] ),
527 'minute' => sanitize_text_field( $fields['orderable_settings']['iconic_onboard_open_hours']['from']['minute'] ),
528 'period' => sanitize_text_field( $fields['orderable_settings']['iconic_onboard_open_hours']['from']['period'] ),
529 );
530 $to = array(
531 'hour' => sanitize_text_field( $fields['orderable_settings']['iconic_onboard_open_hours']['to']['hour'] ),
532 'minute' => sanitize_text_field( $fields['orderable_settings']['iconic_onboard_open_hours']['to']['minute'] ),
533 'period' => sanitize_text_field( $fields['orderable_settings']['iconic_onboard_open_hours']['to']['period'] ),
534 );
535
536 $settings['store_general_open_hours'] = array();
537
538 $days = is_array( $fields['orderable_settings']['iconic_onboard_days'] ) ? $fields['orderable_settings']['iconic_onboard_days'] : array();
539
540 for ( $day = 0; $day <= 6; $day ++ ) {
541 $settings['store_general_open_hours'][ $day ] = array(
542 'enabled' => in_array( (string) $day, $days, true ),
543 'from' => $from,
544 'to' => $to,
545 'max_orders' => '',
546 );
547 }
548
549 // Save Business details.
550 $name = isset( $fields['orderable_settings']['iconic_onboard_business_name'] ) ? wc_clean( wp_unslash( $fields['orderable_settings']['iconic_onboard_business_name'] ) ) : '';
551 $address = isset( $fields['orderable_settings']['iconic_onboard_business_address'] ) ? wc_clean( wp_unslash( $fields['orderable_settings']['iconic_onboard_business_address'] ) ) : '';
552 $address_2 = isset( $fields['orderable_settings']['iconic_onboard_business_address_2'] ) ? wc_clean( wp_unslash( $fields['orderable_settings']['iconic_onboard_business_address_2'] ) ) : '';
553 $city = isset( $fields['orderable_settings']['iconic_onboard_business_city'] ) ? wc_clean( wp_unslash( $fields['orderable_settings']['iconic_onboard_business_city'] ) ) : '';
554 $country_state = isset( $fields['orderable_settings']['iconic_onboard_default_country'] ) ? wc_clean( wp_unslash( $fields['orderable_settings']['iconic_onboard_default_country'] ) ) : '';
555 $stripped_country_state = $country_state ? str_replace( array( 'country:', 'state:' ), '', $country_state ) : '';
556 $stripped_country_state = explode( ':', $stripped_country_state )[0];
557 $postcode = isset( $fields['orderable_settings']['iconic_onboard_business_postcode'] ) ? wc_clean( wp_unslash( $fields['orderable_settings']['iconic_onboard_business_postcode'] ) ) : '';
558 $email = isset( $fields['orderable_settings']['iconic_onboard_business_email'] ) ? wc_clean( wp_unslash( $fields['orderable_settings']['iconic_onboard_business_email'] ) ) : '';
559 $opt_in = ! empty( $fields['orderable_settings']['iconic_onboard_opt_in'] );
560
561 if ( ! empty( $name ) ) {
562 update_option( 'blogname', $name );
563 }
564
565 update_option( 'woocommerce_store_address', $address );
566 update_option( 'woocommerce_store_address_2', $address_2 );
567 update_option( 'woocommerce_store_city', $city );
568 update_option( 'woocommerce_default_country', $stripped_country_state );
569 update_option( 'woocommerce_store_postcode', $postcode );
570 update_option( 'admin_email', $email );
571
572 // Process opt in
573 if ( $opt_in ) {
574 Orderable_Webhooks::subscribe( $email );
575 }
576
577 // Restrict delivery area to country/state.
578 if ( ! empty( $stripped_country_state ) ) {
579 update_option( 'woocommerce_allowed_countries', 'specific' );
580 update_option( 'woocommerce_specific_allowed_countries', array( $stripped_country_state ) );
581 }
582
583 // Save store services.
584 if ( ! empty( $fields['orderable_settings']['iconic_onboard_services'] ) ) {
585 $settings['store_general_services'] = array();
586
587 $existing_zone_id = null;
588 $zone_name = __( 'Local', 'orderable' );
589 $zones = WC_Shipping_Zones::get_zones();
590
591 if ( ! empty( $zones ) ) {
592 foreach ( $zones as $zone ) {
593 if ( $zone_name !== $zone['zone_name'] ) {
594 continue;
595 }
596
597 $existing_zone_id = $zone['id'];
598 $shipping_method_instances = ! empty( $zone['shipping_methods'] ) ? array_keys( $zone['shipping_methods'] ) : false;
599 break;
600 }
601 }
602
603 $zone = new WC_Shipping_Zone( $existing_zone_id );
604 $zone->set_zone_name( $zone_name );
605
606 if ( ! empty( $country_state ) ) {
607 $zone->clear_locations();
608 $location_parts = explode( ':', $country_state );
609
610 switch ( $location_parts[0] ) {
611 case 'state':
612 $zone->add_location( $location_parts[1] . ':' . $location_parts[2], 'state' );
613 break;
614 case 'country':
615 $zone->add_location( $location_parts[1], 'country' );
616 break;
617 case 'continent':
618 $zone->add_location( $location_parts[1], 'continent' );
619 break;
620 }
621 }
622
623 if ( ! empty( $shipping_method_instances ) ) {
624 foreach ( $shipping_method_instances as $shipping_method_instance_id ) {
625 $zone->delete_shipping_method( $shipping_method_instance_id );
626 }
627 }
628
629 $service_types = array(
630 'flat_rate' => 'delivery',
631 'local_pickup' => 'pickup',
632 );
633
634 foreach ( $fields['orderable_settings']['iconic_onboard_services'] as $service ) {
635 $service = wc_clean( $service );
636 $zone->add_shipping_method( $service );
637
638 $settings['store_general_services'][] = $service_types[ $service ];
639 }
640
641 $zone->save();
642 }
643
644 // Misc.
645 $settings['store_general_service_hours_pickup_same'] = 1;
646
647 // Save settings.
648 update_option( 'orderable_settings', $settings );
649
650 /**
651 * Action hook for when the onboard settings are saved.
652 *
653 * @param array $fields The settings.
654 */
655 do_action( 'orderable_onboard_settings_saved', $fields );
656
657 // Return success.
658 return $result;
659 }
660
661 /**
662 * Get countries and states for select field.
663 *
664 * @return array
665 */
666 public static function get_countries_states() {
667 $countries_states = array();
668
669 if ( ! function_exists( 'WC' ) ) {
670 return $countries_states;
671 }
672
673 $countries = WC()->countries->get_countries();
674
675 if ( empty( $countries ) ) {
676 return $countries_states;
677 }
678
679 foreach ( $countries as $key => $value ) {
680 $countries_states[ 'country:' . $key ] = $value;
681 $states = WC()->countries->get_states( $key );
682
683 if ( empty( $states ) ) {
684 continue;
685 }
686
687 $countries_states[ $key ] = array(
688 'group_label' => $value,
689 'values' => array(),
690 );
691
692 foreach ( $states as $state_key => $state_value ) {
693 $state_key = sprintf( 'state:%s:%s', $key, $state_key );
694 $countries_states[ $key ]['values'][ $state_key ] = sprintf( '%s &mdash; %s', $value, $state_value );
695 }
696 }
697
698 return $countries_states;
699 }
700
701 /**
702 * Get open hours fields.
703 *
704 * @return string
705 */
706 public static function get_open_hours_fields() {
707 ob_start();
708 ?>
709 <div class="iconic-onboard-modal-setting__field-section">
710 <div style="margin: 0 0 8px;"><?php _e( 'From:', 'orderable' ); ?></div>
711 <?php
712 echo Orderable_Timings_Settings::get_time_field(
713 'orderable_settings[iconic_onboard_open_hours][from]',
714 array(
715 'hour' => 9,
716 'minute' => '00',
717 'period' => 'AM',
718 )
719 );
720 ?>
721 </div>
722 <div class="iconic-onboard-modal-setting__field-section">
723 <div style="margin: 0 0 8px;"><?php _e( 'To:', 'orderable' ); ?></div>
724 <?php
725 echo Orderable_Timings_Settings::get_time_field(
726 'orderable_settings[iconic_onboard_open_hours][to]',
727 array(
728 'hour' => 5,
729 'minute' => '00',
730 'period' => 'PM',
731 )
732 );
733 ?>
734 </div>
735 <?php
736
737 return ob_get_clean();
738 }
739
740 /**
741 * Category fields.
742 */
743 public static function add_category_fields() {
744 ?>
745 <div class="form-field term-display-type-wrap">
746 <label for="visibility"><?php esc_html_e( 'Visibility', 'orderable' ); ?></label>
747 <select id="visibility" name="visibility" class="postform">
748 <option value=""><?php esc_html_e( 'Default', 'orderable' ); ?></option>
749 <option value="hidden"><?php esc_html_e( 'Hidden', 'orderable' ); ?></option>
750 </select>
751 <p><?php _e( 'Choose whether to hide (on the frontend) this category archive and all single product pages within this category. Hiding them is recommended if using this category in the Orderable menu shortcode.', 'orderable' ); ?></p>
752 </div>
753 <?php
754 }
755
756 /**
757 * Edit category fields.
758 *
759 * @param mixed $term Term (category) being edited.
760 */
761 public static function edit_category_fields( $term ) {
762 $visibility = get_term_meta( $term->term_id, 'visibility', true );
763 ?>
764 <tr class="form-field term-display-type-wrap">
765 <th scope="row" valign="top">
766 <label for="visibility"><?php esc_html_e( 'Visibility', 'orderable' ); ?></label>
767 </th>
768 <td>
769 <select id="visibility" name="visibility" class="postform">
770 <option value="" <?php selected( '', $visibility ); ?>><?php esc_html_e( 'Default', 'orderable' ); ?></option>
771 <option value="hidden" <?php selected( 'hidden', $visibility ); ?>><?php esc_html_e( 'Hidden', 'orderable' ); ?></option>
772 </select>
773 <p><?php _e( 'Choose whether to hide (on the frontend) this category archive and all single product pages within this category. Hiding them is recommended if using this category in the Orderable menu shortcode.', 'orderable' ); ?></p>
774 </td>
775 </tr>
776 <?php
777 }
778
779 /**
780 * Save category fields.
781 *
782 * @param mixed $term_id Term ID being saved.
783 * @param mixed $tt_id Term taxonomy ID.
784 * @param string $taxonomy Taxonomy slug.
785 */
786 public static function save_category_fields( $term_id, $tt_id = '', $taxonomy = '' ) {
787 if ( 'product_cat' !== $taxonomy ) {
788 return;
789 }
790
791 $visibility = isset( $_POST['visibility'] ) ? sanitize_text_field( wp_unslash( $_POST['visibility'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
792
793 update_term_meta( $term_id, 'visibility', $visibility );
794 }
795
796 /**
797 * Get hidden category IDs.
798 *
799 * @return array
800 */
801 public static function get_hidden_categories() {
802 global $wpdb;
803
804 static $hidden_categories = null;
805
806 if ( ! is_null( $hidden_categories ) ) {
807 return apply_filters( 'orderable_hidden_categories', $hidden_categories );
808 }
809
810 $hidden_categories = array();
811
812 $results = $wpdb->get_results(
813 "SELECT DISTINCT tm.term_id
814 FROM $wpdb->term_taxonomy AS tt
815 INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id
816 INNER JOIN $wpdb->termmeta AS tm ON t.term_id = tm.term_id
817 WHERE tt.taxonomy = 'product_cat'
818 AND tm.meta_key = 'visibility'
819 AND tm.meta_value IN ( 'hidden' )
820
821 UNION DISTINCT
822
823 SELECT DISTINCT term_id
824 FROM $wpdb->term_taxonomy
825 WHERE parent IN (
826 SELECT DISTINCT tm.term_id
827 FROM $wpdb->term_taxonomy AS tt
828 INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id
829 INNER JOIN $wpdb->termmeta AS tm ON t.term_id = tm.term_id
830 WHERE tt.taxonomy = 'product_cat'
831 AND tm.meta_key = 'visibility'
832 AND tm.meta_value IN ( 'hidden' )
833 )",
834 ARRAY_A
835 );
836
837 if ( is_wp_error( $results ) || empty( $results ) ) {
838 return apply_filters( 'orderable_hidden_categories', $hidden_categories );
839 }
840
841 $hidden_categories = array_map( 'absint', wp_list_pluck( $results, 'term_id' ) );
842
843 return apply_filters( 'orderable_hidden_categories', $hidden_categories );
844 }
845
846 /**
847 * Add logo to settings page.
848 */
849 public static function settings_logo( $title = '' ) {
850 return sprintf( '<img src="%s" width="164" height="36" />', esc_url( ORDERABLE_ASSETS_URL . 'img/orderable-logo.svg' ) );
851 }
852
853 /**
854 * Help buttons and version number
855 */
856 public static function settings_header() {
857 ?>
858 <style>
859 .wpsf-settings__header {
860 justify-content: space-between;
861 }
862
863 .orderable-settings-header {
864 display: flex;
865 align-items: center;
866 }
867
868 .orderable-settings-header__links {
869 margin: 0 10px 0 0;
870 padding: 0;
871 list-style: none none outside;
872 height: 20px;
873 }
874
875 .orderable-settings-header__links li {
876 margin: 0 10px 0 0;
877 padding: 0;
878 display: inline-block;
879 }
880
881 .orderable-settings-header__links a {
882 text-decoration: none;
883 color: #64707B !important;
884 white-space: nowrap;
885 display: flex;
886 align-items: center;
887 }
888
889 .orderable-settings-header__links a:hover {
890 color: #7031F5 !important;
891 }
892
893 .orderable-settings-header__links a svg {
894 fill: #7031F5;
895 margin-right: 4px;
896 }
897
898 @media screen and (max-width: 648px) {
899 .orderable-settings-header__links {
900 display: none;
901 }
902 }
903 </style>
904 <div class="orderable-settings-header">
905 <ul class="orderable-settings-header__links">
906 <li><a href="https://my.orderable.com/support/?utm_source=orderable&utm_medium=plugin&utm_campaign=settings-header" target="_blank">
907 <svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20px" height="20px">
908 <path d="M 12 2 C 6.4889971 2 2 6.4889971 2 12 C 2 17.511003 6.4889971 22 12 22 C 17.511003 22 22 17.511003 22 12 C 22 6.4889971 17.511003 2 12 2 z M 12 4 C 16.430123 4 20 7.5698774 20 12 C 20 16.430123 16.430123 20 12 20 C 7.5698774 20 4 16.430123 4 12 C 4 7.5698774 7.5698774 4 12 4 z M 12 6 C 9.79 6 8 7.79 8 10 L 10 10 C 10 8.9 10.9 8 12 8 C 13.1 8 14 8.9 14 10 C 14 12 11 12.367 11 15 L 13 15 C 13 13.349 16 12.5 16 10 C 16 7.79 14.21 6 12 6 z M 11 16 L 11 18 L 13 18 L 13 16 L 11 16 z" />
909 </svg> <?php _e( 'Get Help', 'orderable' ); ?></a></li>
910 <li><a href="https://my.orderable.com/roadmap/?utm_source=orderable&utm_medium=plugin&utm_campaign=settings-header" target="_blank">
911 <svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20px" height="20px">
912 <path d="M 11 0 L 11 3 L 13 3 L 13 0 L 11 0 z M 4.2226562 2.8085938 L 2.8085938 4.2226562 L 4.9296875 6.34375 L 6.34375 4.9296875 L 4.2226562 2.8085938 z M 19.777344 2.8085938 L 17.65625 4.9296875 L 19.070312 6.34375 L 21.191406 4.2226562 L 19.777344 2.8085938 z M 12 5 C 8.1456661 5 5 8.1456661 5 12 C 5 14.767788 6.6561188 17.102239 9 18.234375 L 9 21 C 9 22.093063 9.9069372 23 11 23 L 13 23 C 14.093063 23 15 22.093063 15 21 L 15 18.234375 C 17.343881 17.102239 19 14.767788 19 12 C 19 8.1456661 15.854334 5 12 5 z M 12 7 C 14.773666 7 17 9.2263339 17 12 C 17 14.184344 15.605334 16.022854 13.666016 16.708984 L 13 16.943359 L 13 21 L 11 21 L 11 16.943359 L 10.333984 16.708984 C 8.3946664 16.022854 7 14.184344 7 12 C 7 9.2263339 9.2263339 7 12 7 z M 0 11 L 0 13 L 3 13 L 3 11 L 0 11 z M 21 11 L 21 13 L 24 13 L 24 11 L 21 11 z M 4.9296875 17.65625 L 2.8085938 19.777344 L 4.2226562 21.191406 L 6.34375 19.070312 L 4.9296875 17.65625 z M 19.070312 17.65625 L 17.65625 19.070312 L 19.777344 21.191406 L 21.191406 19.777344 L 19.070312 17.65625 z" />
913 </svg> <?php _e( 'Request Feature', 'orderable' ); ?></a></li>
914 <li><a href="https://orderable.com/documentation/?utm_source=orderable&utm_medium=plugin&utm_campaign=settings-header" target="_blank">
915 <svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20px" height="20px">
916 <path d="M 6 2 C 4.895 2 4 2.895 4 4 L 4 20 C 4 21.105 4.895 22 6 22 L 18 22 C 19.105 22 20 21.105 20 20 L 20 4 C 20 2.895 19.105 2 18 2 L 14 2 L 8 2 L 6 2 z M 6 4 L 8 4 L 8 12 L 11 10.5 L 14 12 L 14 4 L 18 4 L 18 20 L 6 20 L 6 4 z M 10 4 L 12 4 L 12 8.7636719 L 11.894531 8.7109375 L 11 8.2636719 L 10.105469 8.7109375 L 10 8.7636719 L 10 4 z" />
917 </svg> <?php _e( 'View Docs', 'orderable' ); ?></a></li>
918 </ul>
919 <?php printf( '<span class="orderable-settings-header__version" style="margin: 0 0 0 auto; background: #f4f5f6; display: inline-block; padding: 0 10px; border-radius: 13px; height: 26px; line-height: 26px; white-space: nowrap; box-sizing: border-box; color: #65707b;">v%s</span>', ORDERABLE_VERSION ); ?>
920 </div>
921 <?php
922 }
923
924 /**
925 * Add pointers.
926 */
927 public static function admin_pointers() {
928 $screen_id = self::screen_has_pointers();
929
930 if ( ! $screen_id ) {
931 return;
932 }
933
934 $pointers = array();
935
936 if ( $screen_id === 'orderable_page_orderable-settings' ) {
937 $pointers['orderable-store-settings'] = array(
938 'target' => '#toplevel_page_orderable a[href="admin.php?page=orderable-location"]',
939 'next' => 'orderable-layout-builder',
940 'options' => array(
941 'content' => '<h3>' . esc_html__( 'Set Up Your Location', 'orderable' ) . '</h3>' .
942 '<p>' .
943 esc_html__( "Configure your location's opening hours, delivery/pickup schedule, and holidays.", 'orderable' ) .
944 ' <a href="https://orderable.com/getting-started?utm_source=orderable&utm_medium=plugin&utm_campaign=pointer" target="_blank">' . esc_html__( 'Learn more' ) . '</a>.' .
945 '</p>',
946 'position' => array(
947 'edge' => 'left',
948 'align' => 'left',
949 ),
950 ),
951 );
952
953 $pointers['orderable-layout-builder'] = array(
954 'target' => '#toplevel_page_orderable a[href="edit.php?post_type=orderable_layouts"]',
955 'next' => 'orderable-order-view',
956 'options' => array(
957 'content' => '<h3>' . esc_html__( 'Product Layouts', 'orderable' ) . '</h3>' .
958 '<p>' .
959 esc_html__( 'Use the Layout Builder to create a product list based on category. Embed your layout using the shortcode or block.', 'orderable' ) .
960 ' <a href="https://orderable.com/layout-builder?utm_source=orderable&utm_medium=plugin&utm_campaign=pointer" target="_blank">' . esc_html__( 'Learn more' ) . '</a>.' .
961 '</p>',
962 'position' => array(
963 'edge' => 'left',
964 'align' => 'left',
965 ),
966 ),
967 );
968
969 $pointers['orderable-order-view'] = array(
970 'target' => '#toplevel_page_orderable a[href="edit.php?post_type=shop_order&orderable_live_view"]',
971 'options' => array(
972 'content' => '<h3>' . esc_html__( 'Live Order View', 'orderable' ) . '</h3>' .
973 '<p>' .
974 esc_html__( 'Use the Live Order View to get notified and manage orders in real time.', 'orderable' ) .
975 ' <a href="https://orderable.com/process-orders?utm_source=orderable&utm_medium=plugin&utm_campaign=pointer" target="_blank">' . esc_html__( 'Learn more' ) . '</a>.' .
976 '</p>',
977 'position' => array(
978 'edge' => 'left',
979 'align' => 'left',
980 ),
981 ),
982 );
983 }
984
985 $pointers = apply_filters( 'orderable_pointers', $pointers );
986
987 $dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
988 $valid_pointers = array();
989
990 if ( in_array( 'orderable-tour-dismissed', $dismissed ) ) {
991 return;
992 }
993
994 // Check pointers and remove dismissed ones.
995 foreach ( $pointers as $pointer_id => $pointer ) {
996 // Sanity check
997 if ( in_array( $pointer_id, $dismissed ) ) {
998 continue;
999 }
1000
1001 // Add the pointer to $valid_pointers array
1002 $valid_pointers[ $pointer_id ] = $pointer;
1003 }
1004
1005 if ( empty( $valid_pointers ) ) {
1006 return;
1007 }
1008
1009 // Add pointers style to queue.
1010 wp_enqueue_style( 'wp-pointer' );
1011
1012 // Add pointer options to script.
1013 wp_localize_script(
1014 'orderable',
1015 'orderable_pointers',
1016 array(
1017 'pointers' => $valid_pointers,
1018 'ajax_url' => admin_url( 'admin-ajax.php' ),
1019 'i18n' => array(
1020 'close' => esc_html__( 'Close', 'orderable' ),
1021 'next' => esc_html__( 'Next', 'orderable' ),
1022 'skip' => esc_html__( 'Skip', 'orderable' ),
1023 ),
1024 )
1025 );
1026 }
1027
1028 /**
1029 * Add script deps for admin scripts.
1030 *
1031 * @param array $deps
1032 *
1033 * @return array
1034 */
1035 public static function admin_script_deps( $deps = array() ) {
1036 if ( ! self::screen_has_pointers() ) {
1037 return $deps;
1038 }
1039
1040 $deps[] = 'wp-pointer';
1041
1042 return $deps;
1043 }
1044
1045 /**
1046 * Does this screen have pointers?
1047 *
1048 * If screen has pointers, return the screen ID. Otherwise false.
1049 *
1050 * @return bool|string
1051 */
1052 public static function screen_has_pointers() {
1053 $screen = get_current_screen();
1054 $screen_id = $screen->id;
1055
1056 $pointers = array( 'orderable_page_orderable-settings' );
1057
1058 return in_array( $screen_id, $pointers, true ) ? $screen_id : false;
1059 }
1060
1061 /**
1062 * Maybe opt in based on previous setting.
1063 */
1064 public static function maybe_opt_in() {
1065 $opt_in = get_option( 'orderable_opt_in' );
1066
1067 // Opt in is only stored in the database if opting in failed.
1068 if ( ! $opt_in ) {
1069 return;
1070 }
1071
1072 Orderable_Webhooks::subscribe();
1073 }
1074
1075 /**
1076 * Universal upgrade page content.
1077 *
1078 * @param string $feature Feature name.
1079 */
1080 public static function upgrade_page_content( $feature = '' ) {
1081 $utm_campaign = str_replace( ' ', '-', strtolower( $feature ) );
1082 include ORDERABLE_TEMPLATES_PATH . 'admin/orderable-pro-page.php';
1083 }
1084
1085 /**
1086 * Get SVG icon of the Orderable logo.
1087 *
1088 * @return string
1089 */
1090 public static function get_orderable_svg_icon() {
1091 return 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQ0IiBoZWlnaHQ9IjE4MyIgdmlld0JveD0iMCAwIDI0NCAxODMiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+DQo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE5MC40NzggMTY2LjdDMjI3LjM2MiAxNDIuNzEgMjUzLjI3MSA5MS44NDM4IDI0MC44NTYgNTYuMzA5NUMyMjguNDQyIDIwLjk1NTcgMTc3LjUyMyAwLjkzMzgzNSAxMjMuNzI3IDAuMDMxOTUxNEM3MC4xMDk3IC0wLjg2OTkzMyAxMy42MTQgMTcuMzQ4MSAyLjI3ODkxIDUxLjI1OUMtOS4yMzYxMyA4NS4xNjk4IDI0LjU4OTMgMTM0Ljk1NCA2NS40MzE3IDE2MS4yODlDMTA2LjA5NCAxODcuNjI0IDE1My43NzQgMTkwLjY5IDE5MC40NzggMTY2LjdaTTE4My4yMzQgNzguNzIwNkMxODMuMjM0IDgzLjkyNDEgMTc4LjEwOCA5MS42Mjg3IDE2Ny4zNDggOTEuNjI4N0MxNTYuNTg4IDkxLjYyODcgMTUxLjQ2MyA4My45MjQxIDE1MS40NjMgNzguNzIwNkMxNTEuNDYzIDczLjUxNzIgMTU2LjU4OCA2NS44MTI2IDE2Ny4zNDggNjUuODEyNkMxNzguMTA4IDY1LjgxMjYgMTgzLjIzNCA3My41MTcyIDE4My4yMzQgNzguNzIwNlpNMTY5LjExOCAxMjQuNjk3QzE5NS4zNDkgMTIzLjgyMiAyMTYuMzI4IDEwMy41NzIgMjE2LjMyOCA3OC43MjA2QzIxNi4zMjggNTMuMzEyNCAxOTQuMzk5IDMyLjcxNSAxNjcuMzQ4IDMyLjcxNUMxNjcuMjU3IDMyLjcxNSAxNjcuMTY3IDMyLjcxNTMgMTY3LjA3NiAzMi43MTU3SDg2LjczNUM4MS42NjMgMzIuNzE1NyA3Ny41NTEyIDM2LjgzNTIgNzcuNTUxMiA0MS45MTY4Qzc3LjU1MTIgNDYuOTk4NSA4MS42NjMgNTEuMTE4IDg2LjczNSA1MS4xMThIMTI4LjE2TDEyOC4xNDQgNTEuMTM3MUMxMzIuOTQyIDUxLjQ1MTEgMTM2LjczNiA1NS40NDE3IDEzNi43MzYgNjAuMzE4M0MxMzYuNzM2IDY1LjQgMTMyLjYxNiA2OS41MTk1IDEyNy41MzQgNjkuNTE5NUgxMTkuMzQ4TDExOS4zNDggNjkuNTIwMkg1My4wNjExQzQ3Ljk4OTEgNjkuNTIwMiA0My44NzczIDczLjYzOTcgNDMuODc3MyA3OC43MjEzQzQzLjg3NzMgODMuODAyOSA0Ny45ODkxIDg3LjkyMjQgNTMuMDYxMSA4Ny45MjI0SDEyNy42NTJDMTMyLjY4IDg3Ljk4NTQgMTM2LjczNiA5Mi4wODA0IDEzNi43MzYgOTcuMTIyOEMxMzYuNzM2IDEwMiAxMzIuOTQyIDEwNS45OSAxMjguMTQ0IDEwNi4zMDRMMTI4LjE2MSAxMDYuMzI1SDc0LjQ5QzY5LjQxNzkgMTA2LjMyNSA2NS4zMDYyIDExMC40NDQgNjUuMzA2MiAxMTUuNTI2QzY1LjMwNjIgMTIwLjYwNyA2OS40MTc5IDEyNC43MjcgNzQuNDkgMTI0LjcyN0gxNjguMzY5QzE2OC42MjEgMTI0LjcyNyAxNjguODcxIDEyNC43MTcgMTY5LjExOCAxMjQuNjk3Wk0xNTAuODY0IDEzNy41NDVDMTUyLjA2NSAxNDEuMTE4IDE0OS41NTggMTQ2LjIzMyAxNDUuOTg4IDE0OC42NDZDMTQyLjQzNSAxNTEuMDU4IDEzNy44MjEgMTUwLjc1IDEzMy44ODUgMTQ4LjEwMkMxMjkuOTMyIDE0NS40NTQgMTI2LjY1OSAxNDAuNDQ3IDEyNy43NzMgMTM3LjAzN0MxMjguODcgMTMzLjYyNyAxMzQuMzM4IDEzMS43OTUgMTM5LjUyNyAxMzEuODg2QzE0NC43MzQgMTMxLjk3NyAxNDkuNjYyIDEzMy45OSAxNTAuODY0IDEzNy41NDVaTTE2MS42NzYgMTM3LjU0NUMxNjAuNDc0IDE0MS4xMTggMTYyLjk4MiAxNDYuMjMzIDE2Ni41NTIgMTQ4LjY0NkMxNzAuMTA0IDE1MS4wNTggMTc0LjcxOSAxNTAuNzUgMTc4LjY1NCAxNDguMTAyQzE4Mi42MDcgMTQ1LjQ1NCAxODUuODgxIDE0MC40NDcgMTg0Ljc2NiAxMzcuMDM3QzE4My42NjkgMTMzLjYyNyAxNzguMjAyIDEzMS43OTUgMTczLjAxMiAxMzEuODg2QzE2Ny44MDYgMTMxLjk3NyAxNjIuODc3IDEzMy45OSAxNjEuNjc2IDEzNy41NDVaIiBmaWxsPSIjOUNBMUE4Ii8+DQo8L3N2Zz4=';
1092 }
1093 }
1094