1
|
|
|
<?php |
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
|
|
|
*/ |
20
|
|
|
|
21
|
|
|
// Exit if accessed directly. |
22
|
|
|
defined( 'ABSPATH' ) || exit; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* API class to talk to diaspora*. |
26
|
|
|
*/ |
27
|
|
|
class WP2D_API { |
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 = []; |
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 = []; |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* List of regex expressions used to filter out details from http request responses. |
116
|
|
|
* |
117
|
|
|
* @var array |
118
|
|
|
*/ |
119
|
|
|
private static $regexes = [ |
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
|
|
|
* |
130
|
|
|
* @return string Full pod url. |
131
|
|
|
*/ |
132
|
|
|
public function get_pod_url( $path = '' ) { |
133
|
|
|
$path = trim( $path, ' /' ); |
134
|
|
|
|
135
|
|
|
// Add a slash to the beginning? |
136
|
|
|
if ( '' !== $path ) { |
137
|
|
|
$path = '/' . $path; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
return sprintf( 'http%s://%s%s', $this->is_secure ? 's' : '', $this->pod, $path ); |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Constructor to initialise the connection to diaspora*. |
145
|
|
|
* |
146
|
|
|
* @param string $pod The pod domain to connect to. |
147
|
|
|
* @param bool $is_secure Is this a secure server? (Default: true). |
148
|
|
|
*/ |
149
|
|
|
public function __construct( $pod, $is_secure = true ) { |
150
|
|
|
// Set class variables. |
151
|
|
|
$this->pod = $pod; |
152
|
|
|
$this->is_secure = (bool) $is_secure; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Initialise the connection to diaspora*. The pod and protocol can be changed by passing new parameters. |
157
|
|
|
* Check if we can connect to the pod to retrieve the token. |
158
|
|
|
* |
159
|
|
|
* @param string $pod Pod domain to connect to, if it should be changed. |
160
|
|
|
* @param bool $is_secure Is this a secure server? (Default: true). |
161
|
|
|
* |
162
|
|
|
* @return bool True if we could get the token, else false. |
163
|
|
|
*/ |
164
|
|
|
public function init( $pod = null, $is_secure = true ) { |
165
|
|
|
// If we are changing pod, we need to fetch a new token. |
166
|
|
|
$force_new_token = false; |
167
|
|
|
|
168
|
|
|
// When initialising a connection, clear the last error. |
169
|
|
|
// This is important when multiple init tries happen. |
170
|
|
|
$this->last_error = null; |
171
|
|
|
|
172
|
|
|
// Change the pod we are connecting to? |
173
|
|
|
if ( null !== $pod && ( $this->pod !== $pod || $this->is_secure !== $is_secure ) ) { |
174
|
|
|
$this->pod = $pod; |
175
|
|
|
$this->is_secure = (bool) $is_secure; |
176
|
|
|
$force_new_token = true; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
// Get and save the token. |
180
|
|
|
if ( null === $this->fetch_token( $force_new_token ) ) { |
181
|
|
|
$error = $this->has_last_error() ? ' ' . $this->get_last_error() : ''; |
182
|
|
|
$this->error( 'wp2d_api_init_failed', |
183
|
|
|
sprintf( |
184
|
|
|
_x( 'Failed to initialise connection to pod "%s".', 'Placeholder is the full pod URL.', 'wp-to-diaspora' ), |
185
|
|
|
$this->get_pod_url() |
186
|
|
|
) . $error, |
187
|
|
|
[ 'help_tab' => 'troubleshooting' ] |
188
|
|
|
); |
189
|
|
|
|
190
|
|
|
return false; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
return true; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* Check if there is an API error around. |
198
|
|
|
* |
199
|
|
|
* @return bool If there is an API error around. |
200
|
|
|
*/ |
201
|
|
|
public function has_last_error() { |
202
|
|
|
return is_wp_error( $this->last_error ); |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* Get the last API error object. |
207
|
|
|
* |
208
|
|
|
* @param bool $clear If the error should be cleared after returning it. |
209
|
|
|
* |
210
|
|
|
* @return WP_Error|null The last API error object or null. |
211
|
|
|
*/ |
212
|
|
|
public function get_last_error_object( $clear = true ) { |
213
|
|
|
$last_error = $this->last_error; |
214
|
|
|
if ( $clear ) { |
215
|
|
|
$this->last_error = null; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
return $last_error; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Get the last API error message. |
223
|
|
|
* |
224
|
|
|
* @param bool $clear If the error should be cleared after returning it. |
225
|
|
|
* |
226
|
|
|
* @return string The last API error message. |
227
|
|
|
*/ |
228
|
|
|
public function get_last_error( $clear = false ) { |
229
|
|
|
$last_error = $this->has_last_error() ? $this->last_error->get_error_message() : ''; |
230
|
|
|
if ( $clear ) { |
231
|
|
|
$this->last_error = null; |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
return $last_error; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* Fetch the secure token from Diaspora and save it for future use. |
239
|
|
|
* |
240
|
|
|
* @param bool $force Force to fetch a new token. |
241
|
|
|
* |
242
|
|
|
* @return string The fetched token. |
243
|
|
|
*/ |
244
|
|
|
private function fetch_token( $force = false ) { |
245
|
|
|
if ( null === $this->token || $force ) { |
246
|
|
|
// Go directly to the sign in page, as it would redirect to there anyway. |
247
|
|
|
// Since _request function automatically saves the new token, just call it with no data. |
248
|
|
|
$this->request( '/users/sign_in' ); |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
return $this->token; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* Check if the API has been initialised. Otherwise set the last error. |
256
|
|
|
* |
257
|
|
|
* @return bool Has the connection been initialised? |
258
|
|
|
*/ |
259
|
|
|
private function check_init() { |
260
|
|
|
if ( null === $this->token ) { |
261
|
|
|
$this->error( 'wp2d_api_connection_not_initialised', __( 'Connection not initialised.', 'wp-to-diaspora' ) ); |
262
|
|
|
|
263
|
|
|
return false; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
return true; |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Check if we're logged in. Otherwise set the last error. |
271
|
|
|
* |
272
|
|
|
* @return bool Are we logged in already? |
273
|
|
|
*/ |
274
|
|
|
private function check_login() { |
275
|
|
|
if ( ! $this->check_init() ) { |
276
|
|
|
return false; |
277
|
|
|
} |
278
|
|
|
if ( ! $this->is_logged_in() ) { |
279
|
|
|
$this->error( 'wp2d_api_not_logged_in', __( 'Not logged in.', 'wp-to-diaspora' ) ); |
280
|
|
|
|
281
|
|
|
return false; |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
return true; |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* Check if we are logged in. |
289
|
|
|
* |
290
|
|
|
* @return bool Are we logged in already? |
291
|
|
|
*/ |
292
|
|
|
public function is_logged_in() { |
293
|
|
|
return $this->is_logged_in; |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
/** |
297
|
|
|
* Log in to diaspora*. |
298
|
|
|
* |
299
|
|
|
* @param string $username Username used for login. |
300
|
|
|
* @param string $password Password used for login. |
301
|
|
|
* @param bool $force Force a new login even if we are already logged in. |
302
|
|
|
* |
303
|
|
|
* @return bool Did the login succeed? |
304
|
|
|
*/ |
305
|
|
|
public function login( $username, $password, $force = false ) { |
306
|
|
|
// Has the connection been initialised? |
307
|
|
|
if ( ! $this->check_init() ) { |
308
|
|
|
$this->logout(); |
309
|
|
|
|
310
|
|
|
return false; |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
// Username and password both need to be set. |
314
|
|
|
if ( ! isset( $username, $password ) || '' === $username || '' === $password ) { |
315
|
|
|
// Invalid credentials. |
316
|
|
|
$this->error( |
317
|
|
|
'wp2d_api_login_failed', |
318
|
|
|
__( 'Invalid credentials. Please re-save your login info.', 'wp-to-diaspora' ), |
319
|
|
|
[ 'help_tab' => 'troubleshooting' ] |
320
|
|
|
); |
321
|
|
|
$this->logout(); |
322
|
|
|
|
323
|
|
|
return false; |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
// If we are already logged in and not forcing a relogin, return. |
327
|
|
|
if ( ! $force && |
328
|
|
|
$username === $this->username && |
329
|
|
|
$password === $this->password && |
330
|
|
|
$this->is_logged_in() |
331
|
|
|
) { |
332
|
|
|
return true; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
// Set the newly passed username and password. |
336
|
|
|
$this->username = $username; |
337
|
|
|
$this->password = $password; |
338
|
|
|
|
339
|
|
|
// Set up the login parameters. |
340
|
|
|
$params = [ |
341
|
|
|
'user[username]' => $this->username, |
342
|
|
|
'user[password]' => $this->password, |
343
|
|
|
'authenticity_token' => $this->fetch_token(), |
344
|
|
|
]; |
345
|
|
|
|
346
|
|
|
$args = [ |
347
|
|
|
'method' => 'POST', |
348
|
|
|
'body' => $params, |
349
|
|
|
]; |
350
|
|
|
|
351
|
|
|
// Try to sign in. |
352
|
|
|
$this->request( '/users/sign_in', $args ); |
353
|
|
|
|
354
|
|
|
// Can we load the bookmarklet to make sure we're logged in? |
355
|
|
|
$response = $this->request( '/bookmarklet' ); |
356
|
|
|
|
357
|
|
|
// If the request isn't successful, we are not logged in correctly. |
358
|
|
|
if ( is_wp_error( $response ) || 200 !== $response->code ) { |
359
|
|
|
// Login failed. |
360
|
|
|
$this->error( |
361
|
|
|
'wp2d_api_login_failed', |
362
|
|
|
__( 'Login failed. Check your login details.', 'wp-to-diaspora' ), |
363
|
|
|
[ 'help_tab' => 'troubleshooting' ] |
364
|
|
|
); |
365
|
|
|
$this->logout(); |
366
|
|
|
|
367
|
|
|
return false; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
// Login succeeded. |
371
|
|
|
$this->is_logged_in = true; |
372
|
|
|
|
373
|
|
|
return true; |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
/** |
377
|
|
|
* Perform a logout, resetting all login info. |
378
|
|
|
* |
379
|
|
|
* @since 1.6.0 |
380
|
|
|
*/ |
381
|
|
|
public function logout() { |
382
|
|
|
$this->is_logged_in = false; |
383
|
|
|
$this->username = null; |
384
|
|
|
$this->password = null; |
385
|
|
|
$this->aspects = []; |
386
|
|
|
$this->services = []; |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
/** |
390
|
|
|
* Perform a de-initialisation, resetting all class variables. |
391
|
|
|
* |
392
|
|
|
* @since 1.7.0 |
393
|
|
|
*/ |
394
|
|
|
public function deinit() { |
395
|
|
|
$this->logout(); |
396
|
|
|
$this->last_error = null; |
397
|
|
|
$this->token = null; |
398
|
|
|
$this->cookies = []; |
399
|
|
|
$this->last_request = null; |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
/** |
403
|
|
|
* Post to diaspora*. |
404
|
|
|
* |
405
|
|
|
* @param string $text The text to post. |
406
|
|
|
* @param array|string $aspects The aspects to post to. Array or comma separated ids. |
407
|
|
|
* @param array $extra_data Any extra data to be added to the post call. |
408
|
|
|
* |
409
|
|
|
* @return bool|object Return the response data of the new diaspora* post if successfully posted, else false. |
410
|
|
|
*/ |
411
|
|
|
public function post( $text, $aspects = 'public', array $extra_data = [] ) { |
412
|
|
|
// Are we logged in? |
413
|
|
|
if ( ! $this->check_login() ) { |
414
|
|
|
return false; |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
// Put the aspects into a clean array. |
418
|
|
|
$aspects = array_filter( WP2D_Helpers::str_to_arr( $aspects ) ); |
419
|
|
|
|
420
|
|
|
// If no aspects have been selected or the public one is also included, choose public only. |
421
|
|
|
if ( empty( $aspects ) || in_array( 'public', $aspects, true ) ) { |
422
|
|
|
$aspects = 'public'; |
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
// Prepare post data. |
426
|
|
|
$post_data = [ |
427
|
|
|
'aspect_ids' => $aspects, |
428
|
|
|
'status_message' => [ |
429
|
|
|
'text' => $text, |
430
|
|
|
'provider_display_name' => $this->provider, |
431
|
|
|
], |
432
|
|
|
]; |
433
|
|
|
|
434
|
|
|
// Add any extra data to the post. |
435
|
|
|
if ( ! empty( $extra_data ) ) { |
436
|
|
|
$post_data += $extra_data; |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
$post_data = wp_json_encode( $post_data ); |
440
|
|
|
|
441
|
|
|
$args = [ |
442
|
|
|
'method' => 'POST', |
443
|
|
|
'body' => $post_data, |
444
|
|
|
'headers' => [ |
445
|
|
|
'Accept' => 'application/json', |
446
|
|
|
'Content-Type' => 'application/json', |
447
|
|
|
'X-CSRF-Token' => $this->fetch_token(), |
448
|
|
|
], |
449
|
|
|
]; |
450
|
|
|
|
451
|
|
|
// Submit the post. |
452
|
|
|
$response = $this->request( '/status_messages', $args ); |
453
|
|
|
|
454
|
|
|
if ( is_wp_error( $response ) ) { |
455
|
|
|
$this->error( 'wp2d_api_post_failed', $response->get_error_message() ); |
456
|
|
|
|
457
|
|
|
return false; |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
$diaspost = json_decode( $response->body ); |
461
|
|
|
if ( 201 !== $response->code ) { |
462
|
|
|
$this->error( |
463
|
|
|
'wp2d_api_post_failed', |
464
|
|
|
$diaspost->error ?? _x( 'Unknown error occurred.', 'When an unknown error occurred in the WP2D_API object.', 'wp-to-diaspora' ) |
465
|
|
|
); |
466
|
|
|
|
467
|
|
|
return false; |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
// Add additional info to our diaspora post object. |
471
|
|
|
$diaspost->permalink = $this->get_pod_url( '/posts/' . $diaspost->guid ); |
472
|
|
|
|
473
|
|
|
return $diaspost; |
474
|
|
|
} |
475
|
|
|
|
476
|
|
|
/** |
477
|
|
|
* Delete a post or comment from diaspora*. |
478
|
|
|
* |
479
|
|
|
* @since 1.6.0 |
480
|
|
|
* |
481
|
|
|
* @param string $what What to delete, 'post' or 'comment'. |
482
|
|
|
* @param string $id The ID of the post or comment to delete. |
483
|
|
|
* |
484
|
|
|
* @return bool If the deletion was successful. |
485
|
|
|
*/ |
486
|
|
|
public function delete( $what, $id ) { |
487
|
|
|
// Are we logged in? |
488
|
|
|
if ( ! $this->check_login() ) { |
489
|
|
|
return false; |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
// For now, only deleting posts and comments is allowed. |
493
|
|
|
if ( ! in_array( $what, [ 'post', 'comment' ], true ) ) { |
494
|
|
|
$this->error( 'wp2d_api_delete_failed', __( 'You can only delete posts and comments.', 'wp-to-diaspora' ) ); |
495
|
|
|
|
496
|
|
|
return false; |
497
|
|
|
} |
498
|
|
|
|
499
|
|
|
$args = [ |
500
|
|
|
'method' => 'DELETE', |
501
|
|
|
'headers' => [ |
502
|
|
|
'Accept' => 'application/json', |
503
|
|
|
'Content-Type' => 'application/json', |
504
|
|
|
'X-CSRF-Token' => $this->fetch_token(), |
505
|
|
|
], |
506
|
|
|
]; |
507
|
|
|
|
508
|
|
|
// Try to delete the post or comment. |
509
|
|
|
$response = $this->request( '/' . $what . 's/' . $id, $args ); |
510
|
|
|
|
511
|
|
|
if ( is_wp_error( $response ) ) { |
512
|
|
|
$error_message = $response->get_error_message(); |
513
|
|
|
} else { |
514
|
|
|
switch ( $response->code ) { |
515
|
|
|
case 204: |
516
|
|
|
return true; |
517
|
|
View Code Duplication |
case 404: |
|
|
|
|
518
|
|
|
$error_message = ( 'post' === $what ) |
519
|
|
|
? __( 'The post you tried to delete does not exist.', 'wp-to-diaspora' ) |
520
|
|
|
: __( 'The comment you tried to delete does not exist.', 'wp-to-diaspora' ); |
521
|
|
|
break; |
522
|
|
View Code Duplication |
case 403: |
|
|
|
|
523
|
|
|
$error_message = ( 'post' === $what ) |
524
|
|
|
? __( 'The post you tried to delete does not belong to you.', 'wp-to-diaspora' ) |
525
|
|
|
: __( 'The comment you tried to delete does not belong to you.', 'wp-to-diaspora' ); |
526
|
|
|
break; |
527
|
|
|
case 500: |
528
|
|
|
default: |
529
|
|
|
$error_message = _x( 'Unknown error occurred.', 'When an unknown error occurred in the WP2D_API object.', 'wp-to-diaspora' ); |
530
|
|
|
break; |
531
|
|
|
} |
532
|
|
|
} |
533
|
|
|
|
534
|
|
|
$this->error( 'wp2d_api_delete_' . $what . '_failed', $error_message ); |
535
|
|
|
|
536
|
|
|
return false; |
537
|
|
|
} |
538
|
|
|
|
539
|
|
|
/** |
540
|
|
|
* Get the list of aspects. |
541
|
|
|
* |
542
|
|
|
* @param bool $force Force to fetch new aspects. |
543
|
|
|
* |
544
|
|
|
* @return array|bool Array of aspect objects or false. |
545
|
|
|
*/ |
546
|
|
|
public function get_aspects( $force = false ) { |
547
|
|
|
$this->aspects = $this->get_aspects_services( 'aspects', $this->aspects, $force ); |
|
|
|
|
548
|
|
|
|
549
|
|
|
return is_array( $this->aspects ) ? $this->aspects : false; |
550
|
|
|
} |
551
|
|
|
|
552
|
|
|
/** |
553
|
|
|
* Get the list of connected services. |
554
|
|
|
* |
555
|
|
|
* @param bool $force Force to fetch new connected services. |
556
|
|
|
* |
557
|
|
|
* @return array|bool Array of service objects or false. |
558
|
|
|
*/ |
559
|
|
|
public function get_services( $force = false ) { |
560
|
|
|
$this->services = $this->get_aspects_services( 'services', $this->services, $force ); |
|
|
|
|
561
|
|
|
|
562
|
|
|
return is_array( $this->services ) ? $this->services : false; |
563
|
|
|
} |
564
|
|
|
|
565
|
|
|
/** |
566
|
|
|
* Get the list of aspects or connected services. |
567
|
|
|
* |
568
|
|
|
* @param string $type Type of list to get. |
569
|
|
|
* @param array $list The current list of items. |
570
|
|
|
* @param bool $force Force to fetch new list. |
571
|
|
|
* |
572
|
|
|
* @return array|bool List of fetched aspects or services, or false. |
573
|
|
|
*/ |
574
|
|
|
private function get_aspects_services( $type, $list, $force ) { |
575
|
|
|
if ( ! $this->check_login() ) { |
576
|
|
|
return false; |
577
|
|
|
} |
578
|
|
|
|
579
|
|
|
// Fetch the new list if the current list is empty or a reload is forced. |
580
|
|
|
if ( $force || empty( $list ) ) { |
581
|
|
|
$response = $this->request( '/bookmarklet' ); |
582
|
|
|
|
583
|
|
|
if ( is_wp_error( $response ) || 200 !== $response->code ) { |
584
|
|
|
switch ( $type ) { |
585
|
|
|
case 'aspects': |
586
|
|
|
$this->error( 'wp2d_api_getting_aspects_failed', __( 'Error loading aspects.', 'wp-to-diaspora' ) ); |
587
|
|
|
break; |
588
|
|
|
case 'services': |
589
|
|
|
$this->error( 'wp2d_api_getting_services_failed', __( 'Error loading services.', 'wp-to-diaspora' ) ); |
590
|
|
|
break; |
591
|
|
|
default: |
592
|
|
|
$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' ) ); |
593
|
|
|
break; |
594
|
|
|
} |
595
|
|
|
|
596
|
|
|
return false; |
597
|
|
|
} |
598
|
|
|
|
599
|
|
|
// Load the aspects or services. |
600
|
|
|
$raw_list = json_decode( $this->parse_regex( $type, $response->body ) ); |
601
|
|
|
if ( is_array( $raw_list ) ) { |
602
|
|
|
// In case this fetch is forced, empty the list. |
603
|
|
|
$list = []; |
604
|
|
|
|
605
|
|
|
if ( 'aspects' === $type ) { |
606
|
|
|
// Add the 'public' aspect, as it's global and not user specific. |
607
|
|
|
$list['public'] = __( 'Public', 'wp-to-diaspora' ); |
608
|
|
|
|
609
|
|
|
// Add all user specific aspects. |
610
|
|
|
foreach ( $raw_list as $aspect ) { |
611
|
|
|
$list[ $aspect->id ] = $aspect->name; |
612
|
|
|
} |
613
|
|
|
} elseif ( 'services' === $type ) { |
614
|
|
|
foreach ( $raw_list as $service ) { |
615
|
|
|
$list[ $service ] = ucfirst( $service ); |
616
|
|
|
} |
617
|
|
|
} |
618
|
|
|
} |
619
|
|
|
} |
620
|
|
|
|
621
|
|
|
return $list; |
622
|
|
|
} |
623
|
|
|
|
624
|
|
|
/** |
625
|
|
|
* Send an http(s) request via WP_HTTP API. |
626
|
|
|
* |
627
|
|
|
* @see WP_Http::request() |
628
|
|
|
* |
629
|
|
|
* @param string $url The URL to request. |
630
|
|
|
* @param array $args Arguments to be posted with the request. |
631
|
|
|
* |
632
|
|
|
* @return object An object containing details about this request. |
633
|
|
|
*/ |
634
|
|
|
private function request( $url, array $args = [] ) { |
635
|
|
|
// Prefix the full pod URL if necessary. |
636
|
|
|
if ( 0 === strpos( $url, '/' ) ) { |
637
|
|
|
$url = $this->get_pod_url( $url ); |
638
|
|
|
} |
639
|
|
|
|
640
|
|
|
// Disable redirections so we can verify HTTP response codes. |
641
|
|
|
$defaults = [ |
642
|
|
|
'method' => 'GET', |
643
|
|
|
'redirection' => 0, |
644
|
|
|
'sslverify' => true, |
645
|
|
|
'timeout' => 60, |
646
|
|
|
]; |
647
|
|
|
|
648
|
|
|
// If the certificate bundle has been downloaded manually, use that instead. |
649
|
|
|
// NOTE: This should actually never be necessary, it's a fallback! |
650
|
|
|
if ( file_exists( WP2D_DIR . '/cacert.pem' ) ) { |
651
|
|
|
$defaults['sslcertificates'] = WP2D_DIR . '/cacert.pem'; |
652
|
|
|
} |
653
|
|
|
|
654
|
|
|
// Set the correct cookie. |
655
|
|
|
if ( ! empty( $this->cookies ) ) { |
656
|
|
|
$defaults['cookies'] = $this->cookies; |
657
|
|
|
} |
658
|
|
|
|
659
|
|
|
$args = wp_parse_args( $args, $defaults ); |
660
|
|
|
|
661
|
|
|
// Get the response from the WP_HTTP request. |
662
|
|
|
$response = wp_remote_request( $url, $args ); |
663
|
|
|
|
664
|
|
|
if ( is_wp_error( $response ) ) { |
665
|
|
|
$this->last_error = $response; |
666
|
|
|
|
667
|
|
|
return $response; |
668
|
|
|
} |
669
|
|
|
|
670
|
|
|
// Get the headers and the HTML response. |
671
|
|
|
$headers = wp_remote_retrieve_headers( $response ); |
672
|
|
|
$body = wp_remote_retrieve_body( $response ); |
673
|
|
|
|
674
|
|
|
// Remember this request. |
675
|
|
|
$this->last_request = new stdClass(); |
676
|
|
|
$this->last_request->response = $response; |
677
|
|
|
$this->last_request->headers = $headers; |
678
|
|
|
$this->last_request->body = $body; |
679
|
|
|
$this->last_request->message = wp_remote_retrieve_response_message( $response ); |
680
|
|
|
$this->last_request->code = wp_remote_retrieve_response_code( $response ); |
681
|
|
|
|
682
|
|
|
// Save the new token. |
683
|
|
|
if ( $token = $this->parse_regex( 'token', $body ) ) { |
684
|
|
|
$this->token = $token; |
685
|
|
|
} |
686
|
|
|
|
687
|
|
|
// Save the latest cookies. |
688
|
|
|
if ( isset( $response['cookies'] ) ) { |
689
|
|
|
$this->cookies = $response['cookies']; |
690
|
|
|
} |
691
|
|
|
|
692
|
|
|
// Return the last request details. |
693
|
|
|
return $this->last_request; |
694
|
|
|
} |
695
|
|
|
|
696
|
|
|
/** |
697
|
|
|
* Helper method to set the last occurred error. |
698
|
|
|
* |
699
|
|
|
* @since 1.6.0 |
700
|
|
|
* |
701
|
|
|
* @see WP_Error::__construct() |
702
|
|
|
* |
703
|
|
|
* @param string|int $code Error code. |
704
|
|
|
* @param string $message Error message. |
705
|
|
|
* @param mixed $data Error data. |
706
|
|
|
*/ |
707
|
|
|
private function error( $code, $message, $data = '' ) { |
708
|
|
|
// Always add the code and message of the last request. |
709
|
|
|
$data = array_merge( array_filter( (array) $data ), [ |
710
|
|
|
'code' => $this->last_request->code ?? null, |
711
|
|
|
'message' => $this->last_request->message ?? null, |
712
|
|
|
] ); |
713
|
|
|
|
714
|
|
|
$this->last_error = new WP_Error( $code, $message, $data ); |
715
|
|
|
} |
716
|
|
|
|
717
|
|
|
/** |
718
|
|
|
* Parse the regex and return the found string. |
719
|
|
|
* |
720
|
|
|
* @param string $regex Shorthand of a saved regex or a custom regex. |
721
|
|
|
* @param string $content Text to parse the regex with. |
722
|
|
|
* |
723
|
|
|
* @return string The found string, or an empty string. |
724
|
|
|
*/ |
725
|
|
|
private function parse_regex( $regex, $content ) { |
726
|
|
|
// Use a shorthand regex if available. |
727
|
|
|
if ( array_key_exists( $regex, self::$regexes ) ) { |
728
|
|
|
$regex = self::$regexes[ $regex ]; |
729
|
|
|
} |
730
|
|
|
|
731
|
|
|
preg_match( $regex, $content, $matches ); |
732
|
|
|
|
733
|
|
|
return trim( array_pop( $matches ) ); |
734
|
|
|
} |
735
|
|
|
} |
736
|
|
|
|
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.