PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.10
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.10
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 / apps / my-wordpress / parts / sections.php

sections.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.10, at apps/my-wordpress/parts/sections.php

292 lines 11.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * My WordPress — the section registry.
4 *
5 * Part of the `my-wordpress` app: required by `my-wordpress.os.php`,
6 * same namespace, plain `.php` on purpose — only `*.os.php` files are
7 * app entries to the framework loader. This part owns what the
8 * explorer OFFERS: the four builtins, every eligible custom post type
9 * with its plugin-group folder (discovered through the same
10 * `openstation_my_wordpress_*` helpers WP Explorer uses), the Agents
11 * tile, and the per-kind sort options.
12 *
13 * @package OpenStation
14 */
15
16 namespace OpenStation\Apps\MyWordPress;
17
18 use OpenStation\App\Os;
19 use OpenStation\App\State;
20
21 // Direct access, unless a standalone host is booting on bare PHP.
22 if ( ! defined( 'ABSPATH' ) ) {
23 defined( 'OPENSTATION_STANDALONE' ) || exit;
24 }
25
26 /** A folder wearing the OpenStation mark — the explorer family icon. */
27 const ICON = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path d="M11 13h12.2a3 3 0 0 1 2.4 1.2l2.8 3.7a3 3 0 0 0 2.4 1.2H53a4 4 0 0 1 4 4v25a4 4 0 0 1-4 4H11a4 4 0 0 1-4-4V17a4 4 0 0 1 4-4z" fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"/><circle cx="32" cy="35.6" r="10" fill="none" stroke="currentColor" stroke-width="3"/><path d="M32 30.6v10M27.4 33l4.6 7.6 4.6-7.6" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg>';
28
29 /**
30 * Every section the current user may browse: the four builtins plus
31 * one per eligible custom post type, carrying the same group fields
32 * WP Explorer computes (`group`, `groupLabel`, `groupIcon`,
33 * `groupOrder`) so plugin CPTs fold into plugin folders at the root.
34 *
35 * @param Os $os Host handle.
36 * @return array<int,array<string,mixed>>
37 */
38 function sections( Os $os ) {
39 $sections = array(
40 array(
41 'id' => 'posts',
42 'label' => __( 'Posts', 'desktop-mode' ),
43 'icon' => 'dashicons-admin-post',
44 'kind' => 'post',
45 'post_type' => 'post',
46 'capability' => 'edit_posts',
47 'thumbnails' => true,
48 // So a row dragged onto the Recycle Bin knows which REST
49 // collection its trash call hits — same for every section.
50 'restPath' => 'wp/v2/posts',
51 ),
52 array(
53 'id' => 'pages',
54 'label' => __( 'Pages', 'desktop-mode' ),
55 'icon' => 'dashicons-admin-page',
56 'kind' => 'post',
57 'post_type' => 'page',
58 'capability' => 'edit_pages',
59 'thumbnails' => true,
60 // The list view offers a Parent column to tree-shaped types.
61 'hierarchical' => true,
62 'restPath' => 'wp/v2/pages',
63 ),
64 array(
65 'id' => 'media',
66 'label' => __( 'Media', 'desktop-mode' ),
67 'icon' => 'dashicons-admin-media',
68 'kind' => 'media',
69 'post_type' => 'attachment',
70 'capability' => 'upload_files',
71 'thumbnails' => true,
72 ),
73 array(
74 'id' => 'users',
75 'label' => __( 'Users', 'desktop-mode' ),
76 'icon' => 'dashicons-admin-users',
77 'kind' => 'user',
78 'post_type' => '',
79 'capability' => 'list_users',
80 'thumbnails' => true,
81 // Whether the toolbar offers Add user — Core's own menu
82 // gate for user-new.php: create_users, or promote_users on
83 // multisite, where a site administrator keeps the
84 // invite-existing half even when the network withholds
85 // brand-new account creation. The add-user action
86 // re-checks server-side.
87 'canAdd' => current_user_can( 'create_users' )
88 || ( is_multisite() && current_user_can( 'promote_users' ) ),
89 ),
90 );
91
92 // The WooCommerce sections (Orders, Customers) — ahead of the CPT
93 // pass, exactly where WP Explorer's entities filter puts them, so
94 // they lead the Woo folder. Empty unless WooCommerce is active.
95 $sections = array_merge( $sections, woo_sections( $os ) );
96
97 // Every eligible CPT, through the same discovery WP Explorer uses —
98 // one list of what the site contains, two windows rendering it.
99 if ( function_exists( 'openstation_my_wordpress_eligible_post_types' ) ) {
100 $claimed = array_column( $sections, 'post_type' );
101 foreach ( openstation_my_wordpress_eligible_post_types() as $name => $post_type ) {
102 // A type an earlier section already declares (Orders claims
103 // `shop_order`) must not become a second, broken folder.
104 if ( in_array( (string) $name, $claimed, true ) ) {
105 continue;
106 }
107 $group = function_exists( 'openstation_my_wordpress_post_type_group' )
108 ? openstation_my_wordpress_post_type_group( $name )
109 : null;
110 $entry = array(
111 'id' => 'cpt-' . $name,
112 'label' => isset( $post_type->labels->name ) && '' !== $post_type->labels->name
113 ? (string) $post_type->labels->name
114 : (string) $name,
115 'icon' => function_exists( 'openstation_my_wordpress_post_type_icon' )
116 ? openstation_my_wordpress_post_type_icon( $post_type )
117 : 'dashicons-admin-post',
118 'kind' => 'post',
119 'post_type' => (string) $name,
120 'capability' => (string) $post_type->cap->edit_posts,
121 'thumbnails' => post_type_supports( $name, 'thumbnail' ),
122 'hierarchical' => is_post_type_hierarchical( $name ),
123 'group' => $group ? (string) $group['id'] : null,
124 'groupLabel' => $group ? (string) $group['label'] : null,
125 'groupIcon' => $group ? (string) $group['icon'] : null,
126 'groupOrder' => $group ? (int) $group['order'] : null,
127 'restPath' => function_exists( 'openstation_my_wordpress_post_type_rest_path' )
128 ? (string) openstation_my_wordpress_post_type_rest_path( $post_type )
129 : '',
130 );
131 $sections[] = woo_decorate_section( $entry, $post_type );
132 }
133 }
134
135 // The Agents section, exactly as WP Explorer lists it: always
136 // discoverable while the user may read agents, even when the
137 // framework is off — the section itself explains how to turn it on.
138 if ( function_exists( 'openstation_agents_user_can_read' ) && openstation_agents_user_can_read() ) {
139 $sections[] = array(
140 'id' => 'agents',
141 'label' => __( 'Agents', 'desktop-mode' ),
142 'icon' => function_exists( 'openstation_agent_avatar_url' )
143 ? openstation_agent_avatar_url()
144 : 'dashicons-superhero',
145 'kind' => 'agent',
146 'post_type' => '',
147 'capability' => '',
148 'thumbnails' => false,
149 );
150 }
151
152 /**
153 * Filter the sections the My WordPress app offers. Runs on every
154 * render — a post type registered at any point of the bootstrap
155 * can appear, unlike a list frozen at registration time.
156 *
157 * @param array[] $sections Each: `id`, `label`, `icon`, `kind`
158 * (`post` | `media` | `user` | `agent`),
159 * `post_type`, `capability`, `thumbnails`,
160 * and the optional `group*` folder fields.
161 */
162 $sections = (array) $os->filter( 'openstation_my_wordpress_app_sections', $sections );
163
164 return array_values(
165 array_filter(
166 $sections,
167 static function ( $section ) use ( $os ) {
168 return is_array( $section ) && ! empty( $section['id'] )
169 && ( empty( $section['capability'] ) || $os->can( (string) $section['capability'] ) );
170 }
171 )
172 );
173 }
174
175 /**
176 * One section by id, or null.
177 *
178 * @param Os $os Host handle.
179 * @param string $id Section id.
180 * @return array<string,mixed>|null
181 */
182 function section_of( Os $os, $id ) {
183 foreach ( sections( $os ) as $section ) {
184 if ( $section['id'] === $id ) {
185 return $section;
186 }
187 }
188 return null;
189 }
190
191 /**
192 * The plugin folders the grouped sections fold into, through the
193 * same collector (and `openstation_my_wordpress_post_type_groups`
194 * filter) WP Explorer uses.
195 *
196 * @param array[] $sections Section descriptors.
197 * @return array[] Each: `id`, `label`, `icon`, `order`.
198 */
199 function groups( array $sections ) {
200 if ( function_exists( 'openstation_my_wordpress_collect_groups' ) ) {
201 return openstation_my_wordpress_collect_groups( $sections );
202 }
203 $groups = array();
204 foreach ( $sections as $section ) {
205 if ( ! empty( $section['group'] ) && ! isset( $groups[ $section['group'] ] ) ) {
206 $groups[ $section['group'] ] = array(
207 'id' => (string) $section['group'],
208 'label' => (string) ( $section['groupLabel'] ?? $section['group'] ),
209 'icon' => (string) ( $section['groupIcon'] ?? 'dashicons-admin-plugins' ),
210 'order' => (int) ( $section['groupOrder'] ?? 20 ),
211 );
212 }
213 }
214 return array_values( $groups );
215 }
216
217 /**
218 * The statuses the explorer lists: everything an editor works on.
219 *
220 * @return string[]
221 */
222 function statuses() {
223 return array( 'publish', 'future', 'draft', 'pending', 'private' );
224 }
225
226 /**
227 * Sort options for a section's kind: `value => [ label, orderby, order ]`.
228 *
229 * @param array<string,mixed> $section Section descriptor.
230 * @return array<string,array{0:string,1:string,2:string}>
231 */
232 function sort_options( array $section ) {
233 if ( 'agent' === $section['kind'] ) {
234 return array();
235 }
236 $woo = woo_sort_options( $section );
237 if ( null !== $woo ) {
238 return $woo;
239 }
240 // Every column the list view can sort by is a sort option here, so
241 // the icon view's "Sort by" menu and the list view's headers are
242 // two doors onto ONE server order — a column is sortable exactly
243 // when both of its keys exist in this table.
244 if ( 'user' === $section['kind'] ) {
245 return array(
246 'default' => array( __( 'Name A–Z', 'desktop-mode' ), 'display_name', 'ASC' ),
247 'title-desc' => array( __( 'Name Z–A', 'desktop-mode' ), 'display_name', 'DESC' ),
248 'newest' => array( __( 'Recently registered', 'desktop-mode' ), 'registered', 'DESC' ),
249 'oldest' => array( __( 'Longest registered', 'desktop-mode' ), 'registered', 'ASC' ),
250 'id-asc' => array( __( 'ID, lowest first', 'desktop-mode' ), 'ID', 'ASC' ),
251 'id-desc' => array( __( 'ID, highest first', 'desktop-mode' ), 'ID', 'DESC' ),
252 'login-asc' => array( __( 'Username A–Z', 'desktop-mode' ), 'login', 'ASC' ),
253 'login-desc' => array( __( 'Username Z–A', 'desktop-mode' ), 'login', 'DESC' ),
254 'email-asc' => array( __( 'Email A–Z', 'desktop-mode' ), 'email', 'ASC' ),
255 'email-desc' => array( __( 'Email Z–A', 'desktop-mode' ), 'email', 'DESC' ),
256 'posts' => array( __( 'Most posts', 'desktop-mode' ), 'post_count', 'DESC' ),
257 'posts-asc' => array( __( 'Fewest posts', 'desktop-mode' ), 'post_count', 'ASC' ),
258 );
259 }
260 $options = array(
261 'default' => array( __( 'Newest first', 'desktop-mode' ), 'date', 'DESC' ),
262 'oldest' => array( __( 'Oldest first', 'desktop-mode' ), 'date', 'ASC' ),
263 'title-asc' => array( __( 'Title A–Z', 'desktop-mode' ), 'title', 'ASC' ),
264 'title-desc' => array( __( 'Title Z–A', 'desktop-mode' ), 'title', 'DESC' ),
265 'id-asc' => array( __( 'ID, lowest first', 'desktop-mode' ), 'ID', 'ASC' ),
266 'id-desc' => array( __( 'ID, highest first', 'desktop-mode' ), 'ID', 'DESC' ),
267 'modified' => array( __( 'Recently modified', 'desktop-mode' ), 'modified', 'DESC' ),
268 'modified-asc' => array( __( 'Least recently modified', 'desktop-mode' ), 'modified', 'ASC' ),
269 'slug-asc' => array( __( 'Slug A–Z', 'desktop-mode' ), 'name', 'ASC' ),
270 'slug-desc' => array( __( 'Slug Z–A', 'desktop-mode' ), 'name', 'DESC' ),
271 );
272 if ( 'media' !== $section['kind'] ) {
273 $options['comments'] = array( __( 'Most comments', 'desktop-mode' ), 'comment_count', 'DESC' );
274 $options['comments-asc'] = array( __( 'Fewest comments', 'desktop-mode' ), 'comment_count', 'ASC' );
275 }
276 return $options;
277 }
278
279 /**
280 * The (orderby, order) pair the state's `sort` resolves to.
281 *
282 * @param array<string,mixed> $section Section descriptor.
283 * @param State $state State.
284 * @return array{0:string,1:string}
285 */
286 function sort_of( array $section, State $state ) {
287 $options = sort_options( $section );
288 $picked = (string) $state->get( 'sort' );
289 $row = $options[ isset( $options[ $picked ] ) ? $picked : 'default' ];
290 return array( $row[1], $row[2] );
291 }
292