Diff: STRATO-apps/wordpress_03/app/wp-includes/class-wp-xmlrpc-server.php
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
<?php
2
+
/**
3
+
* XML-RPC protocol support for WordPress.
4
+
*
5
+
* @package WordPress
6
+
* @subpackage Publishing
7
+
*/
8
+
9
+
/**
10
+
* WordPress XMLRPC server implementation.
11
+
*
12
+
* Implements compatibility for Blogger API, MetaWeblog API, MovableType, and
13
+
* pingback. Additional WordPress API for managing comments, pages, posts,
14
+
* options, etc.
15
+
*
16
+
* As of WordPress 3.5.0, XML-RPC is enabled by default. It can be disabled
17
+
* via the {@see 'xmlrpc_enabled'} filter found in wp_xmlrpc_server::set_is_enabled().
18
+
*
19
+
* @since 1.5.0
20
+
*
21
+
* @see IXR_Server
22
+
*/
23
+
#[AllowDynamicProperties]
24
+
class wp_xmlrpc_server extends IXR_Server {
25
+
/**
26
+
* Methods.
27
+
*
28
+
* @var array
29
+
*/
30
+
public $methods;
31
+
32
+
/**
33
+
* Blog options.
34
+
*
35
+
* @var array
36
+
*/
37
+
public $blog_options;
38
+
39
+
/**
40
+
* IXR_Error instance.
41
+
*
42
+
* @var IXR_Error
43
+
*/
44
+
public $error;
45
+
46
+
/**
47
+
* Flags that the user authentication has failed in this instance of wp_xmlrpc_server.
48
+
*
49
+
* @var bool
50
+
*/
51
+
protected $auth_failed = false;
52
+
53
+
/**
54
+
* Flags that XML-RPC is enabled
55
+
*
56
+
* @var bool
57
+
*/
58
+
private $is_enabled;
59
+
60
+
/**
61
+
* Registers all of the XMLRPC methods that XMLRPC server understands.
62
+
*
63
+
* Sets up server and method property. Passes XMLRPC methods through the
64
+
* {@see 'xmlrpc_methods'} filter to allow plugins to extend or replace
65
+
* XML-RPC methods.
66
+
*
67
+
* @since 1.5.0
68
+
*/
69
+
public function __construct() {
70
+
$this->methods = array(
71
+
// WordPress API.
72
+
'wp.getUsersBlogs' => 'this:wp_getUsersBlogs',
73
+
'wp.newPost' => 'this:wp_newPost',
74
+
'wp.editPost' => 'this:wp_editPost',
75
+
'wp.deletePost' => 'this:wp_deletePost',
76
+
'wp.getPost' => 'this:wp_getPost',
77
+
'wp.getPosts' => 'this:wp_getPosts',
78
+
'wp.newTerm' => 'this:wp_newTerm',
79
+
'wp.editTerm' => 'this:wp_editTerm',
80
+
'wp.deleteTerm' => 'this:wp_deleteTerm',
81
+
'wp.getTerm' => 'this:wp_getTerm',
82
+
'wp.getTerms' => 'this:wp_getTerms',
83
+
'wp.getTaxonomy' => 'this:wp_getTaxonomy',
84
+
'wp.getTaxonomies' => 'this:wp_getTaxonomies',
85
+
'wp.getUser' => 'this:wp_getUser',
86
+
'wp.getUsers' => 'this:wp_getUsers',
87
+
'wp.getProfile' => 'this:wp_getProfile',
88
+
'wp.editProfile' => 'this:wp_editProfile',
89
+
'wp.getPage' => 'this:wp_getPage',
90
+
'wp.getPages' => 'this:wp_getPages',
91
+
'wp.newPage' => 'this:wp_newPage',
92
+
'wp.deletePage' => 'this:wp_deletePage',
93
+
'wp.editPage' => 'this:wp_editPage',
94
+
'wp.getPageList' => 'this:wp_getPageList',
95
+
'wp.getAuthors' => 'this:wp_getAuthors',
96
+
'wp.getCategories' => 'this:mw_getCategories', // Alias.
97
+
'wp.getTags' => 'this:wp_getTags',
98
+
'wp.newCategory' => 'this:wp_newCategory',
99
+
'wp.deleteCategory' => 'this:wp_deleteCategory',
100
+
'wp.suggestCategories' => 'this:wp_suggestCategories',
101
+
'wp.uploadFile' => 'this:mw_newMediaObject', // Alias.
102
+
'wp.deleteFile' => 'this:wp_deletePost', // Alias.
103
+
'wp.getCommentCount' => 'this:wp_getCommentCount',
104
+
'wp.getPostStatusList' => 'this:wp_getPostStatusList',
105
+
'wp.getPageStatusList' => 'this:wp_getPageStatusList',
106
+
'wp.getPageTemplates' => 'this:wp_getPageTemplates',
107
+
'wp.getOptions' => 'this:wp_getOptions',
108
+
'wp.setOptions' => 'this:wp_setOptions',
109
+
'wp.getComment' => 'this:wp_getComment',
110
+
'wp.getComments' => 'this:wp_getComments',
111
+
'wp.deleteComment' => 'this:wp_deleteComment',
112
+
'wp.editComment' => 'this:wp_editComment',
113
+
'wp.newComment' => 'this:wp_newComment',
114
+
'wp.getCommentStatusList' => 'this:wp_getCommentStatusList',
115
+
'wp.getMediaItem' => 'this:wp_getMediaItem',
116
+
'wp.getMediaLibrary' => 'this:wp_getMediaLibrary',
117
+
'wp.getPostFormats' => 'this:wp_getPostFormats',
118
+
'wp.getPostType' => 'this:wp_getPostType',
119
+
'wp.getPostTypes' => 'this:wp_getPostTypes',
120
+
'wp.getRevisions' => 'this:wp_getRevisions',
121
+
'wp.restoreRevision' => 'this:wp_restoreRevision',
122
+
123
+
// Blogger API.
124
+
'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',
125
+
'blogger.getUserInfo' => 'this:blogger_getUserInfo',
126
+
'blogger.getPost' => 'this:blogger_getPost',
127
+
'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',
128
+
'blogger.newPost' => 'this:blogger_newPost',
129
+
'blogger.editPost' => 'this:blogger_editPost',
130
+
'blogger.deletePost' => 'this:blogger_deletePost',
131
+
132
+
// MetaWeblog API (with MT extensions to structs).
133
+
'metaWeblog.newPost' => 'this:mw_newPost',
134
+
'metaWeblog.editPost' => 'this:mw_editPost',
135
+
'metaWeblog.getPost' => 'this:mw_getPost',
136
+
'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts',
137
+
'metaWeblog.getCategories' => 'this:mw_getCategories',
138
+
'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',
139
+
140
+
/*
141
+
* MetaWeblog API aliases for Blogger API.
142
+
* See http://www.xmlrpc.com/stories/storyReader$2460
143
+
*/
144
+
'metaWeblog.deletePost' => 'this:blogger_deletePost',
145
+
'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',
146
+
147
+
// MovableType API.
148
+
'mt.getCategoryList' => 'this:mt_getCategoryList',
149
+
'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles',
150
+
'mt.getPostCategories' => 'this:mt_getPostCategories',
151
+
'mt.setPostCategories' => 'this:mt_setPostCategories',
152
+
'mt.supportedMethods' => 'this:mt_supportedMethods',
153
+
'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',
154
+
'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',
155
+
'mt.publishPost' => 'this:mt_publishPost',
156
+
157
+
// Pingback.
158
+
'pingback.ping' => 'this:pingback_ping',
159
+
'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',
160
+
161
+
'demo.sayHello' => 'this:sayHello',
162
+
'demo.addTwoNumbers' => 'this:addTwoNumbers',
163
+
);
164
+
165
+
$this->initialise_blog_option_info();
166
+
167
+
/**
168
+
* Filters the methods exposed by the XML-RPC server.
169
+
*
170
+
* This filter can be used to add new methods, and remove built-in methods.
171
+
*
172
+
* @since 1.5.0
173
+
*
174
+
* @param string[] $methods An array of XML-RPC methods, keyed by their methodName.
175
+
*/
176
+
$this->methods = apply_filters( 'xmlrpc_methods', $this->methods );
177
+
178
+
$this->set_is_enabled();
179
+
}
180
+
181
+
/**
182
+
* Sets wp_xmlrpc_server::$is_enabled property.
183
+
*
184
+
* Determines whether the xmlrpc server is enabled on this WordPress install
185
+
* and set the is_enabled property accordingly.
186
+
*
187
+
* @since 5.7.3
188
+
*/
189
+
private function set_is_enabled() {
190
+
/*
191
+
* Respect old get_option() filters left for back-compat when the 'enable_xmlrpc'
192
+
* option was deprecated in 3.5.0. Use the {@see 'xmlrpc_enabled'} hook instead.
193
+
*/
194
+
$is_enabled = apply_filters( 'pre_option_enable_xmlrpc', false );
195
+
if ( false === $is_enabled ) {
196
+
$is_enabled = apply_filters( 'option_enable_xmlrpc', true );
197
+
}
198
+
199
+
/**
200
+
* Filters whether XML-RPC methods requiring authentication are enabled.
201
+
*
202
+
* Contrary to the way it's named, this filter does not control whether XML-RPC is *fully*
203
+
* enabled, rather, it only controls whether XML-RPC methods requiring authentication -
204
+
* such as for publishing purposes - are enabled.
205
+
*
206
+
* Further, the filter does not control whether pingbacks or other custom endpoints that don't
207
+
* require authentication are enabled. This behavior is expected, and due to how parity was matched
208
+
* with the `enable_xmlrpc` UI option the filter replaced when it was introduced in 3.5.
209
+
*
210
+
* To disable XML-RPC methods that require authentication, use:
211
+
*
212
+
* add_filter( 'xmlrpc_enabled', '__return_false' );
213
+
*
214
+
* For more granular control over all XML-RPC methods and requests, see the {@see 'xmlrpc_methods'}
215
+
* and {@see 'xmlrpc_element_limit'} hooks.
216
+
*
217
+
* @since 3.5.0
218
+
*
219
+
* @param bool $is_enabled Whether XML-RPC is enabled. Default true.
220
+
*/
221
+
$this->is_enabled = apply_filters( 'xmlrpc_enabled', $is_enabled );
222
+
}
223
+
224
+
/**
225
+
* Makes private/protected methods readable for backward compatibility.
226
+
*
227
+
* @since 4.0.0
228
+
*
229
+
* @param string $name Method to call.
230
+
* @param array $arguments Arguments to pass when calling.
231
+
* @return array|IXR_Error|false Return value of the callback, false otherwise.
232
+
*/
233
+
public function __call( $name, $arguments ) {
234
+
if ( '_multisite_getUsersBlogs' === $name ) {
235
+
return $this->_multisite_getUsersBlogs( ...$arguments );
236
+
}
237
+
return false;
238
+
}
239
+
240
+
/**
241
+
* Serves the XML-RPC request.
242
+
*
243
+
* @since 2.9.0
244
+
*/
245
+
public function serve_request() {
246
+
$this->IXR_Server( $this->methods );
247
+
}
248
+
249
+
/**
250
+
* Tests XMLRPC API by saying, "Hello!" to client.
251
+
*
252
+
* @since 1.5.0
253
+
*
254
+
* @return string Hello string response.
255
+
*/
256
+
public function sayHello() {
257
+
return 'Hello!';
258
+
}
259
+
260
+
/**
261
+
* Tests XMLRPC API by adding two numbers for client.
262
+
*
263
+
* @since 1.5.0
264
+
*
265
+
* @param array $args {
266
+
* Method arguments. Note: arguments must be ordered as documented.
267
+
*
268
+
* @type int $0 A number to add.
269
+
* @type int $1 A second number to add.
270
+
* }
271
+
* @return int Sum of the two given numbers.
272
+
*/
273
+
public function addTwoNumbers( $args ) {
274
+
$number1 = $args[0];
275
+
$number2 = $args[1];
276
+
return $number1 + $number2;
277
+
}
278
+
279
+
/**
280
+
* Logs user in.
281
+
*
282
+
* @since 2.8.0
283
+
*
284
+
* @param string $username User's username.
285
+
* @param string $password User's password.
286
+
* @return WP_User|false WP_User object if authentication passed, false otherwise.
287
+
*/
288
+
public function login(
289
+
$username,
290
+
#[\SensitiveParameter]
291
+
$password
292
+
) {
293
+
if ( ! $this->is_enabled ) {
294
+
$this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site.' ) ) );
295
+
return false;
296
+
}
297
+
298
+
if ( $this->auth_failed ) {
299
+
$user = new WP_Error( 'login_prevented' );
300
+
} else {
301
+
$user = wp_authenticate( $username, $password );
302
+
}
303
+
304
+
if ( is_wp_error( $user ) ) {
305
+
$this->error = new IXR_Error( 403, __( 'Incorrect username or password.' ) );
306
+
307
+
// Flag that authentication has failed once on this wp_xmlrpc_server instance.
308
+
$this->auth_failed = true;
309
+
310
+
/**
311
+
* Filters the XML-RPC user login error message.
312
+
*
313
+
* @since 3.5.0
314
+
*
315
+
* @param IXR_Error $error The XML-RPC error message.
316
+
* @param WP_Error $user WP_Error object.
317
+
*/
318
+
$this->error = apply_filters( 'xmlrpc_login_error', $this->error, $user );
319
+
return false;
320
+
}
321
+
322
+
wp_set_current_user( $user->ID );
323
+
return $user;
324
+
}
325
+
326
+
/**
327
+
* Checks user's credentials. Deprecated.
328
+
*
329
+
* @since 1.5.0
330
+
* @deprecated 2.8.0 Use wp_xmlrpc_server::login()
331
+
* @see wp_xmlrpc_server::login()
332
+
*
333
+
* @param string $username User's username.
334
+
* @param string $password User's password.
335
+
* @return bool Whether authentication passed.
336
+
*/
337
+
public function login_pass_ok(
338
+
$username,
339
+
#[\SensitiveParameter]
340
+
$password
341
+
) {
342
+
return (bool) $this->login( $username, $password );
343
+
}
344
+
345
+
/**
346
+
* Escapes string or array of strings for database.
347
+
*
348
+
* @since 1.5.2
349
+
*
350
+
* @param string|array $data Escape single string or array of strings.
351
+
* @return string|void Returns with string is passed, alters by-reference
352
+
* when array is passed.
353
+
*/
354
+
public function escape( &$data ) {
355
+
if ( ! is_array( $data ) ) {
356
+
return wp_slash( $data );
357
+
}
358
+
359
+
foreach ( $data as &$v ) {
360
+
if ( is_array( $v ) ) {
361
+
$this->escape( $v );
362
+
} elseif ( ! is_object( $v ) ) {
363
+
$v = wp_slash( $v );
364
+
}
365
+
}
366
+
}
367
+
368
+
/**
369
+
* Sends error response to client.
370
+
*
371
+
* Sends an XML error response to the client. If the endpoint is enabled
372
+
* an HTTP 200 response is always sent per the XML-RPC specification.
373
+
*
374
+
* @since 5.7.3
375
+
*
376
+
* @param IXR_Error|string $error Error code or an error object.
377
+
* @param false $message Error message. Optional.
378
+
*/
379
+
public function error( $error, $message = false ) {
380
+
// Accepts either an error object or an error code and message
381
+
if ( $message && ! is_object( $error ) ) {
382
+
$error = new IXR_Error( $error, $message );
383
+
}
384
+
385
+
if ( ! $this->is_enabled ) {
386
+
status_header( $error->code );
387
+
}
388
+
389
+
$this->output( $error->getXml() );
390
+
}
391
+
392
+
/**
393
+
* Retrieves custom fields for post.
394
+
*
395
+
* @since 2.5.0
396
+
*
397
+
* @param int $post_id Post ID.
398
+
* @return array Custom fields, if exist.
399
+
*/
400
+
public function get_custom_fields( $post_id ) {
401
+
$post_id = (int) $post_id;
402
+
403
+
$custom_fields = array();
404
+
405
+
foreach ( (array) has_meta( $post_id ) as $meta ) {
406
+
// Don't expose protected fields.
407
+
if ( ! current_user_can( 'edit_post_meta', $post_id, $meta['meta_key'] ) ) {
408
+
continue;
409
+
}
410
+
411
+
$custom_fields[] = array(
412
+
'id' => $meta['meta_id'],
413
+
'key' => $meta['meta_key'],
414
+
'value' => $meta['meta_value'],
415
+
);
416
+
}
417
+
418
+
return $custom_fields;
419
+
}
420
+
421
+
/**
422
+
* Sets custom fields for post.
423
+
*
424
+
* @since 2.5.0
425
+
*
426
+
* @param int $post_id Post ID.
427
+
* @param array $fields Custom fields.
428
+
*/
429
+
public function set_custom_fields( $post_id, $fields ) {
430
+
$post_id = (int) $post_id;
431
+
432
+
foreach ( (array) $fields as $meta ) {
433
+
if ( isset( $meta['id'] ) ) {
434
+
$meta['id'] = (int) $meta['id'];
435
+
$pmeta = get_metadata_by_mid( 'post', $meta['id'] );
436
+
437
+
if ( ! $pmeta || (int) $pmeta->post_id !== $post_id ) {
438
+
continue;
439
+
}
440
+
441
+
if ( isset( $meta['key'] ) ) {
442
+
$meta['key'] = wp_unslash( $meta['key'] );
443
+
if ( $meta['key'] !== $pmeta->meta_key ) {
444
+
continue;
445
+
}
446
+
$meta['value'] = wp_unslash( $meta['value'] );
447
+
if ( current_user_can( 'edit_post_meta', $post_id, $meta['key'] ) ) {
448
+
update_metadata_by_mid( 'post', $meta['id'], $meta['value'] );
449
+
}
450
+
} elseif ( current_user_can( 'delete_post_meta', $post_id, $pmeta->meta_key ) ) {
451
+
delete_metadata_by_mid( 'post', $meta['id'] );
452
+
}
453
+
} elseif ( current_user_can( 'add_post_meta', $post_id, wp_unslash( $meta['key'] ) ) ) {
454
+
add_post_meta( $post_id, $meta['key'], $meta['value'] );
455
+
}
456
+
}
457
+
}
458
+
459
+
/**
460
+
* Retrieves custom fields for a term.
461
+
*
462
+
* @since 4.9.0
463
+
*
464
+
* @param int $term_id Term ID.
465
+
* @return array Array of custom fields, if they exist.
466
+
*/
467
+
public function get_term_custom_fields( $term_id ) {
468
+
$term_id = (int) $term_id;
469
+
470
+
$custom_fields = array();
471
+
472
+
foreach ( (array) has_term_meta( $term_id ) as $meta ) {
473
+
474
+
if ( ! current_user_can( 'edit_term_meta', $term_id ) ) {
475
+
continue;
476
+
}
477
+
478
+
$custom_fields[] = array(
479
+
'id' => $meta['meta_id'],
480
+
'key' => $meta['meta_key'],
481
+
'value' => $meta['meta_value'],
482
+
);
483
+
}
484
+
485
+
return $custom_fields;
486
+
}
487
+
488
+
/**
489
+
* Sets custom fields for a term.
490
+
*
491
+
* @since 4.9.0
492
+
*
493
+
* @param int $term_id Term ID.
494
+
* @param array $fields Custom fields.
495
+
*/
496
+
public function set_term_custom_fields( $term_id, $fields ) {
497
+
$term_id = (int) $term_id;
498
+
499
+
foreach ( (array) $fields as $meta ) {
500
+
if ( isset( $meta['id'] ) ) {
501
+
$meta['id'] = (int) $meta['id'];
502
+
$pmeta = get_metadata_by_mid( 'term', $meta['id'] );
503
+
if ( isset( $meta['key'] ) ) {
504
+
$meta['key'] = wp_unslash( $meta['key'] );
505
+
if ( $meta['key'] !== $pmeta->meta_key ) {
506
+
continue;
507
+
}
508
+
$meta['value'] = wp_unslash( $meta['value'] );
509
+
if ( current_user_can( 'edit_term_meta', $term_id ) ) {
510
+
update_metadata_by_mid( 'term', $meta['id'], $meta['value'] );
511
+
}
512
+
} elseif ( current_user_can( 'delete_term_meta', $term_id ) ) {
513
+
delete_metadata_by_mid( 'term', $meta['id'] );
514
+
}
515
+
} elseif ( current_user_can( 'add_term_meta', $term_id ) ) {
516
+
add_term_meta( $term_id, $meta['key'], $meta['value'] );
517
+
}
518
+
}
519
+
}
520
+
521
+
/**
522
+
* Sets up blog options property.
523
+
*
524
+
* Passes property through {@see 'xmlrpc_blog_options'} filter.
525
+
*
526
+
* @since 2.6.0
527
+
*/
528
+
public function initialise_blog_option_info() {
529
+
$this->blog_options = array(
530
+
// Read-only options.
531
+
'software_name' => array(
532
+
'desc' => __( 'Software Name' ),
533
+
'readonly' => true,
534
+
'value' => 'WordPress',
535
+
),
536
+
'software_version' => array(
537
+
'desc' => __( 'Software Version' ),
538
+
'readonly' => true,
539
+
'value' => get_bloginfo( 'version' ),
540
+
),
541
+
'blog_url' => array(
542
+
'desc' => __( 'WordPress Address (URL)' ),
543
+
'readonly' => true,
544
+
'option' => 'siteurl',
545
+
),
546
+
'home_url' => array(
547
+
'desc' => __( 'Site Address (URL)' ),
548
+
'readonly' => true,
549
+
'option' => 'home',
550
+
),
551
+
'login_url' => array(
552
+
'desc' => __( 'Login Address (URL)' ),
553
+
'readonly' => true,
554
+
'value' => wp_login_url(),
555
+
),
556
+
'admin_url' => array(
557
+
'desc' => __( 'The URL to the admin area' ),
558
+
'readonly' => true,
559
+
'value' => get_admin_url(),
560
+
),
561
+
'image_default_link_type' => array(
562
+
'desc' => __( 'Image default link type' ),
563
+
'readonly' => true,
564
+
'option' => 'image_default_link_type',
565
+
),
566
+
'image_default_size' => array(
567
+
'desc' => __( 'Image default size' ),
568
+
'readonly' => true,
569
+
'option' => 'image_default_size',
570
+
),
571
+
'image_default_align' => array(
572
+
'desc' => __( 'Image default align' ),
573
+
'readonly' => true,
574
+
'option' => 'image_default_align',
575
+
),
576
+
'template' => array(
577
+
'desc' => __( 'Template' ),
578
+
'readonly' => true,
579
+
'option' => 'template',
580
+
),
581
+
'stylesheet' => array(
582
+
'desc' => __( 'Stylesheet' ),
583
+
'readonly' => true,
584
+
'option' => 'stylesheet',
585
+
),
586
+
'post_thumbnail' => array(
587
+
'desc' => __( 'Post Thumbnail' ),
588
+
'readonly' => true,
589
+
'value' => current_theme_supports( 'post-thumbnails' ),
590
+
),
591
+
592
+
// Updatable options.
593
+
'time_zone' => array(
594
+
'desc' => __( 'Time Zone' ),
595
+
'readonly' => false,
596
+
'option' => 'gmt_offset',
597
+
),
598
+
'blog_title' => array(
599
+
'desc' => __( 'Site Title' ),
600
+
'readonly' => false,
601
+
'option' => 'blogname',
602
+
),
603
+
'blog_tagline' => array(
604
+
'desc' => __( 'Site Tagline' ),
605
+
'readonly' => false,
606
+
'option' => 'blogdescription',
607
+
),
608
+
'date_format' => array(
609
+
'desc' => __( 'Date Format' ),
610
+
'readonly' => false,
611
+
'option' => 'date_format',
612
+
),
613
+
'time_format' => array(
614
+
'desc' => __( 'Time Format' ),
615
+
'readonly' => false,
616
+
'option' => 'time_format',
617
+
),
618
+
'users_can_register' => array(
619
+
'desc' => __( 'Allow new users to sign up' ),
620
+
'readonly' => false,
621
+
'option' => 'users_can_register',
622
+
),
623
+
'thumbnail_size_w' => array(
624
+
'desc' => __( 'Thumbnail Width' ),
625
+
'readonly' => false,
626
+
'option' => 'thumbnail_size_w',
627
+
),
628
+
'thumbnail_size_h' => array(
629
+
'desc' => __( 'Thumbnail Height' ),
630
+
'readonly' => false,
631
+
'option' => 'thumbnail_size_h',
632
+
),
633
+
'thumbnail_crop' => array(
634
+
'desc' => __( 'Crop thumbnail to exact dimensions' ),
635
+
'readonly' => false,
636
+
'option' => 'thumbnail_crop',
637
+
),
638
+
'medium_size_w' => array(
639
+
'desc' => __( 'Medium size image width' ),
640
+
'readonly' => false,
641
+
'option' => 'medium_size_w',
642
+
),
643
+
'medium_size_h' => array(
644
+
'desc' => __( 'Medium size image height' ),
645
+
'readonly' => false,
646
+
'option' => 'medium_size_h',
647
+
),
648
+
'medium_large_size_w' => array(
649
+
'desc' => __( 'Medium-Large size image width' ),
650
+
'readonly' => false,
651
+
'option' => 'medium_large_size_w',
652
+
),
653
+
'medium_large_size_h' => array(
654
+
'desc' => __( 'Medium-Large size image height' ),
655
+
'readonly' => false,
656
+
'option' => 'medium_large_size_h',
657
+
),
658
+
'large_size_w' => array(
659
+
'desc' => __( 'Large size image width' ),
660
+
'readonly' => false,
661
+
'option' => 'large_size_w',
662
+
),
663
+
'large_size_h' => array(
664
+
'desc' => __( 'Large size image height' ),
665
+
'readonly' => false,
666
+
'option' => 'large_size_h',
667
+
),
668
+
'default_comment_status' => array(
669
+
'desc' => __( 'Allow people to submit comments on new posts.' ),
670
+
'readonly' => false,
671
+
'option' => 'default_comment_status',
672
+
),
673
+
'default_ping_status' => array(
674
+
'desc' => __( 'Allow link notifications from other blogs (pingbacks and trackbacks) on new posts.' ),
675
+
'readonly' => false,
676
+
'option' => 'default_ping_status',
677
+
),
678
+
);
679
+
680
+
/**
681
+
* Filters the XML-RPC blog options property.
682
+
*
683
+
* @since 2.6.0
684
+
*
685
+
* @param array $blog_options An array of XML-RPC blog options.
686
+
*/
687
+
$this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options );
688
+
}
689
+
690
+
/**
691
+
* Retrieves the blogs of the user.
692
+
*
693
+
* @since 2.6.0
694
+
*
695
+
* @param array $args {
696
+
* Method arguments. Note: arguments must be ordered as documented.
697
+
*
698
+
* @type string $0 Username.
699
+
* @type string $1 Password.
700
+
* }
701
+
* @return array|IXR_Error Array contains:
702
+
* - 'isAdmin'
703
+
* - 'isPrimary' - whether the blog is the user's primary blog
704
+
* - 'url'
705
+
* - 'blogid'
706
+
* - 'blogName'
707
+
* - 'xmlrpc' - url of xmlrpc endpoint
708
+
*/
709
+
public function wp_getUsersBlogs( $args ) {
710
+
if ( ! $this->minimum_args( $args, 2 ) ) {
711
+
return $this->error;
712
+
}
713
+
714
+
// If this isn't on WPMU then just use blogger_getUsersBlogs().
715
+
if ( ! is_multisite() ) {
716
+
array_unshift( $args, 1 );
717
+
return $this->blogger_getUsersBlogs( $args );
718
+
}
719
+
720
+
$this->escape( $args );
721
+
722
+
$username = $args[0];
723
+
$password = $args[1];
724
+
725
+
$user = $this->login( $username, $password );
726
+
if ( ! $user ) {
727
+
return $this->error;
728
+
}
729
+
730
+
/**
731
+
* Fires after the XML-RPC user has been authenticated but before the rest of
732
+
* the method logic begins.
733
+
*
734
+
* All built-in XML-RPC methods use the action xmlrpc_call, with a parameter
735
+
* equal to the method's name, e.g., wp.getUsersBlogs, wp.newPost, etc.
736
+
*
737
+
* @since 2.5.0
738
+
* @since 5.7.0 Added the `$args` and `$server` parameters.
739
+
*
740
+
* @param string $name The method name.
741
+
* @param array|string $args The escaped arguments passed to the method.
742
+
* @param wp_xmlrpc_server $server The XML-RPC server instance.
743
+
*/
744
+
do_action( 'xmlrpc_call', 'wp.getUsersBlogs', $args, $this );
745
+
746
+
$blogs = (array) get_blogs_of_user( $user->ID );
747
+
$struct = array();
748
+
749
+
$primary_blog_id = 0;
750
+
$active_blog = get_active_blog_for_user( $user->ID );
751
+
if ( $active_blog ) {
752
+
$primary_blog_id = (int) $active_blog->blog_id;
753
+
}
754
+
755
+
$current_network_id = get_current_network_id();
756
+
757
+
foreach ( $blogs as $blog ) {
758
+
// Don't include blogs that aren't hosted at this site.
759
+
if ( $blog->site_id !== $current_network_id ) {
760
+
continue;
761
+
}
762
+
763
+
$blog_id = $blog->userblog_id;
764
+
765
+
switch_to_blog( $blog_id );
766
+
767
+
$is_admin = current_user_can( 'manage_options' );
768
+
$is_primary = ( (int) $blog_id === $primary_blog_id );
769
+
770
+
$struct[] = array(
771
+
'isAdmin' => $is_admin,
772
+
'isPrimary' => $is_primary,
773
+
'url' => home_url( '/' ),
774
+
'blogid' => (string) $blog_id,
775
+
'blogName' => get_option( 'blogname' ),
776
+
'xmlrpc' => site_url( 'xmlrpc.php', 'rpc' ),
777
+
);
778
+
779
+
restore_current_blog();
780
+
}
781
+
782
+
return $struct;
783
+
}
784
+
785
+
/**
786
+
* Checks if the method received at least the minimum number of arguments.
787
+
*
788
+
* @since 3.4.0
789
+
*
790
+
* @param array $args An array of arguments to check.
791
+
* @param int $count Minimum number of arguments.
792
+
* @return bool True if `$args` contains at least `$count` arguments, false otherwise.
793
+
*/
794
+
protected function minimum_args( $args, $count ) {
795
+
if ( ! is_array( $args ) || count( $args ) < $count ) {
796
+
$this->error = new IXR_Error( 400, __( 'Insufficient arguments passed to this XML-RPC method.' ) );
797
+
return false;
798
+
}
799
+
800
+
return true;
801
+
}
802
+
803
+
/**
804
+
* Prepares taxonomy data for return in an XML-RPC object.
805
+
*
806
+
* @param WP_Taxonomy $taxonomy The unprepared taxonomy data.
807
+
* @param array $fields The subset of taxonomy fields to return.
808
+
* @return array The prepared taxonomy data.
809
+
*/
810
+
protected function _prepare_taxonomy( $taxonomy, $fields ) {
811
+
$_taxonomy = array(
812
+
'name' => $taxonomy->name,
813
+
'label' => $taxonomy->label,
814
+
'hierarchical' => (bool) $taxonomy->hierarchical,
815
+
'public' => (bool) $taxonomy->public,
816
+
'show_ui' => (bool) $taxonomy->show_ui,
817
+
'_builtin' => (bool) $taxonomy->_builtin,
818
+
);
819
+
820
+
if ( in_array( 'labels', $fields, true ) ) {
821
+
$_taxonomy['labels'] = (array) $taxonomy->labels;
822
+
}
823
+
824
+
if ( in_array( 'cap', $fields, true ) ) {
825
+
$_taxonomy['cap'] = (array) $taxonomy->cap;
826
+
}
827
+
828
+
if ( in_array( 'menu', $fields, true ) ) {
829
+
$_taxonomy['show_in_menu'] = (bool) $taxonomy->show_in_menu;
830
+
}
831
+
832
+
if ( in_array( 'object_type', $fields, true ) ) {
833
+
$_taxonomy['object_type'] = array_unique( (array) $taxonomy->object_type );
834
+
}
835
+
836
+
/**
837
+
* Filters XML-RPC-prepared data for the given taxonomy.
838
+
*
839
+
* @since 3.4.0
840
+
*
841
+
* @param array $_taxonomy An array of taxonomy data.
842
+
* @param WP_Taxonomy $taxonomy Taxonomy object.
843
+
* @param array $fields The subset of taxonomy fields to return.
844
+
*/
845
+
return apply_filters( 'xmlrpc_prepare_taxonomy', $_taxonomy, $taxonomy, $fields );
846
+
}
847
+
848
+
/**
849
+
* Prepares term data for return in an XML-RPC object.
850
+
*
851
+
* @param array|object $term The unprepared term data.
852
+
* @return array The prepared term data.
853
+
*/
854
+
protected function _prepare_term( $term ) {
855
+
$_term = $term;
856
+
if ( ! is_array( $_term ) ) {
857
+
$_term = get_object_vars( $_term );
858
+
}
859
+
860
+
// For integers which may be larger than XML-RPC supports ensure we return strings.
861
+
$_term['term_id'] = (string) $_term['term_id'];
862
+
$_term['term_group'] = (string) $_term['term_group'];
863
+
$_term['term_taxonomy_id'] = (string) $_term['term_taxonomy_id'];
864
+
$_term['parent'] = (string) $_term['parent'];
865
+
866
+
// Count we are happy to return as an integer because people really shouldn't use terms that much.
867
+
$_term['count'] = (int) $_term['count'];
868
+
869
+
// Get term meta.
870
+
$_term['custom_fields'] = $this->get_term_custom_fields( $_term['term_id'] );
871
+
872
+
/**
873
+
* Filters XML-RPC-prepared data for the given term.
874
+
*
875
+
* @since 3.4.0
876
+
*
877
+
* @param array $_term An array of term data.
878
+
* @param array|object $term Term object or array.
879
+
*/
880
+
return apply_filters( 'xmlrpc_prepare_term', $_term, $term );
881
+
}
882
+
883
+
/**
884
+
* Converts a WordPress date string to an IXR_Date object.
885
+
*
886
+
* @param string $date Date string to convert.
887
+
* @return IXR_Date IXR_Date object.
888
+
*/
889
+
protected function _convert_date( $date ) {
890
+
if ( '0000-00-00 00:00:00' === $date ) {
891
+
return new IXR_Date( '00000000T00:00:00Z' );
892
+
}
893
+
return new IXR_Date( mysql2date( 'Ymd\TH:i:s', $date, false ) );
894
+
}
895
+
896
+
/**
897
+
* Converts a WordPress GMT date string to an IXR_Date object.
898
+
*
899
+
* @param string $date_gmt WordPress GMT date string.
900
+
* @param string $date Date string.
901
+
* @return IXR_Date IXR_Date object.
902
+
*/
903
+
protected function _convert_date_gmt( $date_gmt, $date ) {
904
+
if ( '0000-00-00 00:00:00' !== $date && '0000-00-00 00:00:00' === $date_gmt ) {
905
+
return new IXR_Date( get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $date, false ), 'Ymd\TH:i:s' ) );
906
+
}
907
+
return $this->_convert_date( $date_gmt );
908
+
}
909
+
910
+
/**
911
+
* Prepares post data for return in an XML-RPC object.
912
+
*
913
+
* @param array $post The unprepared post data.
914
+
* @param array $fields The subset of post type fields to return.
915
+
* @return array The prepared post data.
916
+
*/
917
+
protected function _prepare_post( $post, $fields ) {
918
+
// Holds the data for this post. built up based on $fields.
919
+
$_post = array( 'post_id' => (string) $post['ID'] );
920
+
921
+
// Prepare common post fields.
922
+
$post_fields = array(
923
+
'post_title' => $post['post_title'],
924
+
'post_date' => $this->_convert_date( $post['post_date'] ),
925
+
'post_date_gmt' => $this->_convert_date_gmt( $post['post_date_gmt'], $post['post_date'] ),
926
+
'post_modified' => $this->_convert_date( $post['post_modified'] ),
927
+
'post_modified_gmt' => $this->_convert_date_gmt( $post['post_modified_gmt'], $post['post_modified'] ),
928
+
'post_status' => $post['post_status'],
929
+
'post_type' => $post['post_type'],
930
+
'post_name' => $post['post_name'],
931
+
'post_author' => $post['post_author'],
932
+
'post_password' => $post['post_password'],
933
+
'post_excerpt' => $post['post_excerpt'],
934
+
'post_content' => $post['post_content'],
935
+
'post_parent' => (string) $post['post_parent'],
936
+
'post_mime_type' => $post['post_mime_type'],
937
+
'link' => get_permalink( $post['ID'] ),
938
+
'guid' => $post['guid'],
939
+
'menu_order' => (int) $post['menu_order'],
940
+
'comment_status' => $post['comment_status'],
941
+
'ping_status' => $post['ping_status'],
942
+
'sticky' => ( 'post' === $post['post_type'] && is_sticky( $post['ID'] ) ),
943
+
);
944
+
945
+
// Thumbnail.
946
+
$post_fields['post_thumbnail'] = array();
947
+
$thumbnail_id = get_post_thumbnail_id( $post['ID'] );
948
+
if ( $thumbnail_id ) {
949
+
$thumbnail_size = current_theme_supports( 'post-thumbnail' ) ? 'post-thumbnail' : 'thumbnail';
950
+
$post_fields['post_thumbnail'] = $this->_prepare_media_item( get_post( $thumbnail_id ), $thumbnail_size );
951
+
}
952
+
953
+
// Consider future posts as published.
954
+
if ( 'future' === $post_fields['post_status'] ) {
955
+
$post_fields['post_status'] = 'publish';
956
+
}
957
+
958
+
// Fill in blank post format.
959
+
$post_fields['post_format'] = get_post_format( $post['ID'] );
960
+
if ( empty( $post_fields['post_format'] ) ) {
961
+
$post_fields['post_format'] = 'standard';
962
+
}
963
+
964
+
// Merge requested $post_fields fields into $_post.
965
+
if ( in_array( 'post', $fields, true ) ) {
966
+
$_post = array_merge( $_post, $post_fields );
967
+
} else {
968
+
$requested_fields = array_intersect_key( $post_fields, array_flip( $fields ) );
969
+
$_post = array_merge( $_post, $requested_fields );
970
+
}
971
+
972
+
$all_taxonomy_fields = in_array( 'taxonomies', $fields, true );
973
+
974
+
if ( $all_taxonomy_fields || in_array( 'terms', $fields, true ) ) {
975
+
$post_type_taxonomies = get_object_taxonomies( $post['post_type'], 'names' );
976
+
$terms = wp_get_object_terms( $post['ID'], $post_type_taxonomies );
977
+
$_post['terms'] = array();
978
+
foreach ( $terms as $term ) {
979
+
$_post['terms'][] = $this->_prepare_term( $term );
980
+
}
981
+
}
982
+
983
+
if ( in_array( 'custom_fields', $fields, true ) ) {
984
+
$_post['custom_fields'] = $this->get_custom_fields( $post['ID'] );
985
+
}
986
+
987
+
if ( in_array( 'enclosure', $fields, true ) ) {
988
+
$_post['enclosure'] = array();
989
+
$enclosures = (array) get_post_meta( $post['ID'], 'enclosure' );
990
+
if ( ! empty( $enclosures ) ) {
991
+
$encdata = explode( "\n", $enclosures[0] );
992
+
$_post['enclosure']['url'] = trim( htmlspecialchars( $encdata[0] ) );
993
+
$_post['enclosure']['length'] = (int) trim( $encdata[1] );
994
+
$_post['enclosure']['type'] = trim( $encdata[2] );
995
+
}
996
+
}
997
+
998
+
/**
999
+
* Filters XML-RPC-prepared date for the given post.
1000
+
*
1001
+
* @since 3.4.0
1002
+
*
1003
+
* @param array $_post An array of modified post data.
1004
+
* @param array $post An array of post data.
1005
+
* @param array $fields An array of post fields.
1006
+
*/
1007
+
return apply_filters( 'xmlrpc_prepare_post', $_post, $post, $fields );
1008
+
}
1009
+
1010
+
/**
1011
+
* Prepares post data for return in an XML-RPC object.
1012
+
*
1013
+
* @since 3.4.0
1014
+
* @since 4.6.0 Converted the `$post_type` parameter to accept a WP_Post_Type object.
1015
+
*
1016
+
* @param WP_Post_Type $post_type Post type object.
1017
+
* @param array $fields The subset of post fields to return.
1018
+
* @return array The prepared post type data.
1019
+
*/
1020
+
protected function _prepare_post_type( $post_type, $fields ) {
1021
+
$_post_type = array(
1022
+
'name' => $post_type->name,
1023
+
'label' => $post_type->label,
1024
+
'hierarchical' => (bool) $post_type->hierarchical,
1025
+
'public' => (bool) $post_type->public,
1026
+
'show_ui' => (bool) $post_type->show_ui,
1027
+
'_builtin' => (bool) $post_type->_builtin,
1028
+
'has_archive' => (bool) $post_type->has_archive,
1029
+
'supports' => get_all_post_type_supports( $post_type->name ),
1030
+
);
1031
+
1032
+
if ( in_array( 'labels', $fields, true ) ) {
1033
+
$_post_type['labels'] = (array) $post_type->labels;
1034
+
}
1035
+
1036
+
if ( in_array( 'cap', $fields, true ) ) {
1037
+
$_post_type['cap'] = (array) $post_type->cap;
1038
+
$_post_type['map_meta_cap'] = (bool) $post_type->map_meta_cap;
1039
+
}
1040
+
1041
+
if ( in_array( 'menu', $fields, true ) ) {
1042
+
$_post_type['menu_position'] = (int) $post_type->menu_position;
1043
+
$_post_type['menu_icon'] = $post_type->menu_icon;
1044
+
$_post_type['show_in_menu'] = (bool) $post_type->show_in_menu;
1045
+
}
1046
+
1047
+
if ( in_array( 'taxonomies', $fields, true ) ) {
1048
+
$_post_type['taxonomies'] = get_object_taxonomies( $post_type->name, 'names' );
1049
+
}
1050
+
1051
+
/**
1052
+
* Filters XML-RPC-prepared date for the given post type.
1053
+
*
1054
+
* @since 3.4.0
1055
+
* @since 4.6.0 Converted the `$post_type` parameter to accept a WP_Post_Type object.
1056
+
*
1057
+
* @param array $_post_type An array of post type data.
1058
+
* @param WP_Post_Type $post_type Post type object.
1059
+
*/
1060
+
return apply_filters( 'xmlrpc_prepare_post_type', $_post_type, $post_type );
1061
+
}
1062
+
1063
+
/**
1064
+
* Prepares media item data for return in an XML-RPC object.
1065
+
*
1066
+
* @param WP_Post $media_item The unprepared media item data.
1067
+
* @param string $thumbnail_size The image size to use for the thumbnail URL.
1068
+
* @return array The prepared media item data.
1069
+
*/
1070
+
protected function _prepare_media_item( $media_item, $thumbnail_size = 'thumbnail' ) {
1071
+
$_media_item = array(
1072
+
'attachment_id' => (string) $media_item->ID,
1073
+
'date_created_gmt' => $this->_convert_date_gmt( $media_item->post_date_gmt, $media_item->post_date ),
1074
+
'parent' => $media_item->post_parent,
1075
+
'link' => wp_get_attachment_url( $media_item->ID ),
1076
+
'title' => $media_item->post_title,
1077
+
'caption' => $media_item->post_excerpt,
1078
+
'description' => $media_item->post_content,
1079
+
'metadata' => wp_get_attachment_metadata( $media_item->ID ),
1080
+
'type' => $media_item->post_mime_type,
1081
+
'alt' => get_post_meta( $media_item->ID, '_wp_attachment_image_alt', true ),
1082
+
);
1083
+
1084
+
$thumbnail_src = image_downsize( $media_item->ID, $thumbnail_size );
1085
+
if ( $thumbnail_src ) {
1086
+
$_media_item['thumbnail'] = $thumbnail_src[0];
1087
+
} else {
1088
+
$_media_item['thumbnail'] = $_media_item['link'];
1089
+
}
1090
+
1091
+
/**
1092
+
* Filters XML-RPC-prepared data for the given media item.
1093
+
*
1094
+
* @since 3.4.0
1095
+
*
1096
+
* @param array $_media_item An array of media item data.
1097
+
* @param WP_Post $media_item Media item object.
1098
+
* @param string $thumbnail_size Image size.
1099
+
*/
1100
+
return apply_filters( 'xmlrpc_prepare_media_item', $_media_item, $media_item, $thumbnail_size );
1101
+
}
1102
+
1103
+
/**
1104
+
* Prepares page data for return in an XML-RPC object.
1105
+
*
1106
+
* @param WP_Post $page The unprepared page data.
1107
+
* @return array The prepared page data.
1108
+
*/
1109
+
protected function _prepare_page( $page ) {
1110
+
// Get all of the page content and link.
1111
+
$full_page = get_extended( $page->post_content );
1112
+
$link = get_permalink( $page->ID );
1113
+
1114
+
// Get info the page parent if there is one.
1115
+
$parent_title = '';
1116
+
if ( ! empty( $page->post_parent ) ) {
1117
+
$parent = get_post( $page->post_parent );
1118
+
$parent_title = $parent->post_title;
1119
+
}
1120
+
1121
+
// Determine comment and ping settings.
1122
+
$allow_comments = comments_open( $page->ID ) ? 1 : 0;
1123
+
$allow_pings = pings_open( $page->ID ) ? 1 : 0;
1124
+
1125
+
// Format page date.
1126
+
$page_date = $this->_convert_date( $page->post_date );
1127
+
$page_date_gmt = $this->_convert_date_gmt( $page->post_date_gmt, $page->post_date );
1128
+
1129
+
// Pull the categories info together.
1130
+
$categories = array();
1131
+
if ( is_object_in_taxonomy( 'page', 'category' ) ) {
1132
+
foreach ( wp_get_post_categories( $page->ID ) as $cat_id ) {
1133
+
$categories[] = get_cat_name( $cat_id );
1134
+
}
1135
+
}
1136
+
1137
+
// Get the author info.
1138
+
$author = get_userdata( $page->post_author );
1139
+
1140
+
$page_template = get_page_template_slug( $page->ID );
1141
+
if ( empty( $page_template ) ) {
1142
+
$page_template = 'default';
1143
+
}
1144
+
1145
+
$_page = array(
1146
+
'dateCreated' => $page_date,
1147
+
'userid' => $page->post_author,
1148
+
'page_id' => $page->ID,
1149
+
'page_status' => $page->post_status,
1150
+
'description' => $full_page['main'],
1151
+
'title' => $page->post_title,
1152
+
'link' => $link,
1153
+
'permaLink' => $link,
1154
+
'categories' => $categories,
1155
+
'excerpt' => $page->post_excerpt,
1156
+
'text_more' => $full_page['extended'],
1157
+
'mt_allow_comments' => $allow_comments,
1158
+
'mt_allow_pings' => $allow_pings,
1159
+
'wp_slug' => $page->post_name,
1160
+
'wp_password' => $page->post_password,
1161
+
'wp_author' => $author->display_name,
1162
+
'wp_page_parent_id' => $page->post_parent,
1163
+
'wp_page_parent_title' => $parent_title,
1164
+
'wp_page_order' => $page->menu_order,
1165
+
'wp_author_id' => (string) $author->ID,
1166
+
'wp_author_display_name' => $author->display_name,
1167
+
'date_created_gmt' => $page_date_gmt,
1168
+
'custom_fields' => $this->get_custom_fields( $page->ID ),
1169
+
'wp_page_template' => $page_template,
1170
+
);
1171
+
1172
+
/**
1173
+
* Filters XML-RPC-prepared data for the given page.
1174
+
*
1175
+
* @since 3.4.0
1176
+
*
1177
+
* @param array $_page An array of page data.
1178
+
* @param WP_Post $page Page object.
1179
+
*/
1180
+
return apply_filters( 'xmlrpc_prepare_page', $_page, $page );
1181
+
}
1182
+
1183
+
/**
1184
+
* Prepares comment data for return in an XML-RPC object.
1185
+
*
1186
+
* @param WP_Comment $comment The unprepared comment data.
1187
+
* @return array The prepared comment data.
1188
+
*/
1189
+
protected function _prepare_comment( $comment ) {
1190
+
// Format page date.
1191
+
$comment_date_gmt = $this->_convert_date_gmt( $comment->comment_date_gmt, $comment->comment_date );
1192
+
1193
+
if ( '0' === $comment->comment_approved ) {
1194
+
$comment_status = 'hold';
1195
+
} elseif ( 'spam' === $comment->comment_approved ) {
1196
+
$comment_status = 'spam';
1197
+
} elseif ( '1' === $comment->comment_approved ) {
1198
+
$comment_status = 'approve';
1199
+
} else {
1200
+
$comment_status = $comment->comment_approved;
1201
+
}
1202
+
$_comment = array(
1203
+
'date_created_gmt' => $comment_date_gmt,
1204
+
'user_id' => $comment->user_id,
1205
+
'comment_id' => $comment->comment_ID,
1206
+
'parent' => $comment->comment_parent,
1207
+
'status' => $comment_status,
1208
+
'content' => $comment->comment_content,
1209
+
'link' => get_comment_link( $comment ),
1210
+
'post_id' => $comment->comment_post_ID,
1211
+
'post_title' => get_the_title( $comment->comment_post_ID ),
1212
+
'author' => $comment->comment_author,
1213
+
'author_url' => $comment->comment_author_url,
1214
+
'author_email' => $comment->comment_author_email,
1215
+
'author_ip' => $comment->comment_author_IP,
1216
+
'type' => $comment->comment_type,
1217
+
);
1218
+
1219
+
/**
1220
+
* Filters XML-RPC-prepared data for the given comment.
1221
+
*
1222
+
* @since 3.4.0
1223
+
*
1224
+
* @param array $_comment An array of prepared comment data.
1225
+
* @param WP_Comment $comment Comment object.
1226
+
*/
1227
+
return apply_filters( 'xmlrpc_prepare_comment', $_comment, $comment );
1228
+
}
1229
+
1230
+
/**
1231
+
* Prepares user data for return in an XML-RPC object.
1232
+
*
1233
+
* @param WP_User $user The unprepared user object.
1234
+
* @param array $fields The subset of user fields to return.
1235
+
* @return array The prepared user data.
1236
+
*/
1237
+
protected function _prepare_user( $user, $fields ) {
1238
+
$_user = array( 'user_id' => (string) $user->ID );
1239
+
1240
+
$user_fields = array(
1241
+
'username' => $user->user_login,
1242
+
'first_name' => $user->user_firstname,
1243
+
'last_name' => $user->user_lastname,
1244
+
'registered' => $this->_convert_date( $user->user_registered ),
1245
+
'bio' => $user->user_description,
1246
+
'email' => $user->user_email,
1247
+
'nickname' => $user->nickname,
1248
+
'nicename' => $user->user_nicename,
1249
+
'url' => $user->user_url,
1250
+
'display_name' => $user->display_name,
1251
+
'roles' => $user->roles,
1252
+
);
1253
+
1254
+
if ( in_array( 'all', $fields, true ) ) {
1255
+
$_user = array_merge( $_user, $user_fields );
1256
+
} else {
1257
+
if ( in_array( 'basic', $fields, true ) ) {
1258
+
$basic_fields = array( 'username', 'email', 'registered', 'display_name', 'nicename' );
1259
+
$fields = array_merge( $fields, $basic_fields );
1260
+
}
1261
+
$requested_fields = array_intersect_key( $user_fields, array_flip( $fields ) );
1262
+
$_user = array_merge( $_user, $requested_fields );
1263
+
}
1264
+
1265
+
/**
1266
+
* Filters XML-RPC-prepared data for the given user.
1267
+
*
1268
+
* @since 3.5.0
1269
+
*
1270
+
* @param array $_user An array of user data.
1271
+
* @param WP_User $user User object.
1272
+
* @param array $fields An array of user fields.
1273
+
*/
1274
+
return apply_filters( 'xmlrpc_prepare_user', $_user, $user, $fields );
1275
+
}
1276
+
1277
+
/**
1278
+
* Creates a new post for any registered post type.
1279
+
*
1280
+
* @since 3.4.0
1281
+
*
1282
+
* @link https://en.wikipedia.org/wiki/RSS_enclosure for information on RSS enclosures.
1283
+
*
1284
+
* @param array $args {
1285
+
* Method arguments. Note: top-level arguments must be ordered as documented.
1286
+
*
1287
+
* @type int $0 Blog ID (unused).
1288
+
* @type string $1 Username.
1289
+
* @type string $2 Password.
1290
+
* @type array $3 {
1291
+
* Content struct for adding a new post. See wp_insert_post() for information on
1292
+
* additional post fields
1293
+
*
1294
+
* @type string $post_type Post type. Default 'post'.
1295
+
* @type string $post_status Post status. Default 'draft'
1296
+
* @type string $post_title Post title.
1297
+
* @type int $post_author Post author ID.
1298
+
* @type string $post_excerpt Post excerpt.
1299
+
* @type string $post_content Post content.
1300
+
* @type string $post_date_gmt Post date in GMT.
1301
+
* @type string $post_date Post date.
1302
+
* @type string $post_password Post password (20-character limit).
1303
+
* @type string $comment_status Post comment enabled status. Accepts 'open' or 'closed'.
1304
+
* @type string $ping_status Post ping status. Accepts 'open' or 'closed'.
1305
+
* @type bool $sticky Whether the post should be sticky. Automatically false if
1306
+
* `$post_status` is 'private'.
1307
+
* @type int $post_thumbnail ID of an image to use as the post thumbnail/featured image.
1308
+
* @type array $custom_fields Array of meta key/value pairs to add to the post.
1309
+
* @type array $terms Associative array with taxonomy names as keys and arrays
1310
+
* of term IDs as values.
1311
+
* @type array $terms_names Associative array with taxonomy names as keys and arrays
1312
+
* of term names as values.
1313
+
* @type array $enclosure {
1314
+
* Array of feed enclosure data to add to post meta.
1315
+
*
1316
+
* @type string $url URL for the feed enclosure.
1317
+
* @type int $length Size in bytes of the enclosure.
1318
+
* @type string $type Mime-type for the enclosure.
1319
+
* }
1320
+
* }
1321
+
* }
1322
+
* @return int|IXR_Error Post ID on success, IXR_Error instance otherwise.
1323
+
*/
1324
+
public function wp_newPost( $args ) {
1325
+
if ( ! $this->minimum_args( $args, 4 ) ) {
1326
+
return $this->error;
1327
+
}
1328
+
1329
+
$this->escape( $args );
1330
+
1331
+
$username = $args[1];
1332
+
$password = $args[2];
1333
+
$content_struct = $args[3];
1334
+
1335
+
$user = $this->login( $username, $password );
1336
+
if ( ! $user ) {
1337
+
return $this->error;
1338
+
}
1339
+
1340
+
// Convert the date field back to IXR form.
1341
+
if ( isset( $content_struct['post_date'] ) && ! ( $content_struct['post_date'] instanceof IXR_Date ) ) {
1342
+
$content_struct['post_date'] = $this->_convert_date( $content_struct['post_date'] );
1343
+
}
1344
+
1345
+
/*
1346
+
* Ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct,
1347
+
* since _insert_post() will ignore the non-GMT date if the GMT date is set.
1348
+
*/
1349
+
if ( isset( $content_struct['post_date_gmt'] ) && ! ( $content_struct['post_date_gmt'] instanceof IXR_Date ) ) {
1350
+
if ( '0000-00-00 00:00:00' === $content_struct['post_date_gmt'] || isset( $content_struct['post_date'] ) ) {
1351
+
unset( $content_struct['post_date_gmt'] );
1352
+
} else {
1353
+
$content_struct['post_date_gmt'] = $this->_convert_date( $content_struct['post_date_gmt'] );
1354
+
}
1355
+
}
1356
+
1357
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
1358
+
do_action( 'xmlrpc_call', 'wp.newPost', $args, $this );
1359
+
1360
+
unset( $content_struct['ID'] );
1361
+
1362
+
return $this->_insert_post( $user, $content_struct );
1363
+
}
1364
+
1365
+
/**
1366
+
* Helper method for filtering out elements from an array.
1367
+
*
1368
+
* @since 3.4.0
1369
+
*
1370
+
* @param int $count Number to compare to one.
1371
+
* @return bool True if the number is greater than one, false otherwise.
1372
+
*/
1373
+
private function _is_greater_than_one( $count ) {
1374
+
return $count > 1;
1375
+
}
1376
+
1377
+
/**
1378
+
* Encapsulates the logic for sticking a post and determining if
1379
+
* the user has permission to do so.
1380
+
*
1381
+
* @since 4.3.0
1382
+
*
1383
+
* @param array $post_data
1384
+
* @param bool $update
1385
+
* @return void|IXR_Error
1386
+
*/
1387
+
private function _toggle_sticky( $post_data, $update = false ) {
1388
+
$post_type = get_post_type_object( $post_data['post_type'] );
1389
+
1390
+
// Private and password-protected posts cannot be stickied.
1391
+
if ( 'private' === $post_data['post_status'] || ! empty( $post_data['post_password'] ) ) {
1392
+
// Error if the client tried to stick the post, otherwise, silently unstick.
1393
+
if ( ! empty( $post_data['sticky'] ) ) {
1394
+
return new IXR_Error( 401, __( 'Sorry, you cannot stick a private post.' ) );
1395
+
}
1396
+
1397
+
if ( $update ) {
1398
+
unstick_post( $post_data['ID'] );
1399
+
}
1400
+
} elseif ( isset( $post_data['sticky'] ) ) {
1401
+
if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) {
1402
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to make posts sticky.' ) );
1403
+
}
1404
+
1405
+
$sticky = wp_validate_boolean( $post_data['sticky'] );
1406
+
if ( $sticky ) {
1407
+
stick_post( $post_data['ID'] );
1408
+
} else {
1409
+
unstick_post( $post_data['ID'] );
1410
+
}
1411
+
}
1412
+
}
1413
+
1414
+
/**
1415
+
* Helper method for wp_newPost() and wp_editPost(), containing shared logic.
1416
+
*
1417
+
* @since 3.4.0
1418
+
*
1419
+
* @see wp_insert_post()
1420
+
*
1421
+
* @param WP_User $user The post author if post_author isn't set in $content_struct.
1422
+
* @param array|IXR_Error $content_struct Post data to insert.
1423
+
* @return IXR_Error|string
1424
+
*/
1425
+
protected function _insert_post( $user, $content_struct ) {
1426
+
$defaults = array(
1427
+
'post_status' => 'draft',
1428
+
'post_type' => 'post',
1429
+
'post_author' => 0,
1430
+
'post_password' => '',
1431
+
'post_excerpt' => '',
1432
+
'post_content' => '',
1433
+
'post_title' => '',
1434
+
'post_date' => '',
1435
+
'post_date_gmt' => '',
1436
+
'post_format' => null,
1437
+
'post_name' => null,
1438
+
'post_thumbnail' => null,
1439
+
'post_parent' => 0,
1440
+
'ping_status' => '',
1441
+
'comment_status' => '',
1442
+
'custom_fields' => null,
1443
+
'terms_names' => null,
1444
+
'terms' => null,
1445
+
'sticky' => null,
1446
+
'enclosure' => null,
1447
+
'ID' => null,
1448
+
);
1449
+
1450
+
$post_data = wp_parse_args( array_intersect_key( $content_struct, $defaults ), $defaults );
1451
+
1452
+
$post_type = get_post_type_object( $post_data['post_type'] );
1453
+
if ( ! $post_type ) {
1454
+
return new IXR_Error( 403, __( 'Invalid post type.' ) );
1455
+
}
1456
+
1457
+
$update = ! empty( $post_data['ID'] );
1458
+
1459
+
if ( $update ) {
1460
+
if ( ! get_post( $post_data['ID'] ) ) {
1461
+
return new IXR_Error( 401, __( 'Invalid post ID.' ) );
1462
+
}
1463
+
if ( ! current_user_can( 'edit_post', $post_data['ID'] ) ) {
1464
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
1465
+
}
1466
+
if ( get_post_type( $post_data['ID'] ) !== $post_data['post_type'] ) {
1467
+
return new IXR_Error( 401, __( 'The post type may not be changed.' ) );
1468
+
}
1469
+
} else {
1470
+
if ( ! current_user_can( $post_type->cap->create_posts ) || ! current_user_can( $post_type->cap->edit_posts ) ) {
1471
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to post on this site.' ) );
1472
+
}
1473
+
}
1474
+
1475
+
switch ( $post_data['post_status'] ) {
1476
+
case 'draft':
1477
+
case 'pending':
1478
+
break;
1479
+
case 'private':
1480
+
if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
1481
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to create private posts in this post type.' ) );
1482
+
}
1483
+
break;
1484
+
case 'publish':
1485
+
case 'future':
1486
+
if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
1487
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts in this post type.' ) );
1488
+
}
1489
+
break;
1490
+
default:
1491
+
if ( ! get_post_status_object( $post_data['post_status'] ) ) {
1492
+
$post_data['post_status'] = 'draft';
1493
+
}
1494
+
break;
1495
+
}
1496
+
1497
+
if ( ! empty( $post_data['post_password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
1498
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to create password protected posts in this post type.' ) );
1499
+
}
1500
+
1501
+
$post_data['post_author'] = absint( $post_data['post_author'] );
1502
+
if ( ! empty( $post_data['post_author'] ) && $post_data['post_author'] !== $user->ID ) {
1503
+
if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) {
1504
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to create posts as this user.' ) );
1505
+
}
1506
+
1507
+
$author = get_userdata( $post_data['post_author'] );
1508
+
1509
+
if ( ! $author ) {
1510
+
return new IXR_Error( 404, __( 'Invalid author ID.' ) );
1511
+
}
1512
+
} else {
1513
+
$post_data['post_author'] = $user->ID;
1514
+
}
1515
+
1516
+
if ( 'open' !== $post_data['comment_status'] && 'closed' !== $post_data['comment_status'] ) {
1517
+
unset( $post_data['comment_status'] );
1518
+
}
1519
+
1520
+
if ( 'open' !== $post_data['ping_status'] && 'closed' !== $post_data['ping_status'] ) {
1521
+
unset( $post_data['ping_status'] );
1522
+
}
1523
+
1524
+
// Do some timestamp voodoo.
1525
+
if ( ! empty( $post_data['post_date_gmt'] ) ) {
1526
+
// We know this is supposed to be GMT, so we're going to slap that Z on there by force.
1527
+
$date_created = rtrim( $post_data['post_date_gmt']->getIso(), 'Z' ) . 'Z';
1528
+
} elseif ( ! empty( $post_data['post_date'] ) ) {
1529
+
$date_created = $post_data['post_date']->getIso();
1530
+
}
1531
+
1532
+
// Default to not flagging the post date to be edited unless it's intentional.
1533
+
$post_data['edit_date'] = false;
1534
+
1535
+
if ( ! empty( $date_created ) ) {
1536
+
$post_data['post_date'] = iso8601_to_datetime( $date_created );
1537
+
$post_data['post_date_gmt'] = iso8601_to_datetime( $date_created, 'gmt' );
1538
+
1539
+
// Flag the post date to be edited.
1540
+
$post_data['edit_date'] = true;
1541
+
}
1542
+
1543
+
if ( ! isset( $post_data['ID'] ) ) {
1544
+
$post_data['ID'] = get_default_post_to_edit( $post_data['post_type'], true )->ID;
1545
+
}
1546
+
$post_id = $post_data['ID'];
1547
+
1548
+
if ( 'post' === $post_data['post_type'] ) {
1549
+
$error = $this->_toggle_sticky( $post_data, $update );
1550
+
if ( $error ) {
1551
+
return $error;
1552
+
}
1553
+
}
1554
+
1555
+
if ( isset( $post_data['post_thumbnail'] ) ) {
1556
+
// Empty value deletes, non-empty value adds/updates.
1557
+
if ( ! $post_data['post_thumbnail'] ) {
1558
+
delete_post_thumbnail( $post_id );
1559
+
} elseif ( ! get_post( absint( $post_data['post_thumbnail'] ) ) ) {
1560
+
return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
1561
+
}
1562
+
set_post_thumbnail( $post_id, $post_data['post_thumbnail'] );
1563
+
unset( $content_struct['post_thumbnail'] );
1564
+
}
1565
+
1566
+
if ( isset( $post_data['custom_fields'] ) ) {
1567
+
$this->set_custom_fields( $post_id, $post_data['custom_fields'] );
1568
+
}
1569
+
1570
+
if ( isset( $post_data['terms'] ) || isset( $post_data['terms_names'] ) ) {
1571
+
$post_type_taxonomies = get_object_taxonomies( $post_data['post_type'], 'objects' );
1572
+
1573
+
// Accumulate term IDs from terms and terms_names.
1574
+
$terms = array();
1575
+
1576
+
// First validate the terms specified by ID.
1577
+
if ( isset( $post_data['terms'] ) && is_array( $post_data['terms'] ) ) {
1578
+
$taxonomies = array_keys( $post_data['terms'] );
1579
+
1580
+
// Validating term IDs.
1581
+
foreach ( $taxonomies as $taxonomy ) {
1582
+
if ( ! array_key_exists( $taxonomy, $post_type_taxonomies ) ) {
1583
+
return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) );
1584
+
}
1585
+
1586
+
if ( ! current_user_can( $post_type_taxonomies[ $taxonomy ]->cap->assign_terms ) ) {
1587
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) );
1588
+
}
1589
+
1590
+
$term_ids = $post_data['terms'][ $taxonomy ];
1591
+
$terms[ $taxonomy ] = array();
1592
+
foreach ( $term_ids as $term_id ) {
1593
+
$term = get_term_by( 'id', $term_id, $taxonomy );
1594
+
1595
+
if ( ! $term ) {
1596
+
return new IXR_Error( 403, __( 'Invalid term ID.' ) );
1597
+
}
1598
+
1599
+
$terms[ $taxonomy ][] = (int) $term_id;
1600
+
}
1601
+
}
1602
+
}
1603
+
1604
+
// Now validate terms specified by name.
1605
+
if ( isset( $post_data['terms_names'] ) && is_array( $post_data['terms_names'] ) ) {
1606
+
$taxonomies = array_keys( $post_data['terms_names'] );
1607
+
1608
+
foreach ( $taxonomies as $taxonomy ) {
1609
+
if ( ! array_key_exists( $taxonomy, $post_type_taxonomies ) ) {
1610
+
return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) );
1611
+
}
1612
+
1613
+
if ( ! current_user_can( $post_type_taxonomies[ $taxonomy ]->cap->assign_terms ) ) {
1614
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) );
1615
+
}
1616
+
1617
+
/*
1618
+
* For hierarchical taxonomies, we can't assign a term when multiple terms
1619
+
* in the hierarchy share the same name.
1620
+
*/
1621
+
$ambiguous_terms = array();
1622
+
if ( is_taxonomy_hierarchical( $taxonomy ) ) {
1623
+
$tax_term_names = get_terms(
1624
+
array(
1625
+
'taxonomy' => $taxonomy,
1626
+
'fields' => 'names',
1627
+
'hide_empty' => false,
1628
+
)
1629
+
);
1630
+
1631
+
// Count the number of terms with the same name.
1632
+
$tax_term_names_count = array_count_values( $tax_term_names );
1633
+
1634
+
// Filter out non-ambiguous term names.
1635
+
$ambiguous_tax_term_counts = array_filter( $tax_term_names_count, array( $this, '_is_greater_than_one' ) );
1636
+
1637
+
$ambiguous_terms = array_keys( $ambiguous_tax_term_counts );
1638
+
}
1639
+
1640
+
$term_names = $post_data['terms_names'][ $taxonomy ];
1641
+
foreach ( $term_names as $term_name ) {
1642
+
if ( in_array( $term_name, $ambiguous_terms, true ) ) {
1643
+
return new IXR_Error( 401, __( 'Ambiguous term name used in a hierarchical taxonomy. Please use term ID instead.' ) );
1644
+
}
1645
+
1646
+
$term = get_term_by( 'name', $term_name, $taxonomy );
1647
+
1648
+
if ( ! $term ) {
1649
+
// Term doesn't exist, so check that the user is allowed to create new terms.
1650
+
if ( ! current_user_can( $post_type_taxonomies[ $taxonomy ]->cap->edit_terms ) ) {
1651
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to add a term to one of the given taxonomies.' ) );
1652
+
}
1653
+
1654
+
// Create the new term.
1655
+
$term_info = wp_insert_term( $term_name, $taxonomy );
1656
+
if ( is_wp_error( $term_info ) ) {
1657
+
return new IXR_Error( 500, $term_info->get_error_message() );
1658
+
}
1659
+
1660
+
$terms[ $taxonomy ][] = (int) $term_info['term_id'];
1661
+
} else {
1662
+
$terms[ $taxonomy ][] = (int) $term->term_id;
1663
+
}
1664
+
}
1665
+
}
1666
+
}
1667
+
1668
+
$post_data['tax_input'] = $terms;
1669
+
unset( $post_data['terms'], $post_data['terms_names'] );
1670
+
}
1671
+
1672
+
if ( isset( $post_data['post_format'] ) ) {
1673
+
$format = set_post_format( $post_id, $post_data['post_format'] );
1674
+
1675
+
if ( is_wp_error( $format ) ) {
1676
+
return new IXR_Error( 500, $format->get_error_message() );
1677
+
}
1678
+
1679
+
unset( $post_data['post_format'] );
1680
+
}
1681
+
1682
+
// Handle enclosures.
1683
+
$enclosure = isset( $post_data['enclosure'] ) ? $post_data['enclosure'] : null;
1684
+
$this->add_enclosure_if_new( $post_id, $enclosure );
1685
+
1686
+
$this->attach_uploads( $post_id, $post_data['post_content'] );
1687
+
1688
+
/**
1689
+
* Filters post data array to be inserted via XML-RPC.
1690
+
*
1691
+
* @since 3.4.0
1692
+
*
1693
+
* @param array $post_data Parsed array of post data.
1694
+
* @param array $content_struct Post data array.
1695
+
*/
1696
+
$post_data = apply_filters( 'xmlrpc_wp_insert_post_data', $post_data, $content_struct );
1697
+
1698
+
// Remove all null values to allow for using the insert/update post default values for those keys instead.
1699
+
$post_data = array_filter(
1700
+
$post_data,
1701
+
static function ( $value ) {
1702
+
return null !== $value;
1703
+
}
1704
+
);
1705
+
1706
+
$post_id = $update ? wp_update_post( $post_data, true ) : wp_insert_post( $post_data, true );
1707
+
if ( is_wp_error( $post_id ) ) {
1708
+
return new IXR_Error( 500, $post_id->get_error_message() );
1709
+
}
1710
+
1711
+
if ( ! $post_id ) {
1712
+
if ( $update ) {
1713
+
return new IXR_Error( 401, __( 'Sorry, the post could not be updated.' ) );
1714
+
} else {
1715
+
return new IXR_Error( 401, __( 'Sorry, the post could not be created.' ) );
1716
+
}
1717
+
}
1718
+
1719
+
return (string) $post_id;
1720
+
}
1721
+
1722
+
/**
1723
+
* Edits a post for any registered post type.
1724
+
*
1725
+
* The $content_struct parameter only needs to contain fields that
1726
+
* should be changed. All other fields will retain their existing values.
1727
+
*
1728
+
* @since 3.4.0
1729
+
*
1730
+
* @param array $args {
1731
+
* Method arguments. Note: arguments must be ordered as documented.
1732
+
*
1733
+
* @type int $0 Blog ID (unused).
1734
+
* @type string $1 Username.
1735
+
* @type string $2 Password.
1736
+
* @type int $3 Post ID.
1737
+
* @type array $4 Extra content arguments.
1738
+
* }
1739
+
* @return true|IXR_Error True on success, IXR_Error on failure.
1740
+
*/
1741
+
public function wp_editPost( $args ) {
1742
+
if ( ! $this->minimum_args( $args, 5 ) ) {
1743
+
return $this->error;
1744
+
}
1745
+
1746
+
$this->escape( $args );
1747
+
1748
+
$username = $args[1];
1749
+
$password = $args[2];
1750
+
$post_id = (int) $args[3];
1751
+
$content_struct = $args[4];
1752
+
1753
+
$user = $this->login( $username, $password );
1754
+
if ( ! $user ) {
1755
+
return $this->error;
1756
+
}
1757
+
1758
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
1759
+
do_action( 'xmlrpc_call', 'wp.editPost', $args, $this );
1760
+
1761
+
$post = get_post( $post_id, ARRAY_A );
1762
+
1763
+
if ( empty( $post['ID'] ) ) {
1764
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
1765
+
}
1766
+
1767
+
if ( isset( $content_struct['if_not_modified_since'] ) ) {
1768
+
// If the post has been modified since the date provided, return an error.
1769
+
if ( mysql2date( 'U', $post['post_modified_gmt'] ) > $content_struct['if_not_modified_since']->getTimestamp() ) {
1770
+
return new IXR_Error( 409, __( 'There is a revision of this post that is more recent.' ) );
1771
+
}
1772
+
}
1773
+
1774
+
// Convert the date field back to IXR form.
1775
+
$post['post_date'] = $this->_convert_date( $post['post_date'] );
1776
+
1777
+
/*
1778
+
* Ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct,
1779
+
* since _insert_post() will ignore the non-GMT date if the GMT date is set.
1780
+
*/
1781
+
if ( '0000-00-00 00:00:00' === $post['post_date_gmt'] || isset( $content_struct['post_date'] ) ) {
1782
+
unset( $post['post_date_gmt'] );
1783
+
} else {
1784
+
$post['post_date_gmt'] = $this->_convert_date( $post['post_date_gmt'] );
1785
+
}
1786
+
1787
+
/*
1788
+
* If the API client did not provide 'post_date', then we must not perpetuate the value that
1789
+
* was stored in the database, or it will appear to be an intentional edit. Conveying it here
1790
+
* as if it was coming from the API client will cause an otherwise zeroed out 'post_date_gmt'
1791
+
* to get set with the value that was originally stored in the database when the draft was created.
1792
+
*/
1793
+
if ( ! isset( $content_struct['post_date'] ) ) {
1794
+
unset( $post['post_date'] );
1795
+
}
1796
+
1797
+
$this->escape( $post );
1798
+
$merged_content_struct = array_merge( $post, $content_struct );
1799
+
1800
+
$retval = $this->_insert_post( $user, $merged_content_struct );
1801
+
if ( $retval instanceof IXR_Error ) {
1802
+
return $retval;
1803
+
}
1804
+
1805
+
return true;
1806
+
}
1807
+
1808
+
/**
1809
+
* Deletes a post for any registered post type.
1810
+
*
1811
+
* @since 3.4.0
1812
+
*
1813
+
* @see wp_delete_post()
1814
+
*
1815
+
* @param array $args {
1816
+
* Method arguments. Note: arguments must be ordered as documented.
1817
+
*
1818
+
* @type int $0 Blog ID (unused).
1819
+
* @type string $1 Username.
1820
+
* @type string $2 Password.
1821
+
* @type int $3 Post ID.
1822
+
* }
1823
+
* @return true|IXR_Error True on success, IXR_Error instance on failure.
1824
+
*/
1825
+
public function wp_deletePost( $args ) {
1826
+
if ( ! $this->minimum_args( $args, 4 ) ) {
1827
+
return $this->error;
1828
+
}
1829
+
1830
+
$this->escape( $args );
1831
+
1832
+
$username = $args[1];
1833
+
$password = $args[2];
1834
+
$post_id = (int) $args[3];
1835
+
1836
+
$user = $this->login( $username, $password );
1837
+
if ( ! $user ) {
1838
+
return $this->error;
1839
+
}
1840
+
1841
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
1842
+
do_action( 'xmlrpc_call', 'wp.deletePost', $args, $this );
1843
+
1844
+
$post = get_post( $post_id, ARRAY_A );
1845
+
if ( empty( $post['ID'] ) ) {
1846
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
1847
+
}
1848
+
1849
+
if ( ! current_user_can( 'delete_post', $post_id ) ) {
1850
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this post.' ) );
1851
+
}
1852
+
1853
+
$result = wp_delete_post( $post_id );
1854
+
1855
+
if ( ! $result ) {
1856
+
return new IXR_Error( 500, __( 'Sorry, the post could not be deleted.' ) );
1857
+
}
1858
+
1859
+
return true;
1860
+
}
1861
+
1862
+
/**
1863
+
* Retrieves a post.
1864
+
*
1865
+
* @since 3.4.0
1866
+
*
1867
+
* The optional $fields parameter specifies what fields will be included
1868
+
* in the response array. This should be a list of field names. 'post_id' will
1869
+
* always be included in the response regardless of the value of $fields.
1870
+
*
1871
+
* Instead of, or in addition to, individual field names, conceptual group
1872
+
* names can be used to specify multiple fields. The available conceptual
1873
+
* groups are 'post' (all basic fields), 'taxonomies', 'custom_fields',
1874
+
* and 'enclosure'.
1875
+
*
1876
+
* @see get_post()
1877
+
*
1878
+
* @param array $args {
1879
+
* Method arguments. Note: arguments must be ordered as documented.
1880
+
*
1881
+
* @type int $0 Blog ID (unused).
1882
+
* @type string $1 Username.
1883
+
* @type string $2 Password.
1884
+
* @type int $3 Post ID.
1885
+
* @type array $4 Optional. The subset of post type fields to return.
1886
+
* }
1887
+
* @return array|IXR_Error Array contains (based on $fields parameter):
1888
+
* - 'post_id'
1889
+
* - 'post_title'
1890
+
* - 'post_date'
1891
+
* - 'post_date_gmt'
1892
+
* - 'post_modified'
1893
+
* - 'post_modified_gmt'
1894
+
* - 'post_status'
1895
+
* - 'post_type'
1896
+
* - 'post_name'
1897
+
* - 'post_author'
1898
+
* - 'post_password'
1899
+
* - 'post_excerpt'
1900
+
* - 'post_content'
1901
+
* - 'link'
1902
+
* - 'comment_status'
1903
+
* - 'ping_status'
1904
+
* - 'sticky'
1905
+
* - 'custom_fields'
1906
+
* - 'terms'
1907
+
* - 'categories'
1908
+
* - 'tags'
1909
+
* - 'enclosure'
1910
+
*/
1911
+
public function wp_getPost( $args ) {
1912
+
if ( ! $this->minimum_args( $args, 4 ) ) {
1913
+
return $this->error;
1914
+
}
1915
+
1916
+
$this->escape( $args );
1917
+
1918
+
$username = $args[1];
1919
+
$password = $args[2];
1920
+
$post_id = (int) $args[3];
1921
+
1922
+
if ( isset( $args[4] ) ) {
1923
+
$fields = $args[4];
1924
+
} else {
1925
+
/**
1926
+
* Filters the default post query fields used by the given XML-RPC method.
1927
+
*
1928
+
* @since 3.4.0
1929
+
*
1930
+
* @param array $fields An array of post fields to retrieve. By default,
1931
+
* contains 'post', 'terms', and 'custom_fields'.
1932
+
* @param string $method Method name.
1933
+
*/
1934
+
$fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPost' );
1935
+
}
1936
+
1937
+
$user = $this->login( $username, $password );
1938
+
if ( ! $user ) {
1939
+
return $this->error;
1940
+
}
1941
+
1942
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
1943
+
do_action( 'xmlrpc_call', 'wp.getPost', $args, $this );
1944
+
1945
+
$post = get_post( $post_id, ARRAY_A );
1946
+
1947
+
if ( empty( $post['ID'] ) ) {
1948
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
1949
+
}
1950
+
1951
+
if ( ! current_user_can( 'edit_post', $post_id ) ) {
1952
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
1953
+
}
1954
+
1955
+
return $this->_prepare_post( $post, $fields );
1956
+
}
1957
+
1958
+
/**
1959
+
* Retrieves posts.
1960
+
*
1961
+
* @since 3.4.0
1962
+
*
1963
+
* @see wp_get_recent_posts()
1964
+
* @see wp_getPost() for more on `$fields`
1965
+
* @see get_posts() for more on `$filter` values
1966
+
*
1967
+
* @param array $args {
1968
+
* Method arguments. Note: arguments must be ordered as documented.
1969
+
*
1970
+
* @type int $0 Blog ID (unused).
1971
+
* @type string $1 Username.
1972
+
* @type string $2 Password.
1973
+
* @type array $3 Optional. Modifies the query used to retrieve posts. Accepts 'post_type',
1974
+
* 'post_status', 'number', 'offset', 'orderby', 's', and 'order'.
1975
+
* Default empty array.
1976
+
* @type array $4 Optional. The subset of post type fields to return in the response array.
1977
+
* }
1978
+
* @return array|IXR_Error Array containing a collection of posts.
1979
+
*/
1980
+
public function wp_getPosts( $args ) {
1981
+
if ( ! $this->minimum_args( $args, 3 ) ) {
1982
+
return $this->error;
1983
+
}
1984
+
1985
+
$this->escape( $args );
1986
+
1987
+
$username = $args[1];
1988
+
$password = $args[2];
1989
+
$filter = isset( $args[3] ) ? $args[3] : array();
1990
+
1991
+
if ( isset( $args[4] ) ) {
1992
+
$fields = $args[4];
1993
+
} else {
1994
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
1995
+
$fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPosts' );
1996
+
}
1997
+
1998
+
$user = $this->login( $username, $password );
1999
+
if ( ! $user ) {
2000
+
return $this->error;
2001
+
}
2002
+
2003
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2004
+
do_action( 'xmlrpc_call', 'wp.getPosts', $args, $this );
2005
+
2006
+
$query = array();
2007
+
2008
+
if ( isset( $filter['post_type'] ) ) {
2009
+
$post_type = get_post_type_object( $filter['post_type'] );
2010
+
if ( ! ( (bool) $post_type ) ) {
2011
+
return new IXR_Error( 403, __( 'Invalid post type.' ) );
2012
+
}
2013
+
} else {
2014
+
$post_type = get_post_type_object( 'post' );
2015
+
}
2016
+
2017
+
if ( ! current_user_can( $post_type->cap->edit_posts ) ) {
2018
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts in this post type.' ) );
2019
+
}
2020
+
2021
+
$query['post_type'] = $post_type->name;
2022
+
2023
+
if ( isset( $filter['post_status'] ) ) {
2024
+
$query['post_status'] = $filter['post_status'];
2025
+
}
2026
+
2027
+
if ( isset( $filter['number'] ) ) {
2028
+
$query['numberposts'] = absint( $filter['number'] );
2029
+
}
2030
+
2031
+
if ( isset( $filter['offset'] ) ) {
2032
+
$query['offset'] = absint( $filter['offset'] );
2033
+
}
2034
+
2035
+
if ( isset( $filter['orderby'] ) ) {
2036
+
$query['orderby'] = $filter['orderby'];
2037
+
2038
+
if ( isset( $filter['order'] ) ) {
2039
+
$query['order'] = $filter['order'];
2040
+
}
2041
+
}
2042
+
2043
+
if ( isset( $filter['s'] ) ) {
2044
+
$query['s'] = $filter['s'];
2045
+
}
2046
+
2047
+
$posts_list = wp_get_recent_posts( $query );
2048
+
2049
+
if ( ! $posts_list ) {
2050
+
return array();
2051
+
}
2052
+
2053
+
// Holds all the posts data.
2054
+
$struct = array();
2055
+
2056
+
foreach ( $posts_list as $post ) {
2057
+
if ( ! current_user_can( 'edit_post', $post['ID'] ) ) {
2058
+
continue;
2059
+
}
2060
+
2061
+
$struct[] = $this->_prepare_post( $post, $fields );
2062
+
}
2063
+
2064
+
return $struct;
2065
+
}
2066
+
2067
+
/**
2068
+
* Creates a new term.
2069
+
*
2070
+
* @since 3.4.0
2071
+
*
2072
+
* @see wp_insert_term()
2073
+
*
2074
+
* @param array $args {
2075
+
* Method arguments. Note: arguments must be ordered as documented.
2076
+
*
2077
+
* @type int $0 Blog ID (unused).
2078
+
* @type string $1 Username.
2079
+
* @type string $2 Password.
2080
+
* @type array $3 Content struct for adding a new term. The struct must contain
2081
+
* the term 'name' and 'taxonomy'. Optional accepted values include
2082
+
* 'parent', 'description', and 'slug'.
2083
+
* }
2084
+
* @return int|IXR_Error The term ID on success, or an IXR_Error object on failure.
2085
+
*/
2086
+
public function wp_newTerm( $args ) {
2087
+
if ( ! $this->minimum_args( $args, 4 ) ) {
2088
+
return $this->error;
2089
+
}
2090
+
2091
+
$this->escape( $args );
2092
+
2093
+
$username = $args[1];
2094
+
$password = $args[2];
2095
+
$content_struct = $args[3];
2096
+
2097
+
$user = $this->login( $username, $password );
2098
+
if ( ! $user ) {
2099
+
return $this->error;
2100
+
}
2101
+
2102
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2103
+
do_action( 'xmlrpc_call', 'wp.newTerm', $args, $this );
2104
+
2105
+
if ( ! taxonomy_exists( $content_struct['taxonomy'] ) ) {
2106
+
return new IXR_Error( 403, __( 'Invalid taxonomy.' ) );
2107
+
}
2108
+
2109
+
$taxonomy = get_taxonomy( $content_struct['taxonomy'] );
2110
+
2111
+
if ( ! current_user_can( $taxonomy->cap->edit_terms ) ) {
2112
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to create terms in this taxonomy.' ) );
2113
+
}
2114
+
2115
+
$taxonomy = (array) $taxonomy;
2116
+
2117
+
// Hold the data of the term.
2118
+
$term_data = array();
2119
+
2120
+
$term_data['name'] = trim( $content_struct['name'] );
2121
+
if ( empty( $term_data['name'] ) ) {
2122
+
return new IXR_Error( 403, __( 'The term name cannot be empty.' ) );
2123
+
}
2124
+
2125
+
if ( isset( $content_struct['parent'] ) ) {
2126
+
if ( ! $taxonomy['hierarchical'] ) {
2127
+
return new IXR_Error( 403, __( 'This taxonomy is not hierarchical.' ) );
2128
+
}
2129
+
2130
+
$parent_term_id = (int) $content_struct['parent'];
2131
+
$parent_term = get_term( $parent_term_id, $taxonomy['name'] );
2132
+
2133
+
if ( is_wp_error( $parent_term ) ) {
2134
+
return new IXR_Error( 500, $parent_term->get_error_message() );
2135
+
}
2136
+
2137
+
if ( ! $parent_term ) {
2138
+
return new IXR_Error( 403, __( 'Parent term does not exist.' ) );
2139
+
}
2140
+
2141
+
$term_data['parent'] = $content_struct['parent'];
2142
+
}
2143
+
2144
+
if ( isset( $content_struct['description'] ) ) {
2145
+
$term_data['description'] = $content_struct['description'];
2146
+
}
2147
+
2148
+
if ( isset( $content_struct['slug'] ) ) {
2149
+
$term_data['slug'] = $content_struct['slug'];
2150
+
}
2151
+
2152
+
$term = wp_insert_term( $term_data['name'], $taxonomy['name'], $term_data );
2153
+
2154
+
if ( is_wp_error( $term ) ) {
2155
+
return new IXR_Error( 500, $term->get_error_message() );
2156
+
}
2157
+
2158
+
if ( ! $term ) {
2159
+
return new IXR_Error( 500, __( 'Sorry, the term could not be created.' ) );
2160
+
}
2161
+
2162
+
// Add term meta.
2163
+
if ( isset( $content_struct['custom_fields'] ) ) {
2164
+
$this->set_term_custom_fields( $term['term_id'], $content_struct['custom_fields'] );
2165
+
}
2166
+
2167
+
return (string) $term['term_id'];
2168
+
}
2169
+
2170
+
/**
2171
+
* Edits a term.
2172
+
*
2173
+
* @since 3.4.0
2174
+
*
2175
+
* @see wp_update_term()
2176
+
*
2177
+
* @param array $args {
2178
+
* Method arguments. Note: arguments must be ordered as documented.
2179
+
*
2180
+
* @type int $0 Blog ID (unused).
2181
+
* @type string $1 Username.
2182
+
* @type string $2 Password.
2183
+
* @type int $3 Term ID.
2184
+
* @type array $4 Content struct for editing a term. The struct must contain the
2185
+
* term 'taxonomy'. Optional accepted values include 'name', 'parent',
2186
+
* 'description', and 'slug'.
2187
+
* }
2188
+
* @return true|IXR_Error True on success, IXR_Error instance on failure.
2189
+
*/
2190
+
public function wp_editTerm( $args ) {
2191
+
if ( ! $this->minimum_args( $args, 5 ) ) {
2192
+
return $this->error;
2193
+
}
2194
+
2195
+
$this->escape( $args );
2196
+
2197
+
$username = $args[1];
2198
+
$password = $args[2];
2199
+
$term_id = (int) $args[3];
2200
+
$content_struct = $args[4];
2201
+
2202
+
$user = $this->login( $username, $password );
2203
+
if ( ! $user ) {
2204
+
return $this->error;
2205
+
}
2206
+
2207
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2208
+
do_action( 'xmlrpc_call', 'wp.editTerm', $args, $this );
2209
+
2210
+
if ( ! taxonomy_exists( $content_struct['taxonomy'] ) ) {
2211
+
return new IXR_Error( 403, __( 'Invalid taxonomy.' ) );
2212
+
}
2213
+
2214
+
$taxonomy = get_taxonomy( $content_struct['taxonomy'] );
2215
+
2216
+
$taxonomy = (array) $taxonomy;
2217
+
2218
+
// Hold the data of the term.
2219
+
$term_data = array();
2220
+
2221
+
$term = get_term( $term_id, $content_struct['taxonomy'] );
2222
+
2223
+
if ( is_wp_error( $term ) ) {
2224
+
return new IXR_Error( 500, $term->get_error_message() );
2225
+
}
2226
+
2227
+
if ( ! $term ) {
2228
+
return new IXR_Error( 404, __( 'Invalid term ID.' ) );
2229
+
}
2230
+
2231
+
if ( ! current_user_can( 'edit_term', $term_id ) ) {
2232
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this term.' ) );
2233
+
}
2234
+
2235
+
if ( isset( $content_struct['name'] ) ) {
2236
+
$term_data['name'] = trim( $content_struct['name'] );
2237
+
2238
+
if ( empty( $term_data['name'] ) ) {
2239
+
return new IXR_Error( 403, __( 'The term name cannot be empty.' ) );
2240
+
}
2241
+
}
2242
+
2243
+
if ( ! empty( $content_struct['parent'] ) ) {
2244
+
if ( ! $taxonomy['hierarchical'] ) {
2245
+
return new IXR_Error( 403, __( 'Cannot set parent term, taxonomy is not hierarchical.' ) );
2246
+
}
2247
+
2248
+
$parent_term_id = (int) $content_struct['parent'];
2249
+
$parent_term = get_term( $parent_term_id, $taxonomy['name'] );
2250
+
2251
+
if ( is_wp_error( $parent_term ) ) {
2252
+
return new IXR_Error( 500, $parent_term->get_error_message() );
2253
+
}
2254
+
2255
+
if ( ! $parent_term ) {
2256
+
return new IXR_Error( 403, __( 'Parent term does not exist.' ) );
2257
+
}
2258
+
2259
+
$term_data['parent'] = $content_struct['parent'];
2260
+
}
2261
+
2262
+
if ( isset( $content_struct['description'] ) ) {
2263
+
$term_data['description'] = $content_struct['description'];
2264
+
}
2265
+
2266
+
if ( isset( $content_struct['slug'] ) ) {
2267
+
$term_data['slug'] = $content_struct['slug'];
2268
+
}
2269
+
2270
+
$term = wp_update_term( $term_id, $taxonomy['name'], $term_data );
2271
+
2272
+
if ( is_wp_error( $term ) ) {
2273
+
return new IXR_Error( 500, $term->get_error_message() );
2274
+
}
2275
+
2276
+
if ( ! $term ) {
2277
+
return new IXR_Error( 500, __( 'Sorry, editing the term failed.' ) );
2278
+
}
2279
+
2280
+
// Update term meta.
2281
+
if ( isset( $content_struct['custom_fields'] ) ) {
2282
+
$this->set_term_custom_fields( $term_id, $content_struct['custom_fields'] );
2283
+
}
2284
+
2285
+
return true;
2286
+
}
2287
+
2288
+
/**
2289
+
* Deletes a term.
2290
+
*
2291
+
* @since 3.4.0
2292
+
*
2293
+
* @see wp_delete_term()
2294
+
*
2295
+
* @param array $args {
2296
+
* Method arguments. Note: arguments must be ordered as documented.
2297
+
*
2298
+
* @type int $0 Blog ID (unused).
2299
+
* @type string $1 Username.
2300
+
* @type string $2 Password.
2301
+
* @type string $3 Taxonomy name.
2302
+
* @type int $4 Term ID.
2303
+
* }
2304
+
* @return true|IXR_Error True on success, IXR_Error instance on failure.
2305
+
*/
2306
+
public function wp_deleteTerm( $args ) {
2307
+
if ( ! $this->minimum_args( $args, 5 ) ) {
2308
+
return $this->error;
2309
+
}
2310
+
2311
+
$this->escape( $args );
2312
+
2313
+
$username = $args[1];
2314
+
$password = $args[2];
2315
+
$taxonomy = $args[3];
2316
+
$term_id = (int) $args[4];
2317
+
2318
+
$user = $this->login( $username, $password );
2319
+
if ( ! $user ) {
2320
+
return $this->error;
2321
+
}
2322
+
2323
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2324
+
do_action( 'xmlrpc_call', 'wp.deleteTerm', $args, $this );
2325
+
2326
+
if ( ! taxonomy_exists( $taxonomy ) ) {
2327
+
return new IXR_Error( 403, __( 'Invalid taxonomy.' ) );
2328
+
}
2329
+
2330
+
$taxonomy = get_taxonomy( $taxonomy );
2331
+
$term = get_term( $term_id, $taxonomy->name );
2332
+
2333
+
if ( is_wp_error( $term ) ) {
2334
+
return new IXR_Error( 500, $term->get_error_message() );
2335
+
}
2336
+
2337
+
if ( ! $term ) {
2338
+
return new IXR_Error( 404, __( 'Invalid term ID.' ) );
2339
+
}
2340
+
2341
+
if ( ! current_user_can( 'delete_term', $term_id ) ) {
2342
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this term.' ) );
2343
+
}
2344
+
2345
+
$result = wp_delete_term( $term_id, $taxonomy->name );
2346
+
2347
+
if ( is_wp_error( $result ) ) {
2348
+
return new IXR_Error( 500, $result->get_error_message() );
2349
+
}
2350
+
2351
+
if ( ! $result ) {
2352
+
return new IXR_Error( 500, __( 'Sorry, deleting the term failed.' ) );
2353
+
}
2354
+
2355
+
return $result;
2356
+
}
2357
+
2358
+
/**
2359
+
* Retrieves a term.
2360
+
*
2361
+
* @since 3.4.0
2362
+
*
2363
+
* @see get_term()
2364
+
*
2365
+
* @param array $args {
2366
+
* Method arguments. Note: arguments must be ordered as documented.
2367
+
*
2368
+
* @type int $0 Blog ID (unused).
2369
+
* @type string $1 Username.
2370
+
* @type string $2 Password.
2371
+
* @type string $3 Taxonomy name.
2372
+
* @type int $4 Term ID.
2373
+
* }
2374
+
* @return array|IXR_Error IXR_Error on failure, array on success, containing:
2375
+
* - 'term_id'
2376
+
* - 'name'
2377
+
* - 'slug'
2378
+
* - 'term_group'
2379
+
* - 'term_taxonomy_id'
2380
+
* - 'taxonomy'
2381
+
* - 'description'
2382
+
* - 'parent'
2383
+
* - 'count'
2384
+
*/
2385
+
public function wp_getTerm( $args ) {
2386
+
if ( ! $this->minimum_args( $args, 5 ) ) {
2387
+
return $this->error;
2388
+
}
2389
+
2390
+
$this->escape( $args );
2391
+
2392
+
$username = $args[1];
2393
+
$password = $args[2];
2394
+
$taxonomy = $args[3];
2395
+
$term_id = (int) $args[4];
2396
+
2397
+
$user = $this->login( $username, $password );
2398
+
if ( ! $user ) {
2399
+
return $this->error;
2400
+
}
2401
+
2402
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2403
+
do_action( 'xmlrpc_call', 'wp.getTerm', $args, $this );
2404
+
2405
+
if ( ! taxonomy_exists( $taxonomy ) ) {
2406
+
return new IXR_Error( 403, __( 'Invalid taxonomy.' ) );
2407
+
}
2408
+
2409
+
$taxonomy = get_taxonomy( $taxonomy );
2410
+
2411
+
$term = get_term( $term_id, $taxonomy->name, ARRAY_A );
2412
+
2413
+
if ( is_wp_error( $term ) ) {
2414
+
return new IXR_Error( 500, $term->get_error_message() );
2415
+
}
2416
+
2417
+
if ( ! $term ) {
2418
+
return new IXR_Error( 404, __( 'Invalid term ID.' ) );
2419
+
}
2420
+
2421
+
if ( ! current_user_can( 'assign_term', $term_id ) ) {
2422
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign this term.' ) );
2423
+
}
2424
+
2425
+
return $this->_prepare_term( $term );
2426
+
}
2427
+
2428
+
/**
2429
+
* Retrieves all terms for a taxonomy.
2430
+
*
2431
+
* @since 3.4.0
2432
+
*
2433
+
* The optional $filter parameter modifies the query used to retrieve terms.
2434
+
* Accepted keys are 'number', 'offset', 'orderby', 'order', 'hide_empty', and 'search'.
2435
+
*
2436
+
* @see get_terms()
2437
+
*
2438
+
* @param array $args {
2439
+
* Method arguments. Note: arguments must be ordered as documented.
2440
+
*
2441
+
* @type int $0 Blog ID (unused).
2442
+
* @type string $1 Username.
2443
+
* @type string $2 Password.
2444
+
* @type string $3 Taxonomy name.
2445
+
* @type array $4 Optional. Modifies the query used to retrieve posts. Accepts 'number',
2446
+
* 'offset', 'orderby', 'order', 'hide_empty', and 'search'. Default empty array.
2447
+
* }
2448
+
* @return array|IXR_Error An associative array of terms data on success, IXR_Error instance otherwise.
2449
+
*/
2450
+
public function wp_getTerms( $args ) {
2451
+
if ( ! $this->minimum_args( $args, 4 ) ) {
2452
+
return $this->error;
2453
+
}
2454
+
2455
+
$this->escape( $args );
2456
+
2457
+
$username = $args[1];
2458
+
$password = $args[2];
2459
+
$taxonomy = $args[3];
2460
+
$filter = isset( $args[4] ) ? $args[4] : array();
2461
+
2462
+
$user = $this->login( $username, $password );
2463
+
if ( ! $user ) {
2464
+
return $this->error;
2465
+
}
2466
+
2467
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2468
+
do_action( 'xmlrpc_call', 'wp.getTerms', $args, $this );
2469
+
2470
+
if ( ! taxonomy_exists( $taxonomy ) ) {
2471
+
return new IXR_Error( 403, __( 'Invalid taxonomy.' ) );
2472
+
}
2473
+
2474
+
$taxonomy = get_taxonomy( $taxonomy );
2475
+
2476
+
if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) {
2477
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign terms in this taxonomy.' ) );
2478
+
}
2479
+
2480
+
$query = array( 'taxonomy' => $taxonomy->name );
2481
+
2482
+
if ( isset( $filter['number'] ) ) {
2483
+
$query['number'] = absint( $filter['number'] );
2484
+
}
2485
+
2486
+
if ( isset( $filter['offset'] ) ) {
2487
+
$query['offset'] = absint( $filter['offset'] );
2488
+
}
2489
+
2490
+
if ( isset( $filter['orderby'] ) ) {
2491
+
$query['orderby'] = $filter['orderby'];
2492
+
2493
+
if ( isset( $filter['order'] ) ) {
2494
+
$query['order'] = $filter['order'];
2495
+
}
2496
+
}
2497
+
2498
+
if ( isset( $filter['hide_empty'] ) ) {
2499
+
$query['hide_empty'] = $filter['hide_empty'];
2500
+
} else {
2501
+
$query['get'] = 'all';
2502
+
}
2503
+
2504
+
if ( isset( $filter['search'] ) ) {
2505
+
$query['search'] = $filter['search'];
2506
+
}
2507
+
2508
+
$terms = get_terms( $query );
2509
+
2510
+
if ( is_wp_error( $terms ) ) {
2511
+
return new IXR_Error( 500, $terms->get_error_message() );
2512
+
}
2513
+
2514
+
$struct = array();
2515
+
2516
+
foreach ( $terms as $term ) {
2517
+
$struct[] = $this->_prepare_term( $term );
2518
+
}
2519
+
2520
+
return $struct;
2521
+
}
2522
+
2523
+
/**
2524
+
* Retrieves a taxonomy.
2525
+
*
2526
+
* @since 3.4.0
2527
+
*
2528
+
* @see get_taxonomy()
2529
+
*
2530
+
* @param array $args {
2531
+
* Method arguments. Note: arguments must be ordered as documented.
2532
+
*
2533
+
* @type int $0 Blog ID (unused).
2534
+
* @type string $1 Username.
2535
+
* @type string $2 Password.
2536
+
* @type string $3 Taxonomy name.
2537
+
* @type array $4 Optional. Array of taxonomy fields to limit to in the return.
2538
+
* Accepts 'labels', 'cap', 'menu', and 'object_type'.
2539
+
* Default empty array.
2540
+
* }
2541
+
* @return array|IXR_Error An array of taxonomy data on success, IXR_Error instance otherwise.
2542
+
*/
2543
+
public function wp_getTaxonomy( $args ) {
2544
+
if ( ! $this->minimum_args( $args, 4 ) ) {
2545
+
return $this->error;
2546
+
}
2547
+
2548
+
$this->escape( $args );
2549
+
2550
+
$username = $args[1];
2551
+
$password = $args[2];
2552
+
$taxonomy = $args[3];
2553
+
2554
+
if ( isset( $args[4] ) ) {
2555
+
$fields = $args[4];
2556
+
} else {
2557
+
/**
2558
+
* Filters the default taxonomy query fields used by the given XML-RPC method.
2559
+
*
2560
+
* @since 3.4.0
2561
+
*
2562
+
* @param array $fields An array of taxonomy fields to retrieve. By default,
2563
+
* contains 'labels', 'cap', and 'object_type'.
2564
+
* @param string $method The method name.
2565
+
*/
2566
+
$fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomy' );
2567
+
}
2568
+
2569
+
$user = $this->login( $username, $password );
2570
+
if ( ! $user ) {
2571
+
return $this->error;
2572
+
}
2573
+
2574
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2575
+
do_action( 'xmlrpc_call', 'wp.getTaxonomy', $args, $this );
2576
+
2577
+
if ( ! taxonomy_exists( $taxonomy ) ) {
2578
+
return new IXR_Error( 403, __( 'Invalid taxonomy.' ) );
2579
+
}
2580
+
2581
+
$taxonomy = get_taxonomy( $taxonomy );
2582
+
2583
+
if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) {
2584
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign terms in this taxonomy.' ) );
2585
+
}
2586
+
2587
+
return $this->_prepare_taxonomy( $taxonomy, $fields );
2588
+
}
2589
+
2590
+
/**
2591
+
* Retrieves all taxonomies.
2592
+
*
2593
+
* @since 3.4.0
2594
+
*
2595
+
* @see get_taxonomies()
2596
+
*
2597
+
* @param array $args {
2598
+
* Method arguments. Note: arguments must be ordered as documented.
2599
+
*
2600
+
* @type int $0 Blog ID (unused).
2601
+
* @type string $1 Username.
2602
+
* @type string $2 Password.
2603
+
* @type array $3 Optional. An array of arguments for retrieving taxonomies.
2604
+
* @type array $4 Optional. The subset of taxonomy fields to return.
2605
+
* }
2606
+
* @return array|IXR_Error An associative array of taxonomy data with returned fields determined
2607
+
* by `$fields`, or an IXR_Error instance on failure.
2608
+
*/
2609
+
public function wp_getTaxonomies( $args ) {
2610
+
if ( ! $this->minimum_args( $args, 3 ) ) {
2611
+
return $this->error;
2612
+
}
2613
+
2614
+
$this->escape( $args );
2615
+
2616
+
$username = $args[1];
2617
+
$password = $args[2];
2618
+
$filter = isset( $args[3] ) ? $args[3] : array( 'public' => true );
2619
+
2620
+
if ( isset( $args[4] ) ) {
2621
+
$fields = $args[4];
2622
+
} else {
2623
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2624
+
$fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomies' );
2625
+
}
2626
+
2627
+
$user = $this->login( $username, $password );
2628
+
if ( ! $user ) {
2629
+
return $this->error;
2630
+
}
2631
+
2632
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2633
+
do_action( 'xmlrpc_call', 'wp.getTaxonomies', $args, $this );
2634
+
2635
+
$taxonomies = get_taxonomies( $filter, 'objects' );
2636
+
2637
+
// Holds all the taxonomy data.
2638
+
$struct = array();
2639
+
2640
+
foreach ( $taxonomies as $taxonomy ) {
2641
+
// Capability check for post types.
2642
+
if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) {
2643
+
continue;
2644
+
}
2645
+
2646
+
$struct[] = $this->_prepare_taxonomy( $taxonomy, $fields );
2647
+
}
2648
+
2649
+
return $struct;
2650
+
}
2651
+
2652
+
/**
2653
+
* Retrieves a user.
2654
+
*
2655
+
* The optional $fields parameter specifies what fields will be included
2656
+
* in the response array. This should be a list of field names. 'user_id' will
2657
+
* always be included in the response regardless of the value of $fields.
2658
+
*
2659
+
* Instead of, or in addition to, individual field names, conceptual group
2660
+
* names can be used to specify multiple fields. The available conceptual
2661
+
* groups are 'basic' and 'all'.
2662
+
*
2663
+
* @uses get_userdata()
2664
+
*
2665
+
* @param array $args {
2666
+
* Method arguments. Note: arguments must be ordered as documented.
2667
+
*
2668
+
* @type int $0 Blog ID (unused).
2669
+
* @type string $1 Username.
2670
+
* @type string $2 Password.
2671
+
* @type int $3 User ID.
2672
+
* @type array $4 Optional. Array of fields to return.
2673
+
* }
2674
+
* @return array|IXR_Error Array contains (based on $fields parameter):
2675
+
* - 'user_id'
2676
+
* - 'username'
2677
+
* - 'first_name'
2678
+
* - 'last_name'
2679
+
* - 'registered'
2680
+
* - 'bio'
2681
+
* - 'email'
2682
+
* - 'nickname'
2683
+
* - 'nicename'
2684
+
* - 'url'
2685
+
* - 'display_name'
2686
+
* - 'roles'
2687
+
*/
2688
+
public function wp_getUser( $args ) {
2689
+
if ( ! $this->minimum_args( $args, 4 ) ) {
2690
+
return $this->error;
2691
+
}
2692
+
2693
+
$this->escape( $args );
2694
+
2695
+
$username = $args[1];
2696
+
$password = $args[2];
2697
+
$user_id = (int) $args[3];
2698
+
2699
+
if ( isset( $args[4] ) ) {
2700
+
$fields = $args[4];
2701
+
} else {
2702
+
/**
2703
+
* Filters the default user query fields used by the given XML-RPC method.
2704
+
*
2705
+
* @since 3.5.0
2706
+
*
2707
+
* @param array $fields An array of user fields to retrieve. By default, contains 'all'.
2708
+
* @param string $method The method name.
2709
+
*/
2710
+
$fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUser' );
2711
+
}
2712
+
2713
+
$user = $this->login( $username, $password );
2714
+
if ( ! $user ) {
2715
+
return $this->error;
2716
+
}
2717
+
2718
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2719
+
do_action( 'xmlrpc_call', 'wp.getUser', $args, $this );
2720
+
2721
+
if ( ! current_user_can( 'edit_user', $user_id ) ) {
2722
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this user.' ) );
2723
+
}
2724
+
2725
+
$user_data = get_userdata( $user_id );
2726
+
2727
+
if ( ! $user_data ) {
2728
+
return new IXR_Error( 404, __( 'Invalid user ID.' ) );
2729
+
}
2730
+
2731
+
return $this->_prepare_user( $user_data, $fields );
2732
+
}
2733
+
2734
+
/**
2735
+
* Retrieves users.
2736
+
*
2737
+
* The optional $filter parameter modifies the query used to retrieve users.
2738
+
* Accepted keys are 'number' (default: 50), 'offset' (default: 0), 'role',
2739
+
* 'who', 'orderby', and 'order'.
2740
+
*
2741
+
* The optional $fields parameter specifies what fields will be included
2742
+
* in the response array.
2743
+
*
2744
+
* @uses get_users()
2745
+
* @see wp_getUser() for more on $fields and return values
2746
+
*
2747
+
* @param array $args {
2748
+
* Method arguments. Note: arguments must be ordered as documented.
2749
+
*
2750
+
* @type int $0 Blog ID (unused).
2751
+
* @type string $1 Username.
2752
+
* @type string $2 Password.
2753
+
* @type array $3 Optional. Arguments for the user query.
2754
+
* @type array $4 Optional. Fields to return.
2755
+
* }
2756
+
* @return array|IXR_Error users data
2757
+
*/
2758
+
public function wp_getUsers( $args ) {
2759
+
if ( ! $this->minimum_args( $args, 3 ) ) {
2760
+
return $this->error;
2761
+
}
2762
+
2763
+
$this->escape( $args );
2764
+
2765
+
$username = $args[1];
2766
+
$password = $args[2];
2767
+
$filter = isset( $args[3] ) ? $args[3] : array();
2768
+
2769
+
if ( isset( $args[4] ) ) {
2770
+
$fields = $args[4];
2771
+
} else {
2772
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2773
+
$fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUsers' );
2774
+
}
2775
+
2776
+
$user = $this->login( $username, $password );
2777
+
if ( ! $user ) {
2778
+
return $this->error;
2779
+
}
2780
+
2781
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2782
+
do_action( 'xmlrpc_call', 'wp.getUsers', $args, $this );
2783
+
2784
+
if ( ! current_user_can( 'list_users' ) ) {
2785
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to list users.' ) );
2786
+
}
2787
+
2788
+
$query = array( 'fields' => 'all_with_meta' );
2789
+
2790
+
$query['number'] = ( isset( $filter['number'] ) ) ? absint( $filter['number'] ) : 50;
2791
+
$query['offset'] = ( isset( $filter['offset'] ) ) ? absint( $filter['offset'] ) : 0;
2792
+
2793
+
if ( isset( $filter['orderby'] ) ) {
2794
+
$query['orderby'] = $filter['orderby'];
2795
+
2796
+
if ( isset( $filter['order'] ) ) {
2797
+
$query['order'] = $filter['order'];
2798
+
}
2799
+
}
2800
+
2801
+
if ( isset( $filter['role'] ) ) {
2802
+
if ( get_role( $filter['role'] ) === null ) {
2803
+
return new IXR_Error( 403, __( 'Invalid role.' ) );
2804
+
}
2805
+
2806
+
$query['role'] = $filter['role'];
2807
+
}
2808
+
2809
+
if ( isset( $filter['who'] ) ) {
2810
+
$query['who'] = $filter['who'];
2811
+
}
2812
+
2813
+
$users = get_users( $query );
2814
+
2815
+
$_users = array();
2816
+
foreach ( $users as $user_data ) {
2817
+
if ( current_user_can( 'edit_user', $user_data->ID ) ) {
2818
+
$_users[] = $this->_prepare_user( $user_data, $fields );
2819
+
}
2820
+
}
2821
+
return $_users;
2822
+
}
2823
+
2824
+
/**
2825
+
* Retrieves information about the requesting user.
2826
+
*
2827
+
* @uses get_userdata()
2828
+
*
2829
+
* @param array $args {
2830
+
* Method arguments. Note: arguments must be ordered as documented.
2831
+
*
2832
+
* @type int $0 Blog ID (unused).
2833
+
* @type string $1 Username
2834
+
* @type string $2 Password
2835
+
* @type array $3 Optional. Fields to return.
2836
+
* }
2837
+
* @return array|IXR_Error (@see wp_getUser)
2838
+
*/
2839
+
public function wp_getProfile( $args ) {
2840
+
if ( ! $this->minimum_args( $args, 3 ) ) {
2841
+
return $this->error;
2842
+
}
2843
+
2844
+
$this->escape( $args );
2845
+
2846
+
$username = $args[1];
2847
+
$password = $args[2];
2848
+
2849
+
if ( isset( $args[3] ) ) {
2850
+
$fields = $args[3];
2851
+
} else {
2852
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2853
+
$fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getProfile' );
2854
+
}
2855
+
2856
+
$user = $this->login( $username, $password );
2857
+
if ( ! $user ) {
2858
+
return $this->error;
2859
+
}
2860
+
2861
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2862
+
do_action( 'xmlrpc_call', 'wp.getProfile', $args, $this );
2863
+
2864
+
if ( ! current_user_can( 'edit_user', $user->ID ) ) {
2865
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit your profile.' ) );
2866
+
}
2867
+
2868
+
$user_data = get_userdata( $user->ID );
2869
+
2870
+
return $this->_prepare_user( $user_data, $fields );
2871
+
}
2872
+
2873
+
/**
2874
+
* Edits user's profile.
2875
+
*
2876
+
* @uses wp_update_user()
2877
+
*
2878
+
* @param array $args {
2879
+
* Method arguments. Note: arguments must be ordered as documented.
2880
+
*
2881
+
* @type int $0 Blog ID (unused).
2882
+
* @type string $1 Username.
2883
+
* @type string $2 Password.
2884
+
* @type array $3 Content struct. It can optionally contain:
2885
+
* - 'first_name'
2886
+
* - 'last_name'
2887
+
* - 'website'
2888
+
* - 'display_name'
2889
+
* - 'nickname'
2890
+
* - 'nicename'
2891
+
* - 'bio'
2892
+
* }
2893
+
* @return true|IXR_Error True, on success.
2894
+
*/
2895
+
public function wp_editProfile( $args ) {
2896
+
if ( ! $this->minimum_args( $args, 4 ) ) {
2897
+
return $this->error;
2898
+
}
2899
+
2900
+
$this->escape( $args );
2901
+
2902
+
$username = $args[1];
2903
+
$password = $args[2];
2904
+
$content_struct = $args[3];
2905
+
2906
+
$user = $this->login( $username, $password );
2907
+
if ( ! $user ) {
2908
+
return $this->error;
2909
+
}
2910
+
2911
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2912
+
do_action( 'xmlrpc_call', 'wp.editProfile', $args, $this );
2913
+
2914
+
if ( ! current_user_can( 'edit_user', $user->ID ) ) {
2915
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit your profile.' ) );
2916
+
}
2917
+
2918
+
// Holds data of the user.
2919
+
$user_data = array();
2920
+
$user_data['ID'] = $user->ID;
2921
+
2922
+
// Only set the user details if they were given.
2923
+
if ( isset( $content_struct['first_name'] ) ) {
2924
+
$user_data['first_name'] = $content_struct['first_name'];
2925
+
}
2926
+
2927
+
if ( isset( $content_struct['last_name'] ) ) {
2928
+
$user_data['last_name'] = $content_struct['last_name'];
2929
+
}
2930
+
2931
+
if ( isset( $content_struct['url'] ) ) {
2932
+
$user_data['user_url'] = $content_struct['url'];
2933
+
}
2934
+
2935
+
if ( isset( $content_struct['display_name'] ) ) {
2936
+
$user_data['display_name'] = $content_struct['display_name'];
2937
+
}
2938
+
2939
+
if ( isset( $content_struct['nickname'] ) ) {
2940
+
$user_data['nickname'] = $content_struct['nickname'];
2941
+
}
2942
+
2943
+
if ( isset( $content_struct['nicename'] ) ) {
2944
+
$user_data['user_nicename'] = $content_struct['nicename'];
2945
+
}
2946
+
2947
+
if ( isset( $content_struct['bio'] ) ) {
2948
+
$user_data['description'] = $content_struct['bio'];
2949
+
}
2950
+
2951
+
$result = wp_update_user( $user_data );
2952
+
2953
+
if ( is_wp_error( $result ) ) {
2954
+
return new IXR_Error( 500, $result->get_error_message() );
2955
+
}
2956
+
2957
+
if ( ! $result ) {
2958
+
return new IXR_Error( 500, __( 'Sorry, the user could not be updated.' ) );
2959
+
}
2960
+
2961
+
return true;
2962
+
}
2963
+
2964
+
/**
2965
+
* Retrieves a page.
2966
+
*
2967
+
* @since 2.2.0
2968
+
*
2969
+
* @param array $args {
2970
+
* Method arguments. Note: arguments must be ordered as documented.
2971
+
*
2972
+
* @type int $0 Blog ID (unused).
2973
+
* @type int $1 Page ID.
2974
+
* @type string $2 Username.
2975
+
* @type string $3 Password.
2976
+
* }
2977
+
* @return array|IXR_Error
2978
+
*/
2979
+
public function wp_getPage( $args ) {
2980
+
$this->escape( $args );
2981
+
2982
+
$page_id = (int) $args[1];
2983
+
$username = $args[2];
2984
+
$password = $args[3];
2985
+
2986
+
$user = $this->login( $username, $password );
2987
+
if ( ! $user ) {
2988
+
return $this->error;
2989
+
}
2990
+
2991
+
$page = get_post( $page_id );
2992
+
if ( ! $page ) {
2993
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
2994
+
}
2995
+
2996
+
if ( ! current_user_can( 'edit_page', $page_id ) ) {
2997
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this page.' ) );
2998
+
}
2999
+
3000
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3001
+
do_action( 'xmlrpc_call', 'wp.getPage', $args, $this );
3002
+
3003
+
// If we found the page then format the data.
3004
+
if ( $page->ID && ( 'page' === $page->post_type ) ) {
3005
+
return $this->_prepare_page( $page );
3006
+
} else {
3007
+
// If the page doesn't exist, indicate that.
3008
+
return new IXR_Error( 404, __( 'Sorry, no such page.' ) );
3009
+
}
3010
+
}
3011
+
3012
+
/**
3013
+
* Retrieves Pages.
3014
+
*
3015
+
* @since 2.2.0
3016
+
*
3017
+
* @param array $args {
3018
+
* Method arguments. Note: arguments must be ordered as documented.
3019
+
*
3020
+
* @type int $0 Blog ID (unused).
3021
+
* @type string $1 Username.
3022
+
* @type string $2 Password.
3023
+
* @type int $3 Optional. Number of pages. Default 10.
3024
+
* }
3025
+
* @return array|IXR_Error
3026
+
*/
3027
+
public function wp_getPages( $args ) {
3028
+
$this->escape( $args );
3029
+
3030
+
$username = $args[1];
3031
+
$password = $args[2];
3032
+
$num_pages = isset( $args[3] ) ? (int) $args[3] : 10;
3033
+
3034
+
$user = $this->login( $username, $password );
3035
+
if ( ! $user ) {
3036
+
return $this->error;
3037
+
}
3038
+
3039
+
if ( ! current_user_can( 'edit_pages' ) ) {
3040
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit pages.' ) );
3041
+
}
3042
+
3043
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3044
+
do_action( 'xmlrpc_call', 'wp.getPages', $args, $this );
3045
+
3046
+
$pages = get_posts(
3047
+
array(
3048
+
'post_type' => 'page',
3049
+
'post_status' => 'any',
3050
+
'numberposts' => $num_pages,
3051
+
)
3052
+
);
3053
+
$num_pages = count( $pages );
3054
+
3055
+
// If we have pages, put together their info.
3056
+
if ( $num_pages >= 1 ) {
3057
+
$pages_struct = array();
3058
+
3059
+
foreach ( $pages as $page ) {
3060
+
if ( current_user_can( 'edit_page', $page->ID ) ) {
3061
+
$pages_struct[] = $this->_prepare_page( $page );
3062
+
}
3063
+
}
3064
+
3065
+
return $pages_struct;
3066
+
}
3067
+
3068
+
return array();
3069
+
}
3070
+
3071
+
/**
3072
+
* Creates a new page.
3073
+
*
3074
+
* @since 2.2.0
3075
+
*
3076
+
* @see wp_xmlrpc_server::mw_newPost()
3077
+
*
3078
+
* @param array $args {
3079
+
* Method arguments. Note: arguments must be ordered as documented.
3080
+
*
3081
+
* @type int $0 Blog ID (unused).
3082
+
* @type string $1 Username.
3083
+
* @type string $2 Password.
3084
+
* @type array $3 Content struct.
3085
+
* }
3086
+
* @return int|IXR_Error
3087
+
*/
3088
+
public function wp_newPage( $args ) {
3089
+
// Items not escaped here will be escaped in wp_newPost().
3090
+
$username = $this->escape( $args[1] );
3091
+
$password = $this->escape( $args[2] );
3092
+
3093
+
$user = $this->login( $username, $password );
3094
+
if ( ! $user ) {
3095
+
return $this->error;
3096
+
}
3097
+
3098
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3099
+
do_action( 'xmlrpc_call', 'wp.newPage', $args, $this );
3100
+
3101
+
// Mark this as content for a page.
3102
+
$args[3]['post_type'] = 'page';
3103
+
3104
+
// Let mw_newPost() do all of the heavy lifting.
3105
+
return $this->mw_newPost( $args );
3106
+
}
3107
+
3108
+
/**
3109
+
* Deletes a page.
3110
+
*
3111
+
* @since 2.2.0
3112
+
*
3113
+
* @param array $args {
3114
+
* Method arguments. Note: arguments must be ordered as documented.
3115
+
*
3116
+
* @type int $0 Blog ID (unused).
3117
+
* @type string $1 Username.
3118
+
* @type string $2 Password.
3119
+
* @type int $3 Page ID.
3120
+
* }
3121
+
* @return true|IXR_Error True, if success.
3122
+
*/
3123
+
public function wp_deletePage( $args ) {
3124
+
$this->escape( $args );
3125
+
3126
+
$username = $args[1];
3127
+
$password = $args[2];
3128
+
$page_id = (int) $args[3];
3129
+
3130
+
$user = $this->login( $username, $password );
3131
+
if ( ! $user ) {
3132
+
return $this->error;
3133
+
}
3134
+
3135
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3136
+
do_action( 'xmlrpc_call', 'wp.deletePage', $args, $this );
3137
+
3138
+
/*
3139
+
* Get the current page based on the 'page_id' and
3140
+
* make sure it is a page and not a post.
3141
+
*/
3142
+
$actual_page = get_post( $page_id, ARRAY_A );
3143
+
if ( ! $actual_page || ( 'page' !== $actual_page['post_type'] ) ) {
3144
+
return new IXR_Error( 404, __( 'Sorry, no such page.' ) );
3145
+
}
3146
+
3147
+
// Make sure the user can delete pages.
3148
+
if ( ! current_user_can( 'delete_page', $page_id ) ) {
3149
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this page.' ) );
3150
+
}
3151
+
3152
+
// Attempt to delete the page.
3153
+
$result = wp_delete_post( $page_id );
3154
+
if ( ! $result ) {
3155
+
return new IXR_Error( 500, __( 'Failed to delete the page.' ) );
3156
+
}
3157
+
3158
+
/**
3159
+
* Fires after a page has been successfully deleted via XML-RPC.
3160
+
*
3161
+
* @since 3.4.0
3162
+
*
3163
+
* @param int $page_id ID of the deleted page.
3164
+
* @param array $args An array of arguments to delete the page.
3165
+
*/
3166
+
do_action( 'xmlrpc_call_success_wp_deletePage', $page_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
3167
+
3168
+
return true;
3169
+
}
3170
+
3171
+
/**
3172
+
* Edits a page.
3173
+
*
3174
+
* @since 2.2.0
3175
+
*
3176
+
* @param array $args {
3177
+
* Method arguments. Note: arguments must be ordered as documented.
3178
+
*
3179
+
* @type int $0 Blog ID (unused).
3180
+
* @type int $1 Page ID.
3181
+
* @type string $2 Username.
3182
+
* @type string $3 Password.
3183
+
* @type string $4 Content.
3184
+
* @type int $5 Publish flag. 0 for draft, 1 for publish.
3185
+
* }
3186
+
* @return array|IXR_Error
3187
+
*/
3188
+
public function wp_editPage( $args ) {
3189
+
// Items will be escaped in mw_editPost().
3190
+
$page_id = (int) $args[1];
3191
+
$username = $args[2];
3192
+
$password = $args[3];
3193
+
$content = $args[4];
3194
+
$publish = $args[5];
3195
+
3196
+
$escaped_username = $this->escape( $username );
3197
+
$escaped_password = $this->escape( $password );
3198
+
3199
+
$user = $this->login( $escaped_username, $escaped_password );
3200
+
if ( ! $user ) {
3201
+
return $this->error;
3202
+
}
3203
+
3204
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3205
+
do_action( 'xmlrpc_call', 'wp.editPage', $args, $this );
3206
+
3207
+
// Get the page data and make sure it is a page.
3208
+
$actual_page = get_post( $page_id, ARRAY_A );
3209
+
if ( ! $actual_page || ( 'page' !== $actual_page['post_type'] ) ) {
3210
+
return new IXR_Error( 404, __( 'Sorry, no such page.' ) );
3211
+
}
3212
+
3213
+
// Make sure the user is allowed to edit pages.
3214
+
if ( ! current_user_can( 'edit_page', $page_id ) ) {
3215
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this page.' ) );
3216
+
}
3217
+
3218
+
// Mark this as content for a page.
3219
+
$content['post_type'] = 'page';
3220
+
3221
+
// Arrange args in the way mw_editPost() understands.
3222
+
$args = array(
3223
+
$page_id,
3224
+
$username,
3225
+
$password,
3226
+
$content,
3227
+
$publish,
3228
+
);
3229
+
3230
+
// Let mw_editPost() do all of the heavy lifting.
3231
+
return $this->mw_editPost( $args );
3232
+
}
3233
+
3234
+
/**
3235
+
* Retrieves page list.
3236
+
*
3237
+
* @since 2.2.0
3238
+
*
3239
+
* @global wpdb $wpdb WordPress database abstraction object.
3240
+
*
3241
+
* @param array $args {
3242
+
* Method arguments. Note: arguments must be ordered as documented.
3243
+
*
3244
+
* @type int $0 Blog ID (unused).
3245
+
* @type string $1 Username.
3246
+
* @type string $2 Password.
3247
+
* }
3248
+
* @return array|IXR_Error
3249
+
*/
3250
+
public function wp_getPageList( $args ) {
3251
+
global $wpdb;
3252
+
3253
+
$this->escape( $args );
3254
+
3255
+
$username = $args[1];
3256
+
$password = $args[2];
3257
+
3258
+
$user = $this->login( $username, $password );
3259
+
if ( ! $user ) {
3260
+
return $this->error;
3261
+
}
3262
+
3263
+
if ( ! current_user_can( 'edit_pages' ) ) {
3264
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit pages.' ) );
3265
+
}
3266
+
3267
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3268
+
do_action( 'xmlrpc_call', 'wp.getPageList', $args, $this );
3269
+
3270
+
// Get list of page IDs and titles.
3271
+
$page_list = $wpdb->get_results(
3272
+
"
3273
+
SELECT ID page_id,
3274
+
post_title page_title,
3275
+
post_parent page_parent_id,
3276
+
post_date_gmt,
3277
+
post_date,
3278
+
post_status
3279
+
FROM {$wpdb->posts}
3280
+
WHERE post_type = 'page'
3281
+
ORDER BY ID
3282
+
"
3283
+
);
3284
+
3285
+
// The date needs to be formatted properly.
3286
+
$num_pages = count( $page_list );
3287
+
for ( $i = 0; $i < $num_pages; $i++ ) {
3288
+
$page_list[ $i ]->dateCreated = $this->_convert_date( $page_list[ $i ]->post_date );
3289
+
$page_list[ $i ]->date_created_gmt = $this->_convert_date_gmt( $page_list[ $i ]->post_date_gmt, $page_list[ $i ]->post_date );
3290
+
3291
+
unset( $page_list[ $i ]->post_date_gmt );
3292
+
unset( $page_list[ $i ]->post_date );
3293
+
unset( $page_list[ $i ]->post_status );
3294
+
}
3295
+
3296
+
return $page_list;
3297
+
}
3298
+
3299
+
/**
3300
+
* Retrieves authors list.
3301
+
*
3302
+
* @since 2.2.0
3303
+
*
3304
+
* @param array $args {
3305
+
* Method arguments. Note: arguments must be ordered as documented.
3306
+
*
3307
+
* @type int $0 Blog ID (unused).
3308
+
* @type string $1 Username.
3309
+
* @type string $2 Password.
3310
+
* }
3311
+
* @return array|IXR_Error
3312
+
*/
3313
+
public function wp_getAuthors( $args ) {
3314
+
$this->escape( $args );
3315
+
3316
+
$username = $args[1];
3317
+
$password = $args[2];
3318
+
3319
+
$user = $this->login( $username, $password );
3320
+
if ( ! $user ) {
3321
+
return $this->error;
3322
+
}
3323
+
3324
+
if ( ! current_user_can( 'edit_posts' ) ) {
3325
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) );
3326
+
}
3327
+
3328
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3329
+
do_action( 'xmlrpc_call', 'wp.getAuthors', $args, $this );
3330
+
3331
+
$authors = array();
3332
+
foreach ( get_users( array( 'fields' => array( 'ID', 'user_login', 'display_name' ) ) ) as $user ) {
3333
+
$authors[] = array(
3334
+
'user_id' => $user->ID,
3335
+
'user_login' => $user->user_login,
3336
+
'display_name' => $user->display_name,
3337
+
);
3338
+
}
3339
+
3340
+
return $authors;
3341
+
}
3342
+
3343
+
/**
3344
+
* Gets the list of all tags.
3345
+
*
3346
+
* @since 2.7.0
3347
+
*
3348
+
* @param array $args {
3349
+
* Method arguments. Note: arguments must be ordered as documented.
3350
+
*
3351
+
* @type int $0 Blog ID (unused).
3352
+
* @type string $1 Username.
3353
+
* @type string $2 Password.
3354
+
* }
3355
+
* @return array|IXR_Error
3356
+
*/
3357
+
public function wp_getTags( $args ) {
3358
+
$this->escape( $args );
3359
+
3360
+
$username = $args[1];
3361
+
$password = $args[2];
3362
+
3363
+
$user = $this->login( $username, $password );
3364
+
if ( ! $user ) {
3365
+
return $this->error;
3366
+
}
3367
+
3368
+
if ( ! current_user_can( 'edit_posts' ) ) {
3369
+
return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view tags.' ) );
3370
+
}
3371
+
3372
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3373
+
do_action( 'xmlrpc_call', 'wp.getKeywords', $args, $this );
3374
+
3375
+
$tags = array();
3376
+
3377
+
$all_tags = get_tags();
3378
+
if ( $all_tags ) {
3379
+
foreach ( (array) $all_tags as $tag ) {
3380
+
$struct = array();
3381
+
$struct['tag_id'] = $tag->term_id;
3382
+
$struct['name'] = $tag->name;
3383
+
$struct['count'] = $tag->count;
3384
+
$struct['slug'] = $tag->slug;
3385
+
$struct['html_url'] = esc_html( get_tag_link( $tag->term_id ) );
3386
+
$struct['rss_url'] = esc_html( get_tag_feed_link( $tag->term_id ) );
3387
+
3388
+
$tags[] = $struct;
3389
+
}
3390
+
}
3391
+
3392
+
return $tags;
3393
+
}
3394
+
3395
+
/**
3396
+
* Creates a new category.
3397
+
*
3398
+
* @since 2.2.0
3399
+
*
3400
+
* @param array $args {
3401
+
* Method arguments. Note: arguments must be ordered as documented.
3402
+
*
3403
+
* @type int $0 Blog ID (unused).
3404
+
* @type string $1 Username.
3405
+
* @type string $2 Password.
3406
+
* @type array $3 Category.
3407
+
* }
3408
+
* @return int|IXR_Error Category ID.
3409
+
*/
3410
+
public function wp_newCategory( $args ) {
3411
+
$this->escape( $args );
3412
+
3413
+
$username = $args[1];
3414
+
$password = $args[2];
3415
+
$category = $args[3];
3416
+
3417
+
$user = $this->login( $username, $password );
3418
+
if ( ! $user ) {
3419
+
return $this->error;
3420
+
}
3421
+
3422
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3423
+
do_action( 'xmlrpc_call', 'wp.newCategory', $args, $this );
3424
+
3425
+
// Make sure the user is allowed to add a category.
3426
+
if ( ! current_user_can( 'manage_categories' ) ) {
3427
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to add a category.' ) );
3428
+
}
3429
+
3430
+
/*
3431
+
* If no slug was provided, make it empty
3432
+
* so that WordPress will generate one.
3433
+
*/
3434
+
if ( empty( $category['slug'] ) ) {
3435
+
$category['slug'] = '';
3436
+
}
3437
+
3438
+
/*
3439
+
* If no parent_id was provided, make it empty
3440
+
* so that it will be a top-level page (no parent).
3441
+
*/
3442
+
if ( ! isset( $category['parent_id'] ) ) {
3443
+
$category['parent_id'] = '';
3444
+
}
3445
+
3446
+
// If no description was provided, make it empty.
3447
+
if ( empty( $category['description'] ) ) {
3448
+
$category['description'] = '';
3449
+
}
3450
+
3451
+
$new_category = array(
3452
+
'cat_name' => $category['name'],
3453
+
'category_nicename' => $category['slug'],
3454
+
'category_parent' => $category['parent_id'],
3455
+
'category_description' => $category['description'],
3456
+
);
3457
+
3458
+
$cat_id = wp_insert_category( $new_category, true );
3459
+
if ( is_wp_error( $cat_id ) ) {
3460
+
if ( 'term_exists' === $cat_id->get_error_code() ) {
3461
+
return (int) $cat_id->get_error_data();
3462
+
} else {
3463
+
return new IXR_Error( 500, __( 'Sorry, the category could not be created.' ) );
3464
+
}
3465
+
} elseif ( ! $cat_id ) {
3466
+
return new IXR_Error( 500, __( 'Sorry, the category could not be created.' ) );
3467
+
}
3468
+
3469
+
/**
3470
+
* Fires after a new category has been successfully created via XML-RPC.
3471
+
*
3472
+
* @since 3.4.0
3473
+
*
3474
+
* @param int $cat_id ID of the new category.
3475
+
* @param array $args An array of new category arguments.
3476
+
*/
3477
+
do_action( 'xmlrpc_call_success_wp_newCategory', $cat_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
3478
+
3479
+
return $cat_id;
3480
+
}
3481
+
3482
+
/**
3483
+
* Deletes a category.
3484
+
*
3485
+
* @since 2.5.0
3486
+
*
3487
+
* @param array $args {
3488
+
* Method arguments. Note: arguments must be ordered as documented.
3489
+
*
3490
+
* @type int $0 Blog ID (unused).
3491
+
* @type string $1 Username.
3492
+
* @type string $2 Password.
3493
+
* @type int $3 Category ID.
3494
+
* }
3495
+
* @return bool|IXR_Error See wp_delete_term() for return info.
3496
+
*/
3497
+
public function wp_deleteCategory( $args ) {
3498
+
$this->escape( $args );
3499
+
3500
+
$username = $args[1];
3501
+
$password = $args[2];
3502
+
$category_id = (int) $args[3];
3503
+
3504
+
$user = $this->login( $username, $password );
3505
+
if ( ! $user ) {
3506
+
return $this->error;
3507
+
}
3508
+
3509
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3510
+
do_action( 'xmlrpc_call', 'wp.deleteCategory', $args, $this );
3511
+
3512
+
if ( ! current_user_can( 'delete_term', $category_id ) ) {
3513
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this category.' ) );
3514
+
}
3515
+
3516
+
$status = wp_delete_term( $category_id, 'category' );
3517
+
3518
+
if ( true === $status ) {
3519
+
/**
3520
+
* Fires after a category has been successfully deleted via XML-RPC.
3521
+
*
3522
+
* @since 3.4.0
3523
+
*
3524
+
* @param int $category_id ID of the deleted category.
3525
+
* @param array $args An array of arguments to delete the category.
3526
+
*/
3527
+
do_action( 'xmlrpc_call_success_wp_deleteCategory', $category_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
3528
+
}
3529
+
3530
+
return $status;
3531
+
}
3532
+
3533
+
/**
3534
+
* Retrieves category list.
3535
+
*
3536
+
* @since 2.2.0
3537
+
*
3538
+
* @param array $args {
3539
+
* Method arguments. Note: arguments must be ordered as documented.
3540
+
*
3541
+
* @type int $0 Blog ID (unused).
3542
+
* @type string $1 Username.
3543
+
* @type string $2 Password.
3544
+
* @type array $3 Category
3545
+
* @type int $4 Max number of results.
3546
+
* }
3547
+
* @return array|IXR_Error
3548
+
*/
3549
+
public function wp_suggestCategories( $args ) {
3550
+
$this->escape( $args );
3551
+
3552
+
$username = $args[1];
3553
+
$password = $args[2];
3554
+
$category = $args[3];
3555
+
$max_results = (int) $args[4];
3556
+
3557
+
$user = $this->login( $username, $password );
3558
+
if ( ! $user ) {
3559
+
return $this->error;
3560
+
}
3561
+
3562
+
if ( ! current_user_can( 'edit_posts' ) ) {
3563
+
return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
3564
+
}
3565
+
3566
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3567
+
do_action( 'xmlrpc_call', 'wp.suggestCategories', $args, $this );
3568
+
3569
+
$category_suggestions = array();
3570
+
$args = array(
3571
+
'get' => 'all',
3572
+
'number' => $max_results,
3573
+
'name__like' => $category,
3574
+
);
3575
+
foreach ( (array) get_categories( $args ) as $cat ) {
3576
+
$category_suggestions[] = array(
3577
+
'category_id' => $cat->term_id,
3578
+
'category_name' => $cat->name,
3579
+
);
3580
+
}
3581
+
3582
+
return $category_suggestions;
3583
+
}
3584
+
3585
+
/**
3586
+
* Retrieves a comment.
3587
+
*
3588
+
* @since 2.7.0
3589
+
*
3590
+
* @param array $args {
3591
+
* Method arguments. Note: arguments must be ordered as documented.
3592
+
*
3593
+
* @type int $0 Blog ID (unused).
3594
+
* @type string $1 Username.
3595
+
* @type string $2 Password.
3596
+
* @type int $3 Comment ID.
3597
+
* }
3598
+
* @return array|IXR_Error
3599
+
*/
3600
+
public function wp_getComment( $args ) {
3601
+
$this->escape( $args );
3602
+
3603
+
$username = $args[1];
3604
+
$password = $args[2];
3605
+
$comment_id = (int) $args[3];
3606
+
3607
+
$user = $this->login( $username, $password );
3608
+
if ( ! $user ) {
3609
+
return $this->error;
3610
+
}
3611
+
3612
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3613
+
do_action( 'xmlrpc_call', 'wp.getComment', $args, $this );
3614
+
3615
+
$comment = get_comment( $comment_id );
3616
+
if ( ! $comment ) {
3617
+
return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
3618
+
}
3619
+
3620
+
if ( ! current_user_can( 'edit_comment', $comment_id ) ) {
3621
+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to moderate or edit this comment.' ) );
3622
+
}
3623
+
3624
+
return $this->_prepare_comment( $comment );
3625
+
}
3626
+
3627
+
/**
3628
+
* Retrieves comments.
3629
+
*
3630
+
* Besides the common blog_id (unused), username, and password arguments,
3631
+
* it takes a filter array as the last argument.
3632
+
*
3633
+
* Accepted 'filter' keys are 'status', 'post_id', 'offset', and 'number'.
3634
+
*
3635
+
* The defaults are as follows:
3636
+
* - 'status' - Default is ''. Filter by status (e.g., 'approve', 'hold')
3637
+
* - 'post_id' - Default is ''. The post where the comment is posted.
3638
+
* Empty string shows all comments.
3639
+
* - 'number' - Default is 10. Total number of media items to retrieve.
3640
+
* - 'offset' - Default is 0. See WP_Query::query() for more.
3641
+
*
3642
+
* @since 2.7.0
3643
+
*
3644
+
* @param array $args {
3645
+
* Method arguments. Note: arguments must be ordered as documented.
3646
+
*
3647
+
* @type int $0 Blog ID (unused).
3648
+
* @type string $1 Username.
3649
+
* @type string $2 Password.
3650
+
* @type array $3 Optional. Query arguments.
3651
+
* }
3652
+
* @return array|IXR_Error Array containing a collection of comments.
3653
+
* See wp_xmlrpc_server::wp_getComment() for a description
3654
+
* of each item contents.
3655
+
*/
3656
+
public function wp_getComments( $args ) {
3657
+
$this->escape( $args );
3658
+
3659
+
$username = $args[1];
3660
+
$password = $args[2];
3661
+
$struct = isset( $args[3] ) ? $args[3] : array();
3662
+
3663
+
$user = $this->login( $username, $password );
3664
+
if ( ! $user ) {
3665
+
return $this->error;
3666
+
}
3667
+
3668
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3669
+
do_action( 'xmlrpc_call', 'wp.getComments', $args, $this );
3670
+
3671
+
if ( isset( $struct['status'] ) ) {
3672
+
$status = $struct['status'];
3673
+
} else {
3674
+
$status = '';
3675
+
}
3676
+
3677
+
if ( ! current_user_can( 'moderate_comments' ) && 'approve' !== $status ) {
3678
+
return new IXR_Error( 401, __( 'Invalid comment status.' ) );
3679
+
}
3680
+
3681
+
$post_id = '';
3682
+
if ( isset( $struct['post_id'] ) ) {
3683
+
$post_id = absint( $struct['post_id'] );
3684
+
}
3685
+
3686
+
$post_type = '';
3687
+
if ( isset( $struct['post_type'] ) ) {
3688
+
$post_type_object = get_post_type_object( $struct['post_type'] );
3689
+
if ( ! $post_type_object || ! post_type_supports( $post_type_object->name, 'comments' ) ) {
3690
+
return new IXR_Error( 404, __( 'Invalid post type.' ) );
3691
+
}
3692
+
$post_type = $struct['post_type'];
3693
+
}
3694
+
3695
+
$offset = 0;
3696
+
if ( isset( $struct['offset'] ) ) {
3697
+
$offset = absint( $struct['offset'] );
3698
+
}
3699
+
3700
+
$number = 10;
3701
+
if ( isset( $struct['number'] ) ) {
3702
+
$number = absint( $struct['number'] );
3703
+
}
3704
+
3705
+
$comments = get_comments(
3706
+
array(
3707
+
'status' => $status,
3708
+
'post_id' => $post_id,
3709
+
'offset' => $offset,
3710
+
'number' => $number,
3711
+
'post_type' => $post_type,
3712
+
)
3713
+
);
3714
+
3715
+
$comments_struct = array();
3716
+
if ( is_array( $comments ) ) {
3717
+
foreach ( $comments as $comment ) {
3718
+
$comments_struct[] = $this->_prepare_comment( $comment );
3719
+
}
3720
+
}
3721
+
3722
+
return $comments_struct;
3723
+
}
3724
+
3725
+
/**
3726
+
* Deletes a comment.
3727
+
*
3728
+
* By default, the comment will be moved to the Trash instead of deleted.
3729
+
* See wp_delete_comment() for more information on this behavior.
3730
+
*
3731
+
* @since 2.7.0
3732
+
*
3733
+
* @param array $args {
3734
+
* Method arguments. Note: arguments must be ordered as documented.
3735
+
*
3736
+
* @type int $0 Blog ID (unused).
3737
+
* @type string $1 Username.
3738
+
* @type string $2 Password.
3739
+
* @type int $3 Comment ID.
3740
+
* }
3741
+
* @return bool|IXR_Error See wp_delete_comment().
3742
+
*/
3743
+
public function wp_deleteComment( $args ) {
3744
+
$this->escape( $args );
3745
+
3746
+
$username = $args[1];
3747
+
$password = $args[2];
3748
+
$comment_id = (int) $args[3];
3749
+
3750
+
$user = $this->login( $username, $password );
3751
+
if ( ! $user ) {
3752
+
return $this->error;
3753
+
}
3754
+
3755
+
if ( ! get_comment( $comment_id ) ) {
3756
+
return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
3757
+
}
3758
+
3759
+
if ( ! current_user_can( 'edit_comment', $comment_id ) ) {
3760
+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to delete this comment.' ) );
3761
+
}
3762
+
3763
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3764
+
do_action( 'xmlrpc_call', 'wp.deleteComment', $args, $this );
3765
+
3766
+
$status = wp_delete_comment( $comment_id );
3767
+
3768
+
if ( true === $status ) {
3769
+
/**
3770
+
* Fires after a comment has been successfully deleted via XML-RPC.
3771
+
*
3772
+
* @since 3.4.0
3773
+
*
3774
+
* @param int $comment_id ID of the deleted comment.
3775
+
* @param array $args An array of arguments to delete the comment.
3776
+
*/
3777
+
do_action( 'xmlrpc_call_success_wp_deleteComment', $comment_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
3778
+
}
3779
+
3780
+
return $status;
3781
+
}
3782
+
3783
+
/**
3784
+
* Edits a comment.
3785
+
*
3786
+
* Besides the common blog_id (unused), username, and password arguments,
3787
+
* it takes a comment_id integer and a content_struct array as the last argument.
3788
+
*
3789
+
* The allowed keys in the content_struct array are:
3790
+
* - 'author'
3791
+
* - 'author_url'
3792
+
* - 'author_email'
3793
+
* - 'content'
3794
+
* - 'date_created_gmt'
3795
+
* - 'status'. Common statuses are 'approve', 'hold', 'spam'. See get_comment_statuses() for more details.
3796
+
*
3797
+
* @since 2.7.0
3798
+
*
3799
+
* @param array $args {
3800
+
* Method arguments. Note: arguments must be ordered as documented.
3801
+
*
3802
+
* @type int $0 Blog ID (unused).
3803
+
* @type string $1 Username.
3804
+
* @type string $2 Password.
3805
+
* @type int $3 Comment ID.
3806
+
* @type array $4 Content structure.
3807
+
* }
3808
+
* @return true|IXR_Error True, on success.
3809
+
*/
3810
+
public function wp_editComment( $args ) {
3811
+
$this->escape( $args );
3812
+
3813
+
$username = $args[1];
3814
+
$password = $args[2];
3815
+
$comment_id = (int) $args[3];
3816
+
$content_struct = $args[4];
3817
+
3818
+
$user = $this->login( $username, $password );
3819
+
if ( ! $user ) {
3820
+
return $this->error;
3821
+
}
3822
+
3823
+
if ( ! get_comment( $comment_id ) ) {
3824
+
return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
3825
+
}
3826
+
3827
+
if ( ! current_user_can( 'edit_comment', $comment_id ) ) {
3828
+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to moderate or edit this comment.' ) );
3829
+
}
3830
+
3831
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3832
+
do_action( 'xmlrpc_call', 'wp.editComment', $args, $this );
3833
+
$comment = array(
3834
+
'comment_ID' => $comment_id,
3835
+
);
3836
+
3837
+
if ( isset( $content_struct['status'] ) ) {
3838
+
$statuses = get_comment_statuses();
3839
+
$statuses = array_keys( $statuses );
3840
+
3841
+
if ( ! in_array( $content_struct['status'], $statuses, true ) ) {
3842
+
return new IXR_Error( 401, __( 'Invalid comment status.' ) );
3843
+
}
3844
+
3845
+
$comment['comment_approved'] = $content_struct['status'];
3846
+
}
3847
+
3848
+
// Do some timestamp voodoo.
3849
+
if ( ! empty( $content_struct['date_created_gmt'] ) ) {
3850
+
// We know this is supposed to be GMT, so we're going to slap that Z on there by force.
3851
+
$date_created = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z';
3852
+
3853
+
$comment['comment_date'] = get_date_from_gmt( $date_created );
3854
+
$comment['comment_date_gmt'] = iso8601_to_datetime( $date_created, 'gmt' );
3855
+
}
3856
+
3857
+
if ( isset( $content_struct['content'] ) ) {
3858
+
$comment['comment_content'] = $content_struct['content'];
3859
+
}
3860
+
3861
+
if ( isset( $content_struct['author'] ) ) {
3862
+
$comment['comment_author'] = $content_struct['author'];
3863
+
}
3864
+
3865
+
if ( isset( $content_struct['author_url'] ) ) {
3866
+
$comment['comment_author_url'] = $content_struct['author_url'];
3867
+
}
3868
+
3869
+
if ( isset( $content_struct['author_email'] ) ) {
3870
+
$comment['comment_author_email'] = $content_struct['author_email'];
3871
+
}
3872
+
3873
+
$result = wp_update_comment( $comment, true );
3874
+
if ( is_wp_error( $result ) ) {
3875
+
return new IXR_Error( 500, $result->get_error_message() );
3876
+
}
3877
+
3878
+
if ( ! $result ) {
3879
+
return new IXR_Error( 500, __( 'Sorry, the comment could not be updated.' ) );
3880
+
}
3881
+
3882
+
/**
3883
+
* Fires after a comment has been successfully updated via XML-RPC.
3884
+
*
3885
+
* @since 3.4.0
3886
+
*
3887
+
* @param int $comment_id ID of the updated comment.
3888
+
* @param array $args An array of arguments to update the comment.
3889
+
*/
3890
+
do_action( 'xmlrpc_call_success_wp_editComment', $comment_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
3891
+
3892
+
return true;
3893
+
}
3894
+
3895
+
/**
3896
+
* Creates a new comment.
3897
+
*
3898
+
* @since 2.7.0
3899
+
*
3900
+
* @param array $args {
3901
+
* Method arguments. Note: arguments must be ordered as documented.
3902
+
*
3903
+
* @type int $0 Blog ID (unused).
3904
+
* @type string $1 Username.
3905
+
* @type string $2 Password.
3906
+
* @type string|int $3 Post ID or URL.
3907
+
* @type array $4 Content structure.
3908
+
* }
3909
+
* @return int|IXR_Error See wp_new_comment().
3910
+
*/
3911
+
public function wp_newComment( $args ) {
3912
+
$this->escape( $args );
3913
+
3914
+
$username = $args[1];
3915
+
$password = $args[2];
3916
+
$post = $args[3];
3917
+
$content_struct = $args[4];
3918
+
3919
+
/**
3920
+
* Filters whether to allow anonymous comments over XML-RPC.
3921
+
*
3922
+
* @since 2.7.0
3923
+
*
3924
+
* @param bool $allow Whether to allow anonymous commenting via XML-RPC.
3925
+
* Default false.
3926
+
*/
3927
+
$allow_anon = apply_filters( 'xmlrpc_allow_anonymous_comments', false );
3928
+
3929
+
$user = $this->login( $username, $password );
3930
+
3931
+
if ( ! $user ) {
3932
+
$logged_in = false;
3933
+
if ( $allow_anon && get_option( 'comment_registration' ) ) {
3934
+
return new IXR_Error( 403, __( 'Sorry, you must be logged in to comment.' ) );
3935
+
} elseif ( ! $allow_anon ) {
3936
+
return $this->error;
3937
+
}
3938
+
} else {
3939
+
$logged_in = true;
3940
+
}
3941
+
3942
+
if ( is_numeric( $post ) ) {
3943
+
$post_id = absint( $post );
3944
+
} else {
3945
+
$post_id = url_to_postid( $post );
3946
+
}
3947
+
3948
+
if ( ! $post_id ) {
3949
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
3950
+
}
3951
+
3952
+
if ( ! get_post( $post_id ) ) {
3953
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
3954
+
}
3955
+
3956
+
if ( ! comments_open( $post_id ) ) {
3957
+
return new IXR_Error( 403, __( 'Sorry, comments are closed for this item.' ) );
3958
+
}
3959
+
3960
+
if (
3961
+
'publish' === get_post_status( $post_id ) &&
3962
+
! current_user_can( 'edit_post', $post_id ) &&
3963
+
post_password_required( $post_id )
3964
+
) {
3965
+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to comment on this post.' ) );
3966
+
}
3967
+
3968
+
if (
3969
+
'private' === get_post_status( $post_id ) &&
3970
+
! current_user_can( 'read_post', $post_id )
3971
+
) {
3972
+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to comment on this post.' ) );
3973
+
}
3974
+
3975
+
$comment = array(
3976
+
'comment_post_ID' => $post_id,
3977
+
'comment_content' => trim( $content_struct['content'] ),
3978
+
);
3979
+
3980
+
if ( $logged_in ) {
3981
+
$display_name = $user->display_name;
3982
+
$user_email = $user->user_email;
3983
+
$user_url = $user->user_url;
3984
+
3985
+
$comment['comment_author'] = $this->escape( $display_name );
3986
+
$comment['comment_author_email'] = $this->escape( $user_email );
3987
+
$comment['comment_author_url'] = $this->escape( $user_url );
3988
+
$comment['user_id'] = $user->ID;
3989
+
} else {
3990
+
$comment['comment_author'] = '';
3991
+
if ( isset( $content_struct['author'] ) ) {
3992
+
$comment['comment_author'] = $content_struct['author'];
3993
+
}
3994
+
3995
+
$comment['comment_author_email'] = '';
3996
+
if ( isset( $content_struct['author_email'] ) ) {
3997
+
$comment['comment_author_email'] = $content_struct['author_email'];
3998
+
}
3999
+
4000
+
$comment['comment_author_url'] = '';
4001
+
if ( isset( $content_struct['author_url'] ) ) {
4002
+
$comment['comment_author_url'] = $content_struct['author_url'];
4003
+
}
4004
+
4005
+
$comment['user_id'] = 0;
4006
+
4007
+
if ( get_option( 'require_name_email' ) ) {
4008
+
if ( strlen( $comment['comment_author_email'] ) < 6 || '' === $comment['comment_author'] ) {
4009
+
return new IXR_Error( 403, __( 'Comment author name and email are required.' ) );
4010
+
} elseif ( ! is_email( $comment['comment_author_email'] ) ) {
4011
+
return new IXR_Error( 403, __( 'A valid email address is required.' ) );
4012
+
}
4013
+
}
4014
+
}
4015
+
4016
+
$comment['comment_parent'] = isset( $content_struct['comment_parent'] ) ? absint( $content_struct['comment_parent'] ) : 0;
4017
+
4018
+
/** This filter is documented in wp-includes/comment.php */
4019
+
$allow_empty = apply_filters( 'allow_empty_comment', false, $comment );
4020
+
4021
+
if ( ! $allow_empty && '' === $comment['comment_content'] ) {
4022
+
return new IXR_Error( 403, __( 'Comment is required.' ) );
4023
+
}
4024
+
4025
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4026
+
do_action( 'xmlrpc_call', 'wp.newComment', $args, $this );
4027
+
4028
+
$comment_id = wp_new_comment( $comment, true );
4029
+
if ( is_wp_error( $comment_id ) ) {
4030
+
return new IXR_Error( 403, $comment_id->get_error_message() );
4031
+
}
4032
+
4033
+
if ( ! $comment_id ) {
4034
+
return new IXR_Error( 403, __( 'An error occurred while processing your comment. Please ensure all fields are filled correctly and try again.' ) );
4035
+
}
4036
+
4037
+
/**
4038
+
* Fires after a new comment has been successfully created via XML-RPC.
4039
+
*
4040
+
* @since 3.4.0
4041
+
*
4042
+
* @param int $comment_id ID of the new comment.
4043
+
* @param array $args An array of new comment arguments.
4044
+
*/
4045
+
do_action( 'xmlrpc_call_success_wp_newComment', $comment_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
4046
+
4047
+
return $comment_id;
4048
+
}
4049
+
4050
+
/**
4051
+
* Retrieves all of the comment status.
4052
+
*
4053
+
* @since 2.7.0
4054
+
*
4055
+
* @param array $args {
4056
+
* Method arguments. Note: arguments must be ordered as documented.
4057
+
*
4058
+
* @type int $0 Blog ID (unused).
4059
+
* @type string $1 Username.
4060
+
* @type string $2 Password.
4061
+
* }
4062
+
* @return array|IXR_Error
4063
+
*/
4064
+
public function wp_getCommentStatusList( $args ) {
4065
+
$this->escape( $args );
4066
+
4067
+
$username = $args[1];
4068
+
$password = $args[2];
4069
+
4070
+
$user = $this->login( $username, $password );
4071
+
if ( ! $user ) {
4072
+
return $this->error;
4073
+
}
4074
+
4075
+
if ( ! current_user_can( 'publish_posts' ) ) {
4076
+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) );
4077
+
}
4078
+
4079
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4080
+
do_action( 'xmlrpc_call', 'wp.getCommentStatusList', $args, $this );
4081
+
4082
+
return get_comment_statuses();
4083
+
}
4084
+
4085
+
/**
4086
+
* Retrieves comment counts.
4087
+
*
4088
+
* @since 2.5.0
4089
+
*
4090
+
* @param array $args {
4091
+
* Method arguments. Note: arguments must be ordered as documented.
4092
+
*
4093
+
* @type int $0 Blog ID (unused).
4094
+
* @type string $1 Username.
4095
+
* @type string $2 Password.
4096
+
* @type int $3 Post ID.
4097
+
* }
4098
+
* @return array|IXR_Error
4099
+
*/
4100
+
public function wp_getCommentCount( $args ) {
4101
+
$this->escape( $args );
4102
+
4103
+
$username = $args[1];
4104
+
$password = $args[2];
4105
+
$post_id = (int) $args[3];
4106
+
4107
+
$user = $this->login( $username, $password );
4108
+
if ( ! $user ) {
4109
+
return $this->error;
4110
+
}
4111
+
4112
+
$post = get_post( $post_id, ARRAY_A );
4113
+
if ( empty( $post['ID'] ) ) {
4114
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
4115
+
}
4116
+
4117
+
if ( ! current_user_can( 'edit_post', $post_id ) ) {
4118
+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details of this post.' ) );
4119
+
}
4120
+
4121
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4122
+
do_action( 'xmlrpc_call', 'wp.getCommentCount', $args, $this );
4123
+
4124
+
$count = wp_count_comments( $post_id );
4125
+
4126
+
return array(
4127
+
'approved' => $count->approved,
4128
+
'awaiting_moderation' => $count->moderated,
4129
+
'spam' => $count->spam,
4130
+
'total_comments' => $count->total_comments,
4131
+
);
4132
+
}
4133
+
4134
+
/**
4135
+
* Retrieves post statuses.
4136
+
*
4137
+
* @since 2.5.0
4138
+
*
4139
+
* @param array $args {
4140
+
* Method arguments. Note: arguments must be ordered as documented.
4141
+
*
4142
+
* @type int $0 Blog ID (unused).
4143
+
* @type string $1 Username.
4144
+
* @type string $2 Password.
4145
+
* }
4146
+
* @return array|IXR_Error
4147
+
*/
4148
+
public function wp_getPostStatusList( $args ) {
4149
+
$this->escape( $args );
4150
+
4151
+
$username = $args[1];
4152
+
$password = $args[2];
4153
+
4154
+
$user = $this->login( $username, $password );
4155
+
if ( ! $user ) {
4156
+
return $this->error;
4157
+
}
4158
+
4159
+
if ( ! current_user_can( 'edit_posts' ) ) {
4160
+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) );
4161
+
}
4162
+
4163
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4164
+
do_action( 'xmlrpc_call', 'wp.getPostStatusList', $args, $this );
4165
+
4166
+
return get_post_statuses();
4167
+
}
4168
+
4169
+
/**
4170
+
* Retrieves page statuses.
4171
+
*
4172
+
* @since 2.5.0
4173
+
*
4174
+
* @param array $args {
4175
+
* Method arguments. Note: arguments must be ordered as documented.
4176
+
*
4177
+
* @type int $0 Blog ID (unused).
4178
+
* @type string $1 Username.
4179
+
* @type string $2 Password.
4180
+
* }
4181
+
* @return array|IXR_Error
4182
+
*/
4183
+
public function wp_getPageStatusList( $args ) {
4184
+
$this->escape( $args );
4185
+
4186
+
$username = $args[1];
4187
+
$password = $args[2];
4188
+
4189
+
$user = $this->login( $username, $password );
4190
+
if ( ! $user ) {
4191
+
return $this->error;
4192
+
}
4193
+
4194
+
if ( ! current_user_can( 'edit_pages' ) ) {
4195
+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) );
4196
+
}
4197
+
4198
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4199
+
do_action( 'xmlrpc_call', 'wp.getPageStatusList', $args, $this );
4200
+
4201
+
return get_page_statuses();
4202
+
}
4203
+
4204
+
/**
4205
+
* Retrieves page templates.
4206
+
*
4207
+
* @since 2.6.0
4208
+
*
4209
+
* @param array $args {
4210
+
* Method arguments. Note: arguments must be ordered as documented.
4211
+
*
4212
+
* @type int $0 Blog ID (unused).
4213
+
* @type string $1 Username.
4214
+
* @type string $2 Password.
4215
+
* }
4216
+
* @return array|IXR_Error
4217
+
*/
4218
+
public function wp_getPageTemplates( $args ) {
4219
+
$this->escape( $args );
4220
+
4221
+
$username = $args[1];
4222
+
$password = $args[2];
4223
+
4224
+
$user = $this->login( $username, $password );
4225
+
if ( ! $user ) {
4226
+
return $this->error;
4227
+
}
4228
+
4229
+
if ( ! current_user_can( 'edit_pages' ) ) {
4230
+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) );
4231
+
}
4232
+
4233
+
$templates = get_page_templates();
4234
+
$templates['Default'] = 'default';
4235
+
4236
+
return $templates;
4237
+
}
4238
+
4239
+
/**
4240
+
* Retrieves blog options.
4241
+
*
4242
+
* @since 2.6.0
4243
+
*
4244
+
* @param array $args {
4245
+
* Method arguments. Note: arguments must be ordered as documented.
4246
+
*
4247
+
* @type int $0 Blog ID (unused).
4248
+
* @type string $1 Username.
4249
+
* @type string $2 Password.
4250
+
* @type array $3 Optional. Options.
4251
+
* }
4252
+
* @return array|IXR_Error
4253
+
*/
4254
+
public function wp_getOptions( $args ) {
4255
+
$this->escape( $args );
4256
+
4257
+
$username = $args[1];
4258
+
$password = $args[2];
4259
+
$options = isset( $args[3] ) ? (array) $args[3] : array();
4260
+
4261
+
$user = $this->login( $username, $password );
4262
+
if ( ! $user ) {
4263
+
return $this->error;
4264
+
}
4265
+
4266
+
// If no specific options where asked for, return all of them.
4267
+
if ( count( $options ) === 0 ) {
4268
+
$options = array_keys( $this->blog_options );
4269
+
}
4270
+
4271
+
return $this->_getOptions( $options );
4272
+
}
4273
+
4274
+
/**
4275
+
* Retrieves blog options value from list.
4276
+
*
4277
+
* @since 2.6.0
4278
+
*
4279
+
* @param array $options Options to retrieve.
4280
+
* @return array
4281
+
*/
4282
+
public function _getOptions( $options ) {
4283
+
$data = array();
4284
+
$can_manage = current_user_can( 'manage_options' );
4285
+
foreach ( $options as $option ) {
4286
+
if ( array_key_exists( $option, $this->blog_options ) ) {
4287
+
$data[ $option ] = $this->blog_options[ $option ];
4288
+
// Is the value static or dynamic?
4289
+
if ( isset( $data[ $option ]['option'] ) ) {
4290
+
$data[ $option ]['value'] = get_option( $data[ $option ]['option'] );
4291
+
unset( $data[ $option ]['option'] );
4292
+
}
4293
+
4294
+
if ( ! $can_manage ) {
4295
+
$data[ $option ]['readonly'] = true;
4296
+
}
4297
+
}
4298
+
}
4299
+
4300
+
return $data;
4301
+
}
4302
+
4303
+
/**
4304
+
* Updates blog options.
4305
+
*
4306
+
* @since 2.6.0
4307
+
*
4308
+
* @param array $args {
4309
+
* Method arguments. Note: arguments must be ordered as documented.
4310
+
*
4311
+
* @type int $0 Blog ID (unused).
4312
+
* @type string $1 Username.
4313
+
* @type string $2 Password.
4314
+
* @type array $3 Options.
4315
+
* }
4316
+
* @return array|IXR_Error
4317
+
*/
4318
+
public function wp_setOptions( $args ) {
4319
+
$this->escape( $args );
4320
+
4321
+
$username = $args[1];
4322
+
$password = $args[2];
4323
+
$options = (array) $args[3];
4324
+
4325
+
$user = $this->login( $username, $password );
4326
+
if ( ! $user ) {
4327
+
return $this->error;
4328
+
}
4329
+
4330
+
if ( ! current_user_can( 'manage_options' ) ) {
4331
+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to update options.' ) );
4332
+
}
4333
+
4334
+
$option_names = array();
4335
+
foreach ( $options as $o_name => $o_value ) {
4336
+
$option_names[] = $o_name;
4337
+
if ( ! array_key_exists( $o_name, $this->blog_options ) ) {
4338
+
continue;
4339
+
}
4340
+
4341
+
if ( $this->blog_options[ $o_name ]['readonly'] ) {
4342
+
continue;
4343
+
}
4344
+
4345
+
update_option( $this->blog_options[ $o_name ]['option'], wp_unslash( $o_value ) );
4346
+
}
4347
+
4348
+
// Now return the updated values.
4349
+
return $this->_getOptions( $option_names );
4350
+
}
4351
+
4352
+
/**
4353
+
* Retrieves a media item by ID.
4354
+
*
4355
+
* @since 3.1.0
4356
+
*
4357
+
* @param array $args {
4358
+
* Method arguments. Note: arguments must be ordered as documented.
4359
+
*
4360
+
* @type int $0 Blog ID (unused).
4361
+
* @type string $1 Username.
4362
+
* @type string $2 Password.
4363
+
* @type int $3 Attachment ID.
4364
+
* }
4365
+
* @return array|IXR_Error Associative array contains:
4366
+
* - 'date_created_gmt'
4367
+
* - 'parent'
4368
+
* - 'link'
4369
+
* - 'thumbnail'
4370
+
* - 'title'
4371
+
* - 'caption'
4372
+
* - 'description'
4373
+
* - 'metadata'
4374
+
*/
4375
+
public function wp_getMediaItem( $args ) {
4376
+
$this->escape( $args );
4377
+
4378
+
$username = $args[1];
4379
+
$password = $args[2];
4380
+
$attachment_id = (int) $args[3];
4381
+
4382
+
$user = $this->login( $username, $password );
4383
+
if ( ! $user ) {
4384
+
return $this->error;
4385
+
}
4386
+
4387
+
if ( ! current_user_can( 'upload_files' ) ) {
4388
+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to upload files.' ) );
4389
+
}
4390
+
4391
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4392
+
do_action( 'xmlrpc_call', 'wp.getMediaItem', $args, $this );
4393
+
4394
+
$attachment = get_post( $attachment_id );
4395
+
if ( ! $attachment || 'attachment' !== $attachment->post_type ) {
4396
+
return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
4397
+
}
4398
+
4399
+
return $this->_prepare_media_item( $attachment );
4400
+
}
4401
+
4402
+
/**
4403
+
* Retrieves a collection of media library items (or attachments).
4404
+
*
4405
+
* Besides the common blog_id (unused), username, and password arguments,
4406
+
* it takes a filter array as the last argument.
4407
+
*
4408
+
* Accepted 'filter' keys are 'parent_id', 'mime_type', 'offset', and 'number'.
4409
+
*
4410
+
* The defaults are as follows:
4411
+
* - 'number' - Default is 5. Total number of media items to retrieve.
4412
+
* - 'offset' - Default is 0. See WP_Query::query() for more.
4413
+
* - 'parent_id' - Default is ''. The post where the media item is attached.
4414
+
* Empty string shows all media items. 0 shows unattached media items.
4415
+
* - 'mime_type' - Default is ''. Filter by mime type (e.g., 'image/jpeg', 'application/pdf')
4416
+
*
4417
+
* @since 3.1.0
4418
+
*
4419
+
* @param array $args {
4420
+
* Method arguments. Note: arguments must be ordered as documented.
4421
+
*
4422
+
* @type int $0 Blog ID (unused).
4423
+
* @type string $1 Username.
4424
+
* @type string $2 Password.
4425
+
* @type array $3 Optional. Query arguments.
4426
+
* }
4427
+
* @return array|IXR_Error Array containing a collection of media items.
4428
+
* See wp_xmlrpc_server::wp_getMediaItem() for a description
4429
+
* of each item contents.
4430
+
*/
4431
+
public function wp_getMediaLibrary( $args ) {
4432
+
$this->escape( $args );
4433
+
4434
+
$username = $args[1];
4435
+
$password = $args[2];
4436
+
$struct = isset( $args[3] ) ? $args[3] : array();
4437
+
4438
+
$user = $this->login( $username, $password );
4439
+
if ( ! $user ) {
4440
+
return $this->error;
4441
+
}
4442
+
4443
+
if ( ! current_user_can( 'upload_files' ) ) {
4444
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to upload files.' ) );
4445
+
}
4446
+
4447
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4448
+
do_action( 'xmlrpc_call', 'wp.getMediaLibrary', $args, $this );
4449
+
4450
+
$parent_id = ( isset( $struct['parent_id'] ) ) ? absint( $struct['parent_id'] ) : '';
4451
+
$mime_type = ( isset( $struct['mime_type'] ) ) ? $struct['mime_type'] : '';
4452
+
$offset = ( isset( $struct['offset'] ) ) ? absint( $struct['offset'] ) : 0;
4453
+
$number = ( isset( $struct['number'] ) ) ? absint( $struct['number'] ) : -1;
4454
+
4455
+
$attachments = get_posts(
4456
+
array(
4457
+
'post_type' => 'attachment',
4458
+
'post_parent' => $parent_id,
4459
+
'offset' => $offset,
4460
+
'numberposts' => $number,
4461
+
'post_mime_type' => $mime_type,
4462
+
)
4463
+
);
4464
+
4465
+
$attachments_struct = array();
4466
+
4467
+
foreach ( $attachments as $attachment ) {
4468
+
$attachments_struct[] = $this->_prepare_media_item( $attachment );
4469
+
}
4470
+
4471
+
return $attachments_struct;
4472
+
}
4473
+
4474
+
/**
4475
+
* Retrieves a list of post formats used by the site.
4476
+
*
4477
+
* @since 3.1.0
4478
+
*
4479
+
* @param array $args {
4480
+
* Method arguments. Note: arguments must be ordered as documented.
4481
+
*
4482
+
* @type int $0 Blog ID (unused).
4483
+
* @type string $1 Username.
4484
+
* @type string $2 Password.
4485
+
* }
4486
+
* @return array|IXR_Error List of post formats, otherwise IXR_Error object.
4487
+
*/
4488
+
public function wp_getPostFormats( $args ) {
4489
+
$this->escape( $args );
4490
+
4491
+
$username = $args[1];
4492
+
$password = $args[2];
4493
+
4494
+
$user = $this->login( $username, $password );
4495
+
if ( ! $user ) {
4496
+
return $this->error;
4497
+
}
4498
+
4499
+
if ( ! current_user_can( 'edit_posts' ) ) {
4500
+
return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) );
4501
+
}
4502
+
4503
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4504
+
do_action( 'xmlrpc_call', 'wp.getPostFormats', $args, $this );
4505
+
4506
+
$formats = get_post_format_strings();
4507
+
4508
+
// Find out if they want a list of currently supports formats.
4509
+
if ( isset( $args[3] ) && is_array( $args[3] ) ) {
4510
+
if ( $args[3]['show-supported'] ) {
4511
+
if ( current_theme_supports( 'post-formats' ) ) {
4512
+
$supported = get_theme_support( 'post-formats' );
4513
+
4514
+
$data = array();
4515
+
$data['all'] = $formats;
4516
+
$data['supported'] = $supported[0];
4517
+
4518
+
$formats = $data;
4519
+
}
4520
+
}
4521
+
}
4522
+
4523
+
return $formats;
4524
+
}
4525
+
4526
+
/**
4527
+
* Retrieves a post type.
4528
+
*
4529
+
* @since 3.4.0
4530
+
*
4531
+
* @see get_post_type_object()
4532
+
*
4533
+
* @param array $args {
4534
+
* Method arguments. Note: arguments must be ordered as documented.
4535
+
*
4536
+
* @type int $0 Blog ID (unused).
4537
+
* @type string $1 Username.
4538
+
* @type string $2 Password.
4539
+
* @type string $3 Post type name.
4540
+
* @type array $4 Optional. Fields to fetch.
4541
+
* }
4542
+
* @return array|IXR_Error Array contains:
4543
+
* - 'labels'
4544
+
* - 'description'
4545
+
* - 'capability_type'
4546
+
* - 'cap'
4547
+
* - 'map_meta_cap'
4548
+
* - 'hierarchical'
4549
+
* - 'menu_position'
4550
+
* - 'taxonomies'
4551
+
* - 'supports'
4552
+
*/
4553
+
public function wp_getPostType( $args ) {
4554
+
if ( ! $this->minimum_args( $args, 4 ) ) {
4555
+
return $this->error;
4556
+
}
4557
+
4558
+
$this->escape( $args );
4559
+
4560
+
$username = $args[1];
4561
+
$password = $args[2];
4562
+
$post_type_name = $args[3];
4563
+
4564
+
if ( isset( $args[4] ) ) {
4565
+
$fields = $args[4];
4566
+
} else {
4567
+
/**
4568
+
* Filters the default post type query fields used by the given XML-RPC method.
4569
+
*
4570
+
* @since 3.4.0
4571
+
*
4572
+
* @param array $fields An array of post type fields to retrieve. By default,
4573
+
* contains 'labels', 'cap', and 'taxonomies'.
4574
+
* @param string $method The method name.
4575
+
*/
4576
+
$fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostType' );
4577
+
}
4578
+
4579
+
$user = $this->login( $username, $password );
4580
+
if ( ! $user ) {
4581
+
return $this->error;
4582
+
}
4583
+
4584
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4585
+
do_action( 'xmlrpc_call', 'wp.getPostType', $args, $this );
4586
+
4587
+
if ( ! post_type_exists( $post_type_name ) ) {
4588
+
return new IXR_Error( 403, __( 'Invalid post type.' ) );
4589
+
}
4590
+
4591
+
$post_type = get_post_type_object( $post_type_name );
4592
+
4593
+
if ( ! current_user_can( $post_type->cap->edit_posts ) ) {
4594
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts in this post type.' ) );
4595
+
}
4596
+
4597
+
return $this->_prepare_post_type( $post_type, $fields );
4598
+
}
4599
+
4600
+
/**
4601
+
* Retrieves post types.
4602
+
*
4603
+
* @since 3.4.0
4604
+
*
4605
+
* @see get_post_types()
4606
+
*
4607
+
* @param array $args {
4608
+
* Method arguments. Note: arguments must be ordered as documented.
4609
+
*
4610
+
* @type int $0 Blog ID (unused).
4611
+
* @type string $1 Username.
4612
+
* @type string $2 Password.
4613
+
* @type array $3 Optional. Query arguments.
4614
+
* @type array $4 Optional. Fields to fetch.
4615
+
* }
4616
+
* @return array|IXR_Error
4617
+
*/
4618
+
public function wp_getPostTypes( $args ) {
4619
+
if ( ! $this->minimum_args( $args, 3 ) ) {
4620
+
return $this->error;
4621
+
}
4622
+
4623
+
$this->escape( $args );
4624
+
4625
+
$username = $args[1];
4626
+
$password = $args[2];
4627
+
$filter = isset( $args[3] ) ? $args[3] : array( 'public' => true );
4628
+
4629
+
if ( isset( $args[4] ) ) {
4630
+
$fields = $args[4];
4631
+
} else {
4632
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4633
+
$fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostTypes' );
4634
+
}
4635
+
4636
+
$user = $this->login( $username, $password );
4637
+
if ( ! $user ) {
4638
+
return $this->error;
4639
+
}
4640
+
4641
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4642
+
do_action( 'xmlrpc_call', 'wp.getPostTypes', $args, $this );
4643
+
4644
+
$post_types = get_post_types( $filter, 'objects' );
4645
+
4646
+
$struct = array();
4647
+
4648
+
foreach ( $post_types as $post_type ) {
4649
+
if ( ! current_user_can( $post_type->cap->edit_posts ) ) {
4650
+
continue;
4651
+
}
4652
+
4653
+
$struct[ $post_type->name ] = $this->_prepare_post_type( $post_type, $fields );
4654
+
}
4655
+
4656
+
return $struct;
4657
+
}
4658
+
4659
+
/**
4660
+
* Retrieves revisions for a specific post.
4661
+
*
4662
+
* @since 3.5.0
4663
+
*
4664
+
* The optional $fields parameter specifies what fields will be included
4665
+
* in the response array.
4666
+
*
4667
+
* @uses wp_get_post_revisions()
4668
+
* @see wp_getPost() for more on $fields
4669
+
*
4670
+
* @param array $args {
4671
+
* Method arguments. Note: arguments must be ordered as documented.
4672
+
*
4673
+
* @type int $0 Blog ID (unused).
4674
+
* @type string $1 Username.
4675
+
* @type string $2 Password.
4676
+
* @type int $3 Post ID.
4677
+
* @type array $4 Optional. Fields to fetch.
4678
+
* }
4679
+
* @return array|IXR_Error Array containing a collection of posts.
4680
+
*/
4681
+
public function wp_getRevisions( $args ) {
4682
+
if ( ! $this->minimum_args( $args, 4 ) ) {
4683
+
return $this->error;
4684
+
}
4685
+
4686
+
$this->escape( $args );
4687
+
4688
+
$username = $args[1];
4689
+
$password = $args[2];
4690
+
$post_id = (int) $args[3];
4691
+
4692
+
if ( isset( $args[4] ) ) {
4693
+
$fields = $args[4];
4694
+
} else {
4695
+
/**
4696
+
* Filters the default revision query fields used by the given XML-RPC method.
4697
+
*
4698
+
* @since 3.5.0
4699
+
*
4700
+
* @param array $field An array of revision fields to retrieve. By default,
4701
+
* contains 'post_date' and 'post_date_gmt'.
4702
+
* @param string $method The method name.
4703
+
*/
4704
+
$fields = apply_filters( 'xmlrpc_default_revision_fields', array( 'post_date', 'post_date_gmt' ), 'wp.getRevisions' );
4705
+
}
4706
+
4707
+
$user = $this->login( $username, $password );
4708
+
if ( ! $user ) {
4709
+
return $this->error;
4710
+
}
4711
+
4712
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4713
+
do_action( 'xmlrpc_call', 'wp.getRevisions', $args, $this );
4714
+
4715
+
$post = get_post( $post_id );
4716
+
if ( ! $post ) {
4717
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
4718
+
}
4719
+
4720
+
if ( ! current_user_can( 'edit_post', $post_id ) ) {
4721
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) );
4722
+
}
4723
+
4724
+
// Check if revisions are enabled.
4725
+
if ( ! wp_revisions_enabled( $post ) ) {
4726
+
return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) );
4727
+
}
4728
+
4729
+
$revisions = wp_get_post_revisions( $post_id );
4730
+
4731
+
if ( ! $revisions ) {
4732
+
return array();
4733
+
}
4734
+
4735
+
$struct = array();
4736
+
4737
+
foreach ( $revisions as $revision ) {
4738
+
if ( ! current_user_can( 'read_post', $revision->ID ) ) {
4739
+
continue;
4740
+
}
4741
+
4742
+
// Skip autosaves.
4743
+
if ( wp_is_post_autosave( $revision ) ) {
4744
+
continue;
4745
+
}
4746
+
4747
+
$struct[] = $this->_prepare_post( get_object_vars( $revision ), $fields );
4748
+
}
4749
+
4750
+
return $struct;
4751
+
}
4752
+
4753
+
/**
4754
+
* Restores a post revision.
4755
+
*
4756
+
* @since 3.5.0
4757
+
*
4758
+
* @uses wp_restore_post_revision()
4759
+
*
4760
+
* @param array $args {
4761
+
* Method arguments. Note: arguments must be ordered as documented.
4762
+
*
4763
+
* @type int $0 Blog ID (unused).
4764
+
* @type string $1 Username.
4765
+
* @type string $2 Password.
4766
+
* @type int $3 Revision ID.
4767
+
* }
4768
+
* @return bool|IXR_Error false if there was an error restoring, true if success.
4769
+
*/
4770
+
public function wp_restoreRevision( $args ) {
4771
+
if ( ! $this->minimum_args( $args, 3 ) ) {
4772
+
return $this->error;
4773
+
}
4774
+
4775
+
$this->escape( $args );
4776
+
4777
+
$username = $args[1];
4778
+
$password = $args[2];
4779
+
$revision_id = (int) $args[3];
4780
+
4781
+
$user = $this->login( $username, $password );
4782
+
if ( ! $user ) {
4783
+
return $this->error;
4784
+
}
4785
+
4786
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4787
+
do_action( 'xmlrpc_call', 'wp.restoreRevision', $args, $this );
4788
+
4789
+
$revision = wp_get_post_revision( $revision_id );
4790
+
if ( ! $revision ) {
4791
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
4792
+
}
4793
+
4794
+
if ( wp_is_post_autosave( $revision ) ) {
4795
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
4796
+
}
4797
+
4798
+
$post = get_post( $revision->post_parent );
4799
+
if ( ! $post ) {
4800
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
4801
+
}
4802
+
4803
+
if ( ! current_user_can( 'edit_post', $revision->post_parent ) ) {
4804
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
4805
+
}
4806
+
4807
+
// Check if revisions are disabled.
4808
+
if ( ! wp_revisions_enabled( $post ) ) {
4809
+
return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) );
4810
+
}
4811
+
4812
+
$post = wp_restore_post_revision( $revision_id );
4813
+
4814
+
return (bool) $post;
4815
+
}
4816
+
4817
+
/*
4818
+
* Blogger API functions.
4819
+
* Specs on http://plant.blogger.com/api and https://groups.yahoo.com/group/bloggerDev/
4820
+
*/
4821
+
4822
+
/**
4823
+
* Retrieves blogs that user owns.
4824
+
*
4825
+
* Will make more sense once we support multiple blogs.
4826
+
*
4827
+
* @since 1.5.0
4828
+
*
4829
+
* @param array $args {
4830
+
* Method arguments. Note: arguments must be ordered as documented.
4831
+
*
4832
+
* @type int $0 Blog ID (unused).
4833
+
* @type string $1 Username.
4834
+
* @type string $2 Password.
4835
+
* }
4836
+
* @return array|IXR_Error
4837
+
*/
4838
+
public function blogger_getUsersBlogs( $args ) {
4839
+
if ( ! $this->minimum_args( $args, 3 ) ) {
4840
+
return $this->error;
4841
+
}
4842
+
4843
+
if ( is_multisite() ) {
4844
+
return $this->_multisite_getUsersBlogs( $args );
4845
+
}
4846
+
4847
+
$this->escape( $args );
4848
+
4849
+
$username = $args[1];
4850
+
$password = $args[2];
4851
+
4852
+
$user = $this->login( $username, $password );
4853
+
if ( ! $user ) {
4854
+
return $this->error;
4855
+
}
4856
+
4857
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4858
+
do_action( 'xmlrpc_call', 'blogger.getUsersBlogs', $args, $this );
4859
+
4860
+
$is_admin = current_user_can( 'manage_options' );
4861
+
4862
+
$struct = array(
4863
+
'isAdmin' => $is_admin,
4864
+
'url' => get_option( 'home' ) . '/',
4865
+
'blogid' => '1',
4866
+
'blogName' => get_option( 'blogname' ),
4867
+
'xmlrpc' => site_url( 'xmlrpc.php', 'rpc' ),
4868
+
);
4869
+
4870
+
return array( $struct );
4871
+
}
4872
+
4873
+
/**
4874
+
* Private function for retrieving a users blogs for multisite setups.
4875
+
*
4876
+
* @since 3.0.0
4877
+
*
4878
+
* @param array $args {
4879
+
* Method arguments. Note: arguments must be ordered as documented.
4880
+
*
4881
+
* @type int $0 Blog ID (unused).
4882
+
* @type string $1 Username.
4883
+
* @type string $2 Password.
4884
+
* }
4885
+
* @return array|IXR_Error
4886
+
*/
4887
+
protected function _multisite_getUsersBlogs( $args ) {
4888
+
$current_blog = get_site();
4889
+
4890
+
$domain = $current_blog->domain;
4891
+
$path = $current_blog->path . 'xmlrpc.php';
4892
+
4893
+
$blogs = $this->wp_getUsersBlogs( $args );
4894
+
if ( $blogs instanceof IXR_Error ) {
4895
+
return $blogs;
4896
+
}
4897
+
4898
+
if ( $_SERVER['HTTP_HOST'] === $domain && $_SERVER['REQUEST_URI'] === $path ) {
4899
+
return $blogs;
4900
+
} else {
4901
+
foreach ( (array) $blogs as $blog ) {
4902
+
if ( str_contains( $blog['url'], $_SERVER['HTTP_HOST'] ) ) {
4903
+
return array( $blog );
4904
+
}
4905
+
}
4906
+
return array();
4907
+
}
4908
+
}
4909
+
4910
+
/**
4911
+
* Retrieves user's data.
4912
+
*
4913
+
* Gives your client some info about you, so you don't have to.
4914
+
*
4915
+
* @since 1.5.0
4916
+
*
4917
+
* @param array $args {
4918
+
* Method arguments. Note: arguments must be ordered as documented.
4919
+
*
4920
+
* @type int $0 Blog ID (unused).
4921
+
* @type string $1 Username.
4922
+
* @type string $2 Password.
4923
+
* }
4924
+
* @return array|IXR_Error
4925
+
*/
4926
+
public function blogger_getUserInfo( $args ) {
4927
+
$this->escape( $args );
4928
+
4929
+
$username = $args[1];
4930
+
$password = $args[2];
4931
+
4932
+
$user = $this->login( $username, $password );
4933
+
if ( ! $user ) {
4934
+
return $this->error;
4935
+
}
4936
+
4937
+
if ( ! current_user_can( 'edit_posts' ) ) {
4938
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to access user data on this site.' ) );
4939
+
}
4940
+
4941
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4942
+
do_action( 'xmlrpc_call', 'blogger.getUserInfo', $args, $this );
4943
+
4944
+
$struct = array(
4945
+
'nickname' => $user->nickname,
4946
+
'userid' => $user->ID,
4947
+
'url' => $user->user_url,
4948
+
'lastname' => $user->last_name,
4949
+
'firstname' => $user->first_name,
4950
+
);
4951
+
4952
+
return $struct;
4953
+
}
4954
+
4955
+
/**
4956
+
* Retrieves a post.
4957
+
*
4958
+
* @since 1.5.0
4959
+
*
4960
+
* @param array $args {
4961
+
* Method arguments. Note: arguments must be ordered as documented.
4962
+
*
4963
+
* @type int $0 Blog ID (unused).
4964
+
* @type int $1 Post ID.
4965
+
* @type string $2 Username.
4966
+
* @type string $3 Password.
4967
+
* }
4968
+
* @return array|IXR_Error
4969
+
*/
4970
+
public function blogger_getPost( $args ) {
4971
+
$this->escape( $args );
4972
+
4973
+
$post_id = (int) $args[1];
4974
+
$username = $args[2];
4975
+
$password = $args[3];
4976
+
4977
+
$user = $this->login( $username, $password );
4978
+
if ( ! $user ) {
4979
+
return $this->error;
4980
+
}
4981
+
4982
+
$post_data = get_post( $post_id, ARRAY_A );
4983
+
if ( ! $post_data ) {
4984
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
4985
+
}
4986
+
4987
+
if ( ! current_user_can( 'edit_post', $post_id ) ) {
4988
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
4989
+
}
4990
+
4991
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
4992
+
do_action( 'xmlrpc_call', 'blogger.getPost', $args, $this );
4993
+
4994
+
$categories = implode( ',', wp_get_post_categories( $post_id ) );
4995
+
4996
+
$content = '<title>' . wp_unslash( $post_data['post_title'] ) . '</title>';
4997
+
$content .= '<category>' . $categories . '</category>';
4998
+
$content .= wp_unslash( $post_data['post_content'] );
4999
+
5000
+
$struct = array(
5001
+
'userid' => $post_data['post_author'],
5002
+
'dateCreated' => $this->_convert_date( $post_data['post_date'] ),
5003
+
'content' => $content,
5004
+
'postid' => (string) $post_data['ID'],
5005
+
);
5006
+
5007
+
return $struct;
5008
+
}
5009
+
5010
+
/**
5011
+
* Retrieves the list of recent posts.
5012
+
*
5013
+
* @since 1.5.0
5014
+
*
5015
+
* @param array $args {
5016
+
* Method arguments. Note: arguments must be ordered as documented.
5017
+
*
5018
+
* @type string $0 App key (unused).
5019
+
* @type int $1 Blog ID (unused).
5020
+
* @type string $2 Username.
5021
+
* @type string $3 Password.
5022
+
* @type int $4 Optional. Number of posts.
5023
+
* }
5024
+
* @return array|IXR_Error
5025
+
*/
5026
+
public function blogger_getRecentPosts( $args ) {
5027
+
5028
+
$this->escape( $args );
5029
+
5030
+
// $args[0] = appkey - ignored.
5031
+
$username = $args[2];
5032
+
$password = $args[3];
5033
+
if ( isset( $args[4] ) ) {
5034
+
$query = array( 'numberposts' => absint( $args[4] ) );
5035
+
} else {
5036
+
$query = array();
5037
+
}
5038
+
5039
+
$user = $this->login( $username, $password );
5040
+
if ( ! $user ) {
5041
+
return $this->error;
5042
+
}
5043
+
5044
+
if ( ! current_user_can( 'edit_posts' ) ) {
5045
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) );
5046
+
}
5047
+
5048
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
5049
+
do_action( 'xmlrpc_call', 'blogger.getRecentPosts', $args, $this );
5050
+
5051
+
$posts_list = wp_get_recent_posts( $query );
5052
+
5053
+
if ( ! $posts_list ) {
5054
+
$this->error = new IXR_Error( 500, __( 'No posts found or an error occurred while retrieving posts.' ) );
5055
+
return $this->error;
5056
+
}
5057
+
5058
+
$recent_posts = array();
5059
+
foreach ( $posts_list as $entry ) {
5060
+
if ( ! current_user_can( 'edit_post', $entry['ID'] ) ) {
5061
+
continue;
5062
+
}
5063
+
5064
+
$post_date = $this->_convert_date( $entry['post_date'] );
5065
+
$categories = implode( ',', wp_get_post_categories( $entry['ID'] ) );
5066
+
5067
+
$content = '<title>' . wp_unslash( $entry['post_title'] ) . '</title>';
5068
+
$content .= '<category>' . $categories . '</category>';
5069
+
$content .= wp_unslash( $entry['post_content'] );
5070
+
5071
+
$recent_posts[] = array(
5072
+
'userid' => $entry['post_author'],
5073
+
'dateCreated' => $post_date,
5074
+
'content' => $content,
5075
+
'postid' => (string) $entry['ID'],
5076
+
);
5077
+
}
5078
+
5079
+
return $recent_posts;
5080
+
}
5081
+
5082
+
/**
5083
+
* Deprecated.
5084
+
*
5085
+
* @since 1.5.0
5086
+
* @deprecated 3.5.0
5087
+
*
5088
+
* @param array $args Unused.
5089
+
* @return IXR_Error Error object.
5090
+
*/
5091
+
public function blogger_getTemplate( $args ) {
5092
+
return new IXR_Error( 403, __( 'Sorry, this method is not supported.' ) );
5093
+
}
5094
+
5095
+
/**
5096
+
* Deprecated.
5097
+
*
5098
+
* @since 1.5.0
5099
+
* @deprecated 3.5.0
5100
+
*
5101
+
* @param array $args Unused.
5102
+
* @return IXR_Error Error object.
5103
+
*/
5104
+
public function blogger_setTemplate( $args ) {
5105
+
return new IXR_Error( 403, __( 'Sorry, this method is not supported.' ) );
5106
+
}
5107
+
5108
+
/**
5109
+
* Creates a new post.
5110
+
*
5111
+
* @since 1.5.0
5112
+
*
5113
+
* @param array $args {
5114
+
* Method arguments. Note: arguments must be ordered as documented.
5115
+
*
5116
+
* @type string $0 App key (unused).
5117
+
* @type int $1 Blog ID (unused).
5118
+
* @type string $2 Username.
5119
+
* @type string $3 Password.
5120
+
* @type string $4 Content.
5121
+
* @type int $5 Publish flag. 0 for draft, 1 for publish.
5122
+
* }
5123
+
* @return int|IXR_Error
5124
+
*/
5125
+
public function blogger_newPost( $args ) {
5126
+
$this->escape( $args );
5127
+
5128
+
$username = $args[2];
5129
+
$password = $args[3];
5130
+
$content = $args[4];
5131
+
$publish = $args[5];
5132
+
5133
+
$user = $this->login( $username, $password );
5134
+
if ( ! $user ) {
5135
+
return $this->error;
5136
+
}
5137
+
5138
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
5139
+
do_action( 'xmlrpc_call', 'blogger.newPost', $args, $this );
5140
+
5141
+
$cap = ( $publish ) ? 'publish_posts' : 'edit_posts';
5142
+
if ( ! current_user_can( get_post_type_object( 'post' )->cap->create_posts ) || ! current_user_can( $cap ) ) {
5143
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to post on this site.' ) );
5144
+
}
5145
+
5146
+
$post_status = ( $publish ) ? 'publish' : 'draft';
5147
+
5148
+
$post_author = $user->ID;
5149
+
5150
+
$post_title = xmlrpc_getposttitle( $content );
5151
+
$post_category = xmlrpc_getpostcategory( $content );
5152
+
$post_content = xmlrpc_removepostdata( $content );
5153
+
5154
+
$post_date = current_time( 'mysql' );
5155
+
$post_date_gmt = current_time( 'mysql', true );
5156
+
5157
+
$post_data = compact(
5158
+
'post_author',
5159
+
'post_date',
5160
+
'post_date_gmt',
5161
+
'post_content',
5162
+
'post_title',
5163
+
'post_category',
5164
+
'post_status'
5165
+
);
5166
+
5167
+
$post_id = wp_insert_post( $post_data );
5168
+
if ( is_wp_error( $post_id ) ) {
5169
+
return new IXR_Error( 500, $post_id->get_error_message() );
5170
+
}
5171
+
5172
+
if ( ! $post_id ) {
5173
+
return new IXR_Error( 500, __( 'Sorry, the post could not be created.' ) );
5174
+
}
5175
+
5176
+
$this->attach_uploads( $post_id, $post_content );
5177
+
5178
+
/**
5179
+
* Fires after a new post has been successfully created via the XML-RPC Blogger API.
5180
+
*
5181
+
* @since 3.4.0
5182
+
*
5183
+
* @param int $post_id ID of the new post.
5184
+
* @param array $args An array of new post arguments.
5185
+
*/
5186
+
do_action( 'xmlrpc_call_success_blogger_newPost', $post_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
5187
+
5188
+
return $post_id;
5189
+
}
5190
+
5191
+
/**
5192
+
* Edits a post.
5193
+
*
5194
+
* @since 1.5.0
5195
+
*
5196
+
* @param array $args {
5197
+
* Method arguments. Note: arguments must be ordered as documented.
5198
+
*
5199
+
* @type int $0 Blog ID (unused).
5200
+
* @type int $1 Post ID.
5201
+
* @type string $2 Username.
5202
+
* @type string $3 Password.
5203
+
* @type string $4 Content
5204
+
* @type int $5 Publish flag. 0 for draft, 1 for publish.
5205
+
* }
5206
+
* @return true|IXR_Error true when done.
5207
+
*/
5208
+
public function blogger_editPost( $args ) {
5209
+
5210
+
$this->escape( $args );
5211
+
5212
+
$post_id = (int) $args[1];
5213
+
$username = $args[2];
5214
+
$password = $args[3];
5215
+
$content = $args[4];
5216
+
$publish = $args[5];
5217
+
5218
+
$user = $this->login( $username, $password );
5219
+
if ( ! $user ) {
5220
+
return $this->error;
5221
+
}
5222
+
5223
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
5224
+
do_action( 'xmlrpc_call', 'blogger.editPost', $args, $this );
5225
+
5226
+
$actual_post = get_post( $post_id, ARRAY_A );
5227
+
5228
+
if ( ! $actual_post || 'post' !== $actual_post['post_type'] ) {
5229
+
return new IXR_Error( 404, __( 'Sorry, no such post.' ) );
5230
+
}
5231
+
5232
+
$this->escape( $actual_post );
5233
+
5234
+
if ( ! current_user_can( 'edit_post', $post_id ) ) {
5235
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
5236
+
}
5237
+
if ( 'publish' === $actual_post['post_status'] && ! current_user_can( 'publish_posts' ) ) {
5238
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this post.' ) );
5239
+
}
5240
+
5241
+
$postdata = array();
5242
+
$postdata['ID'] = $actual_post['ID'];
5243
+
$postdata['post_content'] = xmlrpc_removepostdata( $content );
5244
+
$postdata['post_title'] = xmlrpc_getposttitle( $content );
5245
+
$postdata['post_category'] = xmlrpc_getpostcategory( $content );
5246
+
$postdata['post_status'] = $actual_post['post_status'];
5247
+
$postdata['post_excerpt'] = $actual_post['post_excerpt'];
5248
+
$postdata['post_status'] = $publish ? 'publish' : 'draft';
5249
+
5250
+
$result = wp_update_post( $postdata );
5251
+
5252
+
if ( ! $result ) {
5253
+
return new IXR_Error( 500, __( 'Sorry, the post could not be updated.' ) );
5254
+
}
5255
+
$this->attach_uploads( $actual_post['ID'], $postdata['post_content'] );
5256
+
5257
+
/**
5258
+
* Fires after a post has been successfully updated via the XML-RPC Blogger API.
5259
+
*
5260
+
* @since 3.4.0
5261
+
*
5262
+
* @param int $post_id ID of the updated post.
5263
+
* @param array $args An array of arguments for the post to edit.
5264
+
*/
5265
+
do_action( 'xmlrpc_call_success_blogger_editPost', $post_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
5266
+
5267
+
return true;
5268
+
}
5269
+
5270
+
/**
5271
+
* Deletes a post.
5272
+
*
5273
+
* @since 1.5.0
5274
+
*
5275
+
* @param array $args {
5276
+
* Method arguments. Note: arguments must be ordered as documented.
5277
+
*
5278
+
* @type int $0 Blog ID (unused).
5279
+
* @type int $1 Post ID.
5280
+
* @type string $2 Username.
5281
+
* @type string $3 Password.
5282
+
* }
5283
+
* @return true|IXR_Error True when post is deleted.
5284
+
*/
5285
+
public function blogger_deletePost( $args ) {
5286
+
$this->escape( $args );
5287
+
5288
+
$post_id = (int) $args[1];
5289
+
$username = $args[2];
5290
+
$password = $args[3];
5291
+
5292
+
$user = $this->login( $username, $password );
5293
+
if ( ! $user ) {
5294
+
return $this->error;
5295
+
}
5296
+
5297
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
5298
+
do_action( 'xmlrpc_call', 'blogger.deletePost', $args, $this );
5299
+
5300
+
$actual_post = get_post( $post_id, ARRAY_A );
5301
+
5302
+
if ( ! $actual_post || 'post' !== $actual_post['post_type'] ) {
5303
+
return new IXR_Error( 404, __( 'Sorry, no such post.' ) );
5304
+
}
5305
+
5306
+
if ( ! current_user_can( 'delete_post', $post_id ) ) {
5307
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this post.' ) );
5308
+
}
5309
+
5310
+
$result = wp_delete_post( $post_id );
5311
+
5312
+
if ( ! $result ) {
5313
+
return new IXR_Error( 500, __( 'Sorry, the post could not be deleted.' ) );
5314
+
}
5315
+
5316
+
/**
5317
+
* Fires after a post has been successfully deleted via the XML-RPC Blogger API.
5318
+
*
5319
+
* @since 3.4.0
5320
+
*
5321
+
* @param int $post_id ID of the deleted post.
5322
+
* @param array $args An array of arguments to delete the post.
5323
+
*/
5324
+
do_action( 'xmlrpc_call_success_blogger_deletePost', $post_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
5325
+
5326
+
return true;
5327
+
}
5328
+
5329
+
/*
5330
+
* MetaWeblog API functions.
5331
+
* Specs on wherever Dave Winer wants them to be.
5332
+
*/
5333
+
5334
+
/**
5335
+
* Creates a new post.
5336
+
*
5337
+
* The 'content_struct' argument must contain:
5338
+
* - title
5339
+
* - description
5340
+
* - mt_excerpt
5341
+
* - mt_text_more
5342
+
* - mt_keywords
5343
+
* - mt_tb_ping_urls
5344
+
* - categories
5345
+
*
5346
+
* Also, it can optionally contain:
5347
+
* - wp_slug
5348
+
* - wp_password
5349
+
* - wp_page_parent_id
5350
+
* - wp_page_order
5351
+
* - wp_author_id
5352
+
* - post_status | page_status - can be 'draft', 'private', 'publish', or 'pending'
5353
+
* - mt_allow_comments - can be 'open' or 'closed'
5354
+
* - mt_allow_pings - can be 'open' or 'closed'
5355
+
* - date_created_gmt
5356
+
* - dateCreated
5357
+
* - wp_post_thumbnail
5358
+
*
5359
+
* @since 1.5.0
5360
+
*
5361
+
* @param array $args {
5362
+
* Method arguments. Note: arguments must be ordered as documented.
5363
+
*
5364
+
* @type int $0 Blog ID (unused).
5365
+
* @type string $1 Username.
5366
+
* @type string $2 Password.
5367
+
* @type array $3 Content structure.
5368
+
* @type int $4 Optional. Publish flag. 0 for draft, 1 for publish. Default 0.
5369
+
* }
5370
+
* @return int|IXR_Error
5371
+
*/
5372
+
public function mw_newPost( $args ) {
5373
+
$this->escape( $args );
5374
+
5375
+
$username = $args[1];
5376
+
$password = $args[2];
5377
+
$content_struct = $args[3];
5378
+
$publish = isset( $args[4] ) ? $args[4] : 0;
5379
+
5380
+
$user = $this->login( $username, $password );
5381
+
if ( ! $user ) {
5382
+
return $this->error;
5383
+
}
5384
+
5385
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
5386
+
do_action( 'xmlrpc_call', 'metaWeblog.newPost', $args, $this );
5387
+
5388
+
$page_template = '';
5389
+
if ( ! empty( $content_struct['post_type'] ) ) {
5390
+
if ( 'page' === $content_struct['post_type'] ) {
5391
+
if ( $publish ) {
5392
+
$cap = 'publish_pages';
5393
+
} elseif ( isset( $content_struct['page_status'] ) && 'publish' === $content_struct['page_status'] ) {
5394
+
$cap = 'publish_pages';
5395
+
} else {
5396
+
$cap = 'edit_pages';
5397
+
}
5398
+
$error_message = __( 'Sorry, you are not allowed to publish pages on this site.' );
5399
+
$post_type = 'page';
5400
+
if ( ! empty( $content_struct['wp_page_template'] ) ) {
5401
+
$page_template = $content_struct['wp_page_template'];
5402
+
}
5403
+
} elseif ( 'post' === $content_struct['post_type'] ) {
5404
+
if ( $publish ) {
5405
+
$cap = 'publish_posts';
5406
+
} elseif ( isset( $content_struct['post_status'] ) && 'publish' === $content_struct['post_status'] ) {
5407
+
$cap = 'publish_posts';
5408
+
} else {
5409
+
$cap = 'edit_posts';
5410
+
}
5411
+
$error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
5412
+
$post_type = 'post';
5413
+
} else {
5414
+
// No other 'post_type' values are allowed here.
5415
+
return new IXR_Error( 401, __( 'Invalid post type.' ) );
5416
+
}
5417
+
} else {
5418
+
if ( $publish ) {
5419
+
$cap = 'publish_posts';
5420
+
} elseif ( isset( $content_struct['post_status'] ) && 'publish' === $content_struct['post_status'] ) {
5421
+
$cap = 'publish_posts';
5422
+
} else {
5423
+
$cap = 'edit_posts';
5424
+
}
5425
+
$error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
5426
+
$post_type = 'post';
5427
+
}
5428
+
5429
+
if ( ! current_user_can( get_post_type_object( $post_type )->cap->create_posts ) ) {
5430
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts on this site.' ) );
5431
+
}
5432
+
if ( ! current_user_can( $cap ) ) {
5433
+
return new IXR_Error( 401, $error_message );
5434
+
}
5435
+
5436
+
// Check for a valid post format if one was given.
5437
+
if ( isset( $content_struct['wp_post_format'] ) ) {
5438
+
$content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] );
5439
+
if ( ! array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) {
5440
+
return new IXR_Error( 404, __( 'Invalid post format.' ) );
5441
+
}
5442
+
}
5443
+
5444
+
// Let WordPress generate the 'post_name' (slug) unless
5445
+
// one has been provided.
5446
+
$post_name = null;
5447
+
if ( isset( $content_struct['wp_slug'] ) ) {
5448
+
$post_name = $content_struct['wp_slug'];
5449
+
}
5450
+
5451
+
// Only use a password if one was given.
5452
+
$post_password = '';
5453
+
if ( isset( $content_struct['wp_password'] ) ) {
5454
+
$post_password = $content_struct['wp_password'];
5455
+
}
5456
+
5457
+
// Only set a post parent if one was given.
5458
+
$post_parent = 0;
5459
+
if ( isset( $content_struct['wp_page_parent_id'] ) ) {
5460
+
$post_parent = $content_struct['wp_page_parent_id'];
5461
+
}
5462
+
5463
+
// Only set the 'menu_order' if it was given.
5464
+
$menu_order = 0;
5465
+
if ( isset( $content_struct['wp_page_order'] ) ) {
5466
+
$menu_order = $content_struct['wp_page_order'];
5467
+
}
5468
+
5469
+
$post_author = $user->ID;
5470
+
5471
+
// If an author ID was provided then use it instead.
5472
+
if ( isset( $content_struct['wp_author_id'] ) && ( $user->ID !== (int) $content_struct['wp_author_id'] ) ) {
5473
+
switch ( $post_type ) {
5474
+
case 'post':
5475
+
if ( ! current_user_can( 'edit_others_posts' ) ) {
5476
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to create posts as this user.' ) );
5477
+
}
5478
+
break;
5479
+
case 'page':
5480
+
if ( ! current_user_can( 'edit_others_pages' ) ) {
5481
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to create pages as this user.' ) );
5482
+
}
5483
+
break;
5484
+
default:
5485
+
return new IXR_Error( 401, __( 'Invalid post type.' ) );
5486
+
}
5487
+
$author = get_userdata( $content_struct['wp_author_id'] );
5488
+
if ( ! $author ) {
5489
+
return new IXR_Error( 404, __( 'Invalid author ID.' ) );
5490
+
}
5491
+
$post_author = $content_struct['wp_author_id'];
5492
+
}
5493
+
5494
+
$post_title = isset( $content_struct['title'] ) ? $content_struct['title'] : '';
5495
+
$post_content = isset( $content_struct['description'] ) ? $content_struct['description'] : '';
5496
+
5497
+
$post_status = $publish ? 'publish' : 'draft';
5498
+
5499
+
if ( isset( $content_struct[ "{$post_type}_status" ] ) ) {
5500
+
switch ( $content_struct[ "{$post_type}_status" ] ) {
5501
+
case 'draft':
5502
+
case 'pending':
5503
+
case 'private':
5504
+
case 'publish':
5505
+
$post_status = $content_struct[ "{$post_type}_status" ];
5506
+
break;
5507
+
default:
5508
+
// Deliberably left empty.
5509
+
break;
5510
+
}
5511
+
}
5512
+
5513
+
$post_excerpt = isset( $content_struct['mt_excerpt'] ) ? $content_struct['mt_excerpt'] : '';
5514
+
$post_more = isset( $content_struct['mt_text_more'] ) ? $content_struct['mt_text_more'] : '';
5515
+
5516
+
$tags_input = isset( $content_struct['mt_keywords'] ) ? $content_struct['mt_keywords'] : array();
5517
+
5518
+
if ( isset( $content_struct['mt_allow_comments'] ) ) {
5519
+
if ( ! is_numeric( $content_struct['mt_allow_comments'] ) ) {
5520
+
switch ( $content_struct['mt_allow_comments'] ) {
5521
+
case 'closed':
5522
+
$comment_status = 'closed';
5523
+
break;
5524
+
case 'open':
5525
+
$comment_status = 'open';
5526
+
break;
5527
+
default:
5528
+
$comment_status = get_default_comment_status( $post_type );
5529
+
break;
5530
+
}
5531
+
} else {
5532
+
switch ( (int) $content_struct['mt_allow_comments'] ) {
5533
+
case 0:
5534
+
case 2:
5535
+
$comment_status = 'closed';
5536
+
break;
5537
+
case 1:
5538
+
$comment_status = 'open';
5539
+
break;
5540
+
default:
5541
+
$comment_status = get_default_comment_status( $post_type );
5542
+
break;
5543
+
}
5544
+
}
5545
+
} else {
5546
+
$comment_status = get_default_comment_status( $post_type );
5547
+
}
5548
+
5549
+
if ( isset( $content_struct['mt_allow_pings'] ) ) {
5550
+
if ( ! is_numeric( $content_struct['mt_allow_pings'] ) ) {
5551
+
switch ( $content_struct['mt_allow_pings'] ) {
5552
+
case 'closed':
5553
+
$ping_status = 'closed';
5554
+
break;
5555
+
case 'open':
5556
+
$ping_status = 'open';
5557
+
break;
5558
+
default:
5559
+
$ping_status = get_default_comment_status( $post_type, 'pingback' );
5560
+
break;
5561
+
}
5562
+
} else {
5563
+
switch ( (int) $content_struct['mt_allow_pings'] ) {
5564
+
case 0:
5565
+
$ping_status = 'closed';
5566
+
break;
5567
+
case 1:
5568
+
$ping_status = 'open';
5569
+
break;
5570
+
default:
5571
+
$ping_status = get_default_comment_status( $post_type, 'pingback' );
5572
+
break;
5573
+
}
5574
+
}
5575
+
} else {
5576
+
$ping_status = get_default_comment_status( $post_type, 'pingback' );
5577
+
}
5578
+
5579
+
if ( $post_more ) {
5580
+
$post_content .= '<!--more-->' . $post_more;
5581
+
}
5582
+
5583
+
$to_ping = '';
5584
+
if ( isset( $content_struct['mt_tb_ping_urls'] ) ) {
5585
+
$to_ping = $content_struct['mt_tb_ping_urls'];
5586
+
if ( is_array( $to_ping ) ) {
5587
+
$to_ping = implode( ' ', $to_ping );
5588
+
}
5589
+
}
5590
+
5591
+
// Do some timestamp voodoo.
5592
+
if ( ! empty( $content_struct['date_created_gmt'] ) ) {
5593
+
// We know this is supposed to be GMT, so we're going to slap that Z on there by force.
5594
+
$date_created = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z';
5595
+
} elseif ( ! empty( $content_struct['dateCreated'] ) ) {
5596
+
$date_created = $content_struct['dateCreated']->getIso();
5597
+
}
5598
+
5599
+
$post_date = '';
5600
+
$post_date_gmt = '';
5601
+
if ( ! empty( $date_created ) ) {
5602
+
$post_date = iso8601_to_datetime( $date_created );
5603
+
$post_date_gmt = iso8601_to_datetime( $date_created, 'gmt' );
5604
+
}
5605
+
5606
+
$post_category = array();
5607
+
if ( isset( $content_struct['categories'] ) ) {
5608
+
$catnames = $content_struct['categories'];
5609
+
5610
+
if ( is_array( $catnames ) ) {
5611
+
foreach ( $catnames as $cat ) {
5612
+
$post_category[] = get_cat_ID( $cat );
5613
+
}
5614
+
}
5615
+
}
5616
+
5617
+
$postdata = compact(
5618
+
'post_author',
5619
+
'post_date',
5620
+
'post_date_gmt',
5621
+
'post_content',
5622
+
'post_title',
5623
+
'post_category',
5624
+
'post_status',
5625
+
'post_excerpt',
5626
+
'comment_status',
5627
+
'ping_status',
5628
+
'to_ping',
5629
+
'post_type',
5630
+
'post_name',
5631
+
'post_password',
5632
+
'post_parent',
5633
+
'menu_order',
5634
+
'tags_input',
5635
+
'page_template'
5636
+
);
5637
+
5638
+
$post_id = get_default_post_to_edit( $post_type, true )->ID;
5639
+
$postdata['ID'] = $post_id;
5640
+
5641
+
// Only posts can be sticky.
5642
+
if ( 'post' === $post_type && isset( $content_struct['sticky'] ) ) {
5643
+
$data = $postdata;
5644
+
$data['sticky'] = $content_struct['sticky'];
5645
+
$error = $this->_toggle_sticky( $data );
5646
+
if ( $error ) {
5647
+
return $error;
5648
+
}
5649
+
}
5650
+
5651
+
if ( isset( $content_struct['custom_fields'] ) ) {
5652
+
$this->set_custom_fields( $post_id, $content_struct['custom_fields'] );
5653
+
}
5654
+
5655
+
if ( isset( $content_struct['wp_post_thumbnail'] ) ) {
5656
+
if ( set_post_thumbnail( $post_id, $content_struct['wp_post_thumbnail'] ) === false ) {
5657
+
return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
5658
+
}
5659
+
5660
+
unset( $content_struct['wp_post_thumbnail'] );
5661
+
}
5662
+
5663
+
// Handle enclosures.
5664
+
$enclosure = isset( $content_struct['enclosure'] ) ? $content_struct['enclosure'] : null;
5665
+
$this->add_enclosure_if_new( $post_id, $enclosure );
5666
+
5667
+
$this->attach_uploads( $post_id, $post_content );
5668
+
5669
+
/*
5670
+
* Handle post formats if assigned, value is validated earlier
5671
+
* in this function.
5672
+
*/
5673
+
if ( isset( $content_struct['wp_post_format'] ) ) {
5674
+
set_post_format( $post_id, $content_struct['wp_post_format'] );
5675
+
}
5676
+
5677
+
$post_id = wp_insert_post( $postdata, true );
5678
+
if ( is_wp_error( $post_id ) ) {
5679
+
return new IXR_Error( 500, $post_id->get_error_message() );
5680
+
}
5681
+
5682
+
if ( ! $post_id ) {
5683
+
return new IXR_Error( 500, __( 'Sorry, the post could not be created.' ) );
5684
+
}
5685
+
5686
+
/**
5687
+
* Fires after a new post has been successfully created via the XML-RPC MovableType API.
5688
+
*
5689
+
* @since 3.4.0
5690
+
*
5691
+
* @param int $post_id ID of the new post.
5692
+
* @param array $args An array of arguments to create the new post.
5693
+
*/
5694
+
do_action( 'xmlrpc_call_success_mw_newPost', $post_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
5695
+
5696
+
return (string) $post_id;
5697
+
}
5698
+
5699
+
/**
5700
+
* Adds an enclosure to a post if it's new.
5701
+
*
5702
+
* @since 2.8.0
5703
+
*
5704
+
* @param int $post_id Post ID.
5705
+
* @param array $enclosure Enclosure data.
5706
+
*/
5707
+
public function add_enclosure_if_new( $post_id, $enclosure ) {
5708
+
if ( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) {
5709
+
$encstring = $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type'] . "\n";
5710
+
$found = false;
5711
+
$enclosures = get_post_meta( $post_id, 'enclosure' );
5712
+
if ( $enclosures ) {
5713
+
foreach ( $enclosures as $enc ) {
5714
+
// This method used to omit the trailing new line. #23219
5715
+
if ( rtrim( $enc, "\n" ) === rtrim( $encstring, "\n" ) ) {
5716
+
$found = true;
5717
+
break;
5718
+
}
5719
+
}
5720
+
}
5721
+
if ( ! $found ) {
5722
+
add_post_meta( $post_id, 'enclosure', $encstring );
5723
+
}
5724
+
}
5725
+
}
5726
+
5727
+
/**
5728
+
* Attaches an upload to a post.
5729
+
*
5730
+
* @since 2.1.0
5731
+
*
5732
+
* @global wpdb $wpdb WordPress database abstraction object.
5733
+
*
5734
+
* @param int $post_id Post ID.
5735
+
* @param string $post_content Post Content for attachment.
5736
+
*/
5737
+
public function attach_uploads( $post_id, $post_content ) {
5738
+
global $wpdb;
5739
+
5740
+
// Find any unattached files.
5741
+
$attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '0' AND post_type = 'attachment'" );
5742
+
if ( is_array( $attachments ) ) {
5743
+
foreach ( $attachments as $file ) {
5744
+
if ( ! empty( $file->guid ) && str_contains( $post_content, $file->guid ) ) {
5745
+
$wpdb->update( $wpdb->posts, array( 'post_parent' => $post_id ), array( 'ID' => $file->ID ) );
5746
+
}
5747
+
}
5748
+
}
5749
+
}
5750
+
5751
+
/**
5752
+
* Edits a post.
5753
+
*
5754
+
* @since 1.5.0
5755
+
*
5756
+
* @param array $args {
5757
+
* Method arguments. Note: arguments must be ordered as documented.
5758
+
*
5759
+
* @type int $0 Post ID.
5760
+
* @type string $1 Username.
5761
+
* @type string $2 Password.
5762
+
* @type array $3 Content structure.
5763
+
* @type int $4 Optional. Publish flag. 0 for draft, 1 for publish. Default 0.
5764
+
* }
5765
+
* @return true|IXR_Error True on success.
5766
+
*/
5767
+
public function mw_editPost( $args ) {
5768
+
$this->escape( $args );
5769
+
5770
+
$post_id = (int) $args[0];
5771
+
$username = $args[1];
5772
+
$password = $args[2];
5773
+
$content_struct = $args[3];
5774
+
$publish = isset( $args[4] ) ? $args[4] : 0;
5775
+
5776
+
$user = $this->login( $username, $password );
5777
+
if ( ! $user ) {
5778
+
return $this->error;
5779
+
}
5780
+
5781
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
5782
+
do_action( 'xmlrpc_call', 'metaWeblog.editPost', $args, $this );
5783
+
5784
+
$postdata = get_post( $post_id, ARRAY_A );
5785
+
5786
+
/*
5787
+
* If there is no post data for the give post ID, stop now and return an error.
5788
+
* Otherwise a new post will be created (which was the old behavior).
5789
+
*/
5790
+
if ( ! $postdata || empty( $postdata['ID'] ) ) {
5791
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
5792
+
}
5793
+
5794
+
if ( ! current_user_can( 'edit_post', $post_id ) ) {
5795
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
5796
+
}
5797
+
5798
+
// Use wp.editPost to edit post types other than post and page.
5799
+
if ( ! in_array( $postdata['post_type'], array( 'post', 'page' ), true ) ) {
5800
+
return new IXR_Error( 401, __( 'Invalid post type.' ) );
5801
+
}
5802
+
5803
+
// Thwart attempt to change the post type.
5804
+
if ( ! empty( $content_struct['post_type'] ) && ( $content_struct['post_type'] !== $postdata['post_type'] ) ) {
5805
+
return new IXR_Error( 401, __( 'The post type may not be changed.' ) );
5806
+
}
5807
+
5808
+
// Check for a valid post format if one was given.
5809
+
if ( isset( $content_struct['wp_post_format'] ) ) {
5810
+
$content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] );
5811
+
if ( ! array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) {
5812
+
return new IXR_Error( 404, __( 'Invalid post format.' ) );
5813
+
}
5814
+
}
5815
+
5816
+
$this->escape( $postdata );
5817
+
5818
+
$post_id = $postdata['ID'];
5819
+
$post_content = $postdata['post_content'];
5820
+
$post_title = $postdata['post_title'];
5821
+
$post_excerpt = $postdata['post_excerpt'];
5822
+
$post_password = $postdata['post_password'];
5823
+
$post_parent = $postdata['post_parent'];
5824
+
$post_type = $postdata['post_type'];
5825
+
$menu_order = $postdata['menu_order'];
5826
+
$ping_status = $postdata['ping_status'];
5827
+
$comment_status = $postdata['comment_status'];
5828
+
5829
+
// Let WordPress manage slug if none was provided.
5830
+
$post_name = $postdata['post_name'];
5831
+
if ( isset( $content_struct['wp_slug'] ) ) {
5832
+
$post_name = $content_struct['wp_slug'];
5833
+
}
5834
+
5835
+
// Only use a password if one was given.
5836
+
if ( isset( $content_struct['wp_password'] ) ) {
5837
+
$post_password = $content_struct['wp_password'];
5838
+
}
5839
+
5840
+
// Only set a post parent if one was given.
5841
+
if ( isset( $content_struct['wp_page_parent_id'] ) ) {
5842
+
$post_parent = $content_struct['wp_page_parent_id'];
5843
+
}
5844
+
5845
+
// Only set the 'menu_order' if it was given.
5846
+
if ( isset( $content_struct['wp_page_order'] ) ) {
5847
+
$menu_order = $content_struct['wp_page_order'];
5848
+
}
5849
+
5850
+
$page_template = '';
5851
+
if ( ! empty( $content_struct['wp_page_template'] ) && 'page' === $post_type ) {
5852
+
$page_template = $content_struct['wp_page_template'];
5853
+
}
5854
+
5855
+
$post_author = $postdata['post_author'];
5856
+
5857
+
// If an author ID was provided then use it instead.
5858
+
if ( isset( $content_struct['wp_author_id'] ) ) {
5859
+
// Check permissions if attempting to switch author to or from another user.
5860
+
if ( $user->ID !== (int) $content_struct['wp_author_id'] || $user->ID !== (int) $post_author ) {
5861
+
switch ( $post_type ) {
5862
+
case 'post':
5863
+
if ( ! current_user_can( 'edit_others_posts' ) ) {
5864
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to change the post author as this user.' ) );
5865
+
}
5866
+
break;
5867
+
case 'page':
5868
+
if ( ! current_user_can( 'edit_others_pages' ) ) {
5869
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to change the page author as this user.' ) );
5870
+
}
5871
+
break;
5872
+
default:
5873
+
return new IXR_Error( 401, __( 'Invalid post type.' ) );
5874
+
}
5875
+
$post_author = $content_struct['wp_author_id'];
5876
+
}
5877
+
}
5878
+
5879
+
if ( isset( $content_struct['mt_allow_comments'] ) ) {
5880
+
if ( ! is_numeric( $content_struct['mt_allow_comments'] ) ) {
5881
+
switch ( $content_struct['mt_allow_comments'] ) {
5882
+
case 'closed':
5883
+
$comment_status = 'closed';
5884
+
break;
5885
+
case 'open':
5886
+
$comment_status = 'open';
5887
+
break;
5888
+
default:
5889
+
$comment_status = get_default_comment_status( $post_type );
5890
+
break;
5891
+
}
5892
+
} else {
5893
+
switch ( (int) $content_struct['mt_allow_comments'] ) {
5894
+
case 0:
5895
+
case 2:
5896
+
$comment_status = 'closed';
5897
+
break;
5898
+
case 1:
5899
+
$comment_status = 'open';
5900
+
break;
5901
+
default:
5902
+
$comment_status = get_default_comment_status( $post_type );
5903
+
break;
5904
+
}
5905
+
}
5906
+
}
5907
+
5908
+
if ( isset( $content_struct['mt_allow_pings'] ) ) {
5909
+
if ( ! is_numeric( $content_struct['mt_allow_pings'] ) ) {
5910
+
switch ( $content_struct['mt_allow_pings'] ) {
5911
+
case 'closed':
5912
+
$ping_status = 'closed';
5913
+
break;
5914
+
case 'open':
5915
+
$ping_status = 'open';
5916
+
break;
5917
+
default:
5918
+
$ping_status = get_default_comment_status( $post_type, 'pingback' );
5919
+
break;
5920
+
}
5921
+
} else {
5922
+
switch ( (int) $content_struct['mt_allow_pings'] ) {
5923
+
case 0:
5924
+
$ping_status = 'closed';
5925
+
break;
5926
+
case 1:
5927
+
$ping_status = 'open';
5928
+
break;
5929
+
default:
5930
+
$ping_status = get_default_comment_status( $post_type, 'pingback' );
5931
+
break;
5932
+
}
5933
+
}
5934
+
}
5935
+
5936
+
if ( isset( $content_struct['title'] ) ) {
5937
+
$post_title = $content_struct['title'];
5938
+
}
5939
+
5940
+
if ( isset( $content_struct['description'] ) ) {
5941
+
$post_content = $content_struct['description'];
5942
+
}
5943
+
5944
+
$post_category = array();
5945
+
if ( isset( $content_struct['categories'] ) ) {
5946
+
$catnames = $content_struct['categories'];
5947
+
if ( is_array( $catnames ) ) {
5948
+
foreach ( $catnames as $cat ) {
5949
+
$post_category[] = get_cat_ID( $cat );
5950
+
}
5951
+
}
5952
+
}
5953
+
5954
+
if ( isset( $content_struct['mt_excerpt'] ) ) {
5955
+
$post_excerpt = $content_struct['mt_excerpt'];
5956
+
}
5957
+
5958
+
$post_more = isset( $content_struct['mt_text_more'] ) ? $content_struct['mt_text_more'] : '';
5959
+
5960
+
$post_status = $publish ? 'publish' : 'draft';
5961
+
if ( isset( $content_struct[ "{$post_type}_status" ] ) ) {
5962
+
switch ( $content_struct[ "{$post_type}_status" ] ) {
5963
+
case 'draft':
5964
+
case 'pending':
5965
+
case 'private':
5966
+
case 'publish':
5967
+
$post_status = $content_struct[ "{$post_type}_status" ];
5968
+
break;
5969
+
default:
5970
+
$post_status = $publish ? 'publish' : 'draft';
5971
+
break;
5972
+
}
5973
+
}
5974
+
5975
+
$tags_input = isset( $content_struct['mt_keywords'] ) ? $content_struct['mt_keywords'] : array();
5976
+
5977
+
if ( 'publish' === $post_status || 'private' === $post_status ) {
5978
+
if ( 'page' === $post_type && ! current_user_can( 'publish_pages' ) ) {
5979
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this page.' ) );
5980
+
} elseif ( ! current_user_can( 'publish_posts' ) ) {
5981
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this post.' ) );
5982
+
}
5983
+
}
5984
+
5985
+
if ( $post_more ) {
5986
+
$post_content = $post_content . '<!--more-->' . $post_more;
5987
+
}
5988
+
5989
+
$to_ping = '';
5990
+
if ( isset( $content_struct['mt_tb_ping_urls'] ) ) {
5991
+
$to_ping = $content_struct['mt_tb_ping_urls'];
5992
+
if ( is_array( $to_ping ) ) {
5993
+
$to_ping = implode( ' ', $to_ping );
5994
+
}
5995
+
}
5996
+
5997
+
// Do some timestamp voodoo.
5998
+
if ( ! empty( $content_struct['date_created_gmt'] ) ) {
5999
+
// We know this is supposed to be GMT, so we're going to slap that Z on there by force.
6000
+
$date_created = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z';
6001
+
} elseif ( ! empty( $content_struct['dateCreated'] ) ) {
6002
+
$date_created = $content_struct['dateCreated']->getIso();
6003
+
}
6004
+
6005
+
// Default to not flagging the post date to be edited unless it's intentional.
6006
+
$edit_date = false;
6007
+
6008
+
if ( ! empty( $date_created ) ) {
6009
+
$post_date = iso8601_to_datetime( $date_created );
6010
+
$post_date_gmt = iso8601_to_datetime( $date_created, 'gmt' );
6011
+
6012
+
// Flag the post date to be edited.
6013
+
$edit_date = true;
6014
+
} else {
6015
+
$post_date = $postdata['post_date'];
6016
+
$post_date_gmt = $postdata['post_date_gmt'];
6017
+
}
6018
+
6019
+
$newpost = array(
6020
+
'ID' => $post_id,
6021
+
);
6022
+
6023
+
$newpost += compact(
6024
+
'post_content',
6025
+
'post_title',
6026
+
'post_category',
6027
+
'post_status',
6028
+
'post_excerpt',
6029
+
'comment_status',
6030
+
'ping_status',
6031
+
'edit_date',
6032
+
'post_date',
6033
+
'post_date_gmt',
6034
+
'to_ping',
6035
+
'post_name',
6036
+
'post_password',
6037
+
'post_parent',
6038
+
'menu_order',
6039
+
'post_author',
6040
+
'tags_input',
6041
+
'page_template'
6042
+
);
6043
+
6044
+
// We've got all the data -- post it.
6045
+
$result = wp_update_post( $newpost, true );
6046
+
if ( is_wp_error( $result ) ) {
6047
+
return new IXR_Error( 500, $result->get_error_message() );
6048
+
}
6049
+
6050
+
if ( ! $result ) {
6051
+
return new IXR_Error( 500, __( 'Sorry, the post could not be updated.' ) );
6052
+
}
6053
+
6054
+
// Only posts can be sticky.
6055
+
if ( 'post' === $post_type && isset( $content_struct['sticky'] ) ) {
6056
+
$data = $newpost;
6057
+
$data['sticky'] = $content_struct['sticky'];
6058
+
$data['post_type'] = 'post';
6059
+
$error = $this->_toggle_sticky( $data, true );
6060
+
if ( $error ) {
6061
+
return $error;
6062
+
}
6063
+
}
6064
+
6065
+
if ( isset( $content_struct['custom_fields'] ) ) {
6066
+
$this->set_custom_fields( $post_id, $content_struct['custom_fields'] );
6067
+
}
6068
+
6069
+
if ( isset( $content_struct['wp_post_thumbnail'] ) ) {
6070
+
6071
+
// Empty value deletes, non-empty value adds/updates.
6072
+
if ( empty( $content_struct['wp_post_thumbnail'] ) ) {
6073
+
delete_post_thumbnail( $post_id );
6074
+
} else {
6075
+
if ( set_post_thumbnail( $post_id, $content_struct['wp_post_thumbnail'] ) === false ) {
6076
+
return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
6077
+
}
6078
+
}
6079
+
unset( $content_struct['wp_post_thumbnail'] );
6080
+
}
6081
+
6082
+
// Handle enclosures.
6083
+
$enclosure = isset( $content_struct['enclosure'] ) ? $content_struct['enclosure'] : null;
6084
+
$this->add_enclosure_if_new( $post_id, $enclosure );
6085
+
6086
+
$this->attach_uploads( $post_id, $post_content );
6087
+
6088
+
// Handle post formats if assigned, validation is handled earlier in this function.
6089
+
if ( isset( $content_struct['wp_post_format'] ) ) {
6090
+
set_post_format( $post_id, $content_struct['wp_post_format'] );
6091
+
}
6092
+
6093
+
/**
6094
+
* Fires after a post has been successfully updated via the XML-RPC MovableType API.
6095
+
*
6096
+
* @since 3.4.0
6097
+
*
6098
+
* @param int $post_id ID of the updated post.
6099
+
* @param array $args An array of arguments to update the post.
6100
+
*/
6101
+
do_action( 'xmlrpc_call_success_mw_editPost', $post_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
6102
+
6103
+
return true;
6104
+
}
6105
+
6106
+
/**
6107
+
* Retrieves a post.
6108
+
*
6109
+
* @since 1.5.0
6110
+
*
6111
+
* @param array $args {
6112
+
* Method arguments. Note: arguments must be ordered as documented.
6113
+
*
6114
+
* @type int $0 Post ID.
6115
+
* @type string $1 Username.
6116
+
* @type string $2 Password.
6117
+
* }
6118
+
* @return array|IXR_Error
6119
+
*/
6120
+
public function mw_getPost( $args ) {
6121
+
$this->escape( $args );
6122
+
6123
+
$post_id = (int) $args[0];
6124
+
$username = $args[1];
6125
+
$password = $args[2];
6126
+
6127
+
$user = $this->login( $username, $password );
6128
+
if ( ! $user ) {
6129
+
return $this->error;
6130
+
}
6131
+
6132
+
$postdata = get_post( $post_id, ARRAY_A );
6133
+
if ( ! $postdata ) {
6134
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
6135
+
}
6136
+
6137
+
if ( ! current_user_can( 'edit_post', $post_id ) ) {
6138
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
6139
+
}
6140
+
6141
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
6142
+
do_action( 'xmlrpc_call', 'metaWeblog.getPost', $args, $this );
6143
+
6144
+
if ( '' !== $postdata['post_date'] ) {
6145
+
$post_date = $this->_convert_date( $postdata['post_date'] );
6146
+
$post_date_gmt = $this->_convert_date_gmt( $postdata['post_date_gmt'], $postdata['post_date'] );
6147
+
$post_modified = $this->_convert_date( $postdata['post_modified'] );
6148
+
$post_modified_gmt = $this->_convert_date_gmt( $postdata['post_modified_gmt'], $postdata['post_modified'] );
6149
+
6150
+
$categories = array();
6151
+
$cat_ids = wp_get_post_categories( $post_id );
6152
+
foreach ( $cat_ids as $cat_id ) {
6153
+
$categories[] = get_cat_name( $cat_id );
6154
+
}
6155
+
6156
+
$tagnames = array();
6157
+
$tags = wp_get_post_tags( $post_id );
6158
+
if ( ! empty( $tags ) ) {
6159
+
foreach ( $tags as $tag ) {
6160
+
$tagnames[] = $tag->name;
6161
+
}
6162
+
$tagnames = implode( ', ', $tagnames );
6163
+
} else {
6164
+
$tagnames = '';
6165
+
}
6166
+
6167
+
$post = get_extended( $postdata['post_content'] );
6168
+
$link = get_permalink( $postdata['ID'] );
6169
+
6170
+
// Get the author info.
6171
+
$author = get_userdata( $postdata['post_author'] );
6172
+
6173
+
$allow_comments = ( 'open' === $postdata['comment_status'] ) ? 1 : 0;
6174
+
$allow_pings = ( 'open' === $postdata['ping_status'] ) ? 1 : 0;
6175
+
6176
+
// Consider future posts as published.
6177
+
if ( 'future' === $postdata['post_status'] ) {
6178
+
$postdata['post_status'] = 'publish';
6179
+
}
6180
+
6181
+
// Get post format.
6182
+
$post_format = get_post_format( $post_id );
6183
+
if ( empty( $post_format ) ) {
6184
+
$post_format = 'standard';
6185
+
}
6186
+
6187
+
$sticky = false;
6188
+
if ( is_sticky( $post_id ) ) {
6189
+
$sticky = true;
6190
+
}
6191
+
6192
+
$enclosure = array();
6193
+
foreach ( (array) get_post_custom( $post_id ) as $key => $val ) {
6194
+
if ( 'enclosure' === $key ) {
6195
+
foreach ( (array) $val as $enc ) {
6196
+
$encdata = explode( "\n", $enc );
6197
+
$enclosure['url'] = trim( htmlspecialchars( $encdata[0] ) );
6198
+
$enclosure['length'] = (int) trim( $encdata[1] );
6199
+
$enclosure['type'] = trim( $encdata[2] );
6200
+
break 2;
6201
+
}
6202
+
}
6203
+
}
6204
+
6205
+
$resp = array(
6206
+
'dateCreated' => $post_date,
6207
+
'userid' => $postdata['post_author'],
6208
+
'postid' => $postdata['ID'],
6209
+
'description' => $post['main'],
6210
+
'title' => $postdata['post_title'],
6211
+
'link' => $link,
6212
+
'permaLink' => $link,
6213
+
// Commented out because no other tool seems to use this.
6214
+
// 'content' => $entry['post_content'],
6215
+
'categories' => $categories,
6216
+
'mt_excerpt' => $postdata['post_excerpt'],
6217
+
'mt_text_more' => $post['extended'],
6218
+
'wp_more_text' => $post['more_text'],
6219
+
'mt_allow_comments' => $allow_comments,
6220
+
'mt_allow_pings' => $allow_pings,
6221
+
'mt_keywords' => $tagnames,
6222
+
'wp_slug' => $postdata['post_name'],
6223
+
'wp_password' => $postdata['post_password'],
6224
+
'wp_author_id' => (string) $author->ID,
6225
+
'wp_author_display_name' => $author->display_name,
6226
+
'date_created_gmt' => $post_date_gmt,
6227
+
'post_status' => $postdata['post_status'],
6228
+
'custom_fields' => $this->get_custom_fields( $post_id ),
6229
+
'wp_post_format' => $post_format,
6230
+
'sticky' => $sticky,
6231
+
'date_modified' => $post_modified,
6232
+
'date_modified_gmt' => $post_modified_gmt,
6233
+
);
6234
+
6235
+
if ( ! empty( $enclosure ) ) {
6236
+
$resp['enclosure'] = $enclosure;
6237
+
}
6238
+
6239
+
$resp['wp_post_thumbnail'] = get_post_thumbnail_id( $postdata['ID'] );
6240
+
6241
+
return $resp;
6242
+
} else {
6243
+
return new IXR_Error( 404, __( 'Sorry, no such post.' ) );
6244
+
}
6245
+
}
6246
+
6247
+
/**
6248
+
* Retrieves list of recent posts.
6249
+
*
6250
+
* @since 1.5.0
6251
+
*
6252
+
* @param array $args {
6253
+
* Method arguments. Note: arguments must be ordered as documented.
6254
+
*
6255
+
* @type int $0 Blog ID (unused).
6256
+
* @type string $1 Username.
6257
+
* @type string $2 Password.
6258
+
* @type int $3 Optional. Number of posts.
6259
+
* }
6260
+
* @return array|IXR_Error
6261
+
*/
6262
+
public function mw_getRecentPosts( $args ) {
6263
+
$this->escape( $args );
6264
+
6265
+
$username = $args[1];
6266
+
$password = $args[2];
6267
+
if ( isset( $args[3] ) ) {
6268
+
$query = array( 'numberposts' => absint( $args[3] ) );
6269
+
} else {
6270
+
$query = array();
6271
+
}
6272
+
6273
+
$user = $this->login( $username, $password );
6274
+
if ( ! $user ) {
6275
+
return $this->error;
6276
+
}
6277
+
6278
+
if ( ! current_user_can( 'edit_posts' ) ) {
6279
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) );
6280
+
}
6281
+
6282
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
6283
+
do_action( 'xmlrpc_call', 'metaWeblog.getRecentPosts', $args, $this );
6284
+
6285
+
$posts_list = wp_get_recent_posts( $query );
6286
+
6287
+
if ( ! $posts_list ) {
6288
+
return array();
6289
+
}
6290
+
6291
+
$recent_posts = array();
6292
+
foreach ( $posts_list as $entry ) {
6293
+
if ( ! current_user_can( 'edit_post', $entry['ID'] ) ) {
6294
+
continue;
6295
+
}
6296
+
6297
+
$post_date = $this->_convert_date( $entry['post_date'] );
6298
+
$post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] );
6299
+
$post_modified = $this->_convert_date( $entry['post_modified'] );
6300
+
$post_modified_gmt = $this->_convert_date_gmt( $entry['post_modified_gmt'], $entry['post_modified'] );
6301
+
6302
+
$categories = array();
6303
+
$cat_ids = wp_get_post_categories( $entry['ID'] );
6304
+
foreach ( $cat_ids as $cat_id ) {
6305
+
$categories[] = get_cat_name( $cat_id );
6306
+
}
6307
+
6308
+
$tagnames = array();
6309
+
$tags = wp_get_post_tags( $entry['ID'] );
6310
+
if ( ! empty( $tags ) ) {
6311
+
foreach ( $tags as $tag ) {
6312
+
$tagnames[] = $tag->name;
6313
+
}
6314
+
$tagnames = implode( ', ', $tagnames );
6315
+
} else {
6316
+
$tagnames = '';
6317
+
}
6318
+
6319
+
$post = get_extended( $entry['post_content'] );
6320
+
$link = get_permalink( $entry['ID'] );
6321
+
6322
+
// Get the post author info.
6323
+
$author = get_userdata( $entry['post_author'] );
6324
+
6325
+
$allow_comments = ( 'open' === $entry['comment_status'] ) ? 1 : 0;
6326
+
$allow_pings = ( 'open' === $entry['ping_status'] ) ? 1 : 0;
6327
+
6328
+
// Consider future posts as published.
6329
+
if ( 'future' === $entry['post_status'] ) {
6330
+
$entry['post_status'] = 'publish';
6331
+
}
6332
+
6333
+
// Get post format.
6334
+
$post_format = get_post_format( $entry['ID'] );
6335
+
if ( empty( $post_format ) ) {
6336
+
$post_format = 'standard';
6337
+
}
6338
+
6339
+
$recent_posts[] = array(
6340
+
'dateCreated' => $post_date,
6341
+
'userid' => $entry['post_author'],
6342
+
'postid' => (string) $entry['ID'],
6343
+
'description' => $post['main'],
6344
+
'title' => $entry['post_title'],
6345
+
'link' => $link,
6346
+
'permaLink' => $link,
6347
+
// Commented out because no other tool seems to use this.
6348
+
// 'content' => $entry['post_content'],
6349
+
'categories' => $categories,
6350
+
'mt_excerpt' => $entry['post_excerpt'],
6351
+
'mt_text_more' => $post['extended'],
6352
+
'wp_more_text' => $post['more_text'],
6353
+
'mt_allow_comments' => $allow_comments,
6354
+
'mt_allow_pings' => $allow_pings,
6355
+
'mt_keywords' => $tagnames,
6356
+
'wp_slug' => $entry['post_name'],
6357
+
'wp_password' => $entry['post_password'],
6358
+
'wp_author_id' => (string) $author->ID,
6359
+
'wp_author_display_name' => $author->display_name,
6360
+
'date_created_gmt' => $post_date_gmt,
6361
+
'post_status' => $entry['post_status'],
6362
+
'custom_fields' => $this->get_custom_fields( $entry['ID'] ),
6363
+
'wp_post_format' => $post_format,
6364
+
'date_modified' => $post_modified,
6365
+
'date_modified_gmt' => $post_modified_gmt,
6366
+
'sticky' => ( 'post' === $entry['post_type'] && is_sticky( $entry['ID'] ) ),
6367
+
'wp_post_thumbnail' => get_post_thumbnail_id( $entry['ID'] ),
6368
+
);
6369
+
}
6370
+
6371
+
return $recent_posts;
6372
+
}
6373
+
6374
+
/**
6375
+
* Retrieves the list of categories on a given blog.
6376
+
*
6377
+
* @since 1.5.0
6378
+
*
6379
+
* @param array $args {
6380
+
* Method arguments. Note: arguments must be ordered as documented.
6381
+
*
6382
+
* @type int $0 Blog ID (unused).
6383
+
* @type string $1 Username.
6384
+
* @type string $2 Password.
6385
+
* }
6386
+
* @return array|IXR_Error
6387
+
*/
6388
+
public function mw_getCategories( $args ) {
6389
+
$this->escape( $args );
6390
+
6391
+
$username = $args[1];
6392
+
$password = $args[2];
6393
+
6394
+
$user = $this->login( $username, $password );
6395
+
if ( ! $user ) {
6396
+
return $this->error;
6397
+
}
6398
+
6399
+
if ( ! current_user_can( 'edit_posts' ) ) {
6400
+
return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
6401
+
}
6402
+
6403
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
6404
+
do_action( 'xmlrpc_call', 'metaWeblog.getCategories', $args, $this );
6405
+
6406
+
$categories_struct = array();
6407
+
6408
+
$cats = get_categories( array( 'get' => 'all' ) );
6409
+
if ( $cats ) {
6410
+
foreach ( $cats as $cat ) {
6411
+
$struct = array();
6412
+
$struct['categoryId'] = $cat->term_id;
6413
+
$struct['parentId'] = $cat->parent;
6414
+
$struct['description'] = $cat->name;
6415
+
$struct['categoryDescription'] = $cat->description;
6416
+
$struct['categoryName'] = $cat->name;
6417
+
$struct['htmlUrl'] = esc_html( get_category_link( $cat->term_id ) );
6418
+
$struct['rssUrl'] = esc_html( get_category_feed_link( $cat->term_id, 'rss2' ) );
6419
+
6420
+
$categories_struct[] = $struct;
6421
+
}
6422
+
}
6423
+
6424
+
return $categories_struct;
6425
+
}
6426
+
6427
+
/**
6428
+
* Uploads a file, following your settings.
6429
+
*
6430
+
* Adapted from a patch by Johann Richard.
6431
+
*
6432
+
* @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/
6433
+
*
6434
+
* @since 1.5.0
6435
+
*
6436
+
* @param array $args {
6437
+
* Method arguments. Note: arguments must be ordered as documented.
6438
+
*
6439
+
* @type int $0 Blog ID (unused).
6440
+
* @type string $1 Username.
6441
+
* @type string $2 Password.
6442
+
* @type array $3 Data.
6443
+
* }
6444
+
* @return array|IXR_Error
6445
+
*/
6446
+
public function mw_newMediaObject( $args ) {
6447
+
$username = $this->escape( $args[1] );
6448
+
$password = $this->escape( $args[2] );
6449
+
$data = $args[3];
6450
+
6451
+
$name = sanitize_file_name( $data['name'] );
6452
+
$type = $data['type'];
6453
+
$bits = $data['bits'];
6454
+
6455
+
$user = $this->login( $username, $password );
6456
+
if ( ! $user ) {
6457
+
return $this->error;
6458
+
}
6459
+
6460
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
6461
+
do_action( 'xmlrpc_call', 'metaWeblog.newMediaObject', $args, $this );
6462
+
6463
+
if ( ! current_user_can( 'upload_files' ) ) {
6464
+
$this->error = new IXR_Error( 401, __( 'Sorry, you are not allowed to upload files.' ) );
6465
+
return $this->error;
6466
+
}
6467
+
6468
+
if ( is_multisite() && upload_is_user_over_quota( false ) ) {
6469
+
$this->error = new IXR_Error(
6470
+
401,
6471
+
sprintf(
6472
+
/* translators: %s: Allowed space allocation. */
6473
+
__( 'Sorry, you have used your space allocation of %s. Please delete some files to upload more files.' ),
6474
+
size_format( get_space_allowed() * MB_IN_BYTES )
6475
+
)
6476
+
);
6477
+
return $this->error;
6478
+
}
6479
+
6480
+
/**
6481
+
* Filters whether to preempt the XML-RPC media upload.
6482
+
*
6483
+
* Returning a truthy value will effectively short-circuit the media upload,
6484
+
* returning that value as a 500 error instead.
6485
+
*
6486
+
* @since 2.1.0
6487
+
*
6488
+
* @param bool $error Whether to pre-empt the media upload. Default false.
6489
+
*/
6490
+
$upload_err = apply_filters( 'pre_upload_error', false );
6491
+
if ( $upload_err ) {
6492
+
return new IXR_Error( 500, $upload_err );
6493
+
}
6494
+
6495
+
$upload = wp_upload_bits( $name, null, $bits );
6496
+
if ( ! empty( $upload['error'] ) ) {
6497
+
/* translators: 1: File name, 2: Error message. */
6498
+
$error_string = sprintf( __( 'Could not write file %1$s (%2$s).' ), $name, $upload['error'] );
6499
+
return new IXR_Error( 500, $error_string );
6500
+
}
6501
+
6502
+
// Construct the attachment array.
6503
+
$post_id = 0;
6504
+
if ( ! empty( $data['post_id'] ) ) {
6505
+
$post_id = (int) $data['post_id'];
6506
+
6507
+
if ( ! current_user_can( 'edit_post', $post_id ) ) {
6508
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
6509
+
}
6510
+
}
6511
+
6512
+
$attachment = array(
6513
+
'post_title' => $name,
6514
+
'post_content' => '',
6515
+
'post_type' => 'attachment',
6516
+
'post_parent' => $post_id,
6517
+
'post_mime_type' => $type,
6518
+
'guid' => $upload['url'],
6519
+
);
6520
+
6521
+
// Save the data.
6522
+
$attachment_id = wp_insert_attachment( $attachment, $upload['file'], $post_id );
6523
+
wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $upload['file'] ) );
6524
+
6525
+
/**
6526
+
* Fires after a new attachment has been added via the XML-RPC MovableType API.
6527
+
*
6528
+
* @since 3.4.0
6529
+
*
6530
+
* @param int $attachment_id ID of the new attachment.
6531
+
* @param array $args An array of arguments to add the attachment.
6532
+
*/
6533
+
do_action( 'xmlrpc_call_success_mw_newMediaObject', $attachment_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase
6534
+
6535
+
$struct = $this->_prepare_media_item( get_post( $attachment_id ) );
6536
+
6537
+
// Deprecated values.
6538
+
$struct['id'] = $struct['attachment_id'];
6539
+
$struct['file'] = $struct['title'];
6540
+
$struct['url'] = $struct['link'];
6541
+
6542
+
return $struct;
6543
+
}
6544
+
6545
+
/*
6546
+
* MovableType API functions.
6547
+
* Specs archive on https://web.archive.org/web/20050220091302/http://www.movabletype.org/docs/mtmanual_programmatic.html
6548
+
*/
6549
+
6550
+
/**
6551
+
* Retrieves the post titles of recent posts.
6552
+
*
6553
+
* @since 1.5.0
6554
+
*
6555
+
* @param array $args {
6556
+
* Method arguments. Note: arguments must be ordered as documented.
6557
+
*
6558
+
* @type int $0 Blog ID (unused).
6559
+
* @type string $1 Username.
6560
+
* @type string $2 Password.
6561
+
* @type int $3 Optional. Number of posts.
6562
+
* }
6563
+
* @return array|IXR_Error
6564
+
*/
6565
+
public function mt_getRecentPostTitles( $args ) {
6566
+
$this->escape( $args );
6567
+
6568
+
$username = $args[1];
6569
+
$password = $args[2];
6570
+
if ( isset( $args[3] ) ) {
6571
+
$query = array( 'numberposts' => absint( $args[3] ) );
6572
+
} else {
6573
+
$query = array();
6574
+
}
6575
+
6576
+
$user = $this->login( $username, $password );
6577
+
if ( ! $user ) {
6578
+
return $this->error;
6579
+
}
6580
+
6581
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
6582
+
do_action( 'xmlrpc_call', 'mt.getRecentPostTitles', $args, $this );
6583
+
6584
+
$posts_list = wp_get_recent_posts( $query );
6585
+
6586
+
if ( ! $posts_list ) {
6587
+
$this->error = new IXR_Error( 500, __( 'No posts found or an error occurred while retrieving posts.' ) );
6588
+
return $this->error;
6589
+
}
6590
+
6591
+
$recent_posts = array();
6592
+
6593
+
foreach ( $posts_list as $entry ) {
6594
+
if ( ! current_user_can( 'edit_post', $entry['ID'] ) ) {
6595
+
continue;
6596
+
}
6597
+
6598
+
$post_date = $this->_convert_date( $entry['post_date'] );
6599
+
$post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] );
6600
+
6601
+
$recent_posts[] = array(
6602
+
'dateCreated' => $post_date,
6603
+
'userid' => $entry['post_author'],
6604
+
'postid' => (string) $entry['ID'],
6605
+
'title' => $entry['post_title'],
6606
+
'post_status' => $entry['post_status'],
6607
+
'date_created_gmt' => $post_date_gmt,
6608
+
);
6609
+
}
6610
+
6611
+
return $recent_posts;
6612
+
}
6613
+
6614
+
/**
6615
+
* Retrieves the list of all categories on a blog.
6616
+
*
6617
+
* @since 1.5.0
6618
+
*
6619
+
* @param array $args {
6620
+
* Method arguments. Note: arguments must be ordered as documented.
6621
+
*
6622
+
* @type int $0 Blog ID (unused).
6623
+
* @type string $1 Username.
6624
+
* @type string $2 Password.
6625
+
* }
6626
+
* @return array|IXR_Error
6627
+
*/
6628
+
public function mt_getCategoryList( $args ) {
6629
+
$this->escape( $args );
6630
+
6631
+
$username = $args[1];
6632
+
$password = $args[2];
6633
+
6634
+
$user = $this->login( $username, $password );
6635
+
if ( ! $user ) {
6636
+
return $this->error;
6637
+
}
6638
+
6639
+
if ( ! current_user_can( 'edit_posts' ) ) {
6640
+
return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
6641
+
}
6642
+
6643
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
6644
+
do_action( 'xmlrpc_call', 'mt.getCategoryList', $args, $this );
6645
+
6646
+
$categories_struct = array();
6647
+
6648
+
$cats = get_categories(
6649
+
array(
6650
+
'hide_empty' => 0,
6651
+
'hierarchical' => 0,
6652
+
)
6653
+
);
6654
+
if ( $cats ) {
6655
+
foreach ( $cats as $cat ) {
6656
+
$struct = array();
6657
+
$struct['categoryId'] = $cat->term_id;
6658
+
$struct['categoryName'] = $cat->name;
6659
+
6660
+
$categories_struct[] = $struct;
6661
+
}
6662
+
}
6663
+
6664
+
return $categories_struct;
6665
+
}
6666
+
6667
+
/**
6668
+
* Retrieves post categories.
6669
+
*
6670
+
* @since 1.5.0
6671
+
*
6672
+
* @param array $args {
6673
+
* Method arguments. Note: arguments must be ordered as documented.
6674
+
*
6675
+
* @type int $0 Post ID.
6676
+
* @type string $1 Username.
6677
+
* @type string $2 Password.
6678
+
* }
6679
+
* @return array|IXR_Error
6680
+
*/
6681
+
public function mt_getPostCategories( $args ) {
6682
+
$this->escape( $args );
6683
+
6684
+
$post_id = (int) $args[0];
6685
+
$username = $args[1];
6686
+
$password = $args[2];
6687
+
6688
+
$user = $this->login( $username, $password );
6689
+
if ( ! $user ) {
6690
+
return $this->error;
6691
+
}
6692
+
6693
+
if ( ! get_post( $post_id ) ) {
6694
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
6695
+
}
6696
+
6697
+
if ( ! current_user_can( 'edit_post', $post_id ) ) {
6698
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
6699
+
}
6700
+
6701
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
6702
+
do_action( 'xmlrpc_call', 'mt.getPostCategories', $args, $this );
6703
+
6704
+
$categories = array();
6705
+
$cat_ids = wp_get_post_categories( (int) $post_id );
6706
+
// First listed category will be the primary category.
6707
+
$is_primary = true;
6708
+
foreach ( $cat_ids as $cat_id ) {
6709
+
$categories[] = array(
6710
+
'categoryName' => get_cat_name( $cat_id ),
6711
+
'categoryId' => (string) $cat_id,
6712
+
'isPrimary' => $is_primary,
6713
+
);
6714
+
$is_primary = false;
6715
+
}
6716
+
6717
+
return $categories;
6718
+
}
6719
+
6720
+
/**
6721
+
* Sets categories for a post.
6722
+
*
6723
+
* @since 1.5.0
6724
+
*
6725
+
* @param array $args {
6726
+
* Method arguments. Note: arguments must be ordered as documented.
6727
+
*
6728
+
* @type int $0 Post ID.
6729
+
* @type string $1 Username.
6730
+
* @type string $2 Password.
6731
+
* @type array $3 Categories.
6732
+
* }
6733
+
* @return true|IXR_Error True on success.
6734
+
*/
6735
+
public function mt_setPostCategories( $args ) {
6736
+
$this->escape( $args );
6737
+
6738
+
$post_id = (int) $args[0];
6739
+
$username = $args[1];
6740
+
$password = $args[2];
6741
+
$categories = $args[3];
6742
+
6743
+
$user = $this->login( $username, $password );
6744
+
if ( ! $user ) {
6745
+
return $this->error;
6746
+
}
6747
+
6748
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
6749
+
do_action( 'xmlrpc_call', 'mt.setPostCategories', $args, $this );
6750
+
6751
+
if ( ! get_post( $post_id ) ) {
6752
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
6753
+
}
6754
+
6755
+
if ( ! current_user_can( 'edit_post', $post_id ) ) {
6756
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
6757
+
}
6758
+
6759
+
$cat_ids = array();
6760
+
foreach ( $categories as $cat ) {
6761
+
$cat_ids[] = $cat['categoryId'];
6762
+
}
6763
+
6764
+
wp_set_post_categories( $post_id, $cat_ids );
6765
+
6766
+
return true;
6767
+
}
6768
+
6769
+
/**
6770
+
* Retrieves an array of methods supported by this server.
6771
+
*
6772
+
* @since 1.5.0
6773
+
*
6774
+
* @return array
6775
+
*/
6776
+
public function mt_supportedMethods() {
6777
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
6778
+
do_action( 'xmlrpc_call', 'mt.supportedMethods', array(), $this );
6779
+
6780
+
return array_keys( $this->methods );
6781
+
}
6782
+
6783
+
/**
6784
+
* Retrieves an empty array because we don't support per-post text filters.
6785
+
*
6786
+
* @since 1.5.0
6787
+
*/
6788
+
public function mt_supportedTextFilters() {
6789
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
6790
+
do_action( 'xmlrpc_call', 'mt.supportedTextFilters', array(), $this );
6791
+
6792
+
/**
6793
+
* Filters the MoveableType text filters list for XML-RPC.
6794
+
*
6795
+
* @since 2.2.0
6796
+
*
6797
+
* @param array $filters An array of text filters.
6798
+
*/
6799
+
return apply_filters( 'xmlrpc_text_filters', array() );
6800
+
}
6801
+
6802
+
/**
6803
+
* Retrieves trackbacks sent to a given post.
6804
+
*
6805
+
* @since 1.5.0
6806
+
*
6807
+
* @global wpdb $wpdb WordPress database abstraction object.
6808
+
*
6809
+
* @param int $post_id
6810
+
* @return array|IXR_Error
6811
+
*/
6812
+
public function mt_getTrackbackPings( $post_id ) {
6813
+
global $wpdb;
6814
+
6815
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
6816
+
do_action( 'xmlrpc_call', 'mt.getTrackbackPings', $post_id, $this );
6817
+
6818
+
$actual_post = get_post( $post_id, ARRAY_A );
6819
+
6820
+
if ( ! $actual_post ) {
6821
+
return new IXR_Error( 404, __( 'Sorry, no such post.' ) );
6822
+
}
6823
+
6824
+
$comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id ) );
6825
+
6826
+
if ( ! $comments ) {
6827
+
return array();
6828
+
}
6829
+
6830
+
$trackback_pings = array();
6831
+
foreach ( $comments as $comment ) {
6832
+
if ( 'trackback' === $comment->comment_type ) {
6833
+
$content = $comment->comment_content;
6834
+
$title = substr( $content, 8, ( strpos( $content, '</strong>' ) - 8 ) );
6835
+
$trackback_pings[] = array(
6836
+
'pingTitle' => $title,
6837
+
'pingURL' => $comment->comment_author_url,
6838
+
'pingIP' => $comment->comment_author_IP,
6839
+
);
6840
+
}
6841
+
}
6842
+
6843
+
return $trackback_pings;
6844
+
}
6845
+
6846
+
/**
6847
+
* Sets a post's publish status to 'publish'.
6848
+
*
6849
+
* @since 1.5.0
6850
+
*
6851
+
* @param array $args {
6852
+
* Method arguments. Note: arguments must be ordered as documented.
6853
+
*
6854
+
* @type int $0 Post ID.
6855
+
* @type string $1 Username.
6856
+
* @type string $2 Password.
6857
+
* }
6858
+
* @return int|IXR_Error
6859
+
*/
6860
+
public function mt_publishPost( $args ) {
6861
+
$this->escape( $args );
6862
+
6863
+
$post_id = (int) $args[0];
6864
+
$username = $args[1];
6865
+
$password = $args[2];
6866
+
6867
+
$user = $this->login( $username, $password );
6868
+
if ( ! $user ) {
6869
+
return $this->error;
6870
+
}
6871
+
6872
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
6873
+
do_action( 'xmlrpc_call', 'mt.publishPost', $args, $this );
6874
+
6875
+
$postdata = get_post( $post_id, ARRAY_A );
6876
+
if ( ! $postdata ) {
6877
+
return new IXR_Error( 404, __( 'Invalid post ID.' ) );
6878
+
}
6879
+
6880
+
if ( ! current_user_can( 'publish_posts' ) || ! current_user_can( 'edit_post', $post_id ) ) {
6881
+
return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this post.' ) );
6882
+
}
6883
+
6884
+
$postdata['post_status'] = 'publish';
6885
+
6886
+
// Retain old categories.
6887
+
$postdata['post_category'] = wp_get_post_categories( $post_id );
6888
+
$this->escape( $postdata );
6889
+
6890
+
return wp_update_post( $postdata );
6891
+
}
6892
+
6893
+
/*
6894
+
* Pingback functions.
6895
+
* Specs on www.hixie.ch/specs/pingback/pingback
6896
+
*/
6897
+
6898
+
/**
6899
+
* Retrieves a pingback and registers it.
6900
+
*
6901
+
* @since 1.5.0
6902
+
*
6903
+
* @global wpdb $wpdb WordPress database abstraction object.
6904
+
*
6905
+
* @param array $args {
6906
+
* Method arguments. Note: arguments must be ordered as documented.
6907
+
*
6908
+
* @type string $0 URL of page linked from.
6909
+
* @type string $1 URL of page linked to.
6910
+
* }
6911
+
* @return string|IXR_Error
6912
+
*/
6913
+
public function pingback_ping( $args ) {
6914
+
global $wpdb;
6915
+
6916
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
6917
+
do_action( 'xmlrpc_call', 'pingback.ping', $args, $this );
6918
+
6919
+
$this->escape( $args );
6920
+
6921
+
$pagelinkedfrom = str_replace( '&', '&', $args[0] );
6922
+
$pagelinkedto = str_replace( '&', '&', $args[1] );
6923
+
$pagelinkedto = str_replace( '&', '&', $pagelinkedto );
6924
+
6925
+
/**
6926
+
* Filters the pingback source URI.
6927
+
*
6928
+
* @since 3.6.0
6929
+
*
6930
+
* @param string $pagelinkedfrom URI of the page linked from.
6931
+
* @param string $pagelinkedto URI of the page linked to.
6932
+
*/
6933
+
$pagelinkedfrom = apply_filters( 'pingback_ping_source_uri', $pagelinkedfrom, $pagelinkedto );
6934
+
6935
+
if ( ! $pagelinkedfrom ) {
6936
+
return $this->pingback_error( 0, __( 'A valid URL was not provided.' ) );
6937
+
}
6938
+
6939
+
// Check if the page linked to is on our site.
6940
+
$pos1 = strpos( $pagelinkedto, str_replace( array( 'http://www.', 'http://', 'https://www.', 'https://' ), '', get_option( 'home' ) ) );
6941
+
if ( ! $pos1 ) {
6942
+
return $this->pingback_error( 0, __( 'Is there no link to us?' ) );
6943
+
}
6944
+
6945
+
/*
6946
+
* Let's find which post is linked to.
6947
+
* FIXME: Does url_to_postid() cover all these cases already?
6948
+
* If so, then let's use it and drop the old code.
6949
+
*/
6950
+
$urltest = parse_url( $pagelinkedto );
6951
+
$post_id = url_to_postid( $pagelinkedto );
6952
+
6953
+
if ( $post_id ) {
6954
+
// $way
6955
+
} elseif ( isset( $urltest['path'] ) && preg_match( '#p/[0-9]{1,}#', $urltest['path'], $match ) ) {
6956
+
// The path defines the post_ID (archives/p/XXXX).
6957
+
$blah = explode( '/', $match[0] );
6958
+
$post_id = (int) $blah[1];
6959
+
} elseif ( isset( $urltest['query'] ) && preg_match( '#p=[0-9]{1,}#', $urltest['query'], $match ) ) {
6960
+
// The query string defines the post_ID (?p=XXXX).
6961
+
$blah = explode( '=', $match[0] );
6962
+
$post_id = (int) $blah[1];
6963
+
} elseif ( isset( $urltest['fragment'] ) ) {
6964
+
// An #anchor is there, it's either...
6965
+
if ( (int) $urltest['fragment'] ) {
6966
+
// ...an integer #XXXX (simplest case),
6967
+
$post_id = (int) $urltest['fragment'];
6968
+
} elseif ( preg_match( '/post-[0-9]+/', $urltest['fragment'] ) ) {
6969
+
// ...a post ID in the form 'post-###',
6970
+
$post_id = preg_replace( '/[^0-9]+/', '', $urltest['fragment'] );
6971
+
} elseif ( is_string( $urltest['fragment'] ) ) {
6972
+
// ...or a string #title, a little more complicated.
6973
+
$title = preg_replace( '/[^a-z0-9]/i', '.', $urltest['fragment'] );
6974
+
$sql = $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", $title );
6975
+
$post_id = $wpdb->get_var( $sql );
6976
+
if ( ! $post_id ) {
6977
+
// Returning unknown error '0' is better than die()'ing.
6978
+
return $this->pingback_error( 0, '' );
6979
+
}
6980
+
}
6981
+
} else {
6982
+
// TODO: Attempt to extract a post ID from the given URL.
6983
+
return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either does not exist, or it is not a pingback-enabled resource.' ) );
6984
+
}
6985
+
6986
+
$post_id = (int) $post_id;
6987
+
$post = get_post( $post_id );
6988
+
6989
+
if ( ! $post ) { // Post not found.
6990
+
return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either does not exist, or it is not a pingback-enabled resource.' ) );
6991
+
}
6992
+
6993
+
if ( url_to_postid( $pagelinkedfrom ) === $post_id ) {
6994
+
return $this->pingback_error( 0, __( 'The source URL and the target URL cannot both point to the same resource.' ) );
6995
+
}
6996
+
6997
+
// Check if pings are on.
6998
+
if ( ! pings_open( $post ) ) {
6999
+
return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either does not exist, or it is not a pingback-enabled resource.' ) );
7000
+
}
7001
+
7002
+
// Let's check that the remote site didn't already pingback this entry.
7003
+
if ( $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_id, $pagelinkedfrom ) ) ) {
7004
+
return $this->pingback_error( 48, __( 'The pingback has already been registered.' ) );
7005
+
}
7006
+
7007
+
/*
7008
+
* The remote site may have sent the pingback before it finished publishing its own content
7009
+
* containing this pingback URL. If that happens then it won't be immediately possible to fetch
7010
+
* the pinging post; adding a small delay reduces the likelihood of this happening.
7011
+
*
7012
+
* While there are more robust methods than calling `sleep()` here (because `sleep()` merely
7013
+
* mitigates the risk of requesting the remote post before it's available), this is effective
7014
+
* enough for most cases and avoids introducing more complexity into this code.
7015
+
*
7016
+
* One way to improve the reliability of this code might be to add failure-handling to the remote
7017
+
* fetch and retry up to a set number of times if it receives a 404. This could also handle 401 and
7018
+
* 403 responses to differentiate the "does not exist" failure from the "may not access" failure.
7019
+
*/
7020
+
sleep( 1 );
7021
+
7022
+
$remote_ip = preg_replace( '/[^0-9a-fA-F:., ]/', '', $_SERVER['REMOTE_ADDR'] );
7023
+
7024
+
/** This filter is documented in wp-includes/class-wp-http.php */
7025
+
$user_agent = apply_filters( 'http_headers_useragent', 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ), $pagelinkedfrom );
7026
+
7027
+
// Let's check the remote site.
7028
+
$http_api_args = array(
7029
+
'timeout' => 10,
7030
+
'redirection' => 0,
7031
+
'limit_response_size' => 153600, // 150 KB
7032
+
'user-agent' => "$user_agent; verifying pingback from $remote_ip",
7033
+
'headers' => array(
7034
+
'X-Pingback-Forwarded-For' => $remote_ip,
7035
+
),
7036
+
);
7037
+
7038
+
$request = wp_safe_remote_get( $pagelinkedfrom, $http_api_args );
7039
+
$remote_source = wp_remote_retrieve_body( $request );
7040
+
$remote_source_original = $remote_source;
7041
+
7042
+
if ( ! $remote_source ) {
7043
+
return $this->pingback_error( 16, __( 'The source URL does not exist.' ) );
7044
+
}
7045
+
7046
+
/**
7047
+
* Filters the pingback remote source.
7048
+
*
7049
+
* @since 2.5.0
7050
+
*
7051
+
* @param string $remote_source Response source for the page linked from.
7052
+
* @param string $pagelinkedto URL of the page linked to.
7053
+
*/
7054
+
$remote_source = apply_filters( 'pre_remote_source', $remote_source, $pagelinkedto );
7055
+
7056
+
// Work around bug in strip_tags():
7057
+
$remote_source = str_replace( '<!DOC', '<DOC', $remote_source );
7058
+
$remote_source = preg_replace( '/[\r\n\t ]+/', ' ', $remote_source ); // normalize spaces
7059
+
$remote_source = preg_replace( '/<\/*(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/', "\n\n", $remote_source );
7060
+
7061
+
preg_match( '|<title>([^<]*?)</title>|is', $remote_source, $matchtitle );
7062
+
$title = isset( $matchtitle[1] ) ? $matchtitle[1] : '';
7063
+
if ( empty( $title ) ) {
7064
+
return $this->pingback_error( 32, __( 'A title on that page cannot be found.' ) );
7065
+
}
7066
+
7067
+
// Remove all script and style tags including their content.
7068
+
$remote_source = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $remote_source );
7069
+
// Just keep the tag we need.
7070
+
$remote_source = strip_tags( $remote_source, '<a>' );
7071
+
7072
+
$p = explode( "\n\n", $remote_source );
7073
+
7074
+
$preg_target = preg_quote( $pagelinkedto, '|' );
7075
+
7076
+
foreach ( $p as $para ) {
7077
+
if ( str_contains( $para, $pagelinkedto ) ) { // It exists, but is it a link?
7078
+
preg_match( '|<a[^>]+?' . $preg_target . '[^>]*>([^>]+?)</a>|', $para, $context );
7079
+
7080
+
// If the URL isn't in a link context, keep looking.
7081
+
if ( empty( $context ) ) {
7082
+
continue;
7083
+
}
7084
+
7085
+
/*
7086
+
* We're going to use this fake tag to mark the context in a bit.
7087
+
* The marker is needed in case the link text appears more than once in the paragraph.
7088
+
*/
7089
+
$excerpt = preg_replace( '|\</?wpcontext\>|', '', $para );
7090
+
7091
+
// prevent really long link text
7092
+
if ( strlen( $context[1] ) > 100 ) {
7093
+
$context[1] = substr( $context[1], 0, 100 ) . '…';
7094
+
}
7095
+
7096
+
$marker = '<wpcontext>' . $context[1] . '</wpcontext>'; // Set up our marker.
7097
+
$excerpt = str_replace( $context[0], $marker, $excerpt ); // Swap out the link for our marker.
7098
+
$excerpt = strip_tags( $excerpt, '<wpcontext>' ); // Strip all tags but our context marker.
7099
+
$excerpt = trim( $excerpt );
7100
+
$preg_marker = preg_quote( $marker, '|' );
7101
+
$excerpt = preg_replace( "|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt );
7102
+
$excerpt = strip_tags( $excerpt ); // YES, again, to remove the marker wrapper.
7103
+
break;
7104
+
}
7105
+
}
7106
+
7107
+
if ( empty( $context ) ) { // Link to target not found.
7108
+
return $this->pingback_error( 17, __( 'The source URL does not contain a link to the target URL, and so cannot be used as a source.' ) );
7109
+
}
7110
+
7111
+
$pagelinkedfrom = str_replace( '&', '&', $pagelinkedfrom );
7112
+
7113
+
$context = '[…] ' . esc_html( $excerpt ) . ' […]';
7114
+
$pagelinkedfrom = $this->escape( $pagelinkedfrom );
7115
+
7116
+
$comment_post_id = (int) $post_id;
7117
+
$comment_author = $title;
7118
+
$comment_author_email = '';
7119
+
$this->escape( $comment_author );
7120
+
$comment_author_url = $pagelinkedfrom;
7121
+
$comment_content = $context;
7122
+
$this->escape( $comment_content );
7123
+
$comment_type = 'pingback';
7124
+
7125
+
$commentdata = array(
7126
+
'comment_post_ID' => $comment_post_id,
7127
+
);
7128
+
7129
+
$commentdata += compact(
7130
+
'comment_author',
7131
+
'comment_author_url',
7132
+
'comment_author_email',
7133
+
'comment_content',
7134
+
'comment_type',
7135
+
'remote_source',
7136
+
'remote_source_original'
7137
+
);
7138
+
7139
+
$comment_id = wp_new_comment( $commentdata );
7140
+
7141
+
if ( is_wp_error( $comment_id ) ) {
7142
+
return $this->pingback_error( 0, $comment_id->get_error_message() );
7143
+
}
7144
+
7145
+
/**
7146
+
* Fires after a post pingback has been sent.
7147
+
*
7148
+
* @since 0.71
7149
+
*
7150
+
* @param int $comment_id Comment ID.
7151
+
*/
7152
+
do_action( 'pingback_post', $comment_id );
7153
+
7154
+
/* translators: 1: URL of the page linked from, 2: URL of the page linked to. */
7155
+
return sprintf( __( 'Pingback from %1$s to %2$s registered. Keep the web talking! :-)' ), $pagelinkedfrom, $pagelinkedto );
7156
+
}
7157
+
7158
+
/**
7159
+
* Retrieves an array of URLs that pingbacked the given URL.
7160
+
*
7161
+
* Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html
7162
+
*
7163
+
* @since 1.5.0
7164
+
*
7165
+
* @global wpdb $wpdb WordPress database abstraction object.
7166
+
*
7167
+
* @param string $url
7168
+
* @return array|IXR_Error
7169
+
*/
7170
+
public function pingback_extensions_getPingbacks( $url ) {
7171
+
global $wpdb;
7172
+
7173
+
/** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
7174
+
do_action( 'xmlrpc_call', 'pingback.extensions.getPingbacks', $url, $this );
7175
+
7176
+
$url = $this->escape( $url );
7177
+
7178
+
$post_id = url_to_postid( $url );
7179
+
if ( ! $post_id ) {
7180
+
// We aren't sure that the resource is available and/or pingback enabled.
7181
+
return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either does not exist, or it is not a pingback-enabled resource.' ) );
7182
+
}
7183
+
7184
+
$actual_post = get_post( $post_id, ARRAY_A );
7185
+
7186
+
if ( ! $actual_post ) {
7187
+
// No such post = resource not found.
7188
+
return $this->pingback_error( 32, __( 'The specified target URL does not exist.' ) );
7189
+
}
7190
+
7191
+
$comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id ) );
7192
+
7193
+
if ( ! $comments ) {
7194
+
return array();
7195
+
}
7196
+
7197
+
$pingbacks = array();
7198
+
foreach ( $comments as $comment ) {
7199
+
if ( 'pingback' === $comment->comment_type ) {
7200
+
$pingbacks[] = $comment->comment_author_url;
7201
+
}
7202
+
}
7203
+
7204
+
return $pingbacks;
7205
+
}
7206
+
7207
+
/**
7208
+
* Sends a pingback error based on the given error code and message.
7209
+
*
7210
+
* @since 3.6.0
7211
+
*
7212
+
* @param int $code Error code.
7213
+
* @param string $message Error message.
7214
+
* @return IXR_Error Error object.
7215
+
*/
7216
+
protected function pingback_error( $code, $message ) {
7217
+
/**
7218
+
* Filters the XML-RPC pingback error return.
7219
+
*
7220
+
* @since 3.5.1
7221
+
*
7222
+
* @param IXR_Error $error An IXR_Error object containing the error code and message.
7223
+
*/
7224
+
return apply_filters( 'xmlrpc_pingback_error', new IXR_Error( $code, $message ) );
7225
+
}
7226
+
}
7227
+