PluginProbe ʕ •ᴥ•ʔ
WooCommerce Square / 3.8.1
WooCommerce Square v3.8.1
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 / Handlers / Sync.php
woocommerce-square / includes / Handlers Last commit date
Product 3 years ago Background_Job.php 3 years ago Category.php 3 years ago Connection.php 3 years ago Email.php 3 years ago Order.php 3 years ago Product.php 3 years ago Products.php 3 years ago Sync.php 3 years ago
Sync.php
429 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\Handlers;
25
26 use WooCommerce\Square\Plugin;
27 use WooCommerce\Square\Sync\Records;
28
29 defined( 'ABSPATH' ) || exit;
30
31 /**
32 * Synchronization handler class
33 *
34 * @since 2.0.0
35 */
36 class Sync {
37
38
39 /** @var string key of the option that stores a timestamp when the last sync job completed */
40 private $last_synced_at_option_key = 'wc_square_last_synced_at';
41
42 /** @var string name of the Action Scheduler event name for syncing with Square */
43 private $sync_scheduled_event_name;
44
45 /** @var Plugin plugin instance */
46 private $plugin;
47
48
49 /**
50 * Constructs the class.
51 *
52 * @since 2.0.0
53 *
54 * @param Plugin $plugin
55 */
56 public function __construct( Plugin $plugin ) {
57
58 $this->plugin = $plugin;
59
60 $this->sync_scheduled_event_name = 'wc_square_sync';
61
62 $this->add_hooks();
63 }
64
65
66 /**
67 * Adds the action & filter hooks.
68 *
69 * @since 2.0.0
70 */
71 private function add_hooks() {
72
73 // schedule the interval sync
74 add_action( 'init', array( $this, 'schedule_sync' ) );
75
76 // run the interval sync when fired by Action Scheduler
77 add_action( $this->sync_scheduled_event_name, array( $this, 'start_interval_sync' ) );
78
79 add_action( 'admin_notices', array( $this, 'render_import_no_navigation_warning' ) );
80 }
81
82 /**
83 * Schedules the interval sync.
84 *
85 * @since 2.0.0
86 */
87 public function schedule_sync() {
88
89 // bail if product sync is not enabled or there hasn't been a previous sync
90 if ( $this->is_sync_in_progress() || ! $this->get_last_synced_at() || ! $this->get_plugin()->get_settings_handler()->is_connected() || ! $this->get_plugin()->get_settings_handler()->is_product_sync_enabled() ) {
91 return;
92 }
93
94 $plugin_id = $this->get_plugin()->get_id();
95 $interval = wc_square()->get_settings_handler()->get_sync_interval();
96
97 if ( false === as_next_scheduled_action( $this->sync_scheduled_event_name, array(), $plugin_id ) ) {
98 as_schedule_recurring_action( time() + $interval, $interval, $this->sync_scheduled_event_name, array(), $plugin_id );
99 }
100 }
101
102
103 /**
104 * Unschedules the interval sync.
105 *
106 * @since 2.0.0
107 */
108 public function unschedule_sync() {
109
110 as_unschedule_action( $this->sync_scheduled_event_name, array(), 'square' );
111 }
112
113 /**
114 * Performs a product import from Square.
115 *
116 * @since 2.0.0
117 *
118 * @param bool $update_during_import whether the store manager has ticked to update products during an import
119 * @return \stdClass|null
120 */
121 public function start_product_import( $update_during_import = false ) {
122
123 $job = $this->get_plugin()->get_background_job_handler()->create_job(
124 array(
125 'action' => 'product_import',
126 'update_products_during_import' => $update_during_import,
127 )
128 );
129
130 if ( $job ) {
131 as_enqueue_async_action( 'wc_square_job_runner' );
132 }
133
134 return $job;
135 }
136
137 /**
138 * Performs a manual sync.
139 *
140 * @since 2.0.0
141 *
142 * @param int[] $product_ids (optional) array of product IDs to sync
143 * @return \stdClass|null
144 */
145 public function start_manual_sync( array $product_ids = array() ) {
146
147 $product_ids = empty( $product_ids ) ? Product::get_products_synced_with_square() : $product_ids;
148
149 $job = $this->get_plugin()->get_background_job_handler()->create_job(
150 array(
151 'action' => 'sync',
152 'manual' => true,
153 'product_ids' => $product_ids,
154 )
155 );
156
157 if ( $job ) {
158 as_enqueue_async_action( 'wc_square_job_runner' );
159 }
160
161 return $job;
162 }
163
164 /**
165 * Performs a manual product deletion.
166 *
167 * @since 2.0.0
168 *
169 * @param int[] $product_ids array of product IDs to delete
170 * @return \stdClass|null
171 */
172 public function start_manual_deletion( array $product_ids ) {
173
174 $job = $this->get_plugin()->get_background_job_handler()->create_job(
175 array(
176 'action' => 'delete',
177 'manual' => true,
178 'product_ids' => $product_ids,
179 )
180 );
181
182 if ( $job ) {
183 as_enqueue_async_action( 'wc_square_job_runner' );
184 }
185
186 return $job;
187 }
188
189 /**
190 * Performs an interval sync with Square.
191 *
192 * @since 2.0.0
193 */
194 public function start_interval_sync() {
195
196 // bail if there is already a sync in progress
197 if ( ! $this->is_sync_enabled() || $this->is_sync_in_progress() ) {
198 return;
199 }
200
201 // use this opportunity to clear old background jobs
202 $this->get_plugin()->get_background_job_handler()->clear_all_jobs();
203
204 $job = $this->get_plugin()->get_background_job_handler()->create_job(
205 array(
206 'action' => 'poll',
207 'manual' => false,
208 'catalog_last_synced_at' => $this->get_last_synced_at(),
209 'inventory_last_synced_at' => $this->get_inventory_last_synced_at(),
210 )
211 );
212
213 if ( $job ) {
214 as_enqueue_async_action( 'wc_square_job_runner' );
215 }
216 }
217
218
219 /** Conditional methods *******************************************************************************************/
220
221
222 /**
223 * Determines whether a sync, scheduled or manual, is in progress.
224 *
225 * @since 2.0.0
226 *
227 * @return bool
228 */
229 public function is_sync_in_progress() {
230
231 return ( defined( 'DOING_SQUARE_SYNC' ) && true === DOING_SQUARE_SYNC )
232 || null !== $this->get_job_in_progress();
233 }
234
235
236 /**
237 * Determines if sync is enabled.
238 *
239 * @since 2.0.0
240 *
241 * @return bool
242 */
243 public function is_sync_enabled() {
244
245 return $this->get_plugin()->get_settings_handler()->is_product_sync_enabled();
246 }
247
248
249 /** Setter methods ************************************************************************************************/
250
251
252 /**
253 * Records a successful sync.
254 *
255 * @since 2.0.0
256 *
257 * @param int[] $product_ids IDs of products synced
258 * @param null|\stdClass $job optional sync job, may be used to set the job ID to prevent duplicates
259 */
260 public function record_sync( array $product_ids, $job = null ) {
261
262 $products = count( $product_ids );
263
264 // only add a record of some products were synced
265 if ( $products ) {
266
267 Records::set_record(
268 array(
269 'id' => $job && isset( $job->id ) ? $job->id : null,
270 'message' => sprintf(
271 /* translators: Placeholder: %d number of products processed */
272 _n( 'Updated data for %d product.', 'Updated data for %d products.', $products, 'woocommerce-square' ),
273 $products
274 ),
275 )
276 );
277 }
278
279 /**
280 * Fires after a set of products are synced with square.
281 *
282 * @since 2.0.0
283 *
284 * @param int[] $product_ids IDs for products that were synced
285 */
286 do_action( 'wc_square_products_synced', $product_ids );
287 }
288
289
290 /**
291 * Updates the time when the last sync job occurred.
292 *
293 * @since 2.0.0
294 *
295 * @param int|string|null $timestamp a valid timestamp in UTC (optional, will default to now)
296 * @return bool success
297 */
298 public function set_last_synced_at( $timestamp = null ) {
299
300 if ( null === $timestamp ) {
301 $timestamp = time();
302 }
303
304 return is_numeric( $timestamp ) && update_option( $this->last_synced_at_option_key, (int) $timestamp );
305 }
306
307
308 /** Getter methods ************************************************************************************************/
309
310
311 /**
312 * Gets a job that is currently in progress.
313 *
314 * @since 2.0.0
315 *
316 * @return null|\stdClass background job object or null if not found
317 */
318 public function get_job_in_progress() {
319
320 $handler = $this->get_plugin()->get_background_job_handler();
321
322 try {
323 $job = $handler->get_job();
324 } catch ( \Exception $e ) {
325 $job = null;
326 }
327
328 return $job && isset( $job->status ) && in_array( $job->status, array( 'created', 'queued', 'processing' ), true ) ? $job : null;
329 }
330
331 /**
332 * Gets the timestamp when the next sync job should start.
333 *
334 * @since 2.0.0
335 *
336 * @return int
337 */
338 public function get_next_sync_at() {
339
340 $timestamp = null;
341 $scheduled = as_next_scheduled_action( $this->sync_scheduled_event_name );
342
343 if ( $scheduled ) {
344 $timestamp = $scheduled;
345 }
346
347 return (int) $timestamp > 1 ? $timestamp : null;
348 }
349
350
351 /**
352 * Gets the timestamp for when the last sync job completed.
353 *
354 * @since 2.0.0
355 *
356 * @return int
357 */
358 public function get_last_synced_at() {
359
360 $timestamp = get_option( $this->last_synced_at_option_key, null );
361
362 return (int) $timestamp > 1 ? $timestamp : null;
363 }
364
365
366 /**
367 * Sets the timestamp for when the last inventory sync job started.
368 *
369 * @since 2.0.0
370 *
371 * @param int|string|null $timestamp a valid timestamp in UTC (optional, will default to now)
372 * @return bool success
373 */
374 public function set_inventory_last_synced_at( $timestamp = null ) {
375
376 if ( null === $timestamp ) {
377 $timestamp = time();
378 }
379
380 return is_numeric( $timestamp ) && update_option( $this->last_synced_at_option_key . '_inventory', $timestamp );
381 }
382
383
384 /**
385 * Gets the timestamp for when the last inventory sync job completed.
386 *
387 * @since 2.0.0
388 *
389 * @return int
390 */
391 public function get_inventory_last_synced_at() {
392
393 $timestamp = get_option( $this->last_synced_at_option_key . '_inventory', null );
394
395 return (int) $timestamp > 1 ? $timestamp : null;
396 }
397
398
399 /**
400 * Gets the plugin instance.
401 *
402 * @since 2.0.0
403 *
404 * @return Plugin
405 */
406 private function get_plugin() {
407
408 return $this->plugin;
409 }
410
411 /**
412 * Show warning not to close or navigate away from the current
413 * page when product import is in progress.
414 */
415 public function render_import_no_navigation_warning() {
416 $job_in_progress = wc_square()->get_sync_handler()->get_job_in_progress();
417
418 if ( $job_in_progress && 'product_import' === $job_in_progress->action ) {
419 wc_square()->get_admin_notice_handler()->add_admin_notice(
420 __( 'Please do not close or navigate away from this page as the product import job is in progress. This page may load several times during the course of sync.', 'woocommerce-square' ),
421 'wc-square-sync-in-progress-message',
422 array(
423 'notice_class' => 'notice-warning',
424 )
425 );
426 }
427 }
428 }
429