* Returns the side-panel detail bundle for one post: * { post: {...}, author, contributors, comments, categories, * attached_media, revisions }. * * @package WPDesktopMode * @since 0.8.2 */ defined( 'ABSPATH' ) || exit; /** * Capability check shared across every endpoint. * * @since 0.8.2 * * @return bool */ function desktop_mode_content_graph_rest_permission() { return desktop_mode_content_graph_user_can_use(); } /** * Register the routes. * * @since 0.8.2 */ function desktop_mode_content_graph_register_routes() { register_rest_route( 'desktop-mode/v1', '/content-graph/post-types', array( 'methods' => WP_REST_Server::READABLE, 'callback' => 'desktop_mode_content_graph_rest_post_types', 'permission_callback' => 'desktop_mode_content_graph_rest_permission', ) ); register_rest_route( 'desktop-mode/v1', '/content-graph/nodes', array( 'methods' => WP_REST_Server::READABLE, 'callback' => 'desktop_mode_content_graph_rest_nodes', 'permission_callback' => 'desktop_mode_content_graph_rest_permission', 'args' => array( 'types' => array( 'description' => 'Comma-separated list of post type slugs to include.', 'type' => 'string', 'default' => '', ), ), ) ); register_rest_route( 'desktop-mode/v1', '/content-graph/post/(?P\d+)', array( 'methods' => WP_REST_Server::READABLE, 'callback' => 'desktop_mode_content_graph_rest_post_detail', 'permission_callback' => 'desktop_mode_content_graph_rest_permission', 'args' => array( 'id' => array( 'type' => 'integer', 'required' => true, ), ), ) ); } add_action( 'rest_api_init', 'desktop_mode_content_graph_register_routes' ); /** * GET /post-types * * @since 0.8.2 * * @return WP_REST_Response */ function desktop_mode_content_graph_rest_post_types() { $types = desktop_mode_content_graph_post_types(); $out = array(); foreach ( $types as $entry ) { $slug = isset( $entry['slug'] ) ? (string) $entry['slug'] : ''; $counts = $slug ? wp_count_posts( $slug ) : null; $count = 0; if ( $counts && isset( $counts->publish ) ) { $count = (int) $counts->publish; if ( isset( $counts->private ) ) { $count += (int) $counts->private; } } $out[] = array( 'slug' => $slug, 'label' => isset( $entry['label'] ) ? (string) $entry['label'] : $slug, 'icon' => isset( $entry['icon'] ) ? (string) $entry['icon'] : 'dashicons-admin-post', 'count' => $count, ); } return rest_ensure_response( $out ); } /** * GET /nodes * * @since 0.8.2 * * @param WP_REST_Request $request * @return WP_REST_Response */ function desktop_mode_content_graph_rest_nodes( WP_REST_Request $request ) { $raw = (string) $request->get_param( 'types' ); $types = '' === $raw ? wp_list_pluck( desktop_mode_content_graph_post_types(), 'slug' ) : array_map( 'trim', explode( ',', $raw ) ); return rest_ensure_response( desktop_mode_content_graph_build( (array) $types ) ); } /** * GET /post/ * * @since 0.8.2 * * @param WP_REST_Request $request * @return WP_REST_Response|WP_Error */ function desktop_mode_content_graph_rest_post_detail( WP_REST_Request $request ) { $id = (int) $request['id']; $post = $id > 0 ? get_post( $id ) : null; if ( ! $post ) { return new WP_Error( 'desktop_mode_content_graph_post_not_found', __( 'Post not found.', 'desktop-mode' ), array( 'status' => 404 ) ); } if ( ! current_user_can( 'read_post', $id ) ) { return new WP_Error( 'desktop_mode_content_graph_forbidden', __( 'Insufficient permissions.', 'desktop-mode' ), array( 'status' => 403 ) ); } $author = desktop_mode_content_graph_format_user( (int) $post->post_author ); $contributors = desktop_mode_content_graph_collect_contributors( $post ); $comments = desktop_mode_content_graph_collect_comments( $post ); $categories = desktop_mode_content_graph_collect_terms( $post ); $attached = desktop_mode_content_graph_collect_attached_media( $post ); $revisions = desktop_mode_content_graph_collect_revisions( $post ); return rest_ensure_response( array( 'post' => array( 'id' => (int) $post->ID, 'type' => $post->post_type, 'title' => get_the_title( $post ), 'status' => $post->post_status, 'slug' => $post->post_name, 'edit_url' => (string) get_edit_post_link( $post->ID, 'raw' ), 'view_url' => (string) get_permalink( $post ), 'date' => mysql2date( 'c', $post->post_date_gmt, false ), 'modified' => mysql2date( 'c', $post->post_modified_gmt, false ), ), 'author' => $author, 'contributors' => $contributors, 'comments' => $comments, 'categories' => $categories, 'attached_media' => $attached, 'revisions' => $revisions, ) ); } /** * Format a user record for the side panel. * * @since 0.8.2 * * @param int $user_id * @return array|null */ function desktop_mode_content_graph_format_user( $user_id ) { $user_id = (int) $user_id; if ( $user_id <= 0 ) { return null; } $user = get_userdata( $user_id ); if ( ! $user ) { return null; } return array( 'id' => $user_id, 'name' => (string) $user->display_name, 'slug' => (string) $user->user_nicename, 'avatar' => (string) get_avatar_url( $user_id, array( 'size' => 64 ) ), 'edit_url' => (string) get_edit_user_link( $user_id ), ); } /** * Collect contributors: distinct revision authors (excluding the * current author) plus distinct comment authors who have a * registered user account. * * @since 0.8.2 * * @param WP_Post $post * @return array[] */ function desktop_mode_content_graph_collect_contributors( WP_Post $post ) { $author_id = (int) $post->post_author; $ids = array(); $revs = wp_get_post_revisions( $post->ID, array( 'posts_per_page' => 100, 'fields' => 'ids', ) ); foreach ( (array) $revs as $rev_id ) { $rev = get_post( $rev_id ); if ( $rev && (int) $rev->post_author > 0 && (int) $rev->post_author !== $author_id ) { $ids[ (int) $rev->post_author ] = true; } } $comment_users = get_comments( array( 'post_id' => $post->ID, 'status' => 'approve', 'fields' => 'ids', ) ); foreach ( (array) $comment_users as $cid ) { $comment = get_comment( $cid ); if ( $comment && (int) $comment->user_id > 0 && (int) $comment->user_id !== $author_id ) { $ids[ (int) $comment->user_id ] = true; } } $out = array(); foreach ( array_keys( $ids ) as $uid ) { $entry = desktop_mode_content_graph_format_user( $uid ); if ( $entry ) { $out[] = $entry; } } return $out; } /** * Collect approved comments (most recent first, capped at 50). * * @since 0.8.2 * * @param WP_Post $post * @return array[] */ function desktop_mode_content_graph_collect_comments( WP_Post $post ) { $comments = get_comments( array( 'post_id' => $post->ID, 'status' => 'approve', 'number' => 50, 'orderby' => 'comment_date_gmt', 'order' => 'DESC', ) ); $out = array(); foreach ( $comments as $comment ) { $out[] = array( 'id' => (int) $comment->comment_ID, 'author' => (string) $comment->comment_author, 'user_id' => (int) $comment->user_id, 'date' => mysql2date( 'c', $comment->comment_date_gmt, false ), 'excerpt' => wp_html_excerpt( wp_strip_all_tags( (string) $comment->comment_content ), 140, '...' ), 'edit_url' => (string) admin_url( 'comment.php?action=editcomment&c=' . (int) $comment->comment_ID ), ); } return $out; } /** * Collect every taxonomy term attached to the post (categories, tags, * and any custom taxonomy registered for the post type). * * @since 0.8.2 * * @param WP_Post $post * @return array[] */ function desktop_mode_content_graph_collect_terms( WP_Post $post ) { $taxes = get_object_taxonomies( $post->post_type, 'objects' ); $out = array(); foreach ( $taxes as $tax ) { if ( ! $tax->public && ! $tax->show_ui ) { continue; } $terms = get_the_terms( $post, $tax->name ); if ( empty( $terms ) || is_wp_error( $terms ) ) { continue; } foreach ( $terms as $term ) { $out[] = array( 'id' => (int) $term->term_id, 'name' => (string) $term->name, 'slug' => (string) $term->slug, 'taxonomy' => (string) $term->taxonomy, 'tax_label' => (string) $tax->labels->singular_name, 'count' => (int) $term->count, 'edit_url' => (string) get_edit_term_link( $term->term_id, $term->taxonomy ), ); } } return $out; } /** * Collect attached media (anything with this post as its `post_parent`) * plus any media referenced from a `wp:image` block. Returns up to 50. * * @since 0.8.2 * * @param WP_Post $post * @return array[] */ function desktop_mode_content_graph_collect_attached_media( WP_Post $post ) { $attachments = get_attached_media( '', $post ); $out = array(); foreach ( $attachments as $att ) { $out[] = array( 'id' => (int) $att->ID, 'title' => (string) get_the_title( $att ), 'mime' => (string) $att->post_mime_type, 'thumb' => (string) wp_get_attachment_image_url( $att->ID, 'thumbnail' ), 'edit_url' => (string) get_edit_post_link( $att->ID, 'raw' ), ); if ( count( $out ) >= 50 ) { break; } } return $out; } /** * Collect post revisions (most recent first, capped at 30). * * @since 0.8.2 * * @param WP_Post $post * @return array[] */ function desktop_mode_content_graph_collect_revisions( WP_Post $post ) { $revs = wp_get_post_revisions( $post->ID, array( 'posts_per_page' => 30, ) ); $out = array(); foreach ( $revs as $rev ) { $out[] = array( 'id' => (int) $rev->ID, 'date' => mysql2date( 'c', $rev->post_date_gmt, false ), 'author' => desktop_mode_content_graph_format_user( (int) $rev->post_author ), 'edit_url' => (string) admin_url( 'revision.php?revision=' . (int) $rev->ID ), ); } return $out; }