WC_Zapier_Feed.php
9.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
<?php
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/**
* Represents a single Zapier Feed.
*
* One or more Zapier Feeds are configured via the user and stored in the WordPress installation.
*
* They define which Zapier webhook URL should be contacted once the specified Zapier Trigger occurs.
*
* Class WC_Zapier_Feed
*/
class WC_Zapier_Feed {
/**
* The Post ID of this Zapier Feed.
* @var int
*/
private $id;
private $post;
/**
* The title/name of this Zapier Feed.
*
* @var string
*/
private $title;
/**
* The Zapier Trigger that this Zapier Feed applies to.
*
* @var WC_Zapier_Trigger
*/
private $trigger;
/**
* The Webhook URL.
*
* @var string
*/
private $webhook_url;
/**
* Whether or not this Zapier Feed is active
*
* @var bool
*/
private $is_active = false;
/**
* Regular Expression used to validate a Webhook URL.
*
* Valid webhook examples:
* - https://zapier.com/hooks/catch/n/abcdef/ (For Zaps created before April 2014)
* - https://zapier.com/hooks/catch/fvc2n/ (For Zaps created between April 2014 and March 2016)
* - https://zapier.com/hooks/catch/12345/abcdef/ (For Zaps created from March 2016 onwards)
*/
const webhook_url_regexp = '#^https://(hooks\.)?zapier\.com\/hooks\/catch(\/n)?\/([a-z0-9]+)\/([a-z0-9]+\/)?\z#';
/**
* Example Zapier Webhook URL. Displayed in the Dashboard.
*/
const webhook_url_example = 'https://hooks.zapier.com/hooks/catch/12345/abcdef/';
/**
* Constructor.
*
* Loads an existing Zapier Feed if its ID is specified
*
* @param null|int|object $id Post ID or post object
*/
public function __construct( $id = null ) {
if ( !empty( $id ) ) {
$this->load( $id );
}
}
/**
* Load the feed from the WordPress database
* @param int|object $id Post ID or post object
*/
public function load( $id ) {
$post = get_post( $id );
if ( is_null( $post ) || 'wc_zapier_feed' != $post->post_type )
return;
$this->post = $post;
$this->populate_from_post_object();
}
/**
* Populate the class properties based on the WordPress WP_Post object.
* @return bool
*/
private function populate_from_post_object() {
try {
$this->id = $this->post->ID;
$this->set_title( $this->post->post_title );
$this->set_webhook_url( $this->post->post_excerpt, false );
$this->set_active( isset( $this->post->post_status ) && 'publish' == $this->post->post_status );
// Do this last in case the trigger is invalid
if ( !empty($this->post->post_content) ) {
$this->set_trigger( WC_Zapier_Trigger_Factory::get_trigger_with_key( $this->post->post_content ) );
}
} catch ( Exception $ex ) {
return false;
}
}
/**
* Obtain an array representing this feed's properties.
* Used when inserting/updating the Feed details into the WordPress database.
*
* @return array
*/
private function get_array_from_properties() {
$data = array(
'post_title' => $this->title(),
'post_content' => empty($this->trigger) ? '' : $this->trigger->get_trigger_key(),
'post_excerpt' => $this->webhook_url(),
'post_status' => $this->is_active() ? 'publish' : 'draft',
'post_type' => 'wc_zapier_feed'
);
if ( $this->id )
$data['ID'] = $this->id;
return $data;
}
/**
* The ID of this Zapier Feed.
*
* @return int
*/
public function id() {
return $this->id;
}
/**
* The Title/Name of this Zapier Feed.
*
* @return string
*/
public function title() {
return $this->title;
}
/**
* The Trigger for this Zapier Feed.
*
* @return WC_Zapier_Trigger
*/
public function trigger() {
return $this->trigger;
}
/**
* The webhook URL of this Zapier Feed.
*
* @return string
*/
public function webhook_url() {
return $this->webhook_url;
}
/**
* Whether or not this Zapier Feed is active.
*
* @return bool
*/
public function is_active() {
return $this->is_active;
}
/**
* Set the Title/Name of this Zapier Feed.
*
* @param string $title
*
* @return bool
*/
public function set_title( $title ) {
$this->title =trim( (string) $title );
return true;
}
/**
* Set the Trigger for this Zapier Feed.
*
* @param WC_Zapier_Trigger $trigger
*
* @return bool
*/
public function set_trigger( WC_Zapier_Trigger $trigger ) {
$this->trigger = $trigger;
return true;
}
/**
* Set the Trigger for this Zapier Feed using a trigger key (rather than a Trigger object)
*
* @param string $trigger_key
*
* @return bool
*/
public function set_trigger_with_key( $trigger_key ) {
if ( WC_Zapier_Trigger_Factory::trigger_exists( $trigger_key ) ) {
$this->trigger = WC_Zapier_Trigger_Factory::get_trigger_with_key( $trigger_key );
return true;
}
$this->trigger = null;
return false;
}
/**
* Set the webhook URL for this Zapier Feed.
*
* Validation occurs to make sure only a valid Webhook URL can be specified.
*
* @param string $webhook_url
* @param bool $validate Whether or not to validate the URL. Defaults to true
*
* @return bool
*/
public function set_webhook_url( $webhook_url, $validate = true ) {
$webhook_url = trim( (string) $webhook_url );
if ( $validate ) {
// Ensure the specified webhook URL matches our expected format
if ( self::is_valid_webhook_url( $webhook_url ) ) {
$this->webhook_url = $webhook_url;
return true;
} else {
$this->webhook_url = '';
}
} else {
// No need to validate/check the webhook URL
$this->webhook_url = $webhook_url;
return true;
}
return false;
}
/**
* Set this Zapier Feed as active or inactive.
*
* @param bool $active
*/
public function set_active( $active = true ) {
$this->is_active = (bool) $active;
}
/**
* Ensure that this feed is valid.
*
* If it isn't valid, ensure it cannot be published.
*
* @return bool|array True if valid, array of validation errors/warnings on failure
*/
public function validate() {
$validation = array(
'errors' => array(),
'warnings' => array()
);
// If the Feed's title is empty, automatically set the Feed's title to match the name of the chosen trigger
if ( empty($this->title) && !is_null( $this->trigger ) ) {
$this->title = $this->trigger()->get_trigger_title();
}
if ( empty($this->title) ) {
$validation['errors'][] = __( '<strong>Title:</strong> A title is required.', 'wc_zapier' );
}
// Ensure unique title
if ( WC_Zapier_Feed_Factory::get_number_of_feeds_with_title( $this->title, $this ) ) {
$validation['errors'][] = __( '<strong>Title:</strong> Another Zapier Feed with this title already exists. Please choose a unique Title.', 'wc_zapier' );
}
if ( ! $this->is_valid_trigger() ) {
$validation['errors'][] = __( 'Invalid Trigger.', 'wc_zapier' );
}
if ( empty( $this->webhook_url ) ) {
$validation['errors'][] = sprintf( __( '<strong>Webhook URL:</strong> Invalid Webhook URL. Zapier Webhook URLs should be in the following format: <code>%s</code>', 'wc_zapier' ), self::webhook_url_example );
}
if ( $this->webhook_url && ! is_null( $this->trigger ) ) {
// Ensure unique trigger/webhook_url combination
if ( WC_Zapier_Feed_Factory::get_number_of_feeds_with_webhook_url_and_trigger( $this->webhook_url, $this->trigger, $this ) ) {
$validation['errors'][] = __( 'Another Zapier Feed with this Trigger and Webhook URL already exists.', 'wc_zapier' );
}
}
if ( empty( $validation['errors'] ) && empty( $validation['warnings'] ) ) {
// The Feed is valid (configured correctly)
// Send sample data to Zapier so that Zapier know what the data structure is like
$result = $this->trigger->send_sample_data_payload( $this );
if ( true !== $result ) {
$validation['errors'][] = sprintf( __( 'There was an error communicating with your Zapier Webhook (%1$s).<br />Error Message: %2$s<br />Please try again, and see <a href="%3$s">here for troubleshooting steps</a>.', 'wc_zapier' ), '<code>' . esc_url( $this->webhook_url() ) . '</code>', '<code>' . esc_html( $result ) . '</code>', esc_url( WC_Zapier::documentation_url . '#troubleshooting' ) );
} else {
}
}
if ( !empty($validation['errors']) ) {
// Ensure the feed is in draft status
$this->set_active( false );
} else {
// Feed is error free
return true;
}
return $validation;
}
/**
* Test whether or not the specified Webhook URL is valid.
*
* @param string $webhook_url
*
* @return int
*/
public static function is_valid_webhook_url( $webhook_url ) {
if ( preg_match( self::webhook_url_regexp, $webhook_url ) )
return true;
return false;
}
/**
* Whether or not this feed has a valid trigger assigned to it.
*
* For example, the feed could contain an invalid trigger if WC Subscriptions has been deactivated since creating the Feed.
*
* @return bool
*/
public function is_valid_trigger() {
return ! is_null( $this->trigger );
}
/**
* The URL that of the Edit Feed screen.
*
* @return string
*/
public function edit_url() {
// Unfortunately we can't use get_edit_post_link() because it doesn't work during cron because no user is logged in
return admin_url( "post.php?post={$this->id}&action=edit" );
}
/**
* Save this Zapier feed to the database.
*
* @return bool
* @throws Exception
*/
public function save() {
$result = null;
if ( is_a( $this->post, 'WP_Post') ) {
// The post could be an auto-draft (for a newly created Zapier Feed), or
// The post could be an existing Zapier Feed
$result = wp_update_post( $this->get_array_from_properties(), true );
} else {
// No post yet. Unlikely, but possible
$result = wp_insert_post( $this->get_array_from_properties(), true );
}
if ( is_wp_error($result) || ! $result ) {
return false;
} else {
// Success
// Re-load the new feed data in case it has somehow changed.
$this->load( $result );
return true;
}
}
}