` taxonomy tabs. The `data-os-posts-*` hooks * are the contract the JS bundle relies on — keep them intact (or * rename via the filter) when customizing the layout. */ function openstation_pages_window_render_template() { ob_start(); ?>

__( 'Pages', 'desktop-mode' ), 'icon' => 'dashicons-admin-page', 'template' => 'openstation_pages_window_render_template', // Both Posts and Pages share a single bundle — `index.ts` // registers `desktop-mode-posts` AND `desktop-mode-pages` from // the same module. Loading `os-posts-window` for the // Pages window reuses the already-cached script + style. 'script' => 'os-posts-window', 'styles' => array( 'os-posts-window' ), 'width' => 1100, 'height' => 720, 'min_width' => 720, 'min_height' => 480, 'placement' => 'none', 'config' => array( // `mode` is the JS-side discriminator the shared bundle reads // to gate Pages-only behavior (parent column, no taxonomy // tabs). The Posts config omits this key, so the bundle // defaults to `'posts'` for backwards compat. 'mode' => 'pages', 'restRoot' => esc_url_raw( rest_url() ), 'restNonce' => wp_create_nonce( 'wp_rest' ), // Use the `/wp/v2/pages` collection rather than `/wp/v2/posts` // so core's hierarchical column shapers (parent, menu_order) // are surfaced without an explicit `post_type` query arg. 'postsUrl' => esc_url_raw( rest_url( 'wp/v2/pages' ) ), 'editPostUrlBase' => esc_url_raw( admin_url( 'post.php' ) ), 'newPostUrl' => esc_url_raw( add_query_arg( 'post_type', 'page', admin_url( 'post-new.php' ) ) ), 'usersUrl' => esc_url_raw( rest_url( 'wp/v2/users' ) ), 'currentUserId' => (int) get_current_user_id(), 'defaultPerPage' => 20, 'queryArgs' => openstation_pages_window_default_query_args(), // Reading-page assignments — surfaced so the title cell can // paint "Front page" / "Posts page" badges on matching rows // (one of the most-asked Pages-list usability gaps in the // WordPress trac + community forums). `0` when unset. 'frontPageId' => (int) get_option( 'page_on_front', 0 ), 'postsPageId' => (int) get_option( 'page_for_posts', 0 ), // Page-template label map: `{ slug: human label }` for the // templates the active theme registers. Falls back to the // raw slug when a theme registers a template the table // hasn't seen — better to show "page-fullwidth.php" than to // hide which template is in use. 'pageTemplates' => openstation_pages_window_template_labels(), ), ); /** * Filter the args used to register the native Pages window. * * @param array $window_args Args passed to `openstation_register_window()`. */ $window_args = (array) apply_filters( 'openstation_pages_window_args', $window_args ); $registered = openstation_register_window( 'desktop-mode-pages', $window_args ); if ( is_wp_error( $registered ) ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log( '[openstation] Native Pages window registration failed: ' . $registered->get_error_message() ); } } add_action( 'init', 'openstation_pages_window_register_window', 20 ); /** * Default REST query args for the Pages window. * * @return array */ function openstation_pages_window_default_query_args() { $args = array( // Pages are usually shallow + ordered by menu_order; embed the // author + featured media for the table cells. '_embed' => 'author,wp:featuredmedia', // `slug`, `template`, `link` and the custom `openstation_comment_count` // field are pulled in for the new Slug / Template / Comments // columns and the public-URL "View" quick-action. Missing them // from the whitelist costs nothing on the wire (REST will skip // them) but does silently break the column — keep them here. '_fields' => 'id,title,status,date,date_gmt,modified,modified_gmt,author,parent,menu_order,slug,link,template,comment_status,excerpt,openstation_lock,openstation_comment_count,_links,_embedded', 'orderby' => 'menu_order', 'order' => 'asc', ); /** * Filter the default outbound REST query args for the Pages window. * * @param array $args Default args. */ return (array) apply_filters( 'openstation_pages_window_query_args', $args ); } /** * Build the `{ slug: label }` map for the active theme's registered * page templates. Used by the Pages window's Template column to * paint friendly labels instead of raw filenames. * * The "default" template (assigned when `page.template` is `''`) is * keyed under the empty string for parity with what core returns in * `/wp/v2/pages` responses. * * @return array */ function openstation_pages_window_template_labels() { $labels = array( '' => __( 'Default template', 'desktop-mode' ), ); if ( function_exists( 'wp_get_theme' ) ) { $theme = wp_get_theme(); if ( $theme && method_exists( $theme, 'get_page_templates' ) ) { $registered = (array) $theme->get_page_templates( null, 'page' ); foreach ( $registered as $slug => $label ) { $labels[ (string) $slug ] = (string) $label; } } } /** * Filter the page-template label map handed to the Pages window. * * @param array $labels Slug → human label. */ return (array) apply_filters( 'openstation_pages_window_template_labels', $labels ); } /** * Register the `openstation_comment_count` REST field on `page`. * * The default `/wp/v2/pages` response doesn't include a comment * count — surfacing one alongside the row keeps parity with the * classic Pages list (where the count is a column) without forcing * the table to embed `_embed=replies`, which is heavy and N+1. * * Registered on `page` only for now; other CPTs can opt in by * cloning this `register_rest_field` call. Posts already track * comments via the classic admin and can be wired the same way * if/when the Posts window grows the column. */ function openstation_pages_window_register_comment_count_field() { register_rest_field( 'page', 'openstation_comment_count', array( 'get_callback' => static function ( $row ) { $id = isset( $row['id'] ) ? (int) $row['id'] : 0; if ( $id <= 0 ) { return 0; } return (int) get_comments_number( $id ); }, 'schema' => array( 'description' => __( 'Total non-trashed comments on this page.', 'desktop-mode' ), 'type' => 'integer', 'context' => array( 'view', 'embed' ), 'readonly' => true, ), ) ); } add_action( 'rest_api_init', 'openstation_pages_window_register_comment_count_field' );