current_user_can( 'edit_post', (int) $request['id'] ); register_rest_route( Routes::NS, '/articles/(?P\d+)/revisions', array( 'methods' => 'GET', 'callback' => array( $this, 'index' ), 'permission_callback' => $can_edit, ) ); register_rest_route( Routes::NS, '/articles/(?P\d+)/revisions/(?P\d+)/diff', array( 'methods' => 'GET', 'callback' => array( $this, 'diff' ), 'args' => array( 'to' => array( 'type' => 'integer' ) ), 'permission_callback' => $can_edit, ) ); register_rest_route( Routes::NS, '/articles/(?P\d+)/revisions/(?P\d+)/restore', array( 'methods' => 'POST', 'callback' => array( $this, 'restore' ), 'permission_callback' => $can_edit, ) ); } /** * The article, or an error. * * @param int $id Id. * @return \WP_Post|\WP_Error */ private function article( $id ) { $post = get_post( (int) $id ); if ( ! $post || PostTypes::ARTICLE !== $post->post_type ) { return new \WP_Error( 'kdocs_not_found', __( 'Article not found.', 'karma-docs' ), array( 'status' => 404 ) ); } return $post; } /** * A revision of the article (or the article itself for its current state). * * @param \WP_Post $post Article. * @param int $id Revision id. * @return \WP_Post|\WP_Error */ private function revision( $post, $id ) { if ( (int) $id === $post->ID ) { return $post; } $revision_id = (int) $id; $revision = wp_get_post_revision( $revision_id ); if ( ! $revision || (int) $revision->post_parent !== $post->ID ) { return new \WP_Error( 'kdocs_not_found', __( 'Revision not found.', 'karma-docs' ), array( 'status' => 404 ) ); } return $revision; } /** * GET /articles/{id}/revisions. * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response|\WP_Error */ public function index( $request ) { $post = $this->article( $request['id'] ); if ( is_wp_error( $post ) ) { return $post; } $list = array(); foreach ( wp_get_post_revisions( $post->ID, array( 'posts_per_page' => 50 ) ) as $revision ) { $author = get_userdata( (int) $revision->post_author ); $list[] = array( 'id' => (int) $revision->ID, 'date' => strtotime( $revision->post_modified_gmt . ' UTC' ), 'author' => $author ? $author->display_name : '', 'autosave' => wp_is_post_autosave( $revision ) ? true : false, 'title' => html_entity_decode( $revision->post_title, ENT_QUOTES, 'UTF-8' ), ); } return rest_ensure_response( array( 'current' => $post->ID, 'revisions' => $list, ) ); } /** * GET /articles/{id}/revisions/{revision}/diff?to=: side-by-side HTML diff of * title, excerpt and content (to = another revision, default the current article). * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response|\WP_Error */ public function diff( $request ) { $post = $this->article( $request['id'] ); if ( is_wp_error( $post ) ) { return $post; } $from = $this->revision( $post, $request['revision'] ); $to = $this->revision( $post, $request['to'] ? $request['to'] : $post->ID ); if ( is_wp_error( $from ) || is_wp_error( $to ) ) { return is_wp_error( $from ) ? $from : $to; } $fields = array( 'post_title' => __( 'Title', 'karma-docs' ), 'post_excerpt' => __( 'Excerpt', 'karma-docs' ), 'post_content' => __( 'Content', 'karma-docs' ), ); $diffs = array(); foreach ( $fields as $field => $label ) { $html = wp_text_diff( self::readable( $from->$field ), self::readable( $to->$field ), array( 'show_split_view' => true ) ); if ( $html ) { $diffs[] = array( 'field' => $label, 'html' => $html, ); } } return rest_ensure_response( array( 'diffs' => $diffs ) ); } /** * Block markup without comment delimiters, one block per line group. * * @param string $text Text. * @return string */ private static function readable( $text ) { $text = preg_replace( '/\n?/', '', (string) $text ); return trim( preg_replace( "/\n{3,}/", "\n\n", $text ) ); } /** * POST /articles/{id}/revisions/{revision}/restore. * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response|\WP_Error */ public function restore( $request ) { $post = $this->article( $request['id'] ); if ( is_wp_error( $post ) ) { return $post; } $revision = $this->revision( $post, $request['revision'] ); if ( is_wp_error( $revision ) || $revision->ID === $post->ID ) { return is_wp_error( $revision ) ? $revision : new \WP_Error( 'kdocs_invalid', __( 'Choose an earlier revision.', 'karma-docs' ), array( 'status' => 400 ) ); } if ( ! wp_restore_post_revision( $revision->ID ) ) { return new \WP_Error( 'kdocs_failed', __( 'The revision could not be restored.', 'karma-docs' ), array( 'status' => 500 ) ); } return rest_ensure_response( array( 'restored' => true ) ); } }