PluginProbe
Orderable – Restaurant & Food Ordering System / 1.10.0
Orderable – Restaurant & Food Ordering System v1.10.0
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.10.0, at inc/class-settings.php

1,080 lines 38.9 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 = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQ0IiBoZWlnaHQ9IjE4MyIgdmlld0JveD0iMCAwIDI0NCAxODMiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+DQo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE5MC40NzggMTY2LjdDMjI3LjM2MiAxNDIuNzEgMjUzLjI3MSA5MS44NDM4IDI0MC44NTYgNTYuMzA5NUMyMjguNDQyIDIwLjk1NTcgMTc3LjUyMyAwLjkzMzgzNSAxMjMuNzI3IDAuMDMxOTUxNEM3MC4xMDk3IC0wLjg2OTkzMyAxMy42MTQgMTcuMzQ4MSAyLjI3ODkxIDUxLjI1OUMtOS4yMzYxMyA4NS4xNjk4IDI0LjU4OTMgMTM0Ljk1NCA2NS40MzE3IDE2MS4yODlDMTA2LjA5NCAxODcuNjI0IDE1My43NzQgMTkwLjY5IDE5MC40NzggMTY2LjdaTTE4My4yMzQgNzguNzIwNkMxODMuMjM0IDgzLjkyNDEgMTc4LjEwOCA5MS42Mjg3IDE2Ny4zNDggOTEuNjI4N0MxNTYuNTg4IDkxLjYyODcgMTUxLjQ2MyA4My45MjQxIDE1MS40NjMgNzguNzIwNkMxNTEuNDYzIDczLjUxNzIgMTU2LjU4OCA2NS44MTI2IDE2Ny4zNDggNjUuODEyNkMxNzguMTA4IDY1LjgxMjYgMTgzLjIzNCA3My41MTcyIDE4My4yMzQgNzguNzIwNlpNMTY5LjExOCAxMjQuNjk3QzE5NS4zNDkgMTIzLjgyMiAyMTYuMzI4IDEwMy41NzIgMjE2LjMyOCA3OC43MjA2QzIxNi4zMjggNTMuMzEyNCAxOTQuMzk5IDMyLjcxNSAxNjcuMzQ4IDMyLjcxNUMxNjcuMjU3IDMyLjcxNSAxNjcuMTY3IDMyLjcxNTMgMTY3LjA3NiAzMi43MTU3SDg2LjczNUM4MS42NjMgMzIuNzE1NyA3Ny41NTEyIDM2LjgzNTIgNzcuNTUxMiA0MS45MTY4Qzc3LjU1MTIgNDYuOTk4NSA4MS42NjMgNTEuMTE4IDg2LjczNSA1MS4xMThIMTI4LjE2TDEyOC4xNDQgNTEuMTM3MUMxMzIuOTQyIDUxLjQ1MTEgMTM2LjczNiA1NS40NDE3IDEzNi43MzYgNjAuMzE4M0MxMzYuNzM2IDY1LjQgMTMyLjYxNiA2OS41MTk1IDEyNy41MzQgNjkuNTE5NUgxMTkuMzQ4TDExOS4zNDggNjkuNTIwMkg1My4wNjExQzQ3Ljk4OTEgNjkuNTIwMiA0My44NzczIDczLjYzOTcgNDMuODc3MyA3OC43MjEzQzQzLjg3NzMgODMuODAyOSA0Ny45ODkxIDg3LjkyMjQgNTMuMDYxMSA4Ny45MjI0SDEyNy42NTJDMTMyLjY4IDg3Ljk4NTQgMTM2LjczNiA5Mi4wODA0IDEzNi43MzYgOTcuMTIyOEMxMzYuNzM2IDEwMiAxMzIuOTQyIDEwNS45OSAxMjguMTQ0IDEwNi4zMDRMMTI4LjE2MSAxMDYuMzI1SDc0LjQ5QzY5LjQxNzkgMTA2LjMyNSA2NS4zMDYyIDExMC40NDQgNjUuMzA2MiAxMTUuNTI2QzY1LjMwNjIgMTIwLjYwNyA2OS40MTc5IDEyNC43MjcgNzQuNDkgMTI0LjcyN0gxNjguMzY5QzE2OC42MjEgMTI0LjcyNyAxNjguODcxIDEyNC43MTcgMTY5LjExOCAxMjQuNjk3Wk0xNTAuODY0IDEzNy41NDVDMTUyLjA2NSAxNDEuMTE4IDE0OS41NTggMTQ2LjIzMyAxNDUuOTg4IDE0OC42NDZDMTQyLjQzNSAxNTEuMDU4IDEzNy44MjEgMTUwLjc1IDEzMy44ODUgMTQ4LjEwMkMxMjkuOTMyIDE0NS40NTQgMTI2LjY1OSAxNDAuNDQ3IDEyNy43NzMgMTM3LjAzN0MxMjguODcgMTMzLjYyNyAxMzQuMzM4IDEzMS43OTUgMTM5LjUyNyAxMzEuODg2QzE0NC43MzQgMTMxLjk3NyAxNDkuNjYyIDEzMy45OSAxNTAuODY0IDEzNy41NDVaTTE2MS42NzYgMTM3LjU0NUMxNjAuNDc0IDE0MS4xMTggMTYyLjk4MiAxNDYuMjMzIDE2Ni41NTIgMTQ4LjY0NkMxNzAuMTA0IDE1MS4wNTggMTc0LjcxOSAxNTAuNzUgMTc4LjY1NCAxNDguMTAyQzE4Mi42MDcgMTQ1LjQ1NCAxODUuODgxIDE0MC40NDcgMTg0Ljc2NiAxMzcuMDM3QzE4My42NjkgMTMzLjYyNyAxNzguMjAyIDEzMS43OTUgMTczLjAxMiAxMzEuODg2QzE2Ny44MDYgMTMxLjk3NyAxNjIuODc3IDEzMy45OSAxNjEuNjc2IDEzNy41NDVaIiBmaWxsPSIjOUNBMUE4Ii8+DQo8L3N2Zz4=';
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( 'orderable_onboarding_slides', array(
342 'welcome' => array(
343 'header_image' => ORDERABLE_URL . '/assets/img/onboarding/orderable-onboarding-header.jpg',
344 'title' => 'Welcome',
345 'description' => 'Thank you for choosing Orderable to add local ordering to your website. This short setup wizard will guide you though configuring Orderable.',
346 'button_text' => 'Begin <span class="dashicons dashicons-arrow-right-alt2"></span>',
347 'button_icon' => '',
348 ),
349 'requirements' => array(
350 'title' => 'Requirements',
351 '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.',
352 'button_text' => 'Install &amp; Activate WooCommerce',
353 'wait' => 'install_plugin',
354 'json_data' => array(
355 'wait_text' => __( 'Installing...', 'orderable' ),
356 'plugin_data' => array(
357 'name' => __( 'WooCommerce', 'orderable' ),
358 'repo-slug' => 'woocommerce',
359 'file' => 'woocommerce.php',
360 ),
361 ),
362 ),
363 'business' => array(
364 'title' => 'Business Info',
365 'description' => 'Orderable needs some basic business information that will be used when orders are placed.',
366 'button_text' => 'Continue <span class="dashicons dashicons-arrow-right-alt2"></span>',
367 'fields' => array(
368 'name' => array(
369 'id' => 'business_name',
370 'title' => __( 'Business Name', 'orderable' ),
371 'desc' => '',
372 'type' => 'text',
373 'default' => get_bloginfo( 'name' ),
374 ),
375 'address' => array(
376 'id' => 'business_address',
377 'title' => __( 'Address line 1', 'orderable' ),
378 'desc' => '',
379 'type' => 'text',
380 ),
381 'address_2' => array(
382 'id' => 'business_address_2',
383 'title' => __( 'Address line 2', 'orderable' ),
384 'desc' => '',
385 'type' => 'text',
386 ),
387 'city' => array(
388 'id' => 'business_city',
389 'title' => __( 'City', 'orderable' ),
390 'desc' => '',
391 'type' => 'text',
392 ),
393 'default_country' => array(
394 'id' => 'default_country',
395 'title' => __( 'Country / State', 'orderable' ),
396 'desc' => '',
397 'type' => 'select',
398 'choices' => array(),
399 ),
400 'postcode' => array(
401 'id' => 'business_postcode',
402 'title' => __( 'Postcode / ZIP', 'orderable' ),
403 'desc' => '',
404 'type' => 'text',
405 ),
406 'email' => array(
407 'id' => 'business_email',
408 'title' => __( 'Business Email', 'orderable' ),
409 'desc' => '',
410 'type' => 'text',
411 'default' => get_option( 'admin_email' ),
412 ),
413 array(
414 'id' => 'opt_in',
415 'title' => '',
416 'desc' => __( 'Please keep me up to date via email on new Orderable training and features', 'orderable' ),
417 'type' => 'checkbox',
418 'default' => 1,
419 ),
420 ),
421 ),
422 'location' => array(
423 "title" => "Location Info",
424 "description" => 'Help us set up your ordering system. You can refine these details further after completing the onboarding process.',
425 "button_text" => "Continue <span class='dashicons dashicons-arrow-right-alt2'></span>",
426 'fields' => array(
427 array(
428 'id' => 'services',
429 'title' => __( 'Which services do you offer?', 'orderable' ),
430 'desc' => '',
431 'type' => 'checkboxes',
432 'choices' => array(
433 'flat_rate' => __( 'Delivery', 'orderable' ),
434 'local_pickup' => __( 'Pickup', 'orderable' ),
435 ),
436 ),
437 array(
438 'id' => 'days',
439 'title' => __( 'Which days of the week are you open?', 'orderable' ),
440 'desc' => '',
441 'type' => 'checkboxes',
442 'choices' => array(
443 1 => __( 'Monday', 'orderable' ),
444 2 => __( 'Tuesday', 'orderable' ),
445 3 => __( 'Wednesday', 'orderable' ),
446 4 => __( 'Thursday', 'orderable' ),
447 5 => __( 'Friday', 'orderable' ),
448 6 => __( 'Saturday', 'orderable' ),
449 0 => __( 'Sunday', 'orderable' ),
450 ),
451 ),
452 array(
453 'id' => 'open_hours',
454 'title' => __( 'What are your normal opening hours?', 'orderable' ),
455 'desc' => '',
456 'type' => 'custom',
457 'default' => self::get_open_hours_fields(),
458 ),
459 ),
460 ),
461 'done' => array(
462 "title" => "All Done",
463 "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.",
464 "button_text" => "Save and Finish <span class='dashicons dashicons-yes'></span>",
465 ),
466 ) );
467
468 if ( function_exists( 'WC' ) ) {
469 unset( $slides['requirements'] );
470
471 $base = wc_get_base_location();
472 $countries_states = self::get_countries_states();
473 $default = '';
474
475 if ( isset( $base['country'] ) && isset( $countries_states[ 'country:' . $base['country'] ] ) ) {
476 $default = 'country:' . $base['country'];
477 }
478
479 if ( isset( $base['country'] ) && isset( $base['state'] ) && isset( $countries_states[ $base['country'] ] ) ) {
480 $state = 'state:' . $base['country'] . ':' . $base['state'];
481 if ( isset( $countries_states[ $base['country'] ]['values'][ $state ] ) ) {
482 $default = $state;
483 }
484 }
485
486 $slides['business']['fields']['default_country']['choices'] = $countries_states;
487 $slides['business']['fields']['default_country']['default'] = $default;
488 $slides['business']['fields']['address']['default'] = WC()->countries->get_base_address();
489 $slides['business']['fields']['address_2']['default'] = WC()->countries->get_base_address_2();
490 $slides['business']['fields']['city']['default'] = WC()->countries->get_base_city();
491 $slides['business']['fields']['postcode']['default'] = WC()->countries->get_base_postcode();
492 }
493
494 Orderable_Onboard::run(
495 array(
496 'version' => ORDERABLE_VERSION,
497 'plugin_slug' => 'orderable',
498 'plugin_url' => ORDERABLE_URL,
499 'plugin_path' => ORDERABLE_PATH,
500 'slides' => $slides,
501 )
502 );
503 }
504
505 /**
506 * Save the onboarding data in database.
507 *
508 * @param array $result Was the submission a success?
509 * @param array $fields Submitted fields.
510 *
511 * @return bool|array $result
512 */
513 public static function save_onboard_settings( $result, $fields ) {
514 if ( empty( $fields['orderable_settings'] ) ) {
515 return false;
516 }
517
518 // Get existing/default settings.
519 $settings = get_option( 'orderable_settings', array() );
520
521 // Assign opening hours.
522 $from = array(
523 'hour' => sanitize_text_field( $fields['orderable_settings']['iconic_onboard_open_hours']['from']['hour'] ),
524 'minute' => sanitize_text_field( $fields['orderable_settings']['iconic_onboard_open_hours']['from']['minute'] ),
525 'period' => sanitize_text_field( $fields['orderable_settings']['iconic_onboard_open_hours']['from']['period'] ),
526 );
527 $to = array(
528 'hour' => sanitize_text_field( $fields['orderable_settings']['iconic_onboard_open_hours']['to']['hour'] ),
529 'minute' => sanitize_text_field( $fields['orderable_settings']['iconic_onboard_open_hours']['to']['minute'] ),
530 'period' => sanitize_text_field( $fields['orderable_settings']['iconic_onboard_open_hours']['to']['period'] ),
531 );
532
533 $settings['store_general_open_hours'] = array();
534
535 $days = is_array( $fields['orderable_settings']['iconic_onboard_days'] ) ? $fields['orderable_settings']['iconic_onboard_days'] : array();
536
537 for ( $day = 0; $day <= 6; $day ++ ) {
538 $settings['store_general_open_hours'][ $day ] = array(
539 'enabled' => in_array( (string) $day, $days, true ),
540 'from' => $from,
541 'to' => $to,
542 'max_orders' => '',
543 );
544 }
545
546 // Save Business details.
547 $name = isset( $fields['orderable_settings']['iconic_onboard_business_name'] ) ? wc_clean( wp_unslash( $fields['orderable_settings']['iconic_onboard_business_name'] ) ) : '';
548 $address = isset( $fields['orderable_settings']['iconic_onboard_business_address'] ) ? wc_clean( wp_unslash( $fields['orderable_settings']['iconic_onboard_business_address'] ) ) : '';
549 $address_2 = isset( $fields['orderable_settings']['iconic_onboard_business_address_2'] ) ? wc_clean( wp_unslash( $fields['orderable_settings']['iconic_onboard_business_address_2'] ) ) : '';
550 $city = isset( $fields['orderable_settings']['iconic_onboard_business_city'] ) ? wc_clean( wp_unslash( $fields['orderable_settings']['iconic_onboard_business_city'] ) ) : '';
551 $country_state = isset( $fields['orderable_settings']['iconic_onboard_default_country'] ) ? wc_clean( wp_unslash( $fields['orderable_settings']['iconic_onboard_default_country'] ) ) : '';
552 $stripped_country_state = $country_state ? str_replace( array( 'country:', 'state:' ), '', $country_state ) : '';
553 $stripped_country_state = explode( ':', $stripped_country_state )[0];
554 $postcode = isset( $fields['orderable_settings']['iconic_onboard_business_postcode'] ) ? wc_clean( wp_unslash( $fields['orderable_settings']['iconic_onboard_business_postcode'] ) ) : '';
555 $email = isset( $fields['orderable_settings']['iconic_onboard_business_email'] ) ? wc_clean( wp_unslash( $fields['orderable_settings']['iconic_onboard_business_email'] ) ) : '';
556 $opt_in = ! empty( $fields['orderable_settings']['iconic_onboard_opt_in'] );
557
558 if ( ! empty( $name ) ) {
559 update_option( 'blogname', $name );
560 }
561
562 update_option( 'woocommerce_store_address', $address );
563 update_option( 'woocommerce_store_address_2', $address_2 );
564 update_option( 'woocommerce_store_city', $city );
565 update_option( 'woocommerce_default_country', $stripped_country_state );
566 update_option( 'woocommerce_store_postcode', $postcode );
567 update_option( 'admin_email', $email );
568
569 // Process opt in
570 if ( $opt_in ) {
571 Orderable_Webhooks::subscribe( $email );
572 }
573
574 // Restrict delivery area to country/state.
575 if ( ! empty( $stripped_country_state ) ) {
576 update_option( 'woocommerce_allowed_countries', 'specific' );
577 update_option( 'woocommerce_specific_allowed_countries', array( $stripped_country_state ) );
578 }
579
580 // Save store services.
581 if ( ! empty( $fields['orderable_settings']['iconic_onboard_services'] ) ) {
582 $settings['store_general_services'] = array();
583
584 $existing_zone_id = null;
585 $zone_name = __( 'Local', 'orderable' );
586 $zones = WC_Shipping_Zones::get_zones();
587
588 if ( ! empty( $zones ) ) {
589 foreach ( $zones as $zone ) {
590 if ( $zone_name !== $zone['zone_name'] ) {
591 continue;
592 }
593
594 $existing_zone_id = $zone['id'];
595 $shipping_method_instances = ! empty( $zone['shipping_methods'] ) ? array_keys( $zone['shipping_methods'] ) : false;
596 break;
597 }
598 }
599
600 $zone = new WC_Shipping_Zone( $existing_zone_id );
601 $zone->set_zone_name( $zone_name );
602
603 if ( ! empty( $country_state ) ) {
604 $zone->clear_locations();
605 $location_parts = explode( ':', $country_state );
606
607 switch ( $location_parts[0] ) {
608 case 'state':
609 $zone->add_location( $location_parts[1] . ':' . $location_parts[2], 'state' );
610 break;
611 case 'country':
612 $zone->add_location( $location_parts[1], 'country' );
613 break;
614 case 'continent':
615 $zone->add_location( $location_parts[1], 'continent' );
616 break;
617 }
618 }
619
620 if ( ! empty( $shipping_method_instances ) ) {
621 foreach ( $shipping_method_instances as $shipping_method_instance_id ) {
622 $zone->delete_shipping_method( $shipping_method_instance_id );
623 }
624 }
625
626 $service_types = array(
627 'flat_rate' => 'delivery',
628 'local_pickup' => 'pickup',
629 );
630
631 foreach ( $fields['orderable_settings']['iconic_onboard_services'] as $service ) {
632 $service = wc_clean( $service );
633 $zone->add_shipping_method( $service );
634
635 $settings['store_general_services'][] = $service_types[ $service ];
636 }
637
638 $zone->save();
639 }
640
641 // Misc.
642 $settings['store_general_service_hours_pickup_same'] = 1;
643
644 // Save settings.
645 update_option( 'orderable_settings', $settings );
646
647 /**
648 * Action hook for when the onboard settings are saved.
649 *
650 * @param array $fields The settings.
651 */
652 do_action( 'orderable_onboard_settings_saved', $fields );
653
654 // Return success.
655 return $result;
656 }
657
658 /**
659 * Get countries and states for select field.
660 *
661 * @return array
662 */
663 public static function get_countries_states() {
664 $countries_states = array();
665
666 if ( ! function_exists( 'WC' ) ) {
667 return $countries_states;
668 }
669
670 $countries = WC()->countries->get_countries();
671
672 if ( empty( $countries ) ) {
673 return $countries_states;
674 }
675
676 foreach ( $countries as $key => $value ) {
677 $countries_states[ 'country:' . $key ] = $value;
678 $states = WC()->countries->get_states( $key );
679
680 if ( empty( $states ) ) {
681 continue;
682 }
683
684 $countries_states[ $key ] = array(
685 'group_label' => $value,
686 'values' => array(),
687 );
688
689 foreach ( $states as $state_key => $state_value ) {
690 $state_key = sprintf( 'state:%s:%s', $key, $state_key );
691 $countries_states[ $key ]['values'][ $state_key ] = sprintf( '%s &mdash; %s', $value, $state_value );
692 }
693 }
694
695 return $countries_states;
696 }
697
698 /**
699 * Get open hours fields.
700 *
701 * @return string
702 */
703 public static function get_open_hours_fields() {
704 ob_start();
705 ?>
706 <div class="iconic-onboard-modal-setting__field-section">
707 <div style="margin: 0 0 8px;"><?php _e( 'From:', 'orderable' ); ?></div>
708 <?php
709 echo Orderable_Timings_Settings::get_time_field(
710 'orderable_settings[iconic_onboard_open_hours][from]',
711 array(
712 'hour' => 9,
713 'minute' => '00',
714 'period' => 'AM',
715 )
716 );
717 ?>
718 </div>
719 <div class="iconic-onboard-modal-setting__field-section">
720 <div style="margin: 0 0 8px;"><?php _e( 'To:', 'orderable' ); ?></div>
721 <?php
722 echo Orderable_Timings_Settings::get_time_field(
723 'orderable_settings[iconic_onboard_open_hours][to]',
724 array(
725 'hour' => 5,
726 'minute' => '00',
727 'period' => 'PM',
728 )
729 );
730 ?>
731 </div>
732 <?php
733
734 return ob_get_clean();
735 }
736
737 /**
738 * Category fields.
739 */
740 public static function add_category_fields() {
741 ?>
742 <div class="form-field term-display-type-wrap">
743 <label for="visibility"><?php esc_html_e( 'Visibility', 'orderable' ); ?></label>
744 <select id="visibility" name="visibility" class="postform">
745 <option value=""><?php esc_html_e( 'Default', 'orderable' ); ?></option>
746 <option value="hidden"><?php esc_html_e( 'Hidden', 'orderable' ); ?></option>
747 </select>
748 <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>
749 </div>
750 <?php
751 }
752
753 /**
754 * Edit category fields.
755 *
756 * @param mixed $term Term (category) being edited.
757 */
758 public static function edit_category_fields( $term ) {
759 $visibility = get_term_meta( $term->term_id, 'visibility', true );
760 ?>
761 <tr class="form-field term-display-type-wrap">
762 <th scope="row" valign="top">
763 <label for="visibility"><?php esc_html_e( 'Visibility', 'orderable' ); ?></label>
764 </th>
765 <td>
766 <select id="visibility" name="visibility" class="postform">
767 <option value="" <?php selected( '', $visibility ); ?>><?php esc_html_e( 'Default', 'orderable' ); ?></option>
768 <option value="hidden" <?php selected( 'hidden', $visibility ); ?>><?php esc_html_e( 'Hidden', 'orderable' ); ?></option>
769 </select>
770 <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>
771 </td>
772 </tr>
773 <?php
774 }
775
776 /**
777 * Save category fields.
778 *
779 * @param mixed $term_id Term ID being saved.
780 * @param mixed $tt_id Term taxonomy ID.
781 * @param string $taxonomy Taxonomy slug.
782 */
783 public static function save_category_fields( $term_id, $tt_id = '', $taxonomy = '' ) {
784 $visibility = empty( $_POST['visibility'] ) ? '' : sanitize_text_field( wp_unslash( $_POST['visibility'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
785
786 if ( ! empty( $visibility ) && 'product_cat' === $taxonomy ) {
787 update_term_meta( $term_id, 'visibility', sanitize_text_field( $visibility ) );
788 }
789 }
790
791 /**
792 * Get hidden category IDs.
793 *
794 * @return array
795 */
796 public static function get_hidden_categories() {
797 global $wpdb;
798
799 static $hidden_categories = null;
800
801 if ( ! is_null( $hidden_categories ) ) {
802 return apply_filters( 'orderable_hidden_categories', $hidden_categories );
803 }
804
805 $hidden_categories = array();
806
807 $results = $wpdb->get_results(
808 "SELECT DISTINCT tm.term_id
809 FROM $wpdb->term_taxonomy AS tt
810 INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id
811 INNER JOIN $wpdb->termmeta AS tm ON t.term_id = tm.term_id
812 WHERE tt.taxonomy = 'product_cat'
813 AND tm.meta_key = 'visibility'
814 AND tm.meta_value IN ( 'hidden' )
815
816 UNION DISTINCT
817
818 SELECT DISTINCT term_id
819 FROM $wpdb->term_taxonomy
820 WHERE parent IN (
821 SELECT DISTINCT tm.term_id
822 FROM $wpdb->term_taxonomy AS tt
823 INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id
824 INNER JOIN $wpdb->termmeta AS tm ON t.term_id = tm.term_id
825 WHERE tt.taxonomy = 'product_cat'
826 AND tm.meta_key = 'visibility'
827 AND tm.meta_value IN ( 'hidden' )
828 )",
829 ARRAY_A
830 );
831
832 if ( is_wp_error( $results ) || empty( $results ) ) {
833 return apply_filters( 'orderable_hidden_categories', $hidden_categories );
834 }
835
836 $hidden_categories = array_map( 'absint', wp_list_pluck( $results, 'term_id' ) );
837
838 return apply_filters( 'orderable_hidden_categories', $hidden_categories );
839 }
840
841 /**
842 * Add logo to settings page.
843 */
844 public static function settings_logo( $title = '' ) {
845 return sprintf( '<img src="%s" width="164" height="36" />', esc_url( ORDERABLE_ASSETS_URL . 'img/orderable-logo.svg' ) );
846 }
847
848 /**
849 * Help buttons and version number
850 */
851 public static function settings_header() {
852 ?>
853 <style>
854 .wpsf-settings__header {
855 justify-content: space-between;
856 }
857
858 .orderable-settings-header {
859 display: flex;
860 align-items: center;
861 }
862
863 .orderable-settings-header__links {
864 margin: 0 10px 0 0;
865 padding: 0;
866 list-style: none none outside;
867 height: 20px;
868 }
869
870 .orderable-settings-header__links li {
871 margin: 0 10px 0 0;
872 padding: 0;
873 display: inline-block;
874 }
875
876 .orderable-settings-header__links a {
877 text-decoration: none;
878 color: #64707B !important;
879 white-space: nowrap;
880 display: flex;
881 align-items: center;
882 }
883
884 .orderable-settings-header__links a:hover {
885 color: #7031F5 !important;
886 }
887
888 .orderable-settings-header__links a svg {
889 fill: #7031F5;
890 margin-right: 4px;
891 }
892
893 @media screen and (max-width: 648px) {
894 .orderable-settings-header__links {
895 display: none;
896 }
897 }
898 </style>
899 <div class="orderable-settings-header">
900 <ul class="orderable-settings-header__links">
901 <li><a href="https://my.orderable.com/support/?utm_source=orderable&utm_medium=plugin&utm_campaign=settings-header" target="_blank">
902 <svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20px" height="20px">
903 <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" />
904 </svg> <?php _e( 'Get Help', 'orderable' ); ?></a></li>
905 <li><a href="https://my.orderable.com/roadmap/?utm_source=orderable&utm_medium=plugin&utm_campaign=settings-header" target="_blank">
906 <svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20px" height="20px">
907 <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" />
908 </svg> <?php _e( 'Request Feature', 'orderable' ); ?></a></li>
909 <li><a href="https://orderable.com/documentation/?utm_source=orderable&utm_medium=plugin&utm_campaign=settings-header" target="_blank">
910 <svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20px" height="20px">
911 <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" />
912 </svg> <?php _e( 'View Docs', 'orderable' ); ?></a></li>
913 </ul>
914 <?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 ); ?>
915 </div>
916 <?php
917 }
918
919 /**
920 * Add pointers.
921 */
922 public static function admin_pointers() {
923 $screen_id = self::screen_has_pointers();
924
925 if ( ! $screen_id ) {
926 return;
927 }
928
929 $pointers = array();
930
931 if ( $screen_id === 'orderable_page_orderable-settings' ) {
932 $pointers['orderable-store-settings'] = array(
933 'target' => '#toplevel_page_orderable a[href="admin.php?page=orderable-location"]',
934 'next' => 'orderable-layout-builder',
935 'options' => array(
936 'content' => '<h3>' . esc_html__( 'Set Up Your Location', 'orderable' ) . '</h3>' .
937 '<p>' .
938 esc_html__( "Configure your location's opening hours, delivery/pickup schedule, and holidays.", 'orderable' ) .
939 ' <a href="https://orderable.com/getting-started?utm_source=orderable&utm_medium=plugin&utm_campaign=pointer" target="_blank">' . esc_html__( 'Learn more' ) . '</a>.' .
940 '</p>',
941 'position' => array(
942 'edge' => 'left',
943 'align' => 'left',
944 ),
945 ),
946 );
947
948 $pointers['orderable-layout-builder'] = array(
949 'target' => '#toplevel_page_orderable a[href="edit.php?post_type=orderable_layouts"]',
950 'next' => 'orderable-order-view',
951 'options' => array(
952 'content' => '<h3>' . esc_html__( 'Product Layouts', 'orderable' ) . '</h3>' .
953 '<p>' .
954 esc_html__( 'Use the Layout Builder to create a product list based on category. Embed your layout using the shortcode or block.', 'orderable' ) .
955 ' <a href="https://orderable.com/layout-builder?utm_source=orderable&utm_medium=plugin&utm_campaign=pointer" target="_blank">' . esc_html__( 'Learn more' ) . '</a>.' .
956 '</p>',
957 'position' => array(
958 'edge' => 'left',
959 'align' => 'left',
960 ),
961 ),
962 );
963
964 $pointers['orderable-order-view'] = array(
965 'target' => '#toplevel_page_orderable a[href="edit.php?post_type=shop_order&orderable_live_view"]',
966 'options' => array(
967 'content' => '<h3>' . esc_html__( 'Live Order View', 'orderable' ) . '</h3>' .
968 '<p>' .
969 esc_html__( 'Use the Live Order View to get notified and manage orders in real time.', 'orderable' ) .
970 ' <a href="https://orderable.com/process-orders?utm_source=orderable&utm_medium=plugin&utm_campaign=pointer" target="_blank">' . esc_html__( 'Learn more' ) . '</a>.' .
971 '</p>',
972 'position' => array(
973 'edge' => 'left',
974 'align' => 'left',
975 ),
976 ),
977 );
978 }
979
980 $pointers = apply_filters( 'orderable_pointers', $pointers );
981
982 $dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
983 $valid_pointers = array();
984
985 if ( in_array( 'orderable-tour-dismissed', $dismissed ) ) {
986 return;
987 }
988
989 // Check pointers and remove dismissed ones.
990 foreach ( $pointers as $pointer_id => $pointer ) {
991 // Sanity check
992 if ( in_array( $pointer_id, $dismissed ) ) {
993 continue;
994 }
995
996 // Add the pointer to $valid_pointers array
997 $valid_pointers[ $pointer_id ] = $pointer;
998 }
999
1000 if ( empty( $valid_pointers ) ) {
1001 return;
1002 }
1003
1004 // Add pointers style to queue.
1005 wp_enqueue_style( 'wp-pointer' );
1006
1007 // Add pointer options to script.
1008 wp_localize_script(
1009 'orderable',
1010 'orderable_pointers',
1011 array(
1012 'pointers' => $valid_pointers,
1013 'ajax_url' => admin_url( 'admin-ajax.php' ),
1014 'i18n' => array(
1015 'close' => esc_html__( 'Close', 'orderable' ),
1016 'next' => esc_html__( 'Next', 'orderable' ),
1017 'skip' => esc_html__( 'Skip', 'orderable' ),
1018 ),
1019 )
1020 );
1021 }
1022
1023 /**
1024 * Add script deps for admin scripts.
1025 *
1026 * @param array $deps
1027 *
1028 * @return array
1029 */
1030 public static function admin_script_deps( $deps = array() ) {
1031 if ( ! self::screen_has_pointers() ) {
1032 return $deps;
1033 }
1034
1035 $deps[] = 'wp-pointer';
1036
1037 return $deps;
1038 }
1039
1040 /**
1041 * Does this screen have pointers?
1042 *
1043 * If screen has pointers, return the screen ID. Otherwise false.
1044 *
1045 * @return bool|string
1046 */
1047 public static function screen_has_pointers() {
1048 $screen = get_current_screen();
1049 $screen_id = $screen->id;
1050
1051 $pointers = array( 'orderable_page_orderable-settings' );
1052
1053 return in_array( $screen_id, $pointers, true ) ? $screen_id : false;
1054 }
1055
1056 /**
1057 * Maybe opt in based on previous setting.
1058 */
1059 public static function maybe_opt_in() {
1060 $opt_in = get_option( 'orderable_opt_in' );
1061
1062 // Opt in is only stored in the database if opting in failed.
1063 if ( ! $opt_in ) {
1064 return;
1065 }
1066
1067 Orderable_Webhooks::subscribe();
1068 }
1069
1070 /**
1071 * Universal upgrade page content.
1072 *
1073 * @param string $feature Feature name.
1074 */
1075 public static function upgrade_page_content( $feature = '' ) {
1076 $utm_campaign = str_replace( ' ', '-', strtolower( $feature ) );
1077 include ORDERABLE_TEMPLATES_PATH . 'admin/orderable-pro-page.php';
1078 }
1079 }
1080