WP_REST_Revisions_Controller

Core class used to access revisions via the REST API.

Defined (1)

The class is defined in the following location(s).

/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php  
  1. class WP_REST_Revisions_Controller extends WP_REST_Controller { 
  2.  
  3. /** 
  4. * Parent post type. 
  5. * @since 4.7.0 
  6. * @access private 
  7. * @var string 
  8. */ 
  9. private $parent_post_type; 
  10.  
  11. /** 
  12. * Parent controller. 
  13. * @since 4.7.0 
  14. * @access private 
  15. * @var WP_REST_Controller 
  16. */ 
  17. private $parent_controller; 
  18.  
  19. /** 
  20. * The base of the parent controller's route. 
  21. * @since 4.7.0 
  22. * @access private 
  23. * @var string 
  24. */ 
  25. private $parent_base; 
  26.  
  27. /** 
  28. * Constructor. 
  29. * @since 4.7.0 
  30. * @access public 
  31. * @param string $parent_post_type Post type of the parent. 
  32. */ 
  33. public function __construct( $parent_post_type ) { 
  34. $this->parent_post_type = $parent_post_type; 
  35. $this->parent_controller = new WP_REST_Posts_Controller( $parent_post_type ); 
  36. $this->namespace = 'wp/v2'; 
  37. $this->rest_base = 'revisions'; 
  38. $post_type_object = get_post_type_object( $parent_post_type ); 
  39. $this->parent_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name; 
  40.  
  41. /** 
  42. * Registers routes for revisions based on post types supporting revisions. 
  43. * @since 4.7.0 
  44. * @access public 
  45. * @see register_rest_route() 
  46. */ 
  47. public function register_routes() { 
  48.  
  49. register_rest_route( $this->namespace, '/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base, array( 
  50. array( 
  51. 'methods' => WP_REST_Server::READABLE,  
  52. 'callback' => array( $this, 'get_items' ),  
  53. 'permission_callback' => array( $this, 'get_items_permissions_check' ),  
  54. 'args' => $this->get_collection_params(),  
  55. ),  
  56. 'schema' => array( $this, 'get_public_item_schema' ),  
  57. ) ); 
  58.  
  59. register_rest_route( $this->namespace, '/' . $this->parent_base . '/(?P<parent>[\d]+)/' . $this->rest_base . '/(?P<id>[\d]+)', array( 
  60. array( 
  61. 'methods' => WP_REST_Server::READABLE,  
  62. 'callback' => array( $this, 'get_item' ),  
  63. 'permission_callback' => array( $this, 'get_item_permissions_check' ),  
  64. 'args' => array( 
  65. 'context' => $this->get_context_param( array( 'default' => 'view' ) ),  
  66. ),  
  67. ),  
  68. array( 
  69. 'methods' => WP_REST_Server::DELETABLE,  
  70. 'callback' => array( $this, 'delete_item' ),  
  71. 'permission_callback' => array( $this, 'delete_item_permissions_check' ),  
  72. 'args' => array( 
  73. 'force' => array( 
  74. 'type' => 'boolean',  
  75. 'default' => false,  
  76. 'description' => __( 'Required to be true, as revisions do not support trashing.' ),  
  77. ),  
  78. ),  
  79. ),  
  80. 'schema' => array( $this, 'get_public_item_schema' ),  
  81. )); 
  82.  
  83.  
  84. /** 
  85. * Checks if a given request has access to get revisions. 
  86. * @since 4.7.0 
  87. * @access public 
  88. * @param WP_REST_Request $request Full data about the request. 
  89. * @return true|WP_Error True if the request has read access, WP_Error object otherwise. 
  90. */ 
  91. public function get_items_permissions_check( $request ) { 
  92.  
  93. $parent = get_post( $request['parent'] ); 
  94. if ( ! $parent ) { 
  95. return true; 
  96. $parent_post_type_obj = get_post_type_object( $parent->post_type ); 
  97. if ( ! current_user_can( $parent_post_type_obj->cap->edit_post, $parent->ID ) ) { 
  98. return new WP_Error( 'rest_cannot_read', __( 'Sorry, you are not allowed to view revisions of this post.' ), array( 'status' => rest_authorization_required_code() ) ); 
  99.  
  100. return true; 
  101.  
  102. /** 
  103. * Gets a collection of revisions. 
  104. * @since 4.7.0 
  105. * @access public 
  106. * @param WP_REST_Request $request Full data about the request. 
  107. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 
  108. */ 
  109. public function get_items( $request ) { 
  110. $parent = get_post( $request['parent'] ); 
  111. if ( ! $request['parent'] || ! $parent || $this->parent_post_type !== $parent->post_type ) { 
  112. return new WP_Error( 'rest_post_invalid_parent', __( 'Invalid post parent ID.' ), array( 'status' => 404 ) ); 
  113.  
  114. $revisions = wp_get_post_revisions( $request['parent'] ); 
  115.  
  116. $response = array(); 
  117. foreach ( $revisions as $revision ) { 
  118. $data = $this->prepare_item_for_response( $revision, $request ); 
  119. $response[] = $this->prepare_response_for_collection( $data ); 
  120. return rest_ensure_response( $response ); 
  121.  
  122. /** 
  123. * Checks if a given request has access to get a specific revision. 
  124. * @since 4.7.0 
  125. * @access public 
  126. * @param WP_REST_Request $request Full data about the request. 
  127. * @return bool|WP_Error True if the request has read access for the item, WP_Error object otherwise. 
  128. */ 
  129. public function get_item_permissions_check( $request ) { 
  130. return $this->get_items_permissions_check( $request ); 
  131.  
  132. /** 
  133. * Retrieves one revision from the collection. 
  134. * @since 4.7.0 
  135. * @access public 
  136. * @param WP_REST_Request $request Full data about the request. 
  137. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 
  138. */ 
  139. public function get_item( $request ) { 
  140. $parent = get_post( $request['parent'] ); 
  141. if ( ! $request['parent'] || ! $parent || $this->parent_post_type !== $parent->post_type ) { 
  142. return new WP_Error( 'rest_post_invalid_parent', __( 'Invalid post parent ID.' ), array( 'status' => 404 ) ); 
  143.  
  144. $revision = get_post( $request['id'] ); 
  145. if ( ! $revision || 'revision' !== $revision->post_type ) { 
  146. return new WP_Error( 'rest_post_invalid_id', __( 'Invalid revision ID.' ), array( 'status' => 404 ) ); 
  147.  
  148. $response = $this->prepare_item_for_response( $revision, $request ); 
  149. return rest_ensure_response( $response ); 
  150.  
  151. /** 
  152. * Checks if a given request has access to delete a revision. 
  153. * @since 4.7.0 
  154. * @access public 
  155. * @param WP_REST_Request $request Full details about the request. 
  156. * @return bool|WP_Error True if the request has access to delete the item, WP_Error object otherwise. 
  157. */ 
  158. public function delete_item_permissions_check( $request ) { 
  159.  
  160. $response = $this->get_items_permissions_check( $request ); 
  161. if ( ! $response || is_wp_error( $response ) ) { 
  162. return $response; 
  163.  
  164. $post = get_post( $request['id'] ); 
  165. if ( ! $post ) { 
  166. return new WP_Error( 'rest_post_invalid_id', __( 'Invalid revision ID.' ), array( 'status' => 404 ) ); 
  167. $post_type = get_post_type_object( 'revision' ); 
  168. return current_user_can( $post_type->cap->delete_post, $post->ID ); 
  169.  
  170. /** 
  171. * Deletes a single revision. 
  172. * @since 4.7.0 
  173. * @access public 
  174. * @param WP_REST_Request $request Full details about the request. 
  175. * @return true|WP_Error True on success, or WP_Error object on failure. 
  176. */ 
  177. public function delete_item( $request ) { 
  178. $force = isset( $request['force'] ) ? (bool) $request['force'] : false; 
  179.  
  180. // We don't support trashing for revisions. 
  181. if ( ! $force ) { 
  182. return new WP_Error( 'rest_trash_not_supported', __( 'Revisions do not support trashing. Set force=true to delete.' ), array( 'status' => 501 ) ); 
  183.  
  184. $revision = get_post( $request['id'] ); 
  185. $previous = $this->prepare_item_for_response( $revision, $request ); 
  186.  
  187. $result = wp_delete_post( $request['id'], true ); 
  188.  
  189. /** 
  190. * Fires after a revision is deleted via the REST API. 
  191. * @since 4.7.0 
  192. * @param (mixed) $result The revision object (if it was deleted or moved to the trash successfully) 
  193. * or false (failure). If the revision was moved to to the trash, $result represents 
  194. * its new state; if it was deleted, $result represents its state before deletion. 
  195. * @param WP_REST_Request $request The request sent to the API. 
  196. */ 
  197. do_action( 'rest_delete_revision', $result, $request ); 
  198.  
  199. if ( ! $result ) { 
  200. return new WP_Error( 'rest_cannot_delete', __( 'The post cannot be deleted.' ), array( 'status' => 500 ) ); 
  201.  
  202. $response = new WP_REST_Response(); 
  203. $response->set_data( array( 'deleted' => true, 'previous' => $previous->get_data() ) ); 
  204. return $response; 
  205.  
  206. /** 
  207. * Prepares the revision for the REST response. 
  208. * @since 4.7.0 
  209. * @access public 
  210. * @param WP_Post $post Post revision object. 
  211. * @param WP_REST_Request $request Request object. 
  212. * @return WP_REST_Response Response object. 
  213. */ 
  214. public function prepare_item_for_response( $post, $request ) { 
  215.  
  216. $schema = $this->get_item_schema(); 
  217.  
  218. $data = array(); 
  219.  
  220. if ( ! empty( $schema['properties']['author'] ) ) { 
  221. $data['author'] = $post->post_author; 
  222.  
  223. if ( ! empty( $schema['properties']['date'] ) ) { 
  224. $data['date'] = $this->prepare_date_response( $post->post_date_gmt, $post->post_date ); 
  225.  
  226. if ( ! empty( $schema['properties']['date_gmt'] ) ) { 
  227. $data['date_gmt'] = $this->prepare_date_response( $post->post_date_gmt ); 
  228.  
  229. if ( ! empty( $schema['properties']['id'] ) ) { 
  230. $data['id'] = $post->ID; 
  231.  
  232. if ( ! empty( $schema['properties']['modified'] ) ) { 
  233. $data['modified'] = $this->prepare_date_response( $post->post_modified_gmt, $post->post_modified ); 
  234.  
  235. if ( ! empty( $schema['properties']['modified_gmt'] ) ) { 
  236. $data['modified_gmt'] = $this->prepare_date_response( $post->post_modified_gmt ); 
  237.  
  238. if ( ! empty( $schema['properties']['parent'] ) ) { 
  239. $data['parent'] = (int) $post->post_parent; 
  240.  
  241. if ( ! empty( $schema['properties']['slug'] ) ) { 
  242. $data['slug'] = $post->post_name; 
  243.  
  244. if ( ! empty( $schema['properties']['guid'] ) ) { 
  245. $data['guid'] = array( 
  246. /** This filter is documented in wp-includes/post-template.php */ 
  247. 'rendered' => apply_filters( 'get_the_guid', $post->guid ),  
  248. 'raw' => $post->guid,  
  249. ); 
  250.  
  251. if ( ! empty( $schema['properties']['title'] ) ) { 
  252. $data['title'] = array( 
  253. 'raw' => $post->post_title,  
  254. 'rendered' => get_the_title( $post->ID ),  
  255. ); 
  256.  
  257. if ( ! empty( $schema['properties']['content'] ) ) { 
  258.  
  259. $data['content'] = array( 
  260. 'raw' => $post->post_content,  
  261. /** This filter is documented in wp-includes/post-template.php */ 
  262. 'rendered' => apply_filters( 'the_content', $post->post_content ),  
  263. ); 
  264.  
  265. if ( ! empty( $schema['properties']['excerpt'] ) ) { 
  266. $data['excerpt'] = array( 
  267. 'raw' => $post->post_excerpt,  
  268. 'rendered' => $this->prepare_excerpt_response( $post->post_excerpt, $post ),  
  269. ); 
  270.  
  271. $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; 
  272. $data = $this->add_additional_fields_to_object( $data, $request ); 
  273. $data = $this->filter_response_by_context( $data, $context ); 
  274. $response = rest_ensure_response( $data ); 
  275.  
  276. if ( ! empty( $data['parent'] ) ) { 
  277. $response->add_link( 'parent', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->parent_base, $data['parent'] ) ) ); 
  278.  
  279. /** 
  280. * Filters a revision returned from the API. 
  281. * Allows modification of the revision right before it is returned. 
  282. * @since 4.7.0 
  283. * @param WP_REST_Response $response The response object. 
  284. * @param WP_Post $post The original revision object. 
  285. * @param WP_REST_Request $request Request used to generate the response. 
  286. */ 
  287. return apply_filters( 'rest_prepare_revision', $response, $post, $request ); 
  288.  
  289. /** 
  290. * Checks the post_date_gmt or modified_gmt and prepare any post or 
  291. * modified date for single post output. 
  292. * @since 4.7.0 
  293. * @access protected 
  294. * @param string $date_gmt GMT publication time. 
  295. * @param string|null $date Optional. Local publication time. Default null. 
  296. * @return string|null ISO8601/RFC3339 formatted datetime, otherwise null. 
  297. */ 
  298. protected function prepare_date_response( $date_gmt, $date = null ) { 
  299. if ( '0000-00-00 00:00:00' === $date_gmt ) { 
  300. return null; 
  301.  
  302. if ( isset( $date ) ) { 
  303. return mysql_to_rfc3339( $date ); 
  304.  
  305. return mysql_to_rfc3339( $date_gmt ); 
  306.  
  307. /** 
  308. * Retrieves the revision's schema, conforming to JSON Schema. 
  309. * @since 4.7.0 
  310. * @access public 
  311. * @return array Item schema data. 
  312. */ 
  313. public function get_item_schema() { 
  314. $schema = array( 
  315. '$schema' => 'http://json-schema.org/schema#',  
  316. 'title' => "{$this->parent_post_type}-revision",  
  317. 'type' => 'object',  
  318. // Base properties for every Revision. 
  319. 'properties' => array( 
  320. 'author' => array( 
  321. 'description' => __( 'The ID for the author of the object.' ),  
  322. 'type' => 'integer',  
  323. 'context' => array( 'view', 'edit', 'embed' ),  
  324. ),  
  325. 'date' => array( 
  326. 'description' => __( "The date the object was published, in the site's timezone." ),  
  327. 'type' => 'string',  
  328. 'format' => 'date-time',  
  329. 'context' => array( 'view', 'edit', 'embed' ),  
  330. ),  
  331. 'date_gmt' => array( 
  332. 'description' => __( 'The date the object was published, as GMT.' ),  
  333. 'type' => 'string',  
  334. 'format' => 'date-time',  
  335. 'context' => array( 'view', 'edit' ),  
  336. ),  
  337. 'guid' => array( 
  338. 'description' => __( 'GUID for the object, as it exists in the database.' ),  
  339. 'type' => 'string',  
  340. 'context' => array( 'view', 'edit' ),  
  341. ),  
  342. 'id' => array( 
  343. 'description' => __( 'Unique identifier for the object.' ),  
  344. 'type' => 'integer',  
  345. 'context' => array( 'view', 'edit', 'embed' ),  
  346. ),  
  347. 'modified' => array( 
  348. 'description' => __( "The date the object was last modified, in the site's timezone." ),  
  349. 'type' => 'string',  
  350. 'format' => 'date-time',  
  351. 'context' => array( 'view', 'edit' ),  
  352. ),  
  353. 'modified_gmt' => array( 
  354. 'description' => __( 'The date the object was last modified, as GMT.' ),  
  355. 'type' => 'string',  
  356. 'format' => 'date-time',  
  357. 'context' => array( 'view', 'edit' ),  
  358. ),  
  359. 'parent' => array( 
  360. 'description' => __( 'The ID for the parent of the object.' ),  
  361. 'type' => 'integer',  
  362. 'context' => array( 'view', 'edit', 'embed' ),  
  363. ),  
  364. 'slug' => array( 
  365. 'description' => __( 'An alphanumeric identifier for the object unique to its type.' ),  
  366. 'type' => 'string',  
  367. 'context' => array( 'view', 'edit', 'embed' ),  
  368. ),  
  369. ),  
  370. ); 
  371.  
  372. $parent_schema = $this->parent_controller->get_item_schema(); 
  373.  
  374. if ( ! empty( $parent_schema['properties']['title'] ) ) { 
  375. $schema['properties']['title'] = $parent_schema['properties']['title']; 
  376.  
  377. if ( ! empty( $parent_schema['properties']['content'] ) ) { 
  378. $schema['properties']['content'] = $parent_schema['properties']['content']; 
  379.  
  380. if ( ! empty( $parent_schema['properties']['excerpt'] ) ) { 
  381. $schema['properties']['excerpt'] = $parent_schema['properties']['excerpt']; 
  382.  
  383. if ( ! empty( $parent_schema['properties']['guid'] ) ) { 
  384. $schema['properties']['guid'] = $parent_schema['properties']['guid']; 
  385.  
  386. return $this->add_additional_fields_schema( $schema ); 
  387.  
  388. /** 
  389. * Retrieves the query params for collections. 
  390. * @since 4.7.0 
  391. * @access public 
  392. * @return array Collection parameters. 
  393. */ 
  394. public function get_collection_params() { 
  395. return array( 
  396. 'context' => $this->get_context_param( array( 'default' => 'view' ) ),  
  397. ); 
  398.  
  399. /** 
  400. * Checks the post excerpt and prepare it for single post output. 
  401. * @since 4.7.0 
  402. * @access protected 
  403. * @param string $excerpt The post excerpt. 
  404. * @param WP_Post $post Post revision object. 
  405. * @return string Prepared excerpt or empty string. 
  406. */ 
  407. protected function prepare_excerpt_response( $excerpt, $post ) { 
  408.  
  409. /** This filter is documented in wp-includes/post-template.php */ 
  410. $excerpt = apply_filters( 'the_excerpt', $excerpt, $post ); 
  411.  
  412. if ( empty( $excerpt ) ) { 
  413. return ''; 
  414.  
  415. return $excerpt;