Completed
Push — master ( 24c2f4...f0df34 )
by Augusto
04:34
created
lib/class-api.php 1 patch
Indentation   +685 added lines, -685 removed lines patch added patch discarded remove patch
@@ -1,22 +1,22 @@  discard block
 block discarded – undo
1 1
 <?php
2 2
 /**
3
- * API-like class to deal with HTTP(S) requests to diaspora* using WP_HTTP API.
4
- *
5
- * Basic functionality includes:
6
- * - Logging in to diaspora*
7
- * - Fetching a user's aspects and connected services
8
- * - Posting to diaspora*
9
- *
10
- * Ideas in this class are based on classes from:
11
- * https://github.com/Faldrian/WP-diaspora-postwidget/blob/master/wp-diaspora-postwidget/diasphp.php -- Thanks, Faldrian!
12
- * https://github.com/meitar/diasposter/blob/master/lib/Diaspora_Connection.php -- Thanks, Meitar
13
- *
14
- * Which in turn are based on:
15
- * https://github.com/cocreature/diaspy/blob/master/client.py -- Thanks, Moritz
16
- *
17
- * @package WP_To_Diaspora\API
18
- * @since 1.2.7
19
- */
3
+   * API-like class to deal with HTTP(S) requests to diaspora* using WP_HTTP API.
4
+   *
5
+   * Basic functionality includes:
6
+   * - Logging in to diaspora*
7
+   * - Fetching a user's aspects and connected services
8
+   * - Posting to diaspora*
9
+   *
10
+   * Ideas in this class are based on classes from:
11
+   * https://github.com/Faldrian/WP-diaspora-postwidget/blob/master/wp-diaspora-postwidget/diasphp.php -- Thanks, Faldrian!
12
+   * https://github.com/meitar/diasposter/blob/master/lib/Diaspora_Connection.php -- Thanks, Meitar
13
+   *
14
+   * Which in turn are based on:
15
+   * https://github.com/cocreature/diaspy/blob/master/client.py -- Thanks, Moritz
16
+   *
17
+   * @package WP_To_Diaspora\API
18
+   * @since 1.2.7
19
+   */
20 20
 
21 21
 // Exit if accessed directly.
22 22
 defined( 'ABSPATH' ) || exit;
@@ -26,672 +26,672 @@  discard block
 block discarded – undo
26 26
  */
27 27
 class WP2D_API {
28 28
 
29
-	/**
30
-	 * The provider name to display when posting to diaspora*.
31
-	 *
32
-	 * @var string
33
-	 */
34
-	public $provider = 'WP to diaspora*';
35
-
36
-	/**
37
-	 * The last http request error that occurred.
38
-	 *
39
-	 * @var WP_Error
40
-	 */
41
-	private $_last_error;
42
-
43
-	/**
44
-	 * Security token to be used for making requests.
45
-	 *
46
-	 * @var string
47
-	 */
48
-	private $_token;
49
-
50
-	/**
51
-	 * Save the cookies for the requests.
52
-	 *
53
-	 * @var array
54
-	 */
55
-	private $_cookies;
56
-
57
-	/**
58
-	 * The last http request made to diaspora*.
59
-	 * Contains the response and request infos.
60
-	 *
61
-	 * @var object
62
-	 */
63
-	private $_last_request;
64
-
65
-	/**
66
-	 * Is this a secure server, use HTTPS instead of HTTP?
67
-	 *
68
-	 * @var boolean
69
-	 */
70
-	private $_is_secure;
71
-
72
-	/**
73
-	 * The pod domain to make the http requests to.
74
-	 *
75
-	 * @var string
76
-	 */
77
-	private $_pod;
78
-
79
-	/**
80
-	 * Username to use when logging in to diaspora*.
81
-	 *
82
-	 * @var string
83
-	 */
84
-	private $_username;
85
-
86
-	/**
87
-	 * Password to use when logging in to diaspora*.
88
-	 *
89
-	 * @var string
90
-	 */
91
-	private $_password;
92
-
93
-	/**
94
-	 * Remember the current login state.
95
-	 *
96
-	 * @var boolean
97
-	 */
98
-	private $_is_logged_in = false;
99
-
100
-	/**
101
-	 * The list of user's aspects, which get set after ever http request.
102
-	 *
103
-	 * @var array
104
-	 */
105
-	private $_aspects = array();
106
-
107
-	/**
108
-	 * The list of user's connected services, which get set after ever http request.
109
-	 *
110
-	 * @var array
111
-	 */
112
-	private $_services = array();
113
-
114
-	/**
115
-	 * List of regex expressions used to filter out details from http request responses.
116
-	 *
117
-	 * @var array
118
-	 */
119
-	private $_regexes = array(
120
-		'token'    => '/content="(.*?)" name="csrf-token"|name="csrf-token" content="(.*?)"/',
121
-		'aspects'  => '/"aspects"\:(\[.*?\])/',
122
-		'services' => '/"configured_services"\:(\[.*?\])/',
123
-	);
124
-
125
-	/**
126
-	 * The full pod url, with the used protocol.
127
-	 *
128
-	 * @param string $path Path to add to the pod url.
129
-	 * @return string Full pod url.
130
-	 */
131
-	public function get_pod_url( $path = '' ) {
132
-		$path = trim( $path, ' /' );
133
-
134
-		// Add a slash to the beginning?
135
-		if ( '' !== $path ) {
136
-			$path = '/' . $path;
137
-		}
138
-
139
-		return sprintf( 'http%s://%s%s', ( $this->_is_secure ) ? 's' : '', $this->_pod, $path );
140
-	}
141
-
142
-	/**
143
-	 * Constructor to initialise the connection to diaspora*.
144
-	 *
145
-	 * @param string  $pod       The pod domain to connect to.
146
-	 * @param boolean $is_secure Is this a secure server? (Default: true).
147
-	 */
148
-	public function __construct( $pod, $is_secure = true ) {
149
-		// Set class variables.
150
-		$this->_pod       = $pod;
151
-		$this->_is_secure = (bool) $is_secure;
152
-	}
153
-
154
-	/**
155
-	 * Initialise the connection to diaspora*. The pod and protocol can be changed by passing new parameters.
156
-	 * Check if we can connect to the pod to retrieve the token.
157
-	 *
158
-	 * @param string  $pod       Pod domain to connect to, if it should be changed.
159
-	 * @param boolean $is_secure Is this a secure server? (Default: true).
160
-	 * @return boolean True if we could get the token, else false.
161
-	 */
162
-	public function init( $pod = null, $is_secure = true ) {
163
-		// If we are changing pod, we need to fetch a new token.
164
-		$force_new_token = false;
165
-
166
-		// When initialising a connection, clear the last error.
167
-		// This is important when multiple init tries happen.
168
-		$this->_last_error = null;
169
-
170
-		// Change the pod we are connecting to?
171
-		if ( isset( $pod ) && ( $this->_pod !== $pod || $this->_is_secure !== $is_secure ) ) {
172
-			$this->_pod       = $pod;
173
-			$this->_is_secure = (bool) $is_secure;
174
-			$force_new_token  = true;
175
-		}
176
-
177
-		// Get and save the token.
178
-		if ( null === $this->_fetch_token( $force_new_token ) ) {
179
-			$error = ( $this->has_last_error() ) ? ' ' . $this->get_last_error() : '';
180
-			$this->_error( 'wp2d_api_init_failed',
181
-				sprintf(
182
-					_x( 'Failed to initialise connection to pod "%s".', 'Placeholder is the full pod URL.', 'wp-to-diaspora' ),
183
-					$this->get_pod_url()
184
-				) . $error,
185
-				array( 'help_tab' => 'troubleshooting' )
186
-			);
187
-
188
-			return false;
189
-		}
190
-		return true;
191
-	}
192
-
193
-	/**
194
-	 * Check if there is an API error around.
195
-	 *
196
-	 * @return boolean If there is an API error around.
197
-	 */
198
-	public function has_last_error() {
199
-		return is_wp_error( $this->_last_error );
200
-	}
201
-
202
-	/**
203
-	 * Get the last API error object.
204
-	 *
205
-	 * @param boolean $clear If the error should be cleared after returning it.
206
-	 * @return WP_Error|null The last API error object or null.
207
-	 */
208
-	public function get_last_error_object( $clear = true ) {
209
-		$last_error = $this->_last_error;
210
-		$clear && $this->_last_error = null;
211
-		return $last_error;
212
-	}
213
-
214
-	/**
215
-	 * Get the last API error message.
216
-	 *
217
-	 * @param boolean $clear If the error should be cleared after returning it.
218
-	 * @return string The last API error message.
219
-	 */
220
-	public function get_last_error( $clear = false ) {
221
-		$last_error = ( $this->has_last_error() ) ? $this->_last_error->get_error_message() : '';
222
-		$clear && $this->_last_error = null;
223
-		return $last_error;
224
-	}
225
-
226
-	/**
227
-	 * Fetch the secure token from Diaspora and save it for future use.
228
-	 *
229
-	 * @param boolean $force Force to fetch a new token.
230
-	 * @return string The fetched token.
231
-	 */
232
-	private function _fetch_token( $force = false ) {
233
-		if ( ! isset( $this->_token ) || (bool) $force ) {
234
-			// Go directly to the sign in page, as it would redirect to there anyway.
235
-			// Since _request function automatically saves the new token, just call it with no data.
236
-			$this->_request( '/users/sign_in' );
237
-		}
238
-		return $this->_token;
239
-	}
240
-
241
-	/**
242
-	 * Check if the API has been initialised. Otherwise set the last error.
243
-	 *
244
-	 * @return boolean Has the connection been initialised?
245
-	 */
246
-	private function _check_init() {
247
-		if ( is_null( $this->_token ) ) {
248
-			$this->_error( 'wp2d_api_connection_not_initialised', __( 'Connection not initialised.', 'wp-to-diaspora' ) );
249
-			return false;
250
-		}
251
-		return true;
252
-	}
253
-
254
-	/**
255
-	 * Check if we're logged in. Otherwise set the last error.
256
-	 *
257
-	 * @return boolean Are we logged in already?
258
-	 */
259
-	private function _check_login() {
260
-		if ( ! $this->_check_init() ) {
261
-			return false;
262
-		}
263
-		if ( ! $this->is_logged_in() ) {
264
-			$this->_error( 'wp2d_api_not_logged_in', __( 'Not logged in.', 'wp-to-diaspora' ) );
265
-			return false;
266
-		}
267
-		return true;
268
-	}
269
-
270
-	/**
271
-	 * Check if we are logged in.
272
-	 *
273
-	 * @return boolean Are we logged in already?
274
-	 */
275
-	public function is_logged_in() {
276
-		return $this->_is_logged_in;
277
-	}
278
-
279
-	/**
280
-	 * Log in to diaspora*.
281
-	 *
282
-	 * @param string  $username Username used for login.
283
-	 * @param string  $password Password used for login.
284
-	 * @param boolean $force    Force a new login even if we are already logged in.
285
-	 * @return boolean Did the login succeed?
286
-	 */
287
-	public function login( $username, $password, $force = false ) {
288
-		// Has the connection been initialised?
289
-		if ( ! $this->_check_init() ) {
290
-			$this->logout();
291
-			return false;
292
-		}
293
-
294
-		// Username and password both need to be set.
295
-		$username = ( isset( $username ) && '' !== $username ) ? $username : null;
296
-		$password = ( isset( $password ) && '' !== $password ) ? $password : null;
297
-		if ( ! isset( $username, $password ) ) {
298
-			$this->logout();
299
-			return false;
300
-		}
301
-
302
-		// If we are already logged in and not forcing a relogin, return.
303
-		if ( ! $force && $this->is_logged_in() &&
304
-			$username === $this->_username &&
305
-			$password === $this->_password ) {
306
-			return true;
307
-		}
308
-
309
-		// Set the newly passed username and password.
310
-		$this->_username = $username;
311
-		$this->_password = $password;
312
-
313
-		// Set up the login parameters.
314
-		$params = array(
315
-			'user[username]'     => $this->_username,
316
-			'user[password]'     => $this->_password,
317
-			'authenticity_token' => $this->_fetch_token(),
318
-		);
319
-
320
-		$args = array(
321
-			'method' => 'POST',
322
-			'body'   => $params,
323
-		);
324
-
325
-		// Try to sign in.
326
-		$this->_request( '/users/sign_in', $args );
327
-
328
-		// Can we load the bookmarklet to make sure we're logged in?
329
-		$response = $this->_request( '/bookmarklet' );
330
-
331
-		// If the request isn't successful, we are not logged in correctly.
332
-		if ( is_wp_error( $response ) || 200 !== $response->code ) {
333
-			// Login failed.
334
-			$this->_error( 'wp2d_api_login_failed', __( 'Login failed. Check your login details.', 'wp-to-diaspora' ), array( 'help_tab' => 'troubleshooting' ) );
335
-			$this->logout();
336
-			return false;
337
-		}
338
-
339
-		// Login succeeded.
340
-		$this->_is_logged_in = true;
341
-		return true;
342
-	}
343
-
344
-	/**
345
-	 * Perform a logout, resetting all login info.
346
-	 *
347
-	 * @since 1.6.0
348
-	 */
349
-	public function logout() {
350
-		$this->_is_logged_in = false;
351
-		$this->_username = null;
352
-		$this->_password = null;
353
-		$this->_aspects = array();
354
-		$this->_services = array();
355
-	}
356
-
357
-	/**
358
-	 * Perform a deinitialisation, resetting all class variables.
359
-	 *
360
-	 * @since 1.7.0
361
-	 */
362
-	public function deinit() {
363
-		$this->logout();
364
-		$this->_last_error = null;
365
-		$this->_token = null;
366
-		$this->_cookies = array();
367
-		$this->_last_request = null;
368
-	}
369
-
370
-	/**
371
-	 * Post to diaspora*.
372
-	 *
373
-	 * @param string       $text       The text to post.
374
-	 * @param array|string $aspects    The aspects to post to. Array or comma seperated ids.
375
-	 * @param array        $extra_data Any extra data to be added to the post call.
376
-	 * @return boolean|object Return the response data of the new diaspora* post if successfully posted, else false.
377
-	 */
378
-	public function post( $text, $aspects = 'public', $extra_data = array() ) {
379
-		// Are we logged in?
380
-		if ( ! $this->_check_login() ) {
381
-			return false;
382
-		}
383
-
384
-		// Put the aspects into a clean array.
385
-		$aspects = array_filter( WP2D_Helpers::str_to_arr( $aspects ) );
386
-
387
-		// If no aspects have been selected or the public one is also included, choose public only.
388
-		if ( empty( $aspects ) || in_array( 'public', $aspects ) ) {
389
-			$aspects = 'public';
390
-		}
391
-
392
-		// Prepare post data.
393
-		$post_data = array(
394
-			'aspect_ids'     => $aspects,
395
-			'status_message' => array(
396
-				'text' => $text,
397
-				'provider_display_name' => $this->provider,
398
-			),
399
-		);
400
-
401
-		// Add any extra data to the post.
402
-		if ( ! empty( $extra_data ) ) {
403
-				$post_data += $extra_data;
404
-		}
405
-
406
-		// Check if we can use the new wp_json_encode function.
407
-		$post_data = ( function_exists( 'wp_json_encode' ) )
408
-			? wp_json_encode( $post_data )
409
-			: json_encode( $post_data );
410
-
411
-		$args = array(
412
-			'method'  => 'POST',
413
-			'body'    => $post_data,
414
-			'headers' => array(
415
-				'Accept'       => 'application/json',
416
-				'Content-Type' => 'application/json',
417
-				'X-CSRF-Token' => $this->_fetch_token(),
418
-			),
419
-		);
420
-
421
-		// Submit the post.
422
-		$response = $this->_request( '/status_messages', $args );
423
-
424
-		if ( is_wp_error( $response ) ) {
425
-			$this->_error( 'wp2d_api_post_failed', $response->get_error_message() );
426
-			return false;
427
-		}
428
-
429
-		$diaspost = json_decode( $response->body );
430
-		if ( 201 !== $response->code ) {
431
-			$this->_error( 'wp2d_api_post_failed', ( isset( $diaspost->error ) ) ? $diaspost->error : _x( 'Unknown error occurred.', 'When an unknown error occurred in the WP2D_API object.', 'wp-to-diaspora' ) );
432
-			return false;
433
-		}
434
-
435
-		// Add additional info to our diaspora post object.
436
-		$diaspost->permalink = $this->get_pod_url( '/posts/' . $diaspost->guid );
437
-
438
-		return $diaspost;
439
-	}
440
-
441
-	/**
442
-	 * Delete a post or comment from diaspora*.
443
-	 *
444
-	 * @since 1.6.0
445
-	 *
446
-	 * @param string $what What to delete, 'post' or 'comment'.
447
-	 * @param string $id The ID of the post or comment to delete.
448
-	 * @return boolean If the deletion was successful.
449
-	 */
450
-	public function delete( $what, $id ) {
451
-		// Are we logged in?
452
-		if ( ! $this->_check_login() ) {
453
-			return false;
454
-		}
455
-
456
-		// For now, only deleting posts and comments is allowed.
457
-		if ( ! in_array( $what, array( 'post', 'comment' ) ) ) {
458
-			$this->_error( 'wp2d_api_delete_failed', __( 'You can only delete posts and comments.', 'wp-to-diaspora' ) );
459
-			return false;
460
-		}
461
-
462
-		$args = array(
463
-			'method'  => 'DELETE',
464
-			'headers' => array(
465
-				'Accept'       => 'application/json',
466
-				'Content-Type' => 'application/json',
467
-				'X-CSRF-Token' => $this->_fetch_token(),
468
-			),
469
-		);
470
-
471
-		// Try to delete the post or comment.
472
-		$response = $this->_request( '/' . $what . 's/' . $id, $args );
473
-
474
-		$error_message = '';
475
-
476
-		if ( is_wp_error( $response ) ) {
477
-			$error_message = $response->get_error_message();
478
-		} else {
479
-			switch ( $response->code ) {
480
-				case 204:
481
-					return true;
482
-				case 404:
483
-					$error_message = ( 'post' === $what )
484
-						? __( 'The post you tried to delete does not exist.', 'wp-to-diaspora' )
485
-						: __( 'The comment you tried to delete does not exist.', 'wp-to-diaspora' );
486
-					break;
487
-
488
-				// Due to diaspora* returning a proper 403 when trying to delete a foreign comment
489
-				// but returning a 500 when trying to delete a foreign post, this needs some special attention.
490
-				case 403:
491
-					if ( 'comment' === $what ) {
492
-						$error_message = __( 'The comment you tried to delete does not belong to you.', 'wp-to-diaspora' );
493
-						break;
494
-					}
495
-					// Fall through...
496
-				case 500:
497
-					if ( 'post' === $what ) {
498
-						$error_message = __( 'The post you tried to delete does not belong to you.', 'wp-to-diaspora' );
499
-						break;
500
-					}
501
-					// Fall through...
502
-				default:
503
-					$error_message = _x( 'Unknown error occurred.', 'When an unknown error occurred in the WP2D_API object.', 'wp-to-diaspora' );
504
-					break;
505
-			}
506
-		}
507
-
508
-		$this->_error( 'wp2d_api_delete_' . $what . '_failed', $error_message );
509
-		return false;
510
-	}
511
-
512
-	/**
513
-	 * Get the list of aspects.
514
-	 *
515
-	 * @param boolean $force Force to fetch new aspects.
516
-	 * @return array Array of aspect objects.
517
-	 */
518
-	public function get_aspects( $force = false ) {
519
-		$this->_aspects = $this->_get_aspects_services( 'aspects', $this->_aspects, $force );
520
-		return ( is_array( $this->_aspects ) ) ? $this->_aspects : false;
521
-	}
522
-
523
-	/**
524
-	 * Get the list of connected services.
525
-	 *
526
-	 * @param boolean $force Force to fetch new connected services.
527
-	 * @return array Array of service objects.
528
-	 */
529
-	public function get_services( $force = false ) {
530
-		$this->_services = $this->_get_aspects_services( 'services', $this->_services, $force );
531
-		return ( is_array( $this->_services ) ) ? $this->_services : false;
532
-	}
533
-
534
-	/**
535
-	 * Get the list of aspects or connected services.
536
-	 *
537
-	 * @param string  $type  Type of list to get.
538
-	 * @param array   $list  The current list of items.
539
-	 * @param boolean $force Force to fetch new list.
540
-	 * @return boolean Was the list fetched successfully?
541
-	 */
542
-	private function _get_aspects_services( $type, $list, $force ) {
543
-		if ( ! $this->_check_login() ) {
544
-			return false;
545
-		}
546
-
547
-		// Fetch the new list if the current list is empty or a reload is forced.
548
-		if ( empty( $list ) || (bool) $force ) {
549
-			$response = $this->_request( '/bookmarklet' );
550
-
551
-			if ( is_wp_error( $response ) || 200 !== $response->code ) {
552
-				switch ( $type ) {
553
-					case 'aspects':
554
-						$this->_error( 'wp2d_api_getting_aspects_failed', __( 'Error loading aspects.', 'wp-to-diaspora' ) );
555
-						break;
556
-					case 'services':
557
-						$this->_error( 'wp2d_api_getting_services_failed', __( 'Error loading services.', 'wp-to-diaspora' ) );
558
-						break;
559
-					default:
560
-						$this->_error( 'wp2d_api_getting_aspects_services_failed', _x( 'Unknown error occurred.', 'When an unknown error occurred in the WP2D_API object.', 'wp-to-diaspora' ) );
561
-						break;
562
-				}
563
-				return false;
564
-			}
565
-
566
-			// Load the aspects or services.
567
-			if ( is_array( $raw_list = json_decode( $this->_parse_regex( $type, $response->body ) ) ) ) {
568
-				// In case this fetch is forced, empty the list.
569
-				$list = array();
570
-
571
-				switch ( $type ) {
572
-					case 'aspects':
573
-						// Add the 'public' aspect, as it's global and not user specific.
574
-						$list['public'] = __( 'Public', 'wp-to-diaspora' );
575
-
576
-						// Add all user specific aspects.
577
-						foreach ( $raw_list as $aspect ) {
578
-							$list[ $aspect->id ] = $aspect->name;
579
-						}
580
-						break;
581
-					case 'services':
582
-						foreach ( $raw_list as $service ) {
583
-							$list[ $service ] = ucfirst( $service );
584
-						}
585
-						break;
586
-				}
587
-			}
588
-		}
589
-		return $list;
590
-	}
591
-
592
-	/**
593
-	 * Send an http(s) request via WP_HTTP API.
594
-	 *
595
-	 * @see WP_Http::request()
596
-	 *
597
-	 * @param string $url  The URL to request.
598
-	 * @param array  $args Arguments to be posted with the request.
599
-	 * @return object An object containing details about this request.
600
-	 */
601
-	private function _request( $url, $args = array() ) {
602
-		// Prefix the full pod URL if necessary.
603
-		if ( 0 === strpos( $url, '/' ) ) {
604
-			$url = $this->get_pod_url( $url );
605
-		}
606
-
607
-		// Disable redirections so we can verify HTTP response codes.
608
-		$defaults = array(
609
-			'redirection' => 0,
610
-			'sslverify'   => true,
611
-			'timeout'     => 60,
612
-			'method'      => 'GET',
613
-		);
614
-
615
-		// If the certificate bundle has been downloaded manually, use that instead.
616
-		// NOTE: This should actually never be necessary, it's a fallback!
617
-		if ( file_exists( WP2D_DIR . '/cacert.pem' ) ) {
618
-			$defaults['sslcertificates'] = WP2D_DIR . '/cacert.pem';
619
-		}
620
-
621
-		// Set the correct cookie.
622
-		if ( ! empty( $this->_cookies ) ) {
623
-			$defaults['cookies'] = $this->_cookies;
624
-		}
625
-
626
-		$args = wp_parse_args( $args, $defaults );
627
-
628
-		// Get the response from the WP_HTTP request.
629
-		$response = wp_remote_request( $url, $args );
630
-
631
-		if ( is_wp_error( $response ) ) {
632
-			$this->_last_error = $response;
633
-			return $response;
634
-		}
635
-
636
-		// Get the headers and the html response.
637
-		$headers = wp_remote_retrieve_headers( $response );
638
-		$body    = wp_remote_retrieve_body( $response );
639
-
640
-		// Remember this request.
641
-		$this->_last_request = new stdClass();
642
-		$this->_last_request->response = $response;
643
-		$this->_last_request->headers  = $headers;
644
-		$this->_last_request->body     = $body;
645
-		$this->_last_request->message  = wp_remote_retrieve_response_message( $response );
646
-		$this->_last_request->code     = wp_remote_retrieve_response_code( $response );
647
-
648
-		// Save the new token.
649
-		if ( $token = $this->_parse_regex( 'token', $body ) ) {
650
-			$this->_token = $token;
651
-		}
652
-
653
-		// Save the latest cookies.
654
-		if ( isset( $response['cookies'] ) ) {
655
-			$this->_cookies = $response['cookies'];
656
-		}
657
-
658
-		// Return the last request details.
659
-		return $this->_last_request;
660
-	}
661
-
662
-	/**
663
-	 * Helper method to set the last occurred error.
664
-	 *
665
-	 * @see WP_Error::__construct()
666
-	 * @since 1.6.0
667
-	 *
668
-	 * @param  string|int $code    Error code.
669
-	 * @param  string     $message Error message.
670
-	 * @param  mixed      $data    Error data.
671
-	 */
672
-	private function _error( $code, $message, $data = '' ) {
673
-		// Always add the code and message of the last request.
674
-		$data = array_merge( array_filter( (array) $data ), array(
675
-			'code'    => ( isset( $this->_last_request->code ) ) ? $this->_last_request->code : null,
676
-			'message' => ( isset( $this->_last_request->message ) ) ? $this->_last_request->message : null,
677
-		) );
678
-		$this->_last_error = new WP_Error( $code, $message, $data );
679
-	}
680
-
681
-	/**
682
-	 * Parse the regex and return the found string.
683
-	 *
684
-	 * @param string $regex   Shorthand of a saved regex or a custom regex.
685
-	 * @param string $content Text to parse the regex with.
686
-	 * @return string The found string, or an empty string.
687
-	 */
688
-	private function _parse_regex( $regex, $content ) {
689
-		// Use a shorthand regex if available.
690
-		if ( array_key_exists( $regex, $this->_regexes ) ) {
691
-			$regex = $this->_regexes[ $regex ];
692
-		}
693
-
694
-		preg_match( $regex, $content, $matches );
695
-		return trim( array_pop( $matches ) );
696
-	}
29
+  /**
30
+   * The provider name to display when posting to diaspora*.
31
+   *
32
+   * @var string
33
+   */
34
+  public $provider = 'WP to diaspora*';
35
+
36
+  /**
37
+   * The last http request error that occurred.
38
+   *
39
+   * @var WP_Error
40
+   */
41
+  private $_last_error;
42
+
43
+  /**
44
+   * Security token to be used for making requests.
45
+   *
46
+   * @var string
47
+   */
48
+  private $_token;
49
+
50
+  /**
51
+   * Save the cookies for the requests.
52
+   *
53
+   * @var array
54
+   */
55
+  private $_cookies;
56
+
57
+  /**
58
+   * The last http request made to diaspora*.
59
+   * Contains the response and request infos.
60
+   *
61
+   * @var object
62
+   */
63
+  private $_last_request;
64
+
65
+  /**
66
+   * Is this a secure server, use HTTPS instead of HTTP?
67
+   *
68
+   * @var boolean
69
+   */
70
+  private $_is_secure;
71
+
72
+  /**
73
+   * The pod domain to make the http requests to.
74
+   *
75
+   * @var string
76
+   */
77
+  private $_pod;
78
+
79
+  /**
80
+   * Username to use when logging in to diaspora*.
81
+   *
82
+   * @var string
83
+   */
84
+  private $_username;
85
+
86
+  /**
87
+   * Password to use when logging in to diaspora*.
88
+   *
89
+   * @var string
90
+   */
91
+  private $_password;
92
+
93
+  /**
94
+   * Remember the current login state.
95
+   *
96
+   * @var boolean
97
+   */
98
+  private $_is_logged_in = false;
99
+
100
+  /**
101
+   * The list of user's aspects, which get set after ever http request.
102
+   *
103
+   * @var array
104
+   */
105
+  private $_aspects = array();
106
+
107
+  /**
108
+   * The list of user's connected services, which get set after ever http request.
109
+   *
110
+   * @var array
111
+   */
112
+  private $_services = array();
113
+
114
+  /**
115
+   * List of regex expressions used to filter out details from http request responses.
116
+   *
117
+   * @var array
118
+   */
119
+  private $_regexes = array(
120
+    'token'    => '/content="(.*?)" name="csrf-token"|name="csrf-token" content="(.*?)"/',
121
+    'aspects'  => '/"aspects"\:(\[.*?\])/',
122
+    'services' => '/"configured_services"\:(\[.*?\])/',
123
+  );
124
+
125
+  /**
126
+   * The full pod url, with the used protocol.
127
+   *
128
+   * @param string $path Path to add to the pod url.
129
+   * @return string Full pod url.
130
+   */
131
+  public function get_pod_url( $path = '' ) {
132
+    $path = trim( $path, ' /' );
133
+
134
+    // Add a slash to the beginning?
135
+    if ( '' !== $path ) {
136
+      $path = '/' . $path;
137
+    }
138
+
139
+    return sprintf( 'http%s://%s%s', ( $this->_is_secure ) ? 's' : '', $this->_pod, $path );
140
+  }
141
+
142
+  /**
143
+   * Constructor to initialise the connection to diaspora*.
144
+   *
145
+   * @param string  $pod       The pod domain to connect to.
146
+   * @param boolean $is_secure Is this a secure server? (Default: true).
147
+   */
148
+  public function __construct( $pod, $is_secure = true ) {
149
+    // Set class variables.
150
+    $this->_pod       = $pod;
151
+    $this->_is_secure = (bool) $is_secure;
152
+  }
153
+
154
+  /**
155
+   * Initialise the connection to diaspora*. The pod and protocol can be changed by passing new parameters.
156
+   * Check if we can connect to the pod to retrieve the token.
157
+   *
158
+   * @param string  $pod       Pod domain to connect to, if it should be changed.
159
+   * @param boolean $is_secure Is this a secure server? (Default: true).
160
+   * @return boolean True if we could get the token, else false.
161
+   */
162
+  public function init( $pod = null, $is_secure = true ) {
163
+    // If we are changing pod, we need to fetch a new token.
164
+    $force_new_token = false;
165
+
166
+    // When initialising a connection, clear the last error.
167
+    // This is important when multiple init tries happen.
168
+    $this->_last_error = null;
169
+
170
+    // Change the pod we are connecting to?
171
+    if ( isset( $pod ) && ( $this->_pod !== $pod || $this->_is_secure !== $is_secure ) ) {
172
+      $this->_pod       = $pod;
173
+      $this->_is_secure = (bool) $is_secure;
174
+      $force_new_token  = true;
175
+    }
176
+
177
+    // Get and save the token.
178
+    if ( null === $this->_fetch_token( $force_new_token ) ) {
179
+      $error = ( $this->has_last_error() ) ? ' ' . $this->get_last_error() : '';
180
+      $this->_error( 'wp2d_api_init_failed',
181
+        sprintf(
182
+          _x( 'Failed to initialise connection to pod "%s".', 'Placeholder is the full pod URL.', 'wp-to-diaspora' ),
183
+          $this->get_pod_url()
184
+        ) . $error,
185
+        array( 'help_tab' => 'troubleshooting' )
186
+      );
187
+
188
+      return false;
189
+    }
190
+    return true;
191
+  }
192
+
193
+  /**
194
+   * Check if there is an API error around.
195
+   *
196
+   * @return boolean If there is an API error around.
197
+   */
198
+  public function has_last_error() {
199
+    return is_wp_error( $this->_last_error );
200
+  }
201
+
202
+  /**
203
+   * Get the last API error object.
204
+   *
205
+   * @param boolean $clear If the error should be cleared after returning it.
206
+   * @return WP_Error|null The last API error object or null.
207
+   */
208
+  public function get_last_error_object( $clear = true ) {
209
+    $last_error = $this->_last_error;
210
+    $clear && $this->_last_error = null;
211
+    return $last_error;
212
+  }
213
+
214
+  /**
215
+   * Get the last API error message.
216
+   *
217
+   * @param boolean $clear If the error should be cleared after returning it.
218
+   * @return string The last API error message.
219
+   */
220
+  public function get_last_error( $clear = false ) {
221
+    $last_error = ( $this->has_last_error() ) ? $this->_last_error->get_error_message() : '';
222
+    $clear && $this->_last_error = null;
223
+    return $last_error;
224
+  }
225
+
226
+  /**
227
+   * Fetch the secure token from Diaspora and save it for future use.
228
+   *
229
+   * @param boolean $force Force to fetch a new token.
230
+   * @return string The fetched token.
231
+   */
232
+  private function _fetch_token( $force = false ) {
233
+    if ( ! isset( $this->_token ) || (bool) $force ) {
234
+      // Go directly to the sign in page, as it would redirect to there anyway.
235
+      // Since _request function automatically saves the new token, just call it with no data.
236
+      $this->_request( '/users/sign_in' );
237
+    }
238
+    return $this->_token;
239
+  }
240
+
241
+  /**
242
+   * Check if the API has been initialised. Otherwise set the last error.
243
+   *
244
+   * @return boolean Has the connection been initialised?
245
+   */
246
+  private function _check_init() {
247
+    if ( is_null( $this->_token ) ) {
248
+      $this->_error( 'wp2d_api_connection_not_initialised', __( 'Connection not initialised.', 'wp-to-diaspora' ) );
249
+      return false;
250
+    }
251
+    return true;
252
+  }
253
+
254
+  /**
255
+   * Check if we're logged in. Otherwise set the last error.
256
+   *
257
+   * @return boolean Are we logged in already?
258
+   */
259
+  private function _check_login() {
260
+    if ( ! $this->_check_init() ) {
261
+      return false;
262
+    }
263
+    if ( ! $this->is_logged_in() ) {
264
+      $this->_error( 'wp2d_api_not_logged_in', __( 'Not logged in.', 'wp-to-diaspora' ) );
265
+      return false;
266
+    }
267
+    return true;
268
+  }
269
+
270
+  /**
271
+   * Check if we are logged in.
272
+   *
273
+   * @return boolean Are we logged in already?
274
+   */
275
+  public function is_logged_in() {
276
+    return $this->_is_logged_in;
277
+  }
278
+
279
+  /**
280
+   * Log in to diaspora*.
281
+   *
282
+   * @param string  $username Username used for login.
283
+   * @param string  $password Password used for login.
284
+   * @param boolean $force    Force a new login even if we are already logged in.
285
+   * @return boolean Did the login succeed?
286
+   */
287
+  public function login( $username, $password, $force = false ) {
288
+    // Has the connection been initialised?
289
+    if ( ! $this->_check_init() ) {
290
+      $this->logout();
291
+      return false;
292
+    }
293
+
294
+    // Username and password both need to be set.
295
+    $username = ( isset( $username ) && '' !== $username ) ? $username : null;
296
+    $password = ( isset( $password ) && '' !== $password ) ? $password : null;
297
+    if ( ! isset( $username, $password ) ) {
298
+      $this->logout();
299
+      return false;
300
+    }
301
+
302
+    // If we are already logged in and not forcing a relogin, return.
303
+    if ( ! $force && $this->is_logged_in() &&
304
+      $username === $this->_username &&
305
+      $password === $this->_password ) {
306
+      return true;
307
+    }
308
+
309
+    // Set the newly passed username and password.
310
+    $this->_username = $username;
311
+    $this->_password = $password;
312
+
313
+    // Set up the login parameters.
314
+    $params = array(
315
+      'user[username]'     => $this->_username,
316
+      'user[password]'     => $this->_password,
317
+      'authenticity_token' => $this->_fetch_token(),
318
+    );
319
+
320
+    $args = array(
321
+      'method' => 'POST',
322
+      'body'   => $params,
323
+    );
324
+
325
+    // Try to sign in.
326
+    $this->_request( '/users/sign_in', $args );
327
+
328
+    // Can we load the bookmarklet to make sure we're logged in?
329
+    $response = $this->_request( '/bookmarklet' );
330
+
331
+    // If the request isn't successful, we are not logged in correctly.
332
+    if ( is_wp_error( $response ) || 200 !== $response->code ) {
333
+      // Login failed.
334
+      $this->_error( 'wp2d_api_login_failed', __( 'Login failed. Check your login details.', 'wp-to-diaspora' ), array( 'help_tab' => 'troubleshooting' ) );
335
+      $this->logout();
336
+      return false;
337
+    }
338
+
339
+    // Login succeeded.
340
+    $this->_is_logged_in = true;
341
+    return true;
342
+  }
343
+
344
+  /**
345
+   * Perform a logout, resetting all login info.
346
+   *
347
+   * @since 1.6.0
348
+   */
349
+  public function logout() {
350
+    $this->_is_logged_in = false;
351
+    $this->_username = null;
352
+    $this->_password = null;
353
+    $this->_aspects = array();
354
+    $this->_services = array();
355
+  }
356
+
357
+  /**
358
+   * Perform a deinitialisation, resetting all class variables.
359
+   *
360
+   * @since 1.7.0
361
+   */
362
+  public function deinit() {
363
+    $this->logout();
364
+    $this->_last_error = null;
365
+    $this->_token = null;
366
+    $this->_cookies = array();
367
+    $this->_last_request = null;
368
+  }
369
+
370
+  /**
371
+   * Post to diaspora*.
372
+   *
373
+   * @param string       $text       The text to post.
374
+   * @param array|string $aspects    The aspects to post to. Array or comma seperated ids.
375
+   * @param array        $extra_data Any extra data to be added to the post call.
376
+   * @return boolean|object Return the response data of the new diaspora* post if successfully posted, else false.
377
+   */
378
+  public function post( $text, $aspects = 'public', $extra_data = array() ) {
379
+    // Are we logged in?
380
+    if ( ! $this->_check_login() ) {
381
+      return false;
382
+    }
383
+
384
+    // Put the aspects into a clean array.
385
+    $aspects = array_filter( WP2D_Helpers::str_to_arr( $aspects ) );
386
+
387
+    // If no aspects have been selected or the public one is also included, choose public only.
388
+    if ( empty( $aspects ) || in_array( 'public', $aspects ) ) {
389
+      $aspects = 'public';
390
+    }
391
+
392
+    // Prepare post data.
393
+    $post_data = array(
394
+      'aspect_ids'     => $aspects,
395
+      'status_message' => array(
396
+        'text' => $text,
397
+        'provider_display_name' => $this->provider,
398
+      ),
399
+    );
400
+
401
+    // Add any extra data to the post.
402
+    if ( ! empty( $extra_data ) ) {
403
+        $post_data += $extra_data;
404
+    }
405
+
406
+    // Check if we can use the new wp_json_encode function.
407
+    $post_data = ( function_exists( 'wp_json_encode' ) )
408
+      ? wp_json_encode( $post_data )
409
+      : json_encode( $post_data );
410
+
411
+    $args = array(
412
+      'method'  => 'POST',
413
+      'body'    => $post_data,
414
+      'headers' => array(
415
+        'Accept'       => 'application/json',
416
+        'Content-Type' => 'application/json',
417
+        'X-CSRF-Token' => $this->_fetch_token(),
418
+      ),
419
+    );
420
+
421
+    // Submit the post.
422
+    $response = $this->_request( '/status_messages', $args );
423
+
424
+    if ( is_wp_error( $response ) ) {
425
+      $this->_error( 'wp2d_api_post_failed', $response->get_error_message() );
426
+      return false;
427
+    }
428
+
429
+    $diaspost = json_decode( $response->body );
430
+    if ( 201 !== $response->code ) {
431
+      $this->_error( 'wp2d_api_post_failed', ( isset( $diaspost->error ) ) ? $diaspost->error : _x( 'Unknown error occurred.', 'When an unknown error occurred in the WP2D_API object.', 'wp-to-diaspora' ) );
432
+      return false;
433
+    }
434
+
435
+    // Add additional info to our diaspora post object.
436
+    $diaspost->permalink = $this->get_pod_url( '/posts/' . $diaspost->guid );
437
+
438
+    return $diaspost;
439
+  }
440
+
441
+  /**
442
+   * Delete a post or comment from diaspora*.
443
+   *
444
+   * @since 1.6.0
445
+   *
446
+   * @param string $what What to delete, 'post' or 'comment'.
447
+   * @param string $id The ID of the post or comment to delete.
448
+   * @return boolean If the deletion was successful.
449
+   */
450
+  public function delete( $what, $id ) {
451
+    // Are we logged in?
452
+    if ( ! $this->_check_login() ) {
453
+      return false;
454
+    }
455
+
456
+    // For now, only deleting posts and comments is allowed.
457
+    if ( ! in_array( $what, array( 'post', 'comment' ) ) ) {
458
+      $this->_error( 'wp2d_api_delete_failed', __( 'You can only delete posts and comments.', 'wp-to-diaspora' ) );
459
+      return false;
460
+    }
461
+
462
+    $args = array(
463
+      'method'  => 'DELETE',
464
+      'headers' => array(
465
+        'Accept'       => 'application/json',
466
+        'Content-Type' => 'application/json',
467
+        'X-CSRF-Token' => $this->_fetch_token(),
468
+      ),
469
+    );
470
+
471
+    // Try to delete the post or comment.
472
+    $response = $this->_request( '/' . $what . 's/' . $id, $args );
473
+
474
+    $error_message = '';
475
+
476
+    if ( is_wp_error( $response ) ) {
477
+      $error_message = $response->get_error_message();
478
+    } else {
479
+      switch ( $response->code ) {
480
+        case 204:
481
+          return true;
482
+        case 404:
483
+          $error_message = ( 'post' === $what )
484
+            ? __( 'The post you tried to delete does not exist.', 'wp-to-diaspora' )
485
+            : __( 'The comment you tried to delete does not exist.', 'wp-to-diaspora' );
486
+          break;
487
+
488
+        // Due to diaspora* returning a proper 403 when trying to delete a foreign comment
489
+        // but returning a 500 when trying to delete a foreign post, this needs some special attention.
490
+        case 403:
491
+          if ( 'comment' === $what ) {
492
+            $error_message = __( 'The comment you tried to delete does not belong to you.', 'wp-to-diaspora' );
493
+            break;
494
+          }
495
+          // Fall through...
496
+        case 500:
497
+          if ( 'post' === $what ) {
498
+            $error_message = __( 'The post you tried to delete does not belong to you.', 'wp-to-diaspora' );
499
+            break;
500
+          }
501
+          // Fall through...
502
+        default:
503
+          $error_message = _x( 'Unknown error occurred.', 'When an unknown error occurred in the WP2D_API object.', 'wp-to-diaspora' );
504
+          break;
505
+      }
506
+    }
507
+
508
+    $this->_error( 'wp2d_api_delete_' . $what . '_failed', $error_message );
509
+    return false;
510
+  }
511
+
512
+  /**
513
+   * Get the list of aspects.
514
+   *
515
+   * @param boolean $force Force to fetch new aspects.
516
+   * @return array Array of aspect objects.
517
+   */
518
+  public function get_aspects( $force = false ) {
519
+    $this->_aspects = $this->_get_aspects_services( 'aspects', $this->_aspects, $force );
520
+    return ( is_array( $this->_aspects ) ) ? $this->_aspects : false;
521
+  }
522
+
523
+  /**
524
+   * Get the list of connected services.
525
+   *
526
+   * @param boolean $force Force to fetch new connected services.
527
+   * @return array Array of service objects.
528
+   */
529
+  public function get_services( $force = false ) {
530
+    $this->_services = $this->_get_aspects_services( 'services', $this->_services, $force );
531
+    return ( is_array( $this->_services ) ) ? $this->_services : false;
532
+  }
533
+
534
+  /**
535
+   * Get the list of aspects or connected services.
536
+   *
537
+   * @param string  $type  Type of list to get.
538
+   * @param array   $list  The current list of items.
539
+   * @param boolean $force Force to fetch new list.
540
+   * @return boolean Was the list fetched successfully?
541
+   */
542
+  private function _get_aspects_services( $type, $list, $force ) {
543
+    if ( ! $this->_check_login() ) {
544
+      return false;
545
+    }
546
+
547
+    // Fetch the new list if the current list is empty or a reload is forced.
548
+    if ( empty( $list ) || (bool) $force ) {
549
+      $response = $this->_request( '/bookmarklet' );
550
+
551
+      if ( is_wp_error( $response ) || 200 !== $response->code ) {
552
+        switch ( $type ) {
553
+          case 'aspects':
554
+            $this->_error( 'wp2d_api_getting_aspects_failed', __( 'Error loading aspects.', 'wp-to-diaspora' ) );
555
+            break;
556
+          case 'services':
557
+            $this->_error( 'wp2d_api_getting_services_failed', __( 'Error loading services.', 'wp-to-diaspora' ) );
558
+            break;
559
+          default:
560
+            $this->_error( 'wp2d_api_getting_aspects_services_failed', _x( 'Unknown error occurred.', 'When an unknown error occurred in the WP2D_API object.', 'wp-to-diaspora' ) );
561
+            break;
562
+        }
563
+        return false;
564
+      }
565
+
566
+      // Load the aspects or services.
567
+      if ( is_array( $raw_list = json_decode( $this->_parse_regex( $type, $response->body ) ) ) ) {
568
+        // In case this fetch is forced, empty the list.
569
+        $list = array();
570
+
571
+        switch ( $type ) {
572
+          case 'aspects':
573
+            // Add the 'public' aspect, as it's global and not user specific.
574
+            $list['public'] = __( 'Public', 'wp-to-diaspora' );
575
+
576
+            // Add all user specific aspects.
577
+            foreach ( $raw_list as $aspect ) {
578
+              $list[ $aspect->id ] = $aspect->name;
579
+            }
580
+            break;
581
+          case 'services':
582
+            foreach ( $raw_list as $service ) {
583
+              $list[ $service ] = ucfirst( $service );
584
+            }
585
+            break;
586
+        }
587
+      }
588
+    }
589
+    return $list;
590
+  }
591
+
592
+  /**
593
+   * Send an http(s) request via WP_HTTP API.
594
+   *
595
+   * @see WP_Http::request()
596
+   *
597
+   * @param string $url  The URL to request.
598
+   * @param array  $args Arguments to be posted with the request.
599
+   * @return object An object containing details about this request.
600
+   */
601
+  private function _request( $url, $args = array() ) {
602
+    // Prefix the full pod URL if necessary.
603
+    if ( 0 === strpos( $url, '/' ) ) {
604
+      $url = $this->get_pod_url( $url );
605
+    }
606
+
607
+    // Disable redirections so we can verify HTTP response codes.
608
+    $defaults = array(
609
+      'redirection' => 0,
610
+      'sslverify'   => true,
611
+      'timeout'     => 60,
612
+      'method'      => 'GET',
613
+    );
614
+
615
+    // If the certificate bundle has been downloaded manually, use that instead.
616
+    // NOTE: This should actually never be necessary, it's a fallback!
617
+    if ( file_exists( WP2D_DIR . '/cacert.pem' ) ) {
618
+      $defaults['sslcertificates'] = WP2D_DIR . '/cacert.pem';
619
+    }
620
+
621
+    // Set the correct cookie.
622
+    if ( ! empty( $this->_cookies ) ) {
623
+      $defaults['cookies'] = $this->_cookies;
624
+    }
625
+
626
+    $args = wp_parse_args( $args, $defaults );
627
+
628
+    // Get the response from the WP_HTTP request.
629
+    $response = wp_remote_request( $url, $args );
630
+
631
+    if ( is_wp_error( $response ) ) {
632
+      $this->_last_error = $response;
633
+      return $response;
634
+    }
635
+
636
+    // Get the headers and the html response.
637
+    $headers = wp_remote_retrieve_headers( $response );
638
+    $body    = wp_remote_retrieve_body( $response );
639
+
640
+    // Remember this request.
641
+    $this->_last_request = new stdClass();
642
+    $this->_last_request->response = $response;
643
+    $this->_last_request->headers  = $headers;
644
+    $this->_last_request->body     = $body;
645
+    $this->_last_request->message  = wp_remote_retrieve_response_message( $response );
646
+    $this->_last_request->code     = wp_remote_retrieve_response_code( $response );
647
+
648
+    // Save the new token.
649
+    if ( $token = $this->_parse_regex( 'token', $body ) ) {
650
+      $this->_token = $token;
651
+    }
652
+
653
+    // Save the latest cookies.
654
+    if ( isset( $response['cookies'] ) ) {
655
+      $this->_cookies = $response['cookies'];
656
+    }
657
+
658
+    // Return the last request details.
659
+    return $this->_last_request;
660
+  }
661
+
662
+  /**
663
+   * Helper method to set the last occurred error.
664
+   *
665
+   * @see WP_Error::__construct()
666
+   * @since 1.6.0
667
+   *
668
+   * @param  string|int $code    Error code.
669
+   * @param  string     $message Error message.
670
+   * @param  mixed      $data    Error data.
671
+   */
672
+  private function _error( $code, $message, $data = '' ) {
673
+    // Always add the code and message of the last request.
674
+    $data = array_merge( array_filter( (array) $data ), array(
675
+      'code'    => ( isset( $this->_last_request->code ) ) ? $this->_last_request->code : null,
676
+      'message' => ( isset( $this->_last_request->message ) ) ? $this->_last_request->message : null,
677
+    ) );
678
+    $this->_last_error = new WP_Error( $code, $message, $data );
679
+  }
680
+
681
+  /**
682
+   * Parse the regex and return the found string.
683
+   *
684
+   * @param string $regex   Shorthand of a saved regex or a custom regex.
685
+   * @param string $content Text to parse the regex with.
686
+   * @return string The found string, or an empty string.
687
+   */
688
+  private function _parse_regex( $regex, $content ) {
689
+    // Use a shorthand regex if available.
690
+    if ( array_key_exists( $regex, $this->_regexes ) ) {
691
+      $regex = $this->_regexes[ $regex ];
692
+    }
693
+
694
+    preg_match( $regex, $content, $matches );
695
+    return trim( array_pop( $matches ) );
696
+  }
697 697
 }
Please login to merge, or discard this patch.