PluginProbe ʕ •ᴥ•ʔ
WooCommerce Square / 4.5.0
WooCommerce Square v4.5.0
5.5.0 5.4.3 5.4.2 5.4.1 5.4.0 trunk 1.0.25 1.0.26 1.0.27 1.0.28 1.0.29 1.0.30 1.0.31 1.0.32 1.0.33 1.0.34 1.0.35 1.0.36 1.0.37 1.0.38 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.7.0 2.8.0 2.9.0 2.9.1 3.0.0 3.0.1 3.0.2 3.0.3 3.1.0 3.2.0 3.3.0 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.7.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 4.0.0 4.1.0 4.2.0 4.2.1 4.2.2 4.2.3 4.3.0 4.3.1 4.3.2 4.4.0 4.4.1 4.4.2 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.6.2 4.6.3 4.6.4 4.7.0 4.7.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.1 4.8.2 4.8.3 4.8.4 4.8.5 4.8.6 4.8.7 4.8.8 4.9.0 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 4.9.6 4.9.7 4.9.8 4.9.9 5.0.0 5.0.1 5.1.0 5.1.1 5.1.2 5.2.0 5.3.0 5.3.1 5.3.2 5.3.3
woocommerce-square / includes / Settings.php
woocommerce-square / includes Last commit date
API 3 years ago Admin 2 years ago Emails 2 years ago Framework 2 years ago Gateway 2 years ago Handlers 2 years ago Sync 2 years ago Utilities 3 years ago AJAX.php 3 years ago API.php 3 years ago Admin.php 3 years ago Functions.php 3 years ago Gateway.php 2 years ago Lifecycle.php 3 years ago Plugin.php 2 years ago Settings.php 2 years ago WC_Order_Square.php 2 years ago
Settings.php
1123 lines
1 <?php
2 /**
3 * WooCommerce Square
4 *
5 * This source file is subject to the GNU General Public License v3.0
6 * that is bundled with this package in the file license.txt.
7 * It is also available through the world-wide-web at this URL:
8 * http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0 or later
9 * If you did not receive a copy of the license and are unable to
10 * obtain it through the world-wide-web, please send an email
11 * to license@woocommerce.com so we can send you a copy immediately.
12 *
13 * DISCLAIMER
14 *
15 * Do not edit or add to this file if you wish to upgrade WooCommerce Square to newer
16 * versions in the future. If you wish to customize WooCommerce Square for your
17 * needs please refer to https://docs.woocommerce.com/document/woocommerce-square/
18 *
19 * @author WooCommerce
20 * @copyright Copyright: (c) 2019, Automattic, Inc.
21 * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0 or later
22 */
23
24 namespace WooCommerce\Square;
25
26 defined( 'ABSPATH' ) || exit;
27
28 /**
29 * The settings API class.
30 *
31 * This handles registering, getting, and storing the general plugin options.
32 *
33 * Note that this is separate from the gateway settings.
34 *
35 * @since 2.0.0
36 */
37 class Settings extends \WC_Settings_API {
38
39
40 /**
41 * Square Sync setting.
42 *
43 * @var string square Sync setting indicator
44 */
45 const SYSTEM_OF_RECORD_SQUARE = 'square';
46
47 /**
48 * Woocommerce Sync setting.
49 *
50 * @var string square Sync setting indicator
51 */
52 const SYSTEM_OF_RECORD_WOOCOMMERCE = 'woocommerce';
53
54 /**
55 * Disabled Sync setting.
56 *
57 * @var string Sync setting indicator for disabled sync
58 */
59 const SYSTEM_OF_RECORD_DISABLED = 'disabled';
60
61
62 /**
63 * Refresh token
64 *
65 * @var string un-encrypted refresh token
66 */
67 protected $refresh_token;
68
69 /**
70 * Access token
71 *
72 * @var string un-encrypted access token
73 */
74 protected $access_token;
75
76 /**
77 * Square business locations
78 *
79 * @var array business locations returned by the API
80 */
81 protected $locations;
82
83 /**
84 * Square plugin instance
85 *
86 * @var Plugin plugin instance
87 */
88 protected $plugin;
89
90
91 /**
92 * Constructs the class.
93 *
94 * @since 2.0.0
95 *
96 * @param Plugin $plugin plugin instance.
97 */
98 public function __construct( Plugin $plugin ) {
99
100 $this->plugin = $plugin;
101 $this->plugin_id = 'wc_';
102 $this->id = $plugin->get_id();
103
104 add_action( 'init', array( $this, 'init' ) );
105
106 // remove some of our custom fields that shouldn't be saved.
107 add_action(
108 'woocommerce_settings_api_sanitized_fields_' . $this->id,
109 function( $fields ) {
110
111 unset( $fields['general'], $fields['connect'], $fields['import_products'] );
112
113 if ( $this->is_sandbox() ) {
114 $this->update_access_token( $fields['sandbox_token'] );
115 $this->access_token = false; // Remove encrypted token.
116 $this->refresh_token = false; // Remove encrypted token.
117 }
118
119 // Update the sync interval if it is changed.
120 $this->maybe_change_sync_interval( $fields );
121
122 $this->init_form_fields(); // Reload form fields after saving token.
123
124 return $fields;
125 }
126 );
127
128 add_action( 'admin_notices', array( $this, 'show_auth_keys_changed_notice' ) );
129 }
130
131 /**
132 * Show warning to reconnect if the `SQUARE_ENCRYPTION_KEY` and `SQUARE_ENCRYPTION_SALT` constants
133 * are newly added.
134 *
135 * @since 4.2.0
136 */
137 public function show_auth_keys_changed_notice() {
138 $is_keys_updated = get_option( 'wc_square_auth_key_updated', false );
139 $show_message = ( $this->is_custom_square_auth_keys_set() && empty( $is_keys_updated ) )
140 || ( ! $this->is_custom_square_auth_keys_set() && $is_keys_updated );
141
142 if ( $show_message ) {
143 wc_square()->get_admin_notice_handler()->add_admin_notice(
144 esc_html__( 'Square was disconnected because authentication keys were changed. Please connect again.', 'woocommerce-square' ),
145 'wc-square-disconnected-keys-changed',
146 array(
147 'dismissible' => false,
148 'notice_class' => 'notice-warning',
149 )
150 );
151
152 delete_option( 'wc_square_access_tokens' );
153 }
154
155 if ( ! $this->is_custom_square_auth_keys_set() && $is_keys_updated ) {
156 delete_option( 'wc_square_auth_key_updated' );
157 }
158 }
159
160 /**
161 * Returns true if `SQUARE_ENCRYPTION_KEY` and `SQUARE_ENCRYPTION_SALT` constants are both set.
162 *
163 * @since 4.2.0
164 *
165 * @return boolean
166 */
167 public function is_custom_square_auth_keys_set() {
168 return defined( 'SQUARE_ENCRYPTION_KEY' ) && defined( 'SQUARE_ENCRYPTION_SALT' );
169 }
170
171 /**
172 * Initializes form fields and settings.
173 *
174 * @since 3.5.1
175 */
176 public function init() {
177 $this->init_form_fields();
178 $this->init_settings();
179 }
180
181
182 /**
183 * Initializes the form fields.
184 *
185 * @since 2.0.0
186 */
187 public function init_form_fields() {
188
189 if ( $this->is_connected() ) {
190
191 $general_description = sprintf(
192 /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
193 __( 'Sync your products and inventory and also accept credit and debit card payments at checkout. %1$sClick here%2$s to configure payments.', 'woocommerce-square' ),
194 '<a href="' . esc_url( $this->get_plugin()->get_payment_gateway_configuration_url( $this->get_plugin()->get_gateway()->get_id() ) ) . '">',
195 '</a>'
196 );
197
198 } else {
199
200 $general_description = __( 'Connect with Square to start syncing your products and inventory and also accept credit and debit card payments at checkout.', 'woocommerce-square' );
201 }
202
203 $fields = array(
204 'general' => array(
205 'type' => 'title',
206 'description' => $general_description,
207 ),
208 );
209
210 $fields['enable_sandbox'] = array(
211 'title' => __( 'Enable Sandbox Mode', 'woocommerce-square' ),
212 'label' => '<span>' . __( 'Enable to set the plugin in sandbox mode.', 'woocommerce-square' ) . '</span>',
213 'type' => 'checkbox',
214 'description' => __( 'After enabling you’ll see a new Sandbox settings section with two fields; Sandbox Application ID & Sandbox Access Token.', 'woocommerce-square' ),
215 );
216
217 $fields['sandbox_settings'] = array(
218 'type' => 'title',
219 'title' => __( 'Sandbox settings', 'woocommerce-square' ),
220 'id' => 'wc_square_sandbox_settings',
221 'description' => sprintf(
222 // translators: Placeholders: %1$s - URL.
223 __( 'Sandbox details can be created at: %s', 'woocommerce-square' ),
224 sprintf( '<a href="%1$s">%1$s</a>', 'https://developer.squareup.com/apps' )
225 ),
226 );
227
228 $fields['sandbox_application_id'] = array(
229 'type' => 'text',
230 'title' => __( 'Sandbox Application ID', 'woocommerce-square' ),
231 'class' => 'wc_square_sandbox_settings',
232 'description' => __( 'Application ID for the Sandbox Application, see the details in the My Applications section.', 'woocommerce-square' ),
233 );
234
235 $fields['sandbox_token'] = array(
236 'type' => 'text',
237 'title' => __( 'Sandbox Access Token', 'woocommerce-square' ),
238 'class' => 'wc_square_sandbox_settings',
239 'description' => __( 'Access Token for the Sandbox Test Account, see the details in the Sandbox Test Account section. Make sure you use the correct Sandbox Access Token for your application. For a given Sandbox Test Account, each Authorized Application is assigned a different Access Token.', 'woocommerce-square' ),
240 );
241
242 // display these fields only if connected.
243 if ( $this->is_connected() ) {
244
245 $fields[ $this->get_environment() . '_location_id' ] = array(
246 'title' => __( 'Business location', 'woocommerce-square' ),
247 'type' => 'select',
248 'class' => 'wc-enhanced-select',
249 'description' => sprintf(
250 /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
251 __( 'Select a location to link to this site. Only %1$sactive%2$s %3$slocations%4$s that support credit card processing in Square can be linked.', 'woocommerce-square' ),
252 '<strong>',
253 '</strong>',
254 '<a href="https://docs.woocommerce.com/document/woocommerce-square/#section-4" target="_blank">',
255 '</a>'
256 ),
257 'options' => array(), // this is populated on display.
258 );
259
260 $fields['system_of_record'] = array(
261 'title' => __( 'Sync settings', 'woocommerce-square' ),
262 'type' => 'select',
263 'class' => 'wc-enhanced-select',
264 'description' => sprintf(
265 /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag, %3$s - <a> tag, %4$s - </a> tag */
266 __( 'Choose where data will be updated for synced products. Inventory in Square is %1$salways%2$s checked for adjustments when sync is enabled.%3$s%4$sLearn more%5$s about choosing a system of record or %6$screate a ticket%7$s if you\'re experiencing technical issues.', 'woocommerce-square' ),
267 '<strong>',
268 '</strong>',
269 '<br>',
270 '<a href="' . esc_url( wc_square()->get_documentation_url() ) . '#section-8">',
271 '</a>',
272 '<a href="https://wordpress.org/support/plugin/woocommerce-square/">',
273 '</a>'
274 ),
275 'options' => array(
276 self::SYSTEM_OF_RECORD_DISABLED => __( 'Do not sync product data', 'woocommerce-square' ),
277 self::SYSTEM_OF_RECORD_SQUARE => __( 'Square', 'woocommerce-square' ),
278 self::SYSTEM_OF_RECORD_WOOCOMMERCE => __( 'WooCommerce', 'woocommerce-square' ),
279 ),
280 'default' => 'disabled',
281 );
282
283 $fields['enable_inventory_sync'] = array(
284 'title' => __( 'Sync inventory', 'woocommerce-square' ),
285 'label' => '<span>' . __( 'Enable to sync product inventory with Square', 'woocommerce-square' ) . '</span>',
286 'type' => 'checkbox',
287 'description' => __( 'Inventory is fetched from Square periodically and updated in WooCommerce', 'woocommerce-square' ),
288 );
289
290 $fields['override_product_images'] = array(
291 'title' => __( 'Override product images', 'woocommerce-square' ),
292 'label' => '<span>' . __( 'Enable to override Product images from Square', 'woocommerce-square' ) . '</span>',
293 'type' => 'checkbox',
294 'description' => __( 'Product images that have been updated in Square will also be updated within WooCommerce during a sync.', 'woocommerce-square' ),
295 );
296
297 $fields['hide_missing_products'] = array(
298 'title' => __( 'Handle missing products', 'woocommerce-square' ),
299 'label' => __( 'Hide synced products when not found in Square', 'woocommerce-square' ),
300 'type' => 'checkbox',
301 'description' => __( 'Products not found in Square will be hidden in the WooCommerce product catalog.', 'woocommerce-square' ),
302 );
303
304 $fields['sync_interval'] = array(
305 'title' => __( 'Sync interval', 'woocommerce-square' ),
306 'type' => 'select',
307 'class' => 'wc-enhanced-select',
308 'default' => '1',
309 'options' => array(
310 '0.25' => esc_html__( '15 minutes', 'woocommerce-square' ),
311 '0.5' => esc_html__( '30 minutes', 'woocommerce-square' ),
312 '0.75' => esc_html__( '45 minutes', 'woocommerce-square' ),
313 '1' => esc_html__( '1 hour', 'woocommerce-square' ),
314 '2' => esc_html__( '2 hours', 'woocommerce-square' ),
315 '3' => esc_html__( '3 hours', 'woocommerce-square' ),
316 '6' => esc_html__( '6 hours', 'woocommerce-square' ),
317 '8' => esc_html__( '8 hours', 'woocommerce-square' ),
318 '12' => esc_html__( '12 hours', 'woocommerce-square' ),
319 '24' => esc_html__( '24 hours', 'woocommerce-square' ),
320 ),
321 'description' => sprintf(
322 esc_html__( 'Frequency for how regularly WooCommerce will sync products with Square.', 'woocommerce-square' )
323 ),
324 );
325
326 $sync_interval = $this->get_sync_interval();
327
328 if ( has_filter( 'wc_square_sync_interval' ) ) {
329 $fields['sync_interval']['custom_attributes']['disabled'] = true;
330 $fields['sync_interval']['description'] = sprintf(
331 // translators: %4$s: interval duration in minutes.
332 esc_html__( 'Frequency for how regularly WooCommerce will sync products with Square.%1$sSync interval settings are disabled as they are being overridden by the %2$swc_square_sync_interval%3$s filter. This filter is setting the sync interval to %4$s minutes.', 'woocommerce-square' ),
333 '<br /><br />',
334 '<code>',
335 '</code>',
336 $sync_interval / HOUR_IN_SECONDS * 60
337 );
338 }
339
340 $fields['import_products'] = array(
341 'title' => __( 'Import Products', 'woocommerce-square' ),
342 'type' => 'import_products',
343 'desc_tip' => __( 'Run an import to create new products in this WooCommerce store for each new product created in Square that has a unique SKU not existing in here. Needs to be run each time new items are created in Square.', 'woocommerce-square' ),
344 );
345 }
346
347 // In sandbox mode we don't want to intially display the connect button, only disconnect.
348 if ( ! ( $this->is_sandbox() && ! $this->is_connected() ) ) {
349 $fields = array_merge(
350 $fields,
351 array(
352 'connect' => array(
353 'title' => __( 'Connection', 'woocommerce-square' ),
354 'type' => 'connect',
355 'desc_tip' => '',
356 ),
357 )
358 );
359 }
360
361 // Always display these fields.
362 $fields = array_merge(
363 $fields,
364 array(
365 'debug_logging_enabled' => array(
366 'title' => __( 'Enable Logging', 'woocommerce-square' ),
367 'type' => 'checkbox',
368 'label' => sprintf(
369 /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
370 __( 'Log debug messages to the %1$sWooCommerce status log%2$s', 'woocommerce-square' ),
371 '<a href="' . esc_url( admin_url( 'admin.php?page=wc-status&tab=logs' ) ) . '">',
372 '</a>'
373 ),
374 ),
375 )
376 );
377
378 $this->form_fields = $fields;
379 }
380
381
382 /**
383 * Gets the form fields.
384 *
385 * Overridden to populate the Location settings options on display.
386 *
387 * @since 2.0.0
388 *
389 * @return array
390 */
391 public function get_form_fields() {
392
393 $fields = parent::get_form_fields();
394
395 // Confirm our local enable sandbox setting matches what is sent from the front end
396 // to account for changes from sandbox to production incorrectly fetching sandbox locations.
397 if ( $this->settings && isset( $_POST['wc_square_environment'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
398
399 $environment = 'yes' === $this->settings['enable_sandbox'] ? 'sandbox' : 'production';
400
401 if ( $environment !== $_POST['wc_square_environment'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
402 return $fields;
403 }
404 }
405
406 $location_id_field_key = '';
407 // Get the location_id field.
408 foreach ( $fields as $key => $value ) {
409 if ( strpos( $key, 'location_id' ) ) {
410 $location_id_field_key = $key;
411 break;
412 }
413 }
414
415 if ( did_action( 'wc_square_initialized' ) && $this->is_admin_settings_screen() && ! empty( $location_id_field_key ) ) {
416
417 $locations = array(
418 '' => __( 'Please choose a location', 'woocommerce-square' ),
419 );
420
421 if ( ! empty( $this->get_locations() ) ) {
422 foreach ( $this->get_locations() as $location ) {
423 if ( 'ACTIVE' === $location->getStatus() && in_array( 'CREDIT_CARD_PROCESSING', (array) $location->getCapabilities(), true ) ) {
424 $locations[ $location->getId() ] = $location->getName();
425 }
426 }
427 }
428
429 $fields[ $location_id_field_key ]['options'] = $locations;
430 }
431
432 return $fields;
433 }
434
435
436 /**
437 * Generates the HTML for import products button.
438 *
439 * @param string $id form id.
440 * @param array $field form fields.
441 */
442 public function generate_import_products_html( $id, $field ) {
443
444 $is_location_set = (bool) $this->get_location_id();
445 $is_sor_set = (bool) $this->get_system_of_record_name();
446 $display = $is_location_set && $is_sor_set ? '' : 'display: none';
447
448 ob_start();
449 ?>
450 <tr valign="top" style="<?php echo esc_attr( $display ); ?>">
451 <th scope="row" class="titledesc">
452 <label for="<?php echo esc_attr( $id ); ?>"><?php echo wp_kses_post( $field['title'] ); ?> <?php echo $this->get_tooltip_html( $field ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></label>
453 </th>
454 <td class="forminp">
455 <a id="wc_square_import_products" href='#' class='button js-import-square-products <?php echo ( ! $this->get_location_id() ? 'disabled' : '' ); ?>'>
456 <?php echo esc_html__( 'Import all products from Square', 'woocommerce-square' ); ?>
457 </a>
458 <p class="description wc_square_save_changes_message" style="display: none;"><?php esc_html_e( 'You have made changes to the settings. Please save the changes to enable the button.', 'woocommerce-square' ); ?></p>
459 </td>
460 </tr>
461 <?php
462
463 return ob_get_clean();
464 }
465
466
467 /**
468 * Generates the Connection field HTML.
469 *
470 * @since 2.0.0
471 *
472 * @param string $id field ID.
473 * @param array $field field data.
474 * @return string
475 */
476 public function generate_connect_html( $id, $field ) {
477
478 ob_start();
479 ?>
480 <tr valign="top">
481 <th scope="row" class="titledesc">
482 <label for="<?php echo esc_attr( $id ); ?>"><?php echo wp_kses_post( $field['title'] ); ?> <?php echo $this->get_tooltip_html( $field ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></label>
483 </th>
484 <td class="forminp">
485 <?php
486 if ( $this->get_access_token() ) {
487 echo $this->get_plugin()->get_connection_handler()->get_disconnect_button_html(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
488 } else {
489 echo $this->get_plugin()->get_connection_handler()->get_connect_button_html( $this->is_sandbox() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
490 }
491 ?>
492 </td>
493 </tr>
494 <?php
495
496 return ob_get_clean();
497 }
498
499
500 /**
501 * Updates the stored refresh token.
502 *
503 * @since 2.0.0
504 *
505 * @param string $token refresh token.
506 */
507 public function update_refresh_token( $token ) {
508
509 $refresh_tokens = $this->get_refresh_tokens();
510 $environment = $this->get_environment();
511
512 if ( ! empty( $token ) ) {
513
514 $this->refresh_token = $token;
515
516 if ( Utilities\Encryption_Utility::is_encryption_supported() ) {
517
518 $encryption = new Utilities\Encryption_Utility();
519
520 try {
521
522 $token = $encryption->encrypt_data( $token );
523
524 } catch ( \Exception $exception ) {
525
526 // log the event, but don't halt the process.
527 $this->get_plugin()->log( 'Could not encrypt refresh token. ' . $exception->getMessage() );
528 }
529 }
530
531 $refresh_tokens[ $environment ] = $token;
532 }
533
534 update_option( 'wc_square_refresh_tokens', $refresh_tokens );
535 }
536
537
538 /**
539 * Updates the stored access token.
540 *
541 * @since 2.0.0
542 *
543 * @param string $token access token.
544 */
545 public function update_access_token( $token ) {
546
547 $access_tokens = $this->get_access_tokens();
548 $environment = $this->get_environment();
549
550 if ( ! empty( $token ) ) {
551
552 $this->access_token = $token;
553
554 if ( Utilities\Encryption_Utility::is_encryption_supported() ) {
555
556 $encryption = new Utilities\Encryption_Utility();
557
558 try {
559
560 $token = $encryption->encrypt_data( $token );
561
562 } catch ( \Exception $exception ) {
563
564 // log the event, but don't halt the process.
565 $this->get_plugin()->log( 'Could not encrypt access token. ' . $exception->getMessage() );
566 }
567 }
568
569 $access_tokens[ $environment ] = $token;
570 } elseif ( isset( $access_tokens[ $environment ] ) ) {
571
572 unset( $access_tokens[ $environment ] );
573 }
574
575 update_option( 'wc_square_access_tokens', $access_tokens );
576 }
577
578
579 /**
580 * Clears any stored refresh tokens.
581 *
582 * @since 2.0.0
583 */
584 public function clear_refresh_tokens() {
585 delete_option( 'wc_square_refresh_tokens' );
586 }
587
588
589 /**
590 * Clears any stored access tokens.
591 *
592 * @since 2.0.0
593 */
594 public function clear_access_tokens() {
595
596 delete_option( 'wc_square_access_tokens' );
597 }
598
599
600 /**
601 * Clears the location ID from the settings.
602 *
603 * This is helpful on disconnect / revoke so that previously set location IDs don't stick around and cause confusion.
604 *
605 * @since 2.0.0
606 */
607 public function clear_location_id() {
608
609 $settings = get_option( $this->get_option_key(), array() );
610
611 $settings[ $this->get_environment() . '_location_id' ] = '';
612
613 update_option( $this->get_option_key(), $settings );
614 }
615
616
617 /** Conditional methods *******************************************************************************************/
618
619
620 /**
621 * Determines if WooCommerce is configured to be the Sync setting.
622 *
623 * @since 2.0.0
624 *
625 * @return bool
626 */
627 public function is_system_of_record_woocommerce() {
628
629 return self::SYSTEM_OF_RECORD_WOOCOMMERCE === $this->get_system_of_record();
630 }
631
632
633 /**
634 * Determines if Square is configured to be the Sync setting.
635 *
636 * @since 2.0.0
637 *
638 * @return bool
639 */
640 public function is_system_of_record_square() {
641
642 return self::SYSTEM_OF_RECORD_SQUARE === $this->get_system_of_record();
643 }
644
645
646 /**
647 * Determines if there is no Sync setting.
648 *
649 * @since 2.0.0
650 *
651 * @return bool
652 */
653 public function is_system_of_record_disabled() {
654
655 $sor = $this->get_system_of_record();
656
657 return empty( $sor ) || self::SYSTEM_OF_RECORD_DISABLED === $sor;
658 }
659
660
661 /**
662 * Determines if inventory sync is enabled.
663 *
664 * @since 2.0.0
665 *
666 * @return bool
667 */
668 public function is_inventory_sync_enabled() {
669
670 /**
671 * Filters the inventory sync setting.
672 *
673 * @since 2.0.0
674 */
675 return (bool) apply_filters( 'wc_square_inventory_sync_enabled', 'yes' === get_option( 'woocommerce_manage_stock' ) && $this->is_product_sync_enabled() && 'yes' === $this->get_option( 'enable_inventory_sync' ) );
676 }
677
678 /**
679 * Determines if image overriding is enabled.
680 *
681 * @since 3.9.0
682 *
683 * @return bool
684 */
685 public function is_override_product_images_enabled() {
686 /**
687 * Filter to enable/disable overriding product images.
688 *
689 * @since 3.9.0
690 *
691 * @param boolean 'should_override' Boolean flag to toggle overriding image feature.
692 */
693 return (bool) apply_filters( 'wc_square_override_product_images_enabled', 'yes' === $this->get_option( 'override_product_images' ) );
694 }
695
696
697 /**
698 * Determines if product sync is enabled.
699 *
700 * @since 2.0.0
701 *
702 * @return bool
703 */
704 public function is_product_sync_enabled() {
705
706 return ! $this->is_system_of_record_disabled();
707 }
708
709
710 /**
711 * Determines whether to hide products that don't exist in square from the catalog.
712 *
713 * @since 2.0.0
714 *
715 * @return bool
716 */
717 public function hide_missing_square_products() {
718
719 return 'yes' === $this->get_option( 'hide_missing_products' );
720 }
721
722 /**
723 * Returns sync interval in seconds.
724 * Returns 1 hr = 3600 seconds as default.
725 *
726 * @since 3.5.1
727 *
728 * @return int
729 */
730 public function get_sync_interval() {
731 $sync_interval = $this->get_option( 'sync_interval', '' );
732 $sync_interval = empty( $sync_interval ) ? HOUR_IN_SECONDS : $sync_interval * HOUR_IN_SECONDS;
733
734 /**
735 * Filters the frequency with which products should be synced.
736 *
737 * @since 2.0.0
738 *
739 * @param int $interval sync interval in seconds (defaults to one hour)
740 */
741 return (int) max( MINUTE_IN_SECONDS, (int) apply_filters( 'wc_square_sync_interval', $sync_interval ) );
742 }
743
744
745 /**
746 * Determines if the plugin settings are fully configured.
747 *
748 * @since 2.0.0
749 *
750 * @return bool
751 */
752 public function is_configured() {
753
754 return $this->get_location_id() && $this->get_system_of_record();
755 }
756
757
758 /**
759 * Determines if the plugin is connected to Square.
760 *
761 * @since 2.0.0
762 *
763 * @return bool
764 */
765 public function is_connected() {
766
767 return (bool) $this->get_access_token();
768 }
769
770
771 /**
772 * Determines if configured in the sandbox environment.
773 *
774 * @since 2.0.0
775 *
776 * @return bool
777 */
778 public function is_sandbox() {
779
780 return 'sandbox' === $this->get_environment();
781 }
782
783
784 /**
785 * Determines if debug logging is enabled.
786 *
787 * @since 2.0.0
788 *
789 * @return bool
790 */
791 public function is_debug_enabled() {
792
793 return 'yes' === $this->get_option( 'debug_logging_enabled' );
794 }
795
796
797 /** Getter methods ************************************************************************************************/
798
799
800 /**
801 * Gets the configured location.
802 *
803 * @since 2.0.0
804 *
805 * @return string
806 */
807 public function get_location_id() {
808 $location_id = $this->get_option( $this->get_environment() . '_location_id' );
809
810 if ( empty( $location_id ) ) {
811 $square_db_version = get_option( $this->get_plugin()->get_plugin_version_name() );
812
813 // if the Square DB version is still pre-2.2.0, fetch the location ID using the previous option name
814 if ( ! empty( $square_db_version ) && version_compare( $square_db_version, '2.2.0', '<' ) ) {
815 $location_id = $this->get_option( 'location_id' );
816 }
817 }
818
819 return $location_id;
820 }
821
822
823 /**
824 * Gets the available locations.
825 *
826 * @since 2.0.0
827 *
828 * @return \Square\Models\Location[]
829 */
830 public function get_locations() {
831
832 if ( is_array( $this->locations ) ) {
833
834 return $this->locations;
835 }
836
837 $locations_transient_key = 'wc_square_locations_' . $this->get_plugin()->get_version();
838
839 $section = isset( $_GET['section'] ) ? sanitize_text_field( wp_unslash( $_GET['section'] ) ) : false; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
840
841 // don't always need to refetch when not on Settings screen.
842 if ( ! $this->is_admin_settings_screen() || ( $this->is_admin_settings_screen() && 'update' === $section ) ) {
843 $this->locations = get_transient( $locations_transient_key );
844 }
845
846 if ( ! is_array( $this->locations ) && did_action( 'wc_square_initialized' ) ) {
847
848 $this->locations = array();
849
850 try {
851
852 // cache the locations returned so they can be used elsewhere.
853 $this->locations = $this->get_plugin()->get_api( $this->get_access_token(), $this->is_sandbox() )->get_locations();
854 set_transient( $locations_transient_key, $this->locations, HOUR_IN_SECONDS );
855
856 // check the returned IDs against what's currently configured.
857 $stored_location_id = $this->get_location_id();
858 $found = ! $stored_location_id;
859
860 foreach ( $this->locations as $location ) {
861
862 if ( $stored_location_id && $location->getId() === $stored_location_id ) {
863 $found = true;
864 break;
865 }
866 }
867
868 // if the currently set location ID is not present in the connected account's available locations, clear it locally.
869 if ( ! $found ) {
870 $this->clear_location_id();
871 }
872 } catch ( \Exception $exception ) {
873
874 $this->get_plugin()->log( 'Could not retrieve business locations.' );
875 }
876 }
877
878 return $this->locations;
879 }
880
881
882 /**
883 * Gets the configured Sync setting.
884 *
885 * @since 2.0.0
886 *
887 * @return string
888 */
889 public function get_system_of_record() {
890
891 return $this->get_option( 'system_of_record' );
892 }
893
894
895 /**
896 * Gets the configured Sync setting name.
897 *
898 * @since 2.0.0
899 *
900 * @return string or empty string if no Sync setting is configured
901 */
902 public function get_system_of_record_name() {
903
904 switch ( $this->get_system_of_record() ) {
905
906 case 'square':
907 $sor = __( 'Square', 'woocommerce-square' );
908 break;
909 case 'woocommerce':
910 $sor = __( 'WooCommerce', 'woocommerce-square' );
911 break;
912 default:
913 $sor = '';
914 break;
915 }
916
917 return $sor;
918 }
919
920 /**
921 * Gets the refresh token.
922 *
923 * @since 2.0.0
924 *
925 * @return string|null
926 */
927 public function get_refresh_token() {
928
929 if ( empty( $this->refresh_token ) ) {
930
931 $tokens = $this->get_refresh_tokens();
932 $token = null;
933
934 if ( ! empty( $tokens[ $this->get_environment() ] ) ) {
935 $token = $tokens[ $this->get_environment() ];
936 }
937
938 if ( $token && Utilities\Encryption_Utility::is_encryption_supported() ) {
939
940 $encryption = new Utilities\Encryption_Utility();
941
942 try {
943
944 $token = $encryption->decrypt_data( $token );
945
946 } catch ( \Exception $exception ) {
947
948 // log the event, but don't halt the process.
949 $this->get_plugin()->log( 'Could not decrypt refresh token. ' . $exception->getMessage() );
950 }
951 }
952
953 $this->refresh_token = $token;
954 }
955
956 /**
957 * Filters the configured refresh token.
958 *
959 * @since 2.0.0
960 *
961 * @param string $refresh_token
962 */
963 return apply_filters( 'wc_square_refresh_token', $this->refresh_token );
964 }
965
966 /**
967 * Gets the access token.
968 *
969 * @since 2.0.0
970 *
971 * @return string|null
972 */
973 public function get_access_token() {
974
975 if ( empty( $this->access_token ) || $this->is_admin_settings_screen() ) {
976
977 $tokens = $this->get_access_tokens();
978 $token = null;
979
980 if ( ! empty( $tokens[ $this->get_environment() ] ) ) {
981 $token = $tokens[ $this->get_environment() ];
982 }
983
984 if ( $token && Utilities\Encryption_Utility::is_encryption_supported() ) {
985
986 $encryption = new Utilities\Encryption_Utility();
987
988 try {
989
990 $token = $encryption->decrypt_data( $token );
991
992 } catch ( \Exception $exception ) {
993
994 // log the event, but don't halt the process.
995 $this->get_plugin()->log( 'Could not decrypt access token. ' . $exception->getMessage() );
996 }
997 }
998
999 $this->access_token = $token;
1000 }
1001
1002 /**
1003 * Filters the configured access token.
1004 *
1005 * @since 2.0.0
1006 *
1007 * @param string $access_token access token
1008 */
1009 return apply_filters( 'wc_square_access_token', $this->access_token );
1010 }
1011
1012
1013 /**
1014 * Gets the stored access tokens.
1015 *
1016 * Each environment may have its own token.
1017 *
1018 * @since 2.0.0
1019 *
1020 * @return array
1021 */
1022 public function get_access_tokens() {
1023 return (array) get_option( 'wc_square_access_tokens', array() );
1024 }
1025
1026
1027 /**
1028 * Gets the stored refresh tokens.
1029 *
1030 * Each environment may have its own token.
1031 *
1032 * @since 2.0.0
1033 *
1034 * @return array
1035 */
1036 public function get_refresh_tokens() {
1037 return (array) get_option( 'wc_square_refresh_tokens', array() );
1038 }
1039
1040 /**
1041 * Gets setting enabled sandbox.
1042 *
1043 * @since 2.1.2
1044 *
1045 * @return string
1046 */
1047 public function get_enable_sandbox() {
1048 return $this->get_option( 'enable_sandbox' );
1049 }
1050
1051 /**
1052 * Tells is if the setting for enabling sandbox is checked.
1053 *
1054 * @since 2.1.2
1055 *
1056 * @return boolean
1057 */
1058 public function is_sandbox_setting_enabled() {
1059 return 'yes' === $this->get_enable_sandbox();
1060 }
1061
1062
1063 /**
1064 * Gets the configured environment.
1065 *
1066 * @since 2.0.0
1067 *
1068 * @return string
1069 */
1070 public function get_environment() {
1071 $sanboxed = ( defined( 'WC_SQUARE_SANDBOX' ) && WC_SQUARE_SANDBOX ) || $this->is_sandbox_setting_enabled();
1072 return $sanboxed ? 'sandbox' : 'production';
1073 }
1074
1075
1076 /**
1077 * Gets the plugin instance.
1078 *
1079 * @since 2.0.0
1080 *
1081 * @return Plugin
1082 */
1083 public function get_plugin() {
1084
1085 return $this->plugin;
1086 }
1087
1088 /**
1089 * Determines if the current request is for the Square admin settings screen.
1090 *
1091 * @since 2.1.5
1092 * @return bool True if the current request is for the Square admin settings, otherwise false.
1093 */
1094 public function is_admin_settings_screen() {
1095 return isset( $_GET['page'], $_GET['tab'] ) && 'wc-settings' === $_GET['page'] && Plugin::PLUGIN_ID === $_GET['tab']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1096 }
1097
1098 /**
1099 * Update the sync interval if it has changed.
1100 *
1101 * @param array $settings
1102 * @return void
1103 */
1104 public function maybe_change_sync_interval( $settings ) {
1105 // Bail if we have a filter in place to manage the sync interval.
1106 if ( has_filter( 'wc_square_sync_interval' ) ) {
1107 return;
1108 }
1109
1110 $old_settings = get_option( $this->get_option_key(), array() );
1111 // Bail if we don't have a sync interval.
1112 if ( empty( $old_settings['sync_interval'] ) || empty( $settings['sync_interval'] ) ) {
1113 return;
1114 }
1115
1116 // If the sync interval has changed, schedule a new sync.
1117 if ( $old_settings['sync_interval'] !== $settings['sync_interval'] ) {
1118 $this->plugin->get_sync_handler()->schedule_sync( true );
1119 }
1120 }
1121
1122 }
1123