PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 8.2.1
Jetpack – WP Security, Backup, Speed, & Growth v8.2.1
15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / json-endpoints / class.wpcom-json-api-update-post-endpoint.php
jetpack / json-endpoints Last commit date
jetpack 6 years ago class.wpcom-json-api-add-widget-endpoint.php 8 years ago class.wpcom-json-api-autosave-post-v1-1-endpoint.php 6 years ago class.wpcom-json-api-bulk-delete-post-endpoint.php 8 years ago class.wpcom-json-api-bulk-restore-post-endpoint.php 8 years ago class.wpcom-json-api-bulk-update-comments-endpoint.php 7 years ago class.wpcom-json-api-comment-endpoint.php 7 years ago class.wpcom-json-api-delete-media-endpoint.php 8 years ago class.wpcom-json-api-delete-media-v1-1-endpoint.php 8 years ago class.wpcom-json-api-edit-media-v1-2-endpoint.php 6 years ago class.wpcom-json-api-get-autosave-v1-1-endpoint.php 6 years ago class.wpcom-json-api-get-comment-counts-endpoint.php 7 years ago class.wpcom-json-api-get-comment-endpoint.php 8 years ago class.wpcom-json-api-get-comment-history-endpoint.php 8 years ago class.wpcom-json-api-get-comments-tree-endpoint.php 8 years ago class.wpcom-json-api-get-comments-tree-v1-1-endpoint.php 8 years ago class.wpcom-json-api-get-comments-tree-v1-2-endpoint.php 8 years ago class.wpcom-json-api-get-customcss.php 8 years ago class.wpcom-json-api-get-media-endpoint.php 8 years ago class.wpcom-json-api-get-media-v1-1-endpoint.php 8 years ago class.wpcom-json-api-get-media-v1-2-endpoint.php 8 years ago class.wpcom-json-api-get-post-counts-v1-1-endpoint.php 6 years ago class.wpcom-json-api-get-post-endpoint.php 8 years ago class.wpcom-json-api-get-post-v1-1-endpoint.php 8 years ago class.wpcom-json-api-get-site-endpoint.php 6 years ago class.wpcom-json-api-get-site-v1-2-endpoint.php 6 years ago class.wpcom-json-api-get-taxonomies-endpoint.php 8 years ago class.wpcom-json-api-get-taxonomy-endpoint.php 8 years ago class.wpcom-json-api-get-term-endpoint.php 8 years ago class.wpcom-json-api-list-comments-endpoint.php 7 years ago class.wpcom-json-api-list-embeds-endpoint.php 8 years ago class.wpcom-json-api-list-media-endpoint.php 8 years ago class.wpcom-json-api-list-media-v1-1-endpoint.php 8 years ago class.wpcom-json-api-list-media-v1-2-endpoint.php 8 years ago class.wpcom-json-api-list-post-type-taxonomies-endpoint.php 8 years ago class.wpcom-json-api-list-post-types-endpoint.php 7 years ago class.wpcom-json-api-list-posts-endpoint.php 6 years ago class.wpcom-json-api-list-posts-v1-1-endpoint.php 6 years ago class.wpcom-json-api-list-posts-v1-2-endpoint.php 6 years ago class.wpcom-json-api-list-roles-endpoint.php 8 years ago class.wpcom-json-api-list-shortcodes-endpoint.php 8 years ago class.wpcom-json-api-list-terms-endpoint.php 8 years ago class.wpcom-json-api-list-users-endpoint.php 8 years ago class.wpcom-json-api-menus-v1-1-endpoint.php 8 years ago class.wpcom-json-api-post-endpoint.php 6 years ago class.wpcom-json-api-post-v1-1-endpoint.php 6 years ago class.wpcom-json-api-render-embed-endpoint.php 6 years ago class.wpcom-json-api-render-embed-reversal-endpoint.php 8 years ago class.wpcom-json-api-render-endpoint.php 9 years ago class.wpcom-json-api-render-shortcode-endpoint.php 8 years ago class.wpcom-json-api-sharing-buttons-endpoint.php 8 years ago class.wpcom-json-api-site-settings-endpoint.php 7 years ago class.wpcom-json-api-site-settings-v1-2-endpoint.php 7 years ago class.wpcom-json-api-site-settings-v1-3-endpoint.php 7 years ago class.wpcom-json-api-site-settings-v1-4-endpoint.php 7 years ago class.wpcom-json-api-site-user-endpoint.php 8 years ago class.wpcom-json-api-taxonomy-endpoint.php 7 years ago class.wpcom-json-api-update-comment-endpoint.php 8 years ago class.wpcom-json-api-update-customcss.php 8 years ago class.wpcom-json-api-update-media-endpoint.php 8 years ago class.wpcom-json-api-update-media-v1-1-endpoint.php 8 years ago class.wpcom-json-api-update-post-endpoint.php 6 years ago class.wpcom-json-api-update-post-v1-1-endpoint.php 6 years ago class.wpcom-json-api-update-post-v1-2-endpoint.php 6 years ago class.wpcom-json-api-update-site-homepage-endpoint.php 8 years ago class.wpcom-json-api-update-site-logo-endpoint.php 8 years ago class.wpcom-json-api-update-taxonomy-endpoint.php 8 years ago class.wpcom-json-api-update-term-endpoint.php 7 years ago class.wpcom-json-api-update-user-endpoint.php 7 years ago class.wpcom-json-api-upload-media-endpoint.php 8 years ago class.wpcom-json-api-upload-media-v1-1-endpoint.php 8 years ago
class.wpcom-json-api-update-post-endpoint.php
922 lines
1 <?php
2
3 new WPCOM_JSON_API_Update_Post_Endpoint( array(
4 'description' => 'Create a post.',
5 'group' => 'posts',
6 'stat' => 'posts:new',
7 'new_version' => '1.2',
8 'max_version' => '1',
9 'method' => 'POST',
10 'path' => '/sites/%s/posts/new',
11 'path_labels' => array(
12 '$site' => '(int|string) Site ID or domain',
13 ),
14
15 'request_format' => array(
16 // explicitly document all input
17 'date' => "(ISO 8601 datetime) The post's creation time.",
18 'title' => '(HTML) The post title.',
19 'content' => '(HTML) The post content.',
20 'excerpt' => '(HTML) An optional post excerpt.',
21 'slug' => '(string) The name (slug) for the post, used in URLs.',
22 'author' => '(string) The username or ID for the user to assign the post to.',
23 'publicize' => '(array|bool) True or false if the post be publicized to external services. An array of services if we only want to publicize to a select few. Defaults to true.',
24 'publicize_message' => '(string) Custom message to be publicized to external services.',
25 'status' => array(
26 'publish' => 'Publish the post.',
27 'private' => 'Privately publish the post.',
28 'draft' => 'Save the post as a draft.',
29 'pending' => 'Mark the post as pending editorial approval.',
30 'auto-draft' => 'Save a placeholder for a newly created post, with no content.',
31 ),
32 'sticky' => array(
33 'false' => 'Post is not marked as sticky.',
34 'true' => 'Stick the post to the front page.',
35 ),
36 'password' => '(string) The plaintext password protecting the post, or, more likely, the empty string if the post is not password protected.',
37 'parent' => "(int) The post ID of the new post's parent.",
38 'type' => "(string) The post type. Defaults to 'post'. Post types besides post and page need to be whitelisted using the <code>rest_api_allowed_post_types</code> filter.",
39 'categories' => "(array|string) Comma-separated list or array of categories (name or id)",
40 'tags' => "(array|string) Comma-separated list or array of tags (name or id)",
41 'format' => array_merge( array( 'default' => 'Use default post format' ), get_post_format_strings() ),
42 'featured_image' => "(string) The post ID of an existing attachment to set as the featured image. Pass an empty string to delete the existing image.",
43 'media' => "(media) An array of files to attach to the post. To upload media, the entire request should be multipart/form-data encoded. Multiple media items will be displayed in a gallery. Accepts jpg, jpeg, png, gif, pdf, doc, ppt, odt, pptx, docx, pps, ppsx, xls, xlsx, key. Audio and Video may also be available. See <code>allowed_file_types</code> in the options response of the site endpoint. <br /><br /><strong>Example</strong>:<br />" .
44 "<code>curl \<br />--form 'title=Image' \<br />--form 'media[]=@/path/to/file.jpg' \<br />-H 'Authorization: BEARER your-token' \<br />'https://public-api.wordpress.com/rest/v1/sites/123/posts/new'</code>",
45 'media_urls' => "(array) An array of URLs for images to attach to a post. Sideloads the media in for a post.",
46 'metadata' => "(array) Array of metadata objects containing the following properties: `key` (metadata key), `id` (meta ID), `previous_value` (if set, the action will only occur for the provided previous value), `value` (the new value to set the meta to), `operation` (the operation to perform: `update` or `add`; defaults to `update`). All unprotected meta keys are available by default for read requests. Both unprotected and protected meta keys are avaiable for authenticated requests with proper capabilities. Protected meta keys can be made available with the <code>rest_api_allowed_public_metadata</code> filter.",
47 'comments_open' => "(bool) Should the post be open to comments? Defaults to the blog's preference.",
48 'pings_open' => "(bool) Should the post be open to comments? Defaults to the blog's preference.",
49 'likes_enabled' => "(bool) Should the post be open to likes? Defaults to the blog's preference.",
50 'sharing_enabled' => "(bool) Should sharing buttons show on this post? Defaults to true.",
51 'menu_order' => "(int) (Pages Only) the order pages should appear in. Use 0 to maintain alphabetical order.",
52 ),
53
54 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/82974409/posts/new/',
55
56 'example_request_data' => array(
57 'headers' => array(
58 'authorization' => 'Bearer YOUR_API_TOKEN'
59 ),
60
61 'body' => array(
62 'title' => 'Hello World',
63 'content' => 'Hello. I am a test post. I was created by the API',
64 'tags' => 'tests',
65 'categories' => 'API'
66 )
67 )
68 ) );
69
70 new WPCOM_JSON_API_Update_Post_Endpoint( array(
71 'description' => 'Edit a post.',
72 'group' => 'posts',
73 'stat' => 'posts:1:POST',
74 'new_version' => '1.2',
75 'max_version' => '1',
76 'method' => 'POST',
77 'path' => '/sites/%s/posts/%d',
78 'path_labels' => array(
79 '$site' => '(int|string) Site ID or domain',
80 '$post_ID' => '(int) The post ID',
81 ),
82
83 'request_format' => array(
84 'date' => "(ISO 8601 datetime) The post's creation time.",
85 'title' => '(HTML) The post title.',
86 'content' => '(HTML) The post content.',
87 'excerpt' => '(HTML) An optional post excerpt.',
88 'slug' => '(string) The name (slug) for the post, used in URLs.',
89 'author' => '(string) The username or ID for the user to assign the post to.',
90 'publicize' => '(array|bool) True or false if the post be publicized to external services. An array of services if we only want to publicize to a select few. Defaults to true.',
91 'publicize_message' => '(string) Custom message to be publicized to external services.',
92 'status' => array(
93 'publish' => 'Publish the post.',
94 'private' => 'Privately publish the post.',
95 'draft' => 'Save the post as a draft.',
96 'pending' => 'Mark the post as pending editorial approval.',
97 'trash' => 'Set the post as trashed.',
98 ),
99 'sticky' => array(
100 'false' => 'Post is not marked as sticky.',
101 'true' => 'Stick the post to the front page.',
102 ),
103 'password' => '(string) The plaintext password protecting the post, or, more likely, the empty string if the post is not password protected.',
104 'parent' => "(int) The post ID of the new post's parent.",
105 'categories' => "(array|string) Comma-separated list or array of categories (name or id)",
106 'tags' => "(array|string) Comma-separated list or array of tags (name or id)",
107 'format' => array_merge( array( 'default' => 'Use default post format' ), get_post_format_strings() ),
108 'comments_open' => '(bool) Should the post be open to comments?',
109 'pings_open' => '(bool) Should the post be open to comments?',
110 'likes_enabled' => "(bool) Should the post be open to likes?",
111 'menu_order' => "(int) (Pages Only) the order pages should appear in. Use 0 to maintain alphabetical order.",
112 'sharing_enabled' => "(bool) Should sharing buttons show on this post?",
113 'featured_image' => "(string) The post ID of an existing attachment to set as the featured image. Pass an empty string to delete the existing image.",
114 'media' => "(media) An array of files to attach to the post. To upload media, the entire request should be multipart/form-data encoded. Multiple media items will be displayed in a gallery. Accepts jpg, jpeg, png, gif, pdf, doc, ppt, odt, pptx, docx, pps, ppsx, xls, xlsx, key. Audio and Video may also be available. See <code>allowed_file_types</code> in the options resposne of the site endpoint. <br /><br /><strong>Example</strong>:<br />" .
115 "<code>curl \<br />--form 'title=Image' \<br />--form 'media[]=@/path/to/file.jpg' \<br />-H 'Authorization: BEARER your-token' \<br />'https://public-api.wordpress.com/rest/v1/sites/123/posts/new'</code>",
116 'media_urls' => "(array) An array of URLs for images to attach to a post. Sideloads the media in for a post.",
117 'metadata' => "(array) Array of metadata objects containing the following properties: `key` (metadata key), `id` (meta ID), `previous_value` (if set, the action will only occur for the provided previous value), `value` (the new value to set the meta to), `operation` (the operation to perform: `update` or `add`; defaults to `update`). All unprotected meta keys are available by default for read requests. Both unprotected and protected meta keys are available for authenticated requests with proper capabilities. Protected meta keys can be made available with the <code>rest_api_allowed_public_metadata</code> filter.",
118 ),
119
120 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/82974409/posts/881',
121
122 'example_request_data' => array(
123 'headers' => array(
124 'authorization' => 'Bearer YOUR_API_TOKEN'
125 ),
126
127 'body' => array(
128 'title' => 'Hello World (Again)',
129 'content' => 'Hello. I am an edited post. I was edited by the API',
130 'tags' => 'tests',
131 'categories' => 'API'
132 )
133 )
134 ) );
135
136 new WPCOM_JSON_API_Update_Post_Endpoint( array(
137 'description' => 'Delete a post. Note: If the trash is enabled, this request will send the post to the trash. A second request will permanently delete the post.',
138 'group' => 'posts',
139 'stat' => 'posts:1:delete',
140 'new_version' => '1.1',
141 'max_version' => '1',
142 'method' => 'POST',
143 'path' => '/sites/%s/posts/%d/delete',
144 'path_labels' => array(
145 '$site' => '(int|string) Site ID or domain',
146 '$post_ID' => '(int) The post ID',
147 ),
148
149 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/82974409/posts/$post_ID/delete/',
150
151 'example_request_data' => array(
152 'headers' => array(
153 'authorization' => 'Bearer YOUR_API_TOKEN'
154 )
155 )
156 ) );
157
158 new WPCOM_JSON_API_Update_Post_Endpoint( array(
159 'description' => 'Restore a post or page from the trash to its previous status.',
160 'group' => 'posts',
161 'stat' => 'posts:1:restore',
162
163 'method' => 'POST',
164 'new_version' => '1.1',
165 'max_version' => '1',
166 'path' => '/sites/%s/posts/%d/restore',
167 'path_labels' => array(
168 '$site' => '(int|string) Site ID or domain',
169 '$post_ID' => '(int) The post ID',
170 ),
171
172 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/82974409/posts/$post_ID/restore/',
173
174 'example_request_data' => array(
175 'headers' => array(
176 'authorization' => 'Bearer YOUR_API_TOKEN'
177 )
178 )
179 ) );
180
181 class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
182 function __construct( $args ) {
183 parent::__construct( $args );
184 if ( $this->api->ends_with( $this->path, '/delete' ) ) {
185 $this->post_object_format['status']['deleted'] = 'The post has been deleted permanently.';
186 }
187 }
188
189 // /sites/%s/posts/new -> $blog_id
190 // /sites/%s/posts/%d -> $blog_id, $post_id
191 // /sites/%s/posts/%d/delete -> $blog_id, $post_id
192 // /sites/%s/posts/%d/restore -> $blog_id, $post_id
193 function callback( $path = '', $blog_id = 0, $post_id = 0 ) {
194 $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
195 if ( is_wp_error( $blog_id ) ) {
196 return $blog_id;
197 }
198
199 if ( $this->api->ends_with( $path, '/delete' ) ) {
200 return $this->delete_post( $path, $blog_id, $post_id );
201 } elseif ( $this->api->ends_with( $path, '/restore' ) ) {
202 return $this->restore_post( $path, $blog_id, $post_id );
203 } else {
204 return $this->write_post( $path, $blog_id, $post_id );
205 }
206 }
207
208 // /sites/%s/posts/new -> $blog_id
209 // /sites/%s/posts/%d -> $blog_id, $post_id
210 function write_post( $path, $blog_id, $post_id ) {
211 $new = $this->api->ends_with( $path, '/new' );
212 $args = $this->query_args();
213
214 // unhook publicize, it's hooked again later -- without this, skipping services is impossible
215 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
216 remove_action( 'save_post', array( $GLOBALS['publicize_ui']->publicize, 'async_publicize_post' ), 100, 2 );
217 add_action( 'rest_api_inserted_post', array( $GLOBALS['publicize_ui']->publicize, 'async_publicize_post' ) );
218 }
219
220 if ( $new ) {
221 $input = $this->input( true );
222
223 if ( 'revision' === $input['type'] ) {
224 if ( ! isset( $input['parent'] ) ) {
225 return new WP_Error( 'invalid_input', 'Invalid request input', 400 );
226 }
227 $input['status'] = 'inherit'; // force inherit for revision type
228 $input['slug'] = $input['parent'] . '-autosave-v1';
229 }
230 elseif ( !isset( $input['title'] ) && !isset( $input['content'] ) && !isset( $input['excerpt'] ) ) {
231 return new WP_Error( 'invalid_input', 'Invalid request input', 400 );
232 }
233
234 // default to post
235 if ( empty( $input['type'] ) )
236 $input['type'] = 'post';
237
238 $post_type = get_post_type_object( $input['type'] );
239
240 if ( ! $this->is_post_type_allowed( $input['type'] ) ) {
241 return new WP_Error( 'unknown_post_type', 'Unknown post type', 404 );
242 }
243
244 if ( ! empty( $input['author'] ) ) {
245 $author_id = $this->parse_and_set_author( $input['author'], $input['type'] );
246 unset( $input['author'] );
247 if ( is_wp_error( $author_id ) )
248 return $author_id;
249 }
250
251 if ( 'publish' === $input['status'] ) {
252 if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
253 if ( current_user_can( $post_type->cap->edit_posts ) ) {
254 $input['status'] = 'pending';
255 } else {
256 return new WP_Error( 'unauthorized', 'User cannot publish posts', 403 );
257 }
258 }
259 } else {
260 if ( !current_user_can( $post_type->cap->edit_posts ) ) {
261 return new WP_Error( 'unauthorized', 'User cannot edit posts', 403 );
262 }
263 }
264 } else {
265 $input = $this->input( false );
266
267 if ( !is_array( $input ) || !$input ) {
268 return new WP_Error( 'invalid_input', 'Invalid request input', 400 );
269 }
270
271 if ( isset( $input['status'] ) && 'trash' === $input['status'] && ! current_user_can( 'delete_post', $post_id ) ) {
272 return new WP_Error( 'unauthorized', 'User cannot delete post', 403 );
273 }
274
275 $post = get_post( $post_id );
276 $_post_type = ( ! empty( $input['type'] ) ) ? $input['type'] : $post->post_type;
277 $post_type = get_post_type_object( $_post_type );
278 if ( !$post || is_wp_error( $post ) ) {
279 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
280 }
281
282 if ( !current_user_can( 'edit_post', $post->ID ) ) {
283 return new WP_Error( 'unauthorized', 'User cannot edit post', 403 );
284 }
285
286 if ( ! empty( $input['author'] ) ) {
287 $author_id = $this->parse_and_set_author( $input['author'], $_post_type );
288 unset( $input['author'] );
289 if ( is_wp_error( $author_id ) )
290 return $author_id;
291 }
292
293 if ( ( isset( $input['status'] ) && 'publish' === $input['status'] ) && 'publish' !== $post->post_status && !current_user_can( 'publish_post', $post->ID ) ) {
294 $input['status'] = 'pending';
295 }
296 $last_status = $post->post_status;
297 $new_status = isset( $input['status'] ) ? $input['status'] : $last_status;
298
299 // Make sure that drafts get the current date when transitioning to publish if not supplied in the post.
300 $date_in_past = ( strtotime($post->post_date_gmt) < time() );
301 if ( 'publish' === $new_status && 'draft' === $last_status && ! isset( $input['date_gmt'] ) && $date_in_past ) {
302 $input['date_gmt'] = gmdate( 'Y-m-d H:i:s' );
303 }
304 }
305
306 if ( function_exists( 'wpcom_switch_to_locale' ) ) {
307 // fixes calypso-pre-oss #12476: respect blog locale when creating the post slug
308 wpcom_switch_to_locale( get_blog_lang_code( $blog_id ) );
309 }
310
311 // If date was set, $this->input will set date_gmt, date still needs to be adjusted for the blog's offset
312 if ( isset( $input['date_gmt'] ) ) {
313 $gmt_offset = get_option( 'gmt_offset' );
314 $time_with_offset = strtotime( $input['date_gmt'] ) + $gmt_offset * HOUR_IN_SECONDS;
315 $input['date'] = date( 'Y-m-d H:i:s', $time_with_offset );
316 }
317
318 if ( ! empty( $author_id ) && get_current_user_id() != $author_id ) {
319 if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) {
320 return new WP_Error( 'unauthorized', "User is not allowed to publish others' posts.", 403 );
321 } elseif ( ! user_can( $author_id, $post_type->cap->edit_posts ) ) {
322 return new WP_Error( 'unauthorized', 'Assigned author cannot publish post.', 403 );
323 }
324 }
325
326 if ( !is_post_type_hierarchical( $post_type->name ) && 'revision' !== $post_type->name ) {
327 unset( $input['parent'] );
328 }
329
330 $tax_input = array();
331
332 foreach ( array( 'categories' => 'category', 'tags' => 'post_tag' ) as $key => $taxonomy ) {
333 if ( ! isset( $input[ $key ] ) ) {
334 continue;
335 }
336
337 $tax_input[ $taxonomy ] = array();
338
339 $is_hierarchical = is_taxonomy_hierarchical( $taxonomy );
340
341 if ( is_array( $input[$key] ) ) {
342 $terms = $input[$key];
343 } else {
344 $terms = explode( ',', $input[$key] );
345 }
346
347 foreach ( $terms as $term ) {
348 /**
349 * `curl --data 'category[]=123'` should be interpreted as a category ID,
350 * not a category whose name is '123'.
351 *
352 * Consequence: To add a category/tag whose name is '123', the client must
353 * first look up its ID.
354 */
355 $term = (string) $term; // ctype_digit compat
356 if ( ctype_digit( $term ) ) {
357 $term = (int) $term;
358 }
359
360 $term_info = term_exists( $term, $taxonomy );
361
362 if ( ! $term_info ) {
363 // A term ID that doesn't already exist. Ignore it: we don't know what name to give it.
364 if ( is_int( $term ) ){
365 continue;
366 }
367 // only add a new tag/cat if the user has access to
368 $tax = get_taxonomy( $taxonomy );
369
370 // see https://core.trac.wordpress.org/ticket/26409
371 if ( 'category' === $taxonomy && ! current_user_can( $tax->cap->edit_terms ) ) {
372 continue;
373 } else if ( ! current_user_can( $tax->cap->assign_terms ) ) {
374 continue;
375 }
376
377 $term_info = wp_insert_term( $term, $taxonomy );
378 }
379
380 if ( ! is_wp_error( $term_info ) ) {
381 if ( $is_hierarchical ) {
382 // Categories must be added by ID
383 $tax_input[$taxonomy][] = (int) $term_info['term_id'];
384 } else {
385 // Tags must be added by name
386 if ( is_int( $term ) ) {
387 $term = get_term( $term, $taxonomy );
388 $tax_input[$taxonomy][] = $term->name;
389 } else {
390 $tax_input[$taxonomy][] = $term;
391 }
392 }
393 }
394 }
395 }
396
397 if ( isset( $input['categories'] ) && empty( $tax_input['category'] ) && 'revision' !== $post_type->name ) {
398 $tax_input['category'][] = get_option( 'default_category' );
399 }
400
401 unset( $input['tags'], $input['categories'] );
402
403 $insert = array();
404
405 if ( !empty( $input['slug'] ) ) {
406 $insert['post_name'] = $input['slug'];
407 unset( $input['slug'] );
408 }
409
410 if ( isset( $input['comments_open'] ) ) {
411 $insert['comment_status'] = ( true === $input['comments_open'] ) ? 'open' : 'closed';
412 }
413
414 if ( isset( $input['pings_open'] ) ) {
415 $insert['ping_status'] = ( true === $input['pings_open'] ) ? 'open' : 'closed';
416 }
417
418 unset( $input['comments_open'], $input['pings_open'] );
419
420 if ( isset( $input['menu_order'] ) ) {
421 $insert['menu_order'] = $input['menu_order'];
422 unset( $input['menu_order'] );
423 }
424
425 $publicize = isset( $input['publicize'] ) ? $input['publicize'] : null;
426 unset( $input['publicize'] );
427
428 $publicize_custom_message = isset( $input['publicize_message'] ) ? $input['publicize_message'] : null;
429 unset( $input['publicize_message'] );
430
431 if ( isset( $input['featured_image'] ) ) {
432 $featured_image = trim( $input['featured_image'] );
433 $delete_featured_image = empty( $featured_image );
434 unset( $input['featured_image'] );
435 }
436
437 $metadata = isset( $input['metadata'] ) ? $input['metadata'] : null;
438 unset( $input['metadata'] );
439
440 $likes = isset( $input['likes_enabled'] ) ? $input['likes_enabled'] : null;
441 unset( $input['likes_enabled'] );
442
443 $sharing = isset( $input['sharing_enabled'] ) ? $input['sharing_enabled'] : null;
444 unset( $input['sharing_enabled'] );
445
446 $sticky = isset( $input['sticky'] ) ? $input['sticky'] : null;
447 unset( $input['sticky'] );
448
449 foreach ( $input as $key => $value ) {
450 $insert["post_$key"] = $value;
451 }
452
453 if ( ! empty( $author_id ) ) {
454 $insert['post_author'] = absint( $author_id );
455 }
456
457 if ( ! empty( $tax_input ) ) {
458 $insert['tax_input'] = $tax_input;
459 }
460
461 $has_media = isset( $input['media'] ) && $input['media'] ? count( $input['media'] ) : false;
462 $has_media_by_url = isset( $input['media_urls'] ) && $input['media_urls'] ? count( $input['media_urls'] ) : false;
463
464 if ( $new ) {
465
466 if ( isset( $input['content'] ) && ! has_shortcode( $input['content'], 'gallery' ) && ( $has_media || $has_media_by_url ) ) {
467 switch ( ( $has_media + $has_media_by_url ) ) {
468 case 0 :
469 // No images - do nothing.
470 break;
471 case 1 :
472 // 1 image - make it big
473 $insert['post_content'] = $input['content'] = "[gallery size=full columns=1]\n\n" . $input['content'];
474 break;
475 default :
476 // Several images - 3 column gallery
477 $insert['post_content'] = $input['content'] = "[gallery]\n\n" . $input['content'];
478 break;
479 }
480 }
481
482 $post_id = wp_insert_post( add_magic_quotes( $insert ), true );
483 } else {
484 $insert['ID'] = $post->ID;
485
486 // wp_update_post ignores date unless edit_date is set
487 // See: https://codex.wordpress.org/Function_Reference/wp_update_post#Scheduling_posts
488 // See: https://core.trac.wordpress.org/browser/tags/3.9.2/src/wp-includes/post.php#L3302
489 if ( isset( $input['date_gmt'] ) || isset( $input['date'] ) ) {
490 $insert['edit_date'] = true;
491 }
492
493 // this two-step process ensures any changes submitted along with status=trash get saved before trashing
494 if ( isset( $input['status'] ) && 'trash' === $input['status'] ) {
495 // if we insert it with status='trash', it will get double-trashed, so insert it as a draft first
496 unset( $insert['status'] );
497 $post_id = wp_update_post( (object) $insert );
498 // now call wp_trash_post so post_meta gets set and any filters get called
499 wp_trash_post( $post_id );
500 } else {
501 $post_id = wp_update_post( (object) $insert );
502 }
503
504 }
505
506 if ( !$post_id || is_wp_error( $post_id ) ) {
507 return $post_id;
508 }
509
510 // make sure this post actually exists and is not an error of some kind (ie, trying to load media in the posts endpoint)
511 $post_check = $this->get_post_by( 'ID', $post_id, $args['context'] );
512 if ( is_wp_error( $post_check ) ) {
513 return $post_check;
514 }
515
516 if ( $has_media ) {
517 $this->api->trap_wp_die( 'upload_error' );
518 foreach ( $input['media'] as $media_item ) {
519 $_FILES['.api.media.item.'] = $media_item;
520 // check for WP_Error if we ever actually need $media_id
521 $media_id = media_handle_upload( '.api.media.item.', $post_id );
522 }
523 $this->api->trap_wp_die( null );
524
525 unset( $_FILES['.api.media.item.'] );
526 }
527
528 if ( $has_media_by_url ) {
529 foreach ( $input['media_urls'] as $url ) {
530 $this->handle_media_sideload( $url, $post_id );
531 }
532 }
533
534 // Set like status for the post
535 /** This filter is documented in modules/likes.php */
536 $sitewide_likes_enabled = (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) );
537 if ( $new ) {
538 if ( $sitewide_likes_enabled ) {
539 if ( false === $likes ) {
540 update_post_meta( $post_id, 'switch_like_status', 0 );
541 } else {
542 delete_post_meta( $post_id, 'switch_like_status' );
543 }
544 } else {
545 if ( $likes ) {
546 update_post_meta( $post_id, 'switch_like_status', 1 );
547 } else {
548 delete_post_meta( $post_id, 'switch_like_status' );
549 }
550 }
551 } else {
552 if ( isset( $likes ) ) {
553 if ( $sitewide_likes_enabled ) {
554 if ( false === $likes ) {
555 update_post_meta( $post_id, 'switch_like_status', 0 );
556 } else {
557 delete_post_meta( $post_id, 'switch_like_status' );
558 }
559 } else {
560 if ( true === $likes ) {
561 update_post_meta( $post_id, 'switch_like_status', 1 );
562 } else {
563 delete_post_meta( $post_id, 'switch_like_status' );
564 }
565 }
566 }
567 }
568
569 // Set sharing status of the post
570 if ( $new ) {
571 $sharing_enabled = isset( $sharing ) ? (bool) $sharing : true;
572 if ( false === $sharing_enabled ) {
573 update_post_meta( $post_id, 'sharing_disabled', 1 );
574 }
575 }
576 else {
577 if ( isset( $sharing ) && true === $sharing ) {
578 delete_post_meta( $post_id, 'sharing_disabled' );
579 } else if ( isset( $sharing ) && false == $sharing ) {
580 update_post_meta( $post_id, 'sharing_disabled', 1 );
581 }
582 }
583
584 if ( isset( $sticky ) ) {
585 if ( true === $sticky ) {
586 stick_post( $post_id );
587 } else {
588 unstick_post( $post_id );
589 }
590 }
591
592 // WPCOM Specific (Jetpack's will get bumped elsewhere
593 // Tracks how many posts are published and sets meta
594 // so we can track some other cool stats (like likes & comments on posts published)
595 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
596 if (
597 ( $new && 'publish' == $input['status'] )
598 || (
599 ! $new && isset( $last_status )
600 && 'publish' != $last_status
601 && isset( $new_status )
602 && 'publish' == $new_status
603 )
604 ) {
605 /** This action is documented in modules/widgets/social-media-icons.php */
606 do_action( 'jetpack_bump_stats_extras', 'api-insights-posts', $this->api->token_details['client_id'] );
607 update_post_meta( $post_id, '_rest_api_published', 1 );
608 update_post_meta( $post_id, '_rest_api_client_id', $this->api->token_details['client_id'] );
609 }
610 }
611
612
613 // We ask the user/dev to pass Publicize services he/she wants activated for the post, but Publicize expects us
614 // to instead flag the ones we don't want to be skipped. proceed with said logic.
615 // any posts coming from Path (client ID 25952) should also not publicize
616 if ( $publicize === false || ( isset( $this->api->token_details['client_id'] ) && 25952 == $this->api->token_details['client_id'] ) ) {
617 // No publicize at all, skip all by ID
618 foreach ( $GLOBALS['publicize_ui']->publicize->get_services( 'all' ) as $name => $service ) {
619 delete_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $name );
620 $service_connections = $GLOBALS['publicize_ui']->publicize->get_connections( $name );
621 if ( ! $service_connections ) {
622 continue;
623 }
624 foreach ( $service_connections as $service_connection ) {
625 update_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $service_connection->unique_id, 1 );
626 }
627 }
628 } else if ( is_array( $publicize ) && ( count ( $publicize ) > 0 ) ) {
629 foreach ( $GLOBALS['publicize_ui']->publicize->get_services( 'all' ) as $name => $service ) {
630 /*
631 * We support both indexed and associative arrays:
632 * * indexed are to pass entire services
633 * * associative are to pass specific connections per service
634 *
635 * We do support mixed arrays: mixed integer and string keys (see 3rd example below).
636 *
637 * EG: array( 'twitter', 'facebook') will only publicize to those, ignoring the other available services
638 * Form data: publicize[]=twitter&publicize[]=facebook
639 * EG: array( 'twitter' => '(int) $pub_conn_id_0, (int) $pub_conn_id_3', 'facebook' => (int) $pub_conn_id_7 ) will publicize to two Twitter accounts, and one Facebook connection, of potentially many.
640 * Form data: publicize[twitter]=$pub_conn_id_0,$pub_conn_id_3&publicize[facebook]=$pub_conn_id_7
641 * EG: array( 'twitter', 'facebook' => '(int) $pub_conn_id_0, (int) $pub_conn_id_3' ) will publicize to all available Twitter accounts, but only 2 of potentially many Facebook connections
642 * Form data: publicize[]=twitter&publicize[facebook]=$pub_conn_id_0,$pub_conn_id_3
643 */
644
645 // Delete any stale SKIP value for the service by name. We'll add it back by ID.
646 delete_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $name );
647
648 // Get the user's connections
649 $service_connections = $GLOBALS['publicize_ui']->publicize->get_connections( $name );
650
651 // if the user doesn't have any connections for this service, move on
652 if ( ! $service_connections ) {
653 continue;
654 }
655
656 if ( !in_array( $name, $publicize ) && !array_key_exists( $name, $publicize ) ) {
657 // Skip the whole service by adding each connection ID
658 foreach ( $service_connections as $service_connection ) {
659 update_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $service_connection->unique_id, 1 );
660 }
661 } else if ( !empty( $publicize[ $name ] ) ) {
662 // Seems we're being asked to only push to [a] specific connection[s].
663 // Explode the list on commas, which will also support a single passed ID
664 $requested_connections = explode( ',', ( preg_replace( '/[\s]*/', '', $publicize[ $name ] ) ) );
665 // Flag the connections we can't match with the requested list to be skipped.
666 foreach ( $service_connections as $service_connection ) {
667 if ( !in_array( $service_connection->meta['connection_data']->id, $requested_connections ) ) {
668 update_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $service_connection->unique_id, 1 );
669 } else {
670 delete_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $service_connection->unique_id );
671 }
672 }
673 } else {
674 // delete all SKIP values; it's okay to publish to all connected IDs for this service
675 foreach ( $service_connections as $service_connection ) {
676 delete_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $service_connection->unique_id );
677 }
678 }
679 }
680 }
681
682 if ( ! is_null( $publicize_custom_message ) ) {
683 if ( empty( $publicize_custom_message ) ) {
684 delete_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_MESS );
685 } else {
686 update_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_MESS, trim( $publicize_custom_message ) );
687 }
688 }
689
690 if ( ! empty( $insert['post_format'] ) ) {
691 if ( 'default' !== strtolower( $insert['post_format'] ) ) {
692 set_post_format( $post_id, $insert['post_format'] );
693 }
694 else {
695 set_post_format( $post_id, get_option( 'default_post_format' ) );
696 }
697 }
698
699 if ( isset( $featured_image ) ) {
700 $this->parse_and_set_featured_image( $post_id, $delete_featured_image, $featured_image );
701 }
702
703 if ( ! empty( $metadata ) ) {
704 foreach ( (array) $metadata as $meta ) {
705
706 $meta = (object) $meta;
707
708 // Custom meta description can only be set on sites that have a business subscription.
709 if ( Jetpack_SEO_Posts::DESCRIPTION_META_KEY == $meta->key && ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() ) {
710 return new WP_Error( 'unauthorized', __( 'SEO tools are not enabled for this site.', 'jetpack' ), 403 );
711 }
712
713 $existing_meta_item = new stdClass;
714
715 if ( empty( $meta->operation ) )
716 $meta->operation = 'update';
717
718 if ( ! empty( $meta->value ) ) {
719 if ( 'true' == $meta->value )
720 $meta->value = true;
721 if ( 'false' == $meta->value )
722 $meta->value = false;
723 }
724
725 if ( ! empty( $meta->id ) ) {
726 $meta->id = absint( $meta->id );
727 $existing_meta_item = get_metadata_by_mid( 'post', $meta->id );
728 if ( $post_id !== (int) $existing_meta_item->post_id ) {
729 // Only allow updates for metadata on this post
730 continue;
731 }
732 }
733
734 $unslashed_meta_key = wp_unslash( $meta->key ); // should match what the final key will be
735 $meta->key = wp_slash( $meta->key );
736 $unslashed_existing_meta_key = wp_unslash( $existing_meta_item->meta_key );
737 $existing_meta_item->meta_key = wp_slash( $existing_meta_item->meta_key );
738
739 // make sure that the meta id passed matches the existing meta key
740 if ( ! empty( $meta->id ) && ! empty( $meta->key ) ) {
741 $meta_by_id = get_metadata_by_mid( 'post', $meta->id );
742 if ( $meta_by_id->meta_key !== $meta->key ) {
743 continue; // skip this meta
744 }
745 }
746
747 switch ( $meta->operation ) {
748 case 'delete':
749
750 if ( ! empty( $meta->id ) && ! empty( $existing_meta_item->meta_key ) && current_user_can( 'delete_post_meta', $post_id, $unslashed_existing_meta_key ) ) {
751 delete_metadata_by_mid( 'post', $meta->id );
752 } elseif ( ! empty( $meta->key ) && ! empty( $meta->previous_value ) && current_user_can( 'delete_post_meta', $post_id, $unslashed_meta_key ) ) {
753 delete_post_meta( $post_id, $meta->key, $meta->previous_value );
754 } elseif ( ! empty( $meta->key ) && current_user_can( 'delete_post_meta', $post_id, $unslashed_meta_key ) ) {
755 delete_post_meta( $post_id, $meta->key );
756 }
757
758 break;
759 case 'add':
760
761 if ( ! empty( $meta->id ) || ! empty( $meta->previous_value ) ) {
762 break;
763 } elseif ( ! empty( $meta->key ) && ! empty( $meta->value ) && ( current_user_can( 'add_post_meta', $post_id, $unslashed_meta_key ) ) || WPCOM_JSON_API_Metadata::is_public( $meta->key ) ) {
764 add_post_meta( $post_id, $meta->key, $meta->value );
765 }
766
767 break;
768 case 'update':
769
770 if ( ! isset( $meta->value ) ) {
771 break;
772 } elseif ( ! empty( $meta->id ) && ! empty( $existing_meta_item->meta_key ) && ( current_user_can( 'edit_post_meta', $post_id, $unslashed_existing_meta_key ) || WPCOM_JSON_API_Metadata::is_public( $meta->key ) ) ) {
773 update_metadata_by_mid( 'post', $meta->id, $meta->value );
774 } elseif ( ! empty( $meta->key ) && ! empty( $meta->previous_value ) && ( current_user_can( 'edit_post_meta', $post_id, $unslashed_meta_key ) || WPCOM_JSON_API_Metadata::is_public( $meta->key ) ) ) {
775 update_post_meta( $post_id, $meta->key,$meta->value, $meta->previous_value );
776 } elseif ( ! empty( $meta->key ) && ( current_user_can( 'edit_post_meta', $post_id, $unslashed_meta_key ) || WPCOM_JSON_API_Metadata::is_public( $meta->key ) ) ) {
777 update_post_meta( $post_id, $meta->key, $meta->value );
778 }
779
780 break;
781 }
782
783 }
784 }
785
786 /**
787 * Fires when a post is created via the REST API.
788 *
789 * @module json-api
790 *
791 * @since 2.3.0
792 *
793 * @param int $post_id Post ID.
794 * @param array $insert Data used to build the post.
795 * @param string $new New post URL suffix.
796 */
797 do_action( 'rest_api_inserted_post', $post_id, $insert, $new );
798
799 $return = $this->get_post_by( 'ID', $post_id, $args['context'] );
800 if ( !$return || is_wp_error( $return ) ) {
801 return $return;
802 }
803
804 if ( isset( $input['type'] ) && 'revision' === $input['type'] ) {
805 $return['preview_nonce'] = wp_create_nonce( 'post_preview_' . $input['parent'] );
806 }
807
808 if ( isset( $sticky ) ) {
809 // workaround for sticky test occasionally failing, maybe a race condition with stick_post() above
810 $return['sticky'] = ( true === $sticky );
811 }
812
813 /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
814 do_action( 'wpcom_json_api_objects', 'posts' );
815
816 return $return;
817 }
818
819 // /sites/%s/posts/%d/delete -> $blog_id, $post_id
820 function delete_post( $path, $blog_id, $post_id ) {
821 $post = get_post( $post_id );
822 if ( !$post || is_wp_error( $post ) ) {
823 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
824 }
825
826 if ( ! $this->is_post_type_allowed( $post->post_type ) ) {
827 return new WP_Error( 'unknown_post_type', 'Unknown post type', 404 );
828 }
829
830 if ( !current_user_can( 'delete_post', $post->ID ) ) {
831 return new WP_Error( 'unauthorized', 'User cannot delete posts', 403 );
832 }
833
834 $args = $this->query_args();
835 $return = $this->get_post_by( 'ID', $post->ID, $args['context'] );
836 if ( !$return || is_wp_error( $return ) ) {
837 return $return;
838 }
839
840 /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
841 do_action( 'wpcom_json_api_objects', 'posts' );
842
843 // we need to call wp_trash_post so that untrash will work correctly for all post types
844 if ( 'trash' === $post->post_status )
845 wp_delete_post( $post->ID );
846 else
847 wp_trash_post( $post->ID );
848
849 $status = get_post_status( $post->ID );
850 if ( false === $status ) {
851 $return['status'] = 'deleted';
852 return $return;
853 }
854
855 return $this->get_post_by( 'ID', $post->ID, $args['context'] );
856 }
857
858 // /sites/%s/posts/%d/restore -> $blog_id, $post_id
859 function restore_post( $path, $blog_id, $post_id ) {
860 $args = $this->query_args();
861 $post = get_post( $post_id );
862
863 if ( !$post || is_wp_error( $post ) ) {
864 return new WP_Error( 'unknown_post', 'Unknown post', 404 );
865 }
866
867 if ( !current_user_can( 'delete_post', $post->ID ) ) {
868 return new WP_Error( 'unauthorized', 'User cannot restore trashed posts', 403 );
869 }
870
871 /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
872 do_action( 'wpcom_json_api_objects', 'posts' );
873
874 wp_untrash_post( $post->ID );
875
876 return $this->get_post_by( 'ID', $post->ID, $args['context'] );
877 }
878
879 private function parse_and_set_featured_image( $post_id, $delete_featured_image, $featured_image ) {
880 if ( $delete_featured_image ) {
881 delete_post_thumbnail( $post_id );
882 return;
883 }
884
885 $featured_image = (string) $featured_image;
886
887 // if we got a post ID, we can just set it as the thumbnail
888 if ( ctype_digit( $featured_image ) && 'attachment' == get_post_type( $featured_image ) ) {
889 set_post_thumbnail( $post_id, $featured_image );
890 return $featured_image;
891 }
892
893 $featured_image_id = $this->handle_media_sideload( $featured_image, $post_id, 'image' );
894
895 if ( empty( $featured_image_id ) || ! is_int( $featured_image_id ) )
896 return false;
897
898 set_post_thumbnail( $post_id, $featured_image_id );
899 return $featured_image_id;
900 }
901
902 private function parse_and_set_author( $author = null, $post_type = 'post' ) {
903 if ( empty( $author ) || ! post_type_supports( $post_type, 'author' ) )
904 return get_current_user_id();
905
906 $author = (string) $author;
907 if ( ctype_digit( $author ) ) {
908 $_user = get_user_by( 'id', $author );
909 if ( ! $_user || is_wp_error( $_user ) )
910 return new WP_Error( 'invalid_author', 'Invalid author provided' );
911
912 return $_user->ID;
913 }
914
915 $_user = get_user_by( 'login', $author );
916 if ( ! $_user || is_wp_error( $_user ) )
917 return new WP_Error( 'invalid_author', 'Invalid author provided' );
918
919 return $_user->ID;
920 }
921 }
922