WC_Webhook   D
last analyzed

Complexity

Total Complexity 83

Size/Duplication

Total Lines 803
Duplicated Lines 4.73 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 38
loc 803
rs 4.4444
c 0
b 0
f 0
wmc 83
lcom 1
cbo 0

32 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 2
A __isset() 0 6 2
A __get() 0 10 2
A enqueue() 0 10 4
A process() 0 21 3
B deliver() 0 39 1
B build_payload() 0 60 7
A generate_signature() 0 6 1
A get_new_delivery_id() 0 16 2
B log_delivery() 0 56 6
A failed_delivery() 0 13 2
B get_delivery_logs() 0 25 3
B get_delivery_log() 0 26 3
A set_topic() 0 21 2
A get_topic_hooks() 0 58 2
B get_status() 17 22 4
A get_i18n_status() 0 6 2
B update_status() 18 25 4
A set_delivery_url() 0 5 2
A get_delivery_url() 0 4 1
A set_secret() 0 4 1
A get_secret() 0 3 1
A get_name() 0 3 1
A get_topic() 0 3 1
A get_hooks() 0 3 1
A get_resource() 0 3 1
A get_event() 0 3 1
A get_failure_count() 0 3 1
A get_user_id() 0 3 1
A get_post_data() 0 3 1
C should_deliver() 0 45 15
A deliver_ping() 3 19 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like WC_Webhook often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WC_Webhook, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
if ( ! defined( 'ABSPATH' ) ) {
4
	exit; // Exit if accessed directly
5
}
6
7
/**
8
 * WooCommerce Webhook class.
9
 *
10
 * This class handles storing and retrieving webhook data from the associated.
11
 * `shop_webhook` custom post type, as well as delivery logs from the `webhook_delivery`.
12
 * comment type.
13
 *
14
 * Webhooks are enqueued to their associated actions, delivered, and logged.
15
 *
16
 * @author      WooThemes
17
 * @category    Webhooks
18
 * @package     WooCommerce/Webhooks
19
 * @since       2.2
20
 */
21
class WC_Webhook {
22
23
	/** @var int webhook ID (post ID) */
24
	public $id;
25
26
	/**
27
	 * Setup webhook & load post data.
28
	 *
29
	 * @since 2.2
30
	 * @param string|int $id
31
	 * @return \WC_Webhook
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
32
	 */
33
	public function __construct( $id ) {
34
35
		$id = absint( $id );
36
37
		if ( ! $id ) {
38
			return;
39
		}
40
41
		$this->id = $id;
42
		$this->post_data = get_post( $id );
43
	}
44
45
46
	/**
47
	 * Magic isset as a wrapper around metadata_exists().
48
	 *
49
	 * @since 2.2
50
	 * @param string $key
51
	 * @return bool true if $key isset, false otherwise
52
	 */
53
	public function __isset( $key ) {
54
		if ( ! $this->id ) {
55
			return false;
56
		}
57
		return metadata_exists( 'post', $this->id, '_' . $key );
58
	}
59
60
61
	/**
62
	 * Magic get, wraps get_post_meta() for all keys except $status.
63
	 *
64
	 * @since 2.2
65
	 * @param string $key
66
	 * @return mixed value
67
	 */
68
	public function __get( $key ) {
69
70
		if ( 'status' === $key ) {
71
			$value = $this->get_status();
72
		} else {
73
			$value = get_post_meta( $this->id, '_' . $key, true );
74
		}
75
76
		return $value;
77
	}
78
79
80
	/**
81
	 * Enqueue the hooks associated with the webhook.
82
	 *
83
	 * @since 2.2
84
	 */
85
	public function enqueue() {
86
		$hooks = $this->get_hooks();
87
		$url   = $this->get_delivery_url();
88
89
		if ( is_array( $hooks ) && ! empty( $url ) ) {
90
			foreach ( $hooks as $hook ) {
91
				add_action( $hook, array( $this, 'process' ) );
92
			}
93
		}
94
	}
95
96
97
	/**
98
	 * Process the webhook for delivery by verifying that it should be delivered.
99
	 * and scheduling the delivery (in the background by default, or immediately).
100
	 *
101
	 * @since 2.2
102
	 * @param mixed $arg the first argument provided from the associated hooks
103
	 */
104
	public function process( $arg ) {
105
106
		// verify that webhook should be processed for delivery
107
		if ( ! $this->should_deliver( $arg ) ) {
108
			return;
109
		}
110
111
		// webhooks are processed in the background by default
112
		// so as to avoid delays or failures in delivery from affecting the
113
		// user who triggered it
114
		if ( apply_filters( 'woocommerce_webhook_deliver_async', true, $this, $arg ) ) {
115
116
			// deliver in background
117
			wp_schedule_single_event( time(), 'woocommerce_deliver_webhook_async', array( $this->id, $arg ) );
118
119
		} else {
120
121
			// deliver immediately
122
			$this->deliver( $arg );
123
		}
124
	}
125
126
	/**
127
	 * Helper to check if the webhook should be delivered, as some hooks.
128
	 * (like `wp_trash_post`) will fire for every post type, not just ours.
129
	 *
130
	 * @since 2.2
131
	 * @param mixed $arg first hook argument
132
	 * @return bool true if webhook should be delivered, false otherwise
133
	 */
134
	private function should_deliver( $arg ) {
135
		$should_deliver = true;
136
		$current_action = current_action();
137
138
		// only active webhooks can be delivered
139
		if ( 'active' != $this->get_status() ) {
140
			$should_deliver = false;
141
142
		// only deliver deleted event for coupons, orders, and products
143
		} elseif ( 'delete_post' === $current_action && ! in_array( $GLOBALS['post_type'], array( 'shop_coupon', 'shop_order', 'product' ) ) ) {
144
			$should_deliver = false;
145
146
		} elseif ( 'delete_user' == $current_action ) {
147
			$user = get_userdata( absint( $arg ) );
148
149
			// only deliver deleted customer event for users with customer role
150
			if ( ! $user || ! in_array( 'customer', (array) $user->roles ) ) {
151
				$should_deliver = false;
152
			}
153
154
		// check if the custom order type has chosen to exclude order webhooks from triggering along with its own webhooks.
155
		} elseif ( 'order' == $this->get_resource() && ! in_array( get_post_type( absint( $arg ) ), wc_get_order_types( 'order-webhooks' ) ) ) {
156
			$should_deliver = false;
157
158
		} elseif ( 0 === strpos( $current_action, 'woocommerce_process_shop' ) || 0 === strpos( $current_action, 'woocommerce_process_product' ) ) {
159
			// the `woocommerce_process_shop_*` and `woocommerce_process_product_*` hooks
160
			// fire for create and update of products and orders, so check the post
161
			// creation date to determine the actual event
162
			$resource = get_post( absint( $arg ) );
163
164
			// a resource is considered created when the hook is executed within 10 seconds of the post creation date
165
			$resource_created = ( ( time() - 10 ) <= strtotime( $resource->post_date_gmt ) );
166
167
			if ( 'created' == $this->get_event() && ! $resource_created ) {
168
				$should_deliver = false;
169
			} elseif ( 'updated' == $this->get_event() && $resource_created ) {
170
				$should_deliver = false;
171
			}
172
		}
173
174
		/*
175
		 * Let other plugins intercept deliver for some messages queue like rabbit/zeromq
176
		 */
177
		return apply_filters( 'woocommerce_webhook_should_deliver', $should_deliver, $this, $arg );
178
	}
179
180
181
	/**
182
	 * Deliver the webhook payload using wp_safe_remote_request().
183
	 *
184
	 * @since 2.2
185
	 * @param mixed $arg First hook argument.
186
	 */
187
	public function deliver( $arg ) {
188
189
		$payload = $this->build_payload( $arg );
190
191
		// Setup request args.
192
		$http_args = array(
193
			'method'      => 'POST',
194
			'timeout'     => MINUTE_IN_SECONDS,
195
			'redirection' => 0,
196
			'httpversion' => '1.0',
197
			'blocking'    => true,
198
			'user-agent'  => sprintf( 'WooCommerce/%s Hookshot (WordPress/%s)', WC_VERSION, $GLOBALS['wp_version'] ),
199
			'body'        => trim( json_encode( $payload ) ),
200
			'headers'     => array( 'Content-Type' => 'application/json' ),
201
			'cookies'     => array(),
202
		);
203
204
		$http_args = apply_filters( 'woocommerce_webhook_http_args', $http_args, $arg, $this->id );
205
206
		// Add custom headers.
207
		$http_args['headers']['X-WC-Webhook-Source']      = home_url( '/' ); // Since 2.6.0.
208
		$http_args['headers']['X-WC-Webhook-Topic']       = $this->get_topic();
209
		$http_args['headers']['X-WC-Webhook-Resource']    = $this->get_resource();
210
		$http_args['headers']['X-WC-Webhook-Event']       = $this->get_event();
211
		$http_args['headers']['X-WC-Webhook-Signature']   = $this->generate_signature( $http_args['body'] );
212
		$http_args['headers']['X-WC-Webhook-ID']          = $this->id;
213
		$http_args['headers']['X-WC-Webhook-Delivery-ID'] = $delivery_id = $this->get_new_delivery_id();
214
215
		$start_time = microtime( true );
216
217
		// Webhook away!
218
		$response = wp_safe_remote_request( $this->get_delivery_url(), $http_args );
219
220
		$duration = round( microtime( true ) - $start_time, 5 );
221
222
		$this->log_delivery( $delivery_id, $http_args, $response, $duration );
223
224
		do_action( 'woocommerce_webhook_delivery', $http_args, $response, $duration, $arg, $this->id );
225
	}
226
227
228
	/**
229
	 * Build the payload data for the webhook.
230
	 *
231
	 * @since 2.2
232
	 * @param mixed $resource_id first hook argument, typically the resource ID
233
	 * @return mixed payload data
234
	 */
235
	private function build_payload( $resource_id ) {
236
237
		// build the payload with the same user context as the user who created
238
		// the webhook -- this avoids permission errors as background processing
239
		// runs with no user context
240
		$current_user = get_current_user_id();
241
		wp_set_current_user( $this->get_user_id() );
242
243
		$resource = $this->get_resource();
244
		$event    = $this->get_event();
245
246
		// if a resource has been deleted, just include the ID
247
		if ( 'deleted' == $event ) {
248
249
			$payload = array(
250
				'id' => $resource_id,
251
			);
252
253
		} else {
254
255
			// include & load API classes
256
			WC()->api->includes();
257
			WC()->api->register_resources( new WC_API_Server( '/' ) );
258
259
			switch( $resource ) {
260
261
				case 'coupon':
262
					$payload = WC()->api->WC_API_Coupons->get_coupon( $resource_id );
263
					break;
264
265
				case 'customer':
266
					$payload = WC()->api->WC_API_Customers->get_customer( $resource_id );
267
					break;
268
269
				case 'order':
270
					$payload = WC()->api->WC_API_Orders->get_order( $resource_id );
271
					break;
272
273
				case 'product':
274
					$payload = WC()->api->WC_API_Products->get_product( $resource_id );
275
					break;
276
277
				// custom topics include the first hook argument
278
				case 'action':
279
					$payload = array(
280
						'action' => current( $this->get_hooks() ),
281
						'arg'    => $resource_id,
282
					);
283
					break;
284
285
				default:
286
					$payload = array();
287
			}
288
		}
289
290
		// restore the current user
291
		wp_set_current_user( $current_user );
292
293
		return apply_filters( 'woocommerce_webhook_payload', $payload, $resource, $resource_id, $this->id );
294
	}
295
296
297
	/**
298
	 * Generate a base64-encoded HMAC-SHA256 signature of the payload body so the.
299
	 * recipient can verify the authenticity of the webhook. Note that the signature.
300
	 * is calculated after the body has already been encoded (JSON by default).
301
	 *
302
	 * @since 2.2
303
	 * @param string $payload payload data to hash
304
	 * @return string hash
305
	 */
306
	public function generate_signature( $payload ) {
307
308
		$hash_algo = apply_filters( 'woocommerce_webhook_hash_algorithm', 'sha256', $payload, $this->id );
309
310
		return base64_encode( hash_hmac( $hash_algo, $payload, $this->get_secret(), true ) );
311
	}
312
313
314
	/**
315
	 * Create a new comment for log the delivery request/response and.
316
	 * return the ID for inclusion in the webhook request.
317
	 *
318
	 * @since 2.2
319
	 * @return int delivery (comment) ID
320
	 */
321
	public function get_new_delivery_id() {
322
323
		$comment_data = apply_filters( 'woocommerce_new_webhook_delivery_data', array(
324
			'comment_author'       => __( 'WooCommerce', 'woocommerce' ),
325
			'comment_author_email' => sanitize_email( sprintf( '%s@%s', strtolower( __( 'WooCommerce', 'woocommerce' ) ), isset( $_SERVER['HTTP_HOST'] ) ? str_replace( 'www.', '', $_SERVER['HTTP_HOST'] ) : 'noreply.com' ) ),
326
			'comment_post_ID'      => $this->id,
327
			'comment_agent'        => 'WooCommerce Hookshot',
328
			'comment_type'         => 'webhook_delivery',
329
			'comment_parent'       => 0,
330
			'comment_approved'     => 1,
331
		), $this->id );
332
333
		$comment_id = wp_insert_comment( $comment_data );
334
335
		return $comment_id;
336
	}
337
338
339
	/**
340
	 * Log the delivery request/response.
341
	 *
342
	 * @since 2.2
343
	 * @param int $delivery_id previously created comment ID
344
	 * @param array $request request data
345
	 * @param array|WP_Error $response response data
346
	 * @param float $duration request duration
347
	 */
348
	public function log_delivery( $delivery_id, $request, $response, $duration ) {
349
350
		// save request data
351
		add_comment_meta( $delivery_id, '_request_method', $request['method'] );
352
		add_comment_meta( $delivery_id, '_request_headers', array_merge( array( 'User-Agent' => $request['user-agent'] ), $request['headers'] ) );
353
		add_comment_meta( $delivery_id, '_request_body', $request['body'] );
354
355
		// parse response
356
		if ( is_wp_error( $response ) ) {
357
			$response_code    = $response->get_error_code();
358
			$response_message = $response->get_error_message();
359
			$response_headers = $response_body = array();
360
361
		} else {
362
			$response_code    = wp_remote_retrieve_response_code( $response );
363
			$response_message = wp_remote_retrieve_response_message( $response );
364
			$response_headers = wp_remote_retrieve_headers( $response );
365
			$response_body    = wp_remote_retrieve_body( $response );
366
		}
367
368
		// save response data
369
		add_comment_meta( $delivery_id, '_response_code', $response_code );
370
		add_comment_meta( $delivery_id, '_response_message', $response_message );
371
		add_comment_meta( $delivery_id, '_response_headers', $response_headers );
372
		add_comment_meta( $delivery_id, '_response_body', $response_body );
373
374
		// save duration
375
		add_comment_meta( $delivery_id, '_duration', $duration );
376
377
		// set a summary for quick display
378
		$args = array(
379
			'comment_ID' => $delivery_id,
380
			'comment_content' => sprintf( 'HTTP %s %s: %s', $response_code, $response_message, $response_body ),
381
		);
382
383
		wp_update_comment( $args );
384
385
		// track failures
386
		if ( intval( $response_code ) >= 200 && intval( $response_code ) < 300 ) {
387
			delete_post_meta( $this->id, '_failure_count' );
388
		} else {
389
			$this->failed_delivery();
390
		}
391
392
		// keep the 25 most recent delivery logs
393
		$log = wp_count_comments( $this->id );
394
		if ( $log->total_comments > apply_filters( 'woocommerce_max_webhook_delivery_logs', 25 ) ) {
395
			global $wpdb;
396
397
			$comment_id = $wpdb->get_var( $wpdb->prepare( "SELECT comment_ID FROM {$wpdb->comments} WHERE comment_post_ID = %d ORDER BY comment_date_gmt ASC LIMIT 1", $this->id ) );
398
399
			if ( $comment_id ) {
400
				wp_delete_comment( $comment_id, true );
401
			}
402
		}
403
	}
404
405
	/**
406
	 * Track consecutive delivery failures and automatically disable the webhook.
407
	 * if more than 5 consecutive failures occur. A failure is defined as a.
408
	 * non-2xx response.
409
	 *
410
	 * @since 2.2
411
	 */
412
	private function failed_delivery() {
413
414
		$failures = $this->get_failure_count();
415
416
		if ( $failures > apply_filters( 'woocommerce_max_webhook_delivery_failures', 5 ) ) {
417
418
			$this->update_status( 'disabled' );
419
420
		} else {
421
422
			update_post_meta( $this->id, '_failure_count', ++$failures );
423
		}
424
	}
425
426
427
	/**
428
	 * Get the delivery logs for this webhook.
429
	 *
430
	 * @since 2.2
431
	 * @return array
432
	 */
433
	public function get_delivery_logs() {
434
435
		$args = array(
436
			'post_id' => $this->id,
437
			'status'  => 'approve',
438
			'type'    => 'webhook_delivery',
439
		);
440
441
		remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_webhook_comments' ), 10, 1 );
442
443
		$logs = get_comments( $args );
444
445
		add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_webhook_comments' ), 10, 1 );
446
447
		$delivery_logs = array();
448
449
		foreach ( $logs as $log ) {
450
451
			$log = $this->get_delivery_log( $log->comment_ID );
452
453
			$delivery_logs[] = ( ! empty( $log )  ? $log : array() );
454
		}
455
456
		return $delivery_logs;
457
	}
458
459
460
	/**
461
	 * Get the delivery log specified by the ID. The delivery log includes:
462
	 *
463
	 * + duration
464
	 * + summary
465
	 * + request method/url
466
	 * + request headers/body
467
	 * + response code/message/headers/body
468
	 *
469
	 * @since 2.2
470
	 * @param int $delivery_id
471
	 * @return bool|array false if invalid delivery ID, array of log data otherwise
472
	 */
473
	public function get_delivery_log( $delivery_id ) {
474
475
		$log = get_comment( $delivery_id );
476
477
		// valid comment and ensure delivery log belongs to this webhook
478
		if ( is_null( $log ) || $log->comment_post_ID != $this->id ) {
479
			return false;
480
		}
481
482
		$delivery_log = array(
483
			'id'               => intval( $delivery_id ),
484
			'duration'         => get_comment_meta( $delivery_id, '_duration', true ),
485
			'summary'          => $log->comment_content,
486
			'request_method'   => get_comment_meta( $delivery_id, '_request_method', true ),
487
			'request_url'      => $this->get_delivery_url(),
488
			'request_headers'  => get_comment_meta( $delivery_id, '_request_headers', true ),
489
			'request_body'     => get_comment_meta( $delivery_id, '_request_body', true ),
490
			'response_code'    => get_comment_meta( $delivery_id, '_response_code', true ),
491
			'response_message' => get_comment_meta( $delivery_id, '_response_message', true ),
492
			'response_headers' => get_comment_meta( $delivery_id, '_response_headers', true ),
493
			'response_body'    => get_comment_meta( $delivery_id, '_response_body', true ),
494
			'comment'          => $log,
495
		);
496
497
		return apply_filters( 'woocommerce_webhook_delivery_log', $delivery_log, $delivery_id, $this->id );
498
	}
499
500
	/**
501
	 * Set the webhook topic and associated hooks. The topic resource & event.
502
	 * are also saved separately.
503
	 *
504
	 * @since 2.2
505
	 * @param string $topic
506
	 */
507
	public function set_topic( $topic ) {
508
509
		$topic = strtolower( $topic );
510
511
		list( $resource, $event ) = explode( '.', $topic );
512
513
		update_post_meta( $this->id, '_topic', $topic );
514
		update_post_meta( $this->id, '_resource', $resource );
515
		update_post_meta( $this->id, '_event', $event );
516
517
		// custom topics are mapped to a single hook
518
		if ( 'action' === $resource ) {
519
520
			update_post_meta( $this->id, '_hooks', array( $event ) );
521
522
		} else {
523
524
			// API topics have multiple hooks
525
			update_post_meta( $this->id, '_hooks', $this->get_topic_hooks( $topic ) );
526
		}
527
	}
528
529
	/**
530
	 * Get the associated hook names for a topic.
531
	 *
532
	 * @since 2.2
533
	 * @param string $topic
534
	 * @return array hook names
535
	 */
536
	private function get_topic_hooks( $topic ) {
537
538
		$topic_hooks = array(
539
			'coupon.created' => array(
540
				'woocommerce_process_shop_coupon_meta',
541
				'woocommerce_api_create_coupon',
542
			),
543
			'coupon.updated' => array(
544
				'woocommerce_process_shop_coupon_meta',
545
				'woocommerce_api_edit_coupon',
546
			),
547
			'coupon.deleted' => array(
548
				'wp_trash_post',
549
			),
550
			'customer.created' => array(
551
				'user_register',
552
				'woocommerce_created_customer',
553
				'woocommerce_api_create_customer'
554
			),
555
			'customer.updated' => array(
556
				'profile_update',
557
				'woocommerce_api_edit_customer',
558
				'woocommerce_customer_save_address',
559
			),
560
			'customer.deleted' => array(
561
				'delete_user',
562
			),
563
			'order.created'    => array(
564
				'woocommerce_checkout_order_processed',
565
				'woocommerce_process_shop_order_meta',
566
				'woocommerce_api_create_order',
567
			),
568
			'order.updated' => array(
569
				'woocommerce_process_shop_order_meta',
570
				'woocommerce_api_edit_order',
571
				'woocommerce_order_edit_status',
572
				'woocommerce_order_status_changed'
573
			),
574
			'order.deleted' => array(
575
				'wp_trash_post',
576
			),
577
			'product.created' => array(
578
				'woocommerce_process_product_meta',
579
				'woocommerce_api_create_product',
580
			),
581
			'product.updated' => array(
582
				'woocommerce_process_product_meta',
583
				'woocommerce_api_edit_product',
584
			),
585
			'product.deleted' => array(
586
				'wp_trash_post',
587
			),
588
		);
589
590
		$topic_hooks = apply_filters( 'woocommerce_webhook_topic_hooks', $topic_hooks, $this );
591
592
		return isset( $topic_hooks[ $topic ] ) ? $topic_hooks[ $topic ] : array();
593
	}
594
595
	/**
596
	 * Send a test ping to the delivery URL, sent when the webhook is first created.
597
	 *
598
	 * @since 2.2
599
	 * @return bool|WP_Error
600
	 */
601
	public function deliver_ping() {
602
		$args = array(
603
			'user-agent' => sprintf( 'WooCommerce/%s Hookshot (WordPress/%s)', WC_VERSION, $GLOBALS['wp_version'] ),
604
			'body'       => "webhook_id={$this->id}",
605
		);
606
607
		$test          = wp_safe_remote_post( $this->get_delivery_url(), $args );
608
		$response_code = wp_remote_retrieve_response_code( $test );
609
610 View Code Duplication
		if ( is_wp_error( $test ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
611
			return new WP_Error( 'error', sprintf( __( 'Error: Delivery URL cannot be reached: %s', 'woocommerce' ), $test->get_error_message() ) );
612
		}
613
614
		if ( 200 !== $response_code ) {
615
			return new WP_Error( 'error', sprintf( __( 'Error: Delivery URL returned response code: %s', 'woocommerce' ), absint( $response_code ) ) );
616
		}
617
618
		return true;
619
	}
620
621
	/**
622
	 * Get the webhook status:
623
	 *
624
	 * + `active` - delivers payload.
625
	 * + `paused` - does not deliver payload, paused by admin.
626
	 * + `disabled` - does not delivery payload, paused automatically due to.
627
	 * consecutive failures.
628
	 *
629
	 * @since 2.2
630
	 * @return string status
631
	 */
632
	public function get_status() {
633
634 View Code Duplication
		switch ( $this->get_post_data()->post_status ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
635
636
			case 'publish':
637
				$status = 'active';
638
				break;
639
640
			case 'draft':
641
				$status = 'paused';
642
				break;
643
644
			case 'pending':
645
				$status = 'disabled';
646
				break;
647
648
			default:
649
				$status = 'paused';
650
		}
651
652
		return apply_filters( 'woocommerce_webhook_status', $status, $this->id );
653
	}
654
655
	/**
656
	 * Get the webhook i18n status.
657
	 *
658
	 * @return string
659
	 */
660
	public function get_i18n_status() {
661
		$status   = $this->get_status();
662
		$statuses = wc_get_webhook_statuses();
663
664
		return isset( $statuses[ $status ] ) ? $statuses[ $status ] : $status;
665
	}
666
667
	/**
668
	 * Update the webhook status, see get_status() for valid statuses.
669
	 *
670
	 * @since 2.2
671
	 * @param $status
672
	 */
673
	public function update_status( $status ) {
674
		global $wpdb;
675
676 View Code Duplication
		switch ( $status ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
677
678
			case 'active' :
679
				$post_status = 'publish';
680
				break;
681
682
			case 'paused' :
683
				$post_status = 'draft';
684
				break;
685
686
			case 'disabled' :
687
				$post_status = 'pending';
688
				break;
689
690
			default :
691
				$post_status = 'draft';
692
				break;
693
		}
694
695
		$wpdb->update( $wpdb->posts, array( 'post_status' => $post_status ), array( 'ID' => $this->id ) );
696
		clean_post_cache( $this->id );
697
	}
698
699
	/**
700
	 * Set the delivery URL.
701
	 *
702
	 * @since 2.2
703
	 * @param string $url
704
	 */
705
	public function set_delivery_url( $url ) {
706
		if ( update_post_meta( $this->id, '_delivery_url', esc_url_raw( $url, array( 'http', 'https' ) ) ) ) {
707
			update_post_meta( $this->id, '_webhook_pending_delivery', true );
708
		}
709
	}
710
711
	/**
712
	 * Get the delivery URL.
713
	 *
714
	 * @since 2.2
715
	 * @return string
716
	 */
717
	public function get_delivery_url() {
718
719
		return apply_filters( 'woocommerce_webhook_delivery_url', $this->delivery_url, $this->id );
720
	}
721
722
	/**
723
	 * Set the secret used for generating the HMAC-SHA256 signature.
724
	 *
725
	 * @since 2.2
726
	 * @param string $secret
727
	 */
728
	public function set_secret( $secret ) {
729
730
		update_post_meta( $this->id, '_secret', $secret );
731
	}
732
733
	/**
734
	 * Get the secret used for generating the HMAC-SHA256 signature.
735
	 *
736
	 * @since 2.2
737
	 * @return string
738
	 */
739
	public function get_secret() {
740
		return apply_filters( 'woocommerce_webhook_secret', $this->secret, $this->id );
741
	}
742
743
	/**
744
	 * Get the friendly name for the webhook.
745
	 *
746
	 * @since 2.2
747
	 * @return string
748
	 */
749
	public function get_name() {
750
		return apply_filters( 'woocommerce_webhook_name', $this->get_post_data()->post_title, $this->id );
751
	}
752
753
	/**
754
	 * Get the webhook topic, e.g. `order.created`.
755
	 *
756
	 * @since 2.2
757
	 * @return string
758
	 */
759
	public function get_topic() {
760
		return apply_filters( 'woocommerce_webhook_topic', $this->topic, $this->id );
761
	}
762
763
	/**
764
	 * Get the hook names for the webhook.
765
	 *
766
	 * @since 2.2
767
	 * @return array hook names
768
	 */
769
	public function get_hooks() {
770
		return apply_filters( 'woocommerce_webhook_hooks', $this->hooks, $this->id );
771
	}
772
773
	/**
774
	 * Get the resource for the webhook, e.g. `order`.
775
	 *
776
	 * @since 2.2
777
	 * @return string
778
	 */
779
	public function get_resource() {
780
		return apply_filters( 'woocommerce_webhook_resource', $this->resource, $this->id );
781
	}
782
783
	/**
784
	 * Get the event for the webhook, e.g. `created`.
785
	 *
786
	 * @since 2.2
787
	 * @return string
788
	 */
789
	public function get_event() {
790
		return apply_filters( 'woocommerce_webhook_event', $this->event, $this->id );
791
	}
792
793
	/**
794
	 * Get the failure count.
795
	 *
796
	 * @since 2.2
797
	 * @return int
798
	 */
799
	public function get_failure_count() {
800
		return intval( $this->failure_count );
801
	}
802
803
	/**
804
	 * Get the user ID for this webhook.
805
	 *
806
	 * @since 2.2
807
	 * @return int|string user ID
808
	 */
809
	public function get_user_id() {
810
		return $this->get_post_data()->post_author;
811
	}
812
813
	/**
814
	 * Get the post data for the webhook.
815
	 *
816
	 * @since 2.2
817
	 * @return null|WP_Post
818
	 */
819
	public function get_post_data() {
820
		return $this->post_data;
821
	}
822
823
}
824