PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 0.9.8
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v0.9.8
1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 All 34 releases
desktop-mode / includes / pages-window / window.php

window.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 0.9.8, at includes/pages-window/window.php

290 lines 11.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Desktop Mode — Native Pages Window: registration + template.
4 *
5 * Mirrors the structure of the Posts window (`includes/posts-window/window.php`),
6 * adapted for the `page` post type:
7 * - No taxonomy tabs (pages have no Categories/Tags surface in core).
8 * - Hierarchical: surfaces a Parent column and `orderby=menu_order` default.
9 * - Same lock badge via the `desktop_mode_lock` REST field.
10 *
11 * @package WPDesktopMode
12 */
13
14 defined( 'ABSPATH' ) || exit;
15
16 /**
17 * Echoes the native Pages window's template body.
18 *
19 * Same toolbar/table/pager structure as the Posts window, minus the
20 * `<wpd-tabs>` taxonomy tabs. The `data-desktop-mode-posts-*` hooks
21 * are the contract the JS bundle relies on — keep them intact (or
22 * rename via the filter) when customizing the layout.
23 */
24 function desktop_mode_pages_window_render_template() {
25 ob_start();
26 ?>
27 <div class="desktop-mode-posts" data-desktop-mode-posts-root>
28 <div class="desktop-mode-posts__panel">
29 <header class="desktop-mode-posts__toolbar" data-desktop-mode-posts-toolbar>
30 <div class="desktop-mode-posts__toolbar-left">
31 <wpd-segmented data-desktop-mode-posts-status value=""></wpd-segmented>
32 <wpd-text-field
33 data-desktop-mode-posts-search
34 placeholder="<?php esc_attr_e( 'Search pages…', 'desktop-mode' ); ?>"
35 ></wpd-text-field>
36 </div>
37 <div class="desktop-mode-posts__toolbar-right" data-desktop-mode-posts-bulk hidden>
38 <span class="desktop-mode-posts__count" data-desktop-mode-posts-count></span>
39 <span class="desktop-mode-posts__bulk-actions" data-desktop-mode-posts-bulk-actions></span>
40 </div>
41 <div class="desktop-mode-posts__toolbar-trailing">
42 <span class="desktop-mode-posts__toolbar-extras" data-desktop-mode-posts-toolbar-extras></span>
43 <wpd-button variant="ghost" data-desktop-mode-posts-refresh title="<?php esc_attr_e( 'Refresh', 'desktop-mode' ); ?>">
44 <span class="dashicons dashicons-update" aria-hidden="true"></span>
45 </wpd-button>
46 <wpd-button variant="primary" data-desktop-mode-posts-new>
47 <span class="dashicons dashicons-plus" aria-hidden="true"></span>
48 <?php esc_html_e( 'Add New', 'desktop-mode' ); ?>
49 </wpd-button>
50 </div>
51 </header>
52 <div class="desktop-mode-posts__body" data-desktop-mode-posts-body>
53 <wpd-table
54 data-desktop-mode-posts-table
55 selectable="multi"
56 sticky-header
57 sticky-columns="1"
58 hover
59 striped
60 bordered
61 loading
62 >
63 <div slot="empty" class="desktop-mode-posts__empty">
64 <span class="dashicons dashicons-admin-page" aria-hidden="true"></span>
65 <p><?php esc_html_e( 'No pages found.', 'desktop-mode' ); ?></p>
66 <p class="desktop-mode-posts__empty-hint">
67 <?php esc_html_e( 'Try a different search or change the status filter.', 'desktop-mode' ); ?>
68 </p>
69 </div>
70 </wpd-table>
71 </div>
72 <footer class="desktop-mode-posts__pager" data-desktop-mode-posts-pager>
73 <div class="desktop-mode-posts__pager-meta">
74 <span data-desktop-mode-posts-page-indicator></span>
75 </div>
76 <div class="desktop-mode-posts__pager-nav">
77 <wpd-button variant="ghost" data-desktop-mode-posts-prev disabled>
78 <span class="dashicons dashicons-arrow-left-alt2" aria-hidden="true"></span>
79 <?php esc_html_e( 'Previous', 'desktop-mode' ); ?>
80 </wpd-button>
81 <wpd-button variant="ghost" data-desktop-mode-posts-next disabled>
82 <?php esc_html_e( 'Next', 'desktop-mode' ); ?>
83 <span class="dashicons dashicons-arrow-right-alt2" aria-hidden="true"></span>
84 </wpd-button>
85 <label class="desktop-mode-posts__pager-perpage">
86 <?php esc_html_e( 'Per page', 'desktop-mode' ); ?>
87 <select data-desktop-mode-posts-per-page>
88 <option value="10">10</option>
89 <option value="20" selected>20</option>
90 <option value="50">50</option>
91 <option value="100">100</option>
92 </select>
93 </label>
94 </div>
95 </footer>
96 </div>
97 </div>
98 <?php
99 $html = (string) ob_get_clean();
100
101 /**
102 * Filter the native Pages window's template HTML.
103 *
104 * Keep the `data-desktop-mode-posts-*` hooks intact so the JS
105 * render callback can find its mount points.
106 *
107 * @param string $html Default template HTML.
108 */
109 $filtered = (string) apply_filters( 'desktop_mode_pages_window_template_html', $html );
110
111 if ( function_exists( 'desktop_mode_kses_native_window_template' ) ) {
112 echo desktop_mode_kses_native_window_template( $filtered ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- helper kses-escapes.
113 } else {
114 echo wp_kses( $filtered, wp_kses_allowed_html( 'post' ) );
115 }
116 }
117
118 /**
119 * Register the native Pages window on `init` (priority 20).
120 */
121 function desktop_mode_pages_window_register_window() {
122 if ( ! desktop_mode_pages_window_user_can_register() ) {
123 return;
124 }
125
126 $window_args = array(
127 'title' => __( 'Pages', 'desktop-mode' ),
128 'icon' => 'dashicons-admin-page',
129 'template' => 'desktop_mode_pages_window_render_template',
130 // Both Posts and Pages share a single bundle — `index.ts`
131 // registers `desktop-mode-posts` AND `desktop-mode-pages` from
132 // the same module. Loading `desktop-mode-posts-window` for the
133 // Pages window reuses the already-cached script + style.
134 'script' => 'desktop-mode-posts-window',
135 'style' => 'desktop-mode-posts-window',
136 'width' => 1100,
137 'height' => 720,
138 'min_width' => 720,
139 'min_height' => 480,
140 'placement' => 'none',
141 'config' => array(
142 // `mode` is the JS-side discriminator the shared bundle reads
143 // to gate Pages-only behavior (parent column, no taxonomy
144 // tabs, Pages intro slug). The Posts config omits this key,
145 // so the bundle defaults to `'posts'` for backwards compat.
146 'mode' => 'pages',
147 'introSlug' => 'pages',
148 'restRoot' => esc_url_raw( rest_url() ),
149 'restNonce' => wp_create_nonce( 'wp_rest' ),
150 // Use the `/wp/v2/pages` collection rather than `/wp/v2/posts`
151 // so core's hierarchical column shapers (parent, menu_order)
152 // are surfaced without an explicit `post_type` query arg.
153 'postsUrl' => esc_url_raw( rest_url( 'wp/v2/pages' ) ),
154 'editPostUrlBase' => esc_url_raw( admin_url( 'post.php' ) ),
155 'newPostUrl' => esc_url_raw( add_query_arg( 'post_type', 'page', admin_url( 'post-new.php' ) ) ),
156 'usersUrl' => esc_url_raw( rest_url( 'wp/v2/users' ) ),
157 'currentUserId' => (int) get_current_user_id(),
158 'defaultPerPage' => 20,
159 'queryArgs' => desktop_mode_pages_window_default_query_args(),
160 'introSeen' => desktop_mode_has_seen_intro( get_current_user_id(), 'pages' ),
161 'introUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/intros/seen' ) ),
162 // Reading-page assignments — surfaced so the title cell can
163 // paint "Front page" / "Posts page" badges on matching rows
164 // (one of the most-asked Pages-list usability gaps in the
165 // WordPress trac + community forums). `0` when unset.
166 'frontPageId' => (int) get_option( 'page_on_front', 0 ),
167 'postsPageId' => (int) get_option( 'page_for_posts', 0 ),
168 // Page-template label map: `{ slug: human label }` for the
169 // templates the active theme registers. Falls back to the
170 // raw slug when a theme registers a template the table
171 // hasn't seen — better to show "page-fullwidth.php" than to
172 // hide which template is in use.
173 'pageTemplates' => desktop_mode_pages_window_template_labels(),
174 ),
175 );
176
177 /**
178 * Filter the args used to register the native Pages window.
179 *
180 * @param array $window_args Args passed to `desktop_mode_register_window()`.
181 */
182 $window_args = (array) apply_filters( 'desktop_mode_pages_window_args', $window_args );
183
184 $registered = desktop_mode_register_window( 'desktop-mode-pages', $window_args );
185 if ( is_wp_error( $registered ) ) {
186 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
187 error_log( '[desktop-mode] Native Pages window registration failed: ' . $registered->get_error_message() );
188 }
189 }
190 add_action( 'init', 'desktop_mode_pages_window_register_window', 20 );
191
192 /**
193 * Default REST query args for the Pages window.
194 *
195 * @return array
196 */
197 function desktop_mode_pages_window_default_query_args() {
198 $args = array(
199 // Pages are usually shallow + ordered by menu_order; embed the
200 // author + featured media for the table cells.
201 '_embed' => 'author,wp:featuredmedia',
202 // `slug`, `template`, `link` and the custom `desktop_mode_comment_count`
203 // field are pulled in for the new Slug / Template / Comments
204 // columns and the public-URL "View" quick-action. Missing them
205 // from the whitelist costs nothing on the wire (REST will skip
206 // them) but does silently break the column — keep them here.
207 '_fields' =>
208 'id,title,status,date,date_gmt,modified,modified_gmt,author,parent,menu_order,slug,link,template,comment_status,excerpt,desktop_mode_lock,desktop_mode_comment_count,_links,_embedded',
209 'orderby' => 'menu_order',
210 'order' => 'asc',
211 );
212
213 /**
214 * Filter the default outbound REST query args for the Pages window.
215 *
216 * @param array $args Default args.
217 */
218 return (array) apply_filters( 'desktop_mode_pages_window_query_args', $args );
219 }
220
221 /**
222 * Build the `{ slug: label }` map for the active theme's registered
223 * page templates. Used by the Pages window's Template column to
224 * paint friendly labels instead of raw filenames.
225 *
226 * The "default" template (assigned when `page.template` is `''`) is
227 * keyed under the empty string for parity with what core returns in
228 * `/wp/v2/pages` responses.
229 *
230 * @return array<string,string>
231 */
232 function desktop_mode_pages_window_template_labels() {
233 $labels = array(
234 '' => __( 'Default template', 'desktop-mode' ),
235 );
236 if ( function_exists( 'wp_get_theme' ) ) {
237 $theme = wp_get_theme();
238 if ( $theme && method_exists( $theme, 'get_page_templates' ) ) {
239 $registered = (array) $theme->get_page_templates( null, 'page' );
240 foreach ( $registered as $slug => $label ) {
241 $labels[ (string) $slug ] = (string) $label;
242 }
243 }
244 }
245
246 /**
247 * Filter the page-template label map handed to the Pages window.
248 *
249 * @param array<string,string> $labels Slug → human label.
250 */
251 return (array) apply_filters( 'desktop_mode_pages_window_template_labels', $labels );
252 }
253
254 /**
255 * Register the `desktop_mode_comment_count` REST field on `page`.
256 *
257 * The default `/wp/v2/pages` response doesn't include a comment
258 * count — surfacing one alongside the row keeps parity with the
259 * classic Pages list (where the count is a column) without forcing
260 * the table to embed `_embed=replies`, which is heavy and N+1.
261 *
262 * Registered on `page` only for now; other CPTs can opt in by
263 * cloning this `register_rest_field` call. Posts already track
264 * comments via the classic admin and can be wired the same way
265 * if/when the Posts window grows the column.
266 */
267 function desktop_mode_pages_window_register_comment_count_field() {
268 register_rest_field(
269 'page',
270 'desktop_mode_comment_count',
271 array(
272 'get_callback' => static function ( $row ) {
273 $id = isset( $row['id'] ) ? (int) $row['id'] : 0;
274 if ( $id <= 0 ) {
275 return 0;
276 }
277 return (int) get_comments_number( $id );
278 },
279 'schema' => array(
280 'description' => __( 'Total non-trashed comments on this page.', 'desktop-mode' ),
281 'type' => 'integer',
282 'context' => array( 'view', 'embed' ),
283 'readonly' => true,
284 ),
285 )
286 );
287 }
288 add_action( 'rest_api_init', 'desktop_mode_pages_window_register_comment_count_field' );
289
290