PluginProbe ʕ •ᴥ•ʔ
WooCommerce Square / 5.5.0
WooCommerce Square v5.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 / Admin.php
woocommerce-square / includes Last commit date
API 5 months ago Admin 1 month ago Emails 1 year ago Framework 1 month ago Gateway 3 months ago Handlers 2 days ago Internal 3 months ago Sync 2 days ago Utilities 3 months ago AJAX.php 2 years ago API.php 2 days ago Admin.php 2 days ago Coupons.php 5 months ago Functions.php 3 years ago Gateway.php 4 months ago Lifecycle.php 2 years ago Plugin.php 2 days ago Settings.php 5 months ago WC_Order_Square.php 2 years ago WC_Payments_Compatibility.php 2 years ago
Admin.php
529 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 use WooCommerce\Square\Admin\Analytics\Revenue;
29 use WooCommerce\Square\Handlers\Products;
30 use WooCommerce\Square\Handlers\Product;
31
32 /**
33 * The base admin handler class.
34 *
35 * @since 2.0.0
36 */
37 class Admin {
38
39
40 /**
41 * Product handler.
42 *
43 * @var Handlers\Products
44 */
45 private $products_handler;
46
47 /**
48 * Privacy handler.
49 *
50 * @var Admin\Privacy
51 */
52 private $privacy_handler;
53
54 /**
55 * Plugin
56 *
57 * @var Plugin plugin instance
58 */
59 private $plugin;
60
61
62 /**
63 * Constructs the class.
64 *
65 * @since 2.0.0
66 *
67 * @param Plugin $plugin plugin instance.
68 */
69 public function __construct( Plugin $plugin ) {
70
71 $this->plugin = $plugin;
72
73 $this->products_handler = $this->plugin->get_products_handler();
74
75 // privacy
76 $this->privacy_handler = new Admin\Privacy();
77
78 new Revenue();
79
80 $this->add_hooks();
81 }
82
83
84 /**
85 * Adds the action & filter hooks.
86 *
87 * @since 2.0.0
88 */
89 private function add_hooks() {
90
91 // add the settings page.
92 add_filter(
93 'woocommerce_get_settings_pages',
94 function ( $pages ) {
95
96 $pages[] = new Admin\Settings_Page( $this->get_plugin()->get_settings_handler() );
97
98 return $pages;
99 }
100 );
101
102 // load admin scripts.
103 add_action(
104 'admin_enqueue_scripts',
105 function ( $hook ) {
106 $this->load_scripts_styles( $hook );
107 }
108 );
109
110 // Show warning notice for the user to manually trigger a sync if it has been a while since the last successful sync.
111 add_action( 'admin_notices', array( $this, 'maybe_show_sync_status_notice' ) );
112
113 // Show a notice when a stalled sync job was automatically recovered, prompting a re-run.
114 add_action( 'admin_notices', array( $this, 'maybe_show_sync_recovered_notice' ) );
115 }
116
117
118 /**
119 * Loads and enqueues admin scripts and styles.
120 *
121 * @param string $hook The current admin page.
122 *
123 * @since 2.0.0
124 */
125 private function load_scripts_styles( $hook ) {
126 global $typenow;
127
128 if ( 'product' === $typenow ) {
129
130 wp_enqueue_script(
131 'wc-square-admin-products',
132 $this->get_plugin()->get_plugin_url() . '/build/assets/admin/wc-square-admin-products.js',
133 array( 'jquery' ),
134 Plugin::VERSION,
135 true
136 );
137
138 wp_enqueue_style(
139 'wc-square-admin-products',
140 $this->get_plugin()->get_plugin_url() . '/build/assets/admin/wc-square-admin-products-styles.css',
141 array(),
142 Plugin::VERSION
143 );
144
145 wp_localize_script(
146 'wc-square-admin-products',
147 'wc_square_admin_products',
148 array(
149 'ajax_url' => admin_url( 'admin-ajax.php' ),
150 'settings_url' => esc_url( $this->get_plugin()->get_settings_url() ),
151 'variable_product_types' => $this->get_variable_product_types(),
152 'synced_with_square_taxonomy' => Product::SYNCED_WITH_SQUARE_TAXONOMY,
153 'is_product_sync_enabled' => $this->get_plugin()->get_settings_handler()->is_product_sync_enabled(),
154 'is_woocommerce_sor' => $this->get_plugin()->get_settings_handler()->is_system_of_record_woocommerce(),
155 'is_square_sor' => $this->get_plugin()->get_settings_handler()->is_system_of_record_square(),
156 'is_inventory_sync_enabled' => $this->get_plugin()->get_settings_handler()->is_inventory_sync_enabled(),
157 'get_quick_edit_product_details_nonce' => wp_create_nonce( 'get-quick-edit-product-details' ),
158 'fetch_product_stock_with_square_nonce' => wp_create_nonce( 'fetch-product-stock-with-square' ),
159 'supported_products_for_sync' => array( 'simple', 'variable' ),
160 'i18n' => array(
161 'inventory_tracking_disabled' => __( 'Inventory tracking is disabled for this product', 'woocommerce-square' ),
162 'synced_with_square' => __( 'Synced with Square', 'woocommerce-square' ),
163 'managed_by_square' => __( 'Managed by Square', 'woocommerce-square' ),
164 'fetch_stock_with_square' => __( 'Fetch stock from Square', 'woocommerce-square' ),
165 'sync_inventory' => __( 'Sync inventory', 'woocommerce-square' ),
166 'sync_stock_from_square' => __( 'Sync stock from Square', 'woocommerce-square' ),
167 'attribute_name_too_long' => __( 'Attribute name is too long, maximum allowed are 65 characters', 'woocommerce-square' ),
168 'too_many_attributes' => __( 'Too many attributes, maximum allowed are 6.', 'woocommerce-square' ),
169 /* translators: %d - maximum allowed attribute values */
170 'too_many_attribute_values' => __( 'Too many attribute values: %d (max 250)', 'woocommerce-square' ),
171 'too_many_variations' => __( 'Too many variations, maximum allowed are 250.', 'woocommerce-square' ),
172 ),
173 )
174 );
175 } elseif ( $this->get_plugin()->is_plugin_settings() ) {
176 wp_enqueue_style(
177 'wc-square-admin',
178 $this->get_plugin()->get_plugin_url() . '/build/assets/admin/wc-square-admin.css',
179 array(),
180 Plugin::VERSION
181 );
182
183 wp_enqueue_media();
184
185 wp_enqueue_script(
186 'wc-square-admin-settings',
187 $this->get_plugin()->get_plugin_url() . '/build/assets/admin/wc-square-admin-settings.js',
188 array( 'jquery', 'jquery-blockui', 'backbone', 'wc-backbone-modal' ),
189 Plugin::VERSION,
190 true
191 );
192
193 $sync_job = $this->get_plugin()->get_sync_handler()->get_job_in_progress();
194
195 if ( $sync_job ) {
196 $existing_sync_id = $sync_job->id;
197 } else {
198 $existing_sync_id = false;
199 }
200
201 wp_localize_script(
202 'wc-square-admin-settings',
203 'wc_square_admin_settings',
204 array(
205 'ajax_url' => admin_url( 'admin-ajax.php' ),
206 'is_product_sync_enabled' => $this->get_plugin()->get_settings_handler()->is_product_sync_enabled(),
207 'is_woocommerce_sor' => $this->get_plugin()->get_settings_handler()->is_system_of_record_woocommerce(),
208 'is_square_sor' => $this->get_plugin()->get_settings_handler()->is_system_of_record_square(),
209 'is_inventory_sync_enabled' => $this->get_plugin()->get_settings_handler()->is_inventory_sync_enabled(),
210 'is_sandbox' => $this->get_plugin()->get_settings_handler()->is_sandbox(),
211 'existing_sync_job_id' => $existing_sync_id,
212 'import_products_from_square' => wp_create_nonce( 'import-products-from-square' ),
213 'sync_products_with_square' => wp_create_nonce( 'sync-products-with-square' ),
214 'get_sync_with_square_status_nonce' => wp_create_nonce( 'get-sync-with-square-status' ),
215 'handle_sync_with_square_records' => wp_create_nonce( 'handle-sync-with-square-records' ),
216 'i18n' => array(
217 'resolved' => __( 'Resolved', 'woocommerce-square' ),
218 'no_records_found' => __( 'No records found', 'woocommerce-square' ),
219 'skipped' => __( 'Skipped', 'woocommerce-square' ),
220 'updated' => __( 'Updated', 'woocommerce-square' ),
221 'imported' => __( 'Imported', 'woocommerce-square' ),
222 'sync_inventory_label' => array(
223 'square' => __( 'Enable to fetch inventory changes from Square', 'woocommerce-square' ),
224 'woocommerce' => __( 'Enable to push inventory changes to Square', 'woocommerce-square' ),
225 ),
226 'sync_inventory_description' => array(
227 'square' => __( 'Inventory is fetched from Square periodically and updated in WooCommerce', 'woocommerce-square' ),
228 'woocommerce' => sprintf(
229 /* translators: Placeholders: %1$s - <strong> tag, %2$s - </strong> tag */
230 __( 'Inventory is %1$salways fetched from Square%2$s periodically to account for sales from other channels.', 'woocommerce-square' ),
231 '<strong>',
232 '</strong>'
233 ),
234 ),
235 ),
236 )
237 );
238
239 $asset_file = WC_SQUARE_PLUGIN_PATH . 'build/settings.asset.php';
240
241 if ( ! file_exists( $asset_file ) ) {
242 return;
243 }
244
245 $asset = include $asset_file;
246
247 wp_enqueue_script(
248 'woocommerce-square-settings-js',
249 WC_SQUARE_PLUGIN_URL . 'build/settings.js',
250 $asset['dependencies'],
251 $asset['version'],
252 array(
253 'in_footer' => true,
254 )
255 );
256
257 $gift_card_placeholder_url = Products::get_gift_card_default_placeholder_url();
258
259 if ( empty( $gift_card_placeholder_url ) ) {
260 $gift_card_placeholder_url = WC_SQUARE_PLUGIN_URL . 'build/images/gift-card-featured-image.png';
261 }
262
263 wp_localize_script(
264 'woocommerce-square-settings-js',
265 'wcSquareSettings',
266 array(
267 'nonce' => wp_create_nonce( 'wc_square_settings' ),
268 'homeUrl' => home_url(),
269 'adminUrl' => admin_url(),
270 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
271 'depsCheck' => $this->get_plugin()->get_dependency_handler()->meets_php_dependencies(),
272 'gcPlaceholderUrl' => esc_url( $gift_card_placeholder_url ),
273 )
274 );
275
276 wp_enqueue_style(
277 'woocommerce-square-settings-css',
278 WC_SQUARE_PLUGIN_URL . 'build/settings.css',
279 array(),
280 $asset['version'],
281 );
282 } elseif ( 'woocommerce_page_woocommerce-square-onboarding' === $hook ) {
283 $asset_file = WC_SQUARE_PLUGIN_PATH . 'build/onboarding.asset.php';
284
285 if ( ! file_exists( $asset_file ) ) {
286 return;
287 }
288
289 $asset = include $asset_file;
290
291 wp_enqueue_script(
292 'woocommerce-square-onboarding-js',
293 WC_SQUARE_PLUGIN_URL . 'build/onboarding.js',
294 $asset['dependencies'],
295 $asset['version'],
296 array(
297 'in_footer' => true,
298 )
299 );
300
301 wp_localize_script(
302 'woocommerce-square-onboarding-js',
303 'wcSquareSettings',
304 array(
305 'nonce' => wp_create_nonce( 'wc_square_settings' ),
306 'homeUrl' => home_url(),
307 'adminUrl' => admin_url(),
308 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
309 )
310 );
311
312 wp_enqueue_style(
313 'woocommerce-square-onboarding-css',
314 WC_SQUARE_PLUGIN_URL . 'build/onboarding.css',
315 array(),
316 $asset['version'],
317 );
318
319 wp_localize_script(
320 'woocommerce-square-onboarding-js',
321 'wcSquareOnboarding',
322 array(
323 'plugin_version' => WC_SQUARE_PLUGIN_VERSION,
324 'is_mobile' => wp_is_mobile(),
325 )
326 );
327 }
328
329 wp_enqueue_style( 'wp-components' );
330 }
331
332
333 /**
334 * Gets a list of variable product types.
335 *
336 * @since 2.0.0
337 *
338 * @return string[]
339 */
340 private function get_variable_product_types() {
341
342 /**
343 * Filters the variable product types.
344 *
345 * @since 2.0.0
346 *
347 * @param string[] array of product types
348 */
349 return (array) apply_filters( 'wc_square_variable_product_types', array( 'variable', 'variable-subscription' ) );
350 }
351
352
353 /**
354 * Gets the products handler.
355 *
356 * @since 2.0.0
357 *
358 * @return Admin\Products
359 */
360 public function get_products_handler() {
361
362 return $this->products_handler;
363 }
364
365
366 /**
367 * Gets the plugin instance.
368 *
369 * @since 2.0.0
370 *
371 * @return Plugin
372 */
373 protected function get_plugin() {
374
375 return $this->plugin;
376 }
377
378 /**
379 * Maybe show a warning notice for the user to manually trigger a sync if it has been a while since the last successful sync.
380 *
381 * If the following conditions are met, show the notice:
382 * - The connection is established.
383 * - The location is set.
384 * - The product sync is enabled.
385 * - The last synced at is set and is before the threshold time (default is 24 hours, 48 hours if the sync interval is 24 hours).
386 * - The synced products count is greater than 0 (at least one product is synced with Square).
387 *
388 * @since 5.3.3
389 *
390 * @return void
391 */
392 public function maybe_show_sync_status_notice() {
393 // Bail if the user does not have the manage_woocommerce capability.
394 if ( ! current_user_can( 'manage_woocommerce' ) ) { // phpcs:ignore WordPress.WP.Capabilities.Unknown
395 return;
396 }
397
398 // Bail if the connection is not established, the location is not set, or the product sync is not enabled.
399 $is_connected = wc_square()->get_settings_handler()->is_connected();
400 $location_id = wc_square()->get_settings_handler()->get_location_id();
401 $is_product_sync_enabled = wc_square()->get_settings_handler()->is_product_sync_enabled();
402 if ( ! $is_connected || ! $location_id || ! $is_product_sync_enabled ) {
403 return;
404 }
405
406 // Bail if the last synced at is not set.
407 $last_synced_at = wc_square()->get_sync_handler()->get_last_synced_at();
408 if ( ! $last_synced_at ) {
409 return;
410 }
411
412 // The threshold time is 24 hours by default, 48 hours if the sync interval is 24 hours.
413 $sync_interval = wc_square()->get_settings_handler()->get_sync_interval();
414 $threshold_seconds = $sync_interval >= 24 * HOUR_IN_SECONDS ? 48 * HOUR_IN_SECONDS : 24 * HOUR_IN_SECONDS;
415
416 /**
417 * Filters the threshold time for the sync status notice.
418 *
419 * @since 5.3.3
420 *
421 * @param int $threshold_seconds The threshold time in seconds. Default is 24 hours, or 48 hours when the sync interval is 24 hours.
422 * @param int $sync_interval The sync interval in seconds.
423 */
424 $threshold_seconds = apply_filters( 'wc_square_sync_status_notice_threshold_seconds', $threshold_seconds, $sync_interval );
425 $threshold_time = time() - $threshold_seconds;
426
427 // Bail if the last synced at is after the threshold time.
428 if ( $last_synced_at > $threshold_time ) {
429 return;
430 }
431
432 // Get the synced products count.
433 $synced_products_count_key = 'wc_square_synced_products_count_' . $location_id;
434 $synced_products_count = get_transient( $synced_products_count_key );
435
436 if ( false === $synced_products_count ) {
437 $synced_products_count = count( Product::get_products_synced_with_square() );
438
439 // Set the transient for the synced products count.
440 set_transient( $synced_products_count_key, $synced_products_count, $threshold_seconds );
441 }
442
443 // Bail if synced products count is 0.
444 if ( 0 === (int) $synced_products_count ) {
445 return;
446 }
447
448 // Show the notice if the last synced at is before the threshold time.
449 ?>
450 <div class="notice notice-warning is-dismissible">
451 <p>
452 <?php
453 echo wp_kses(
454 sprintf(
455 /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag, %3$s - <a> tag, %4$s - </a> tag */
456 __( 'It has been a while since the last successful sync. Please manually trigger a sync from the %1$supdate page%2$s, or clear the ongoing sync from the %3$stools page%4$s if it is stuck, to ensure that your products are up to date.', 'woocommerce-square' ),
457 '<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=square&section=update' ) ) . '">',
458 '</a>',
459 '<a href="' . esc_url( admin_url( 'admin.php?page=wc-status&tab=tools' ) ) . '">',
460 '</a>'
461 ),
462 array(
463 'a' => array(
464 'href' => array(),
465 ),
466 )
467 );
468 ?>
469 </p>
470 </div>
471 <?php
472 }
473
474 /**
475 * Maybe show a notice that a stalled sync job was automatically recovered.
476 *
477 * Shown when auto-recovery has fired and no successful sync has completed since, so the
478 * merchant knows a stuck sync was stopped and can start a new one.
479 * Clears itself once a sync completes successfully after the recovery.
480 *
481 * @since 5.5.0
482 *
483 * @return void
484 */
485 public function maybe_show_sync_recovered_notice() {
486 if ( ! current_user_can( 'manage_woocommerce' ) ) { // phpcs:ignore WordPress.WP.Capabilities.Unknown
487 return;
488 }
489
490 $settings = wc_square()->get_settings_handler();
491 if ( ! $settings->is_connected() || ! $settings->get_location_id() || ! $settings->is_product_sync_enabled() ) {
492 return;
493 }
494
495 $recovered_at = (int) get_option( 'wc_square_sync_auto_recovered_at', 0 );
496 if ( ! $recovered_at ) {
497 return;
498 }
499
500 // The flag itself is the only signal used here. It is cleared when a sync the merchant started
501 // begins, and again when such a job completes, so there is nothing left to infer.
502 //
503 // It deliberately does NOT compare against the last synced timestamp: interval polling advances
504 // that every few minutes, so any comparison against it would dismiss this notice long before
505 // the merchant saw it, which is the same reason polls are excluded from both clearing paths.
506 ?>
507 <div class="notice notice-warning is-dismissible">
508 <p>
509 <?php
510 echo wp_kses(
511 sprintf(
512 /* translators: Placeholders: %1$s - <a> tag, %2$s - </a> tag */
513 __( 'A stuck Square sync job was detected and automatically stopped. Product data may be out of date, please start a new sync from the %1$supdate page%2$s.', 'woocommerce-square' ),
514 '<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=square&section=update' ) ) . '">',
515 '</a>'
516 ),
517 array(
518 'a' => array(
519 'href' => array(),
520 ),
521 )
522 );
523 ?>
524 </p>
525 </div>
526 <?php
527 }
528 }
529