PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.9
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.9
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 0.8.6 All 33 releases
desktop-mode / includes / my-wordpress / owner.php

owner.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.9, at includes/my-wordpress/owner.php

261 lines 8.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OpenStation — My WordPress: post-type ownership → folder groups.
4 *
5 * Custom post types are grouped in the site window by whoever
6 * registered them: a plugin's CPTs share one folder named after the
7 * plugin, a theme's CPTs share a folder named after the theme, and
8 * anything unattributable stays loose at the root alongside Posts and
9 * Pages.
10 *
11 * Attribution reads the registration-time file path captured by
12 * `openstation_record_type_registrant()` (`includes/core/payload.php`),
13 * which hooks `registered_post_type` and walks the backtrace to the
14 * first frame inside an extension directory.
15 *
16 * Display names are resolved WITHOUT `get_plugins()`. That function
17 * lives in `wp-admin/includes/plugin.php`, which Core only loads after
18 * `init` has already run — and it scans the whole plugins directory,
19 * which is not a cost worth paying on every admin request. Plugin
20 * headers are read straight from the main file with `get_file_data()`
21 * (first 8 KB only) and themes via `wp_get_theme()`, both of which live
22 * in `wp-includes` and are always available.
23 *
24 * Filterable surface:
25 *
26 * - `openstation_my_wordpress_post_type_group`
27 * - `openstation_my_wordpress_post_type_groups`
28 *
29 * @package OpenStation
30 */
31
32 defined( 'ABSPATH' ) || exit;
33
34 /**
35 * Resolve the folder group a post type belongs to.
36 *
37 * @param string $post_type Post type slug.
38 * @return array|null Group descriptor with `id`, `label`, `icon`, and
39 * `order` keys, or null when the type should sit
40 * loose at the root.
41 */
42 function openstation_my_wordpress_post_type_group( $post_type ) {
43 $group = null;
44
45 if ( function_exists( 'openstation_type_registrant_file' ) ) {
46 $file = openstation_type_registrant_file( (string) $post_type, 'post_type' );
47 if ( null !== $file ) {
48 $group = openstation_my_wordpress_group_for_path( $file );
49 }
50 }
51
52 /**
53 * Filter the folder group resolved for a single post type.
54 *
55 * Return null to pull the type out of its folder and render it
56 * loose at the root of the site window, or return a descriptor
57 * (`id`, `label`, `icon`, `order`) to override the attribution —
58 * useful for a suite of plugins that should share one folder.
59 *
60 * **Status: Experimental** — the descriptor may gain fields.
61 *
62 * @param array|null $group Resolved group descriptor, or null.
63 * @param string $post_type Post type slug.
64 */
65 $group = apply_filters( 'openstation_my_wordpress_post_type_group', $group, $post_type );
66
67 return is_array( $group ) && ! empty( $group['id'] ) ? $group : null;
68 }
69
70 /**
71 * Map an absolute registration path to a group descriptor.
72 *
73 * @param string $file Normalized absolute path.
74 * @return array|null Group descriptor or null.
75 */
76 function openstation_my_wordpress_group_for_path( $file ) {
77 $path = wp_normalize_path( (string) $file );
78 if ( '' === $path ) {
79 return null;
80 }
81
82 // mu-plugins first: on some installs `WPMU_PLUGIN_DIR` sits inside
83 // a path that also prefix-matches a theme root, and a mu-plugin is
84 // the more specific answer.
85 if ( defined( 'WPMU_PLUGIN_DIR' ) ) {
86 $mu_dir = trailingslashit( wp_normalize_path( WPMU_PLUGIN_DIR ) );
87 if ( 0 === strpos( $path, $mu_dir ) ) {
88 $rel = ltrim( substr( $path, strlen( $mu_dir ) ), '/' );
89 $slug = ( false !== strpos( $rel, '/' ) ) ? strtok( $rel, '/' ) : $rel;
90 if ( '' === $slug ) {
91 return null;
92 }
93 $name = openstation_my_wordpress_plugin_header_name( $mu_dir . $rel );
94 return array(
95 'id' => 'mu-plugin:' . $slug,
96 'label' => '' !== $name ? $name : $slug,
97 'icon' => 'dashicons-admin-plugins',
98 'order' => 20,
99 );
100 }
101 }
102
103 if ( defined( 'WP_PLUGIN_DIR' ) ) {
104 $plugins_dir = trailingslashit( wp_normalize_path( WP_PLUGIN_DIR ) );
105 if ( 0 === strpos( $path, $plugins_dir ) ) {
106 $rel = ltrim( substr( $path, strlen( $plugins_dir ) ), '/' );
107 $folder = ( false !== strpos( $rel, '/' ) ) ? strtok( $rel, '/' ) : '';
108 if ( '' === $folder ) {
109 // Single-file plugin living directly in `plugins/`.
110 $name = openstation_my_wordpress_plugin_header_name( $plugins_dir . $rel );
111 return array(
112 'id' => 'plugin:' . $rel,
113 'label' => '' !== $name ? $name : $rel,
114 'icon' => 'dashicons-admin-plugins',
115 'order' => 20,
116 );
117 }
118 return array(
119 'id' => 'plugin:' . $folder,
120 'label' => openstation_my_wordpress_plugin_folder_name( $folder ),
121 'icon' => 'dashicons-admin-plugins',
122 'order' => 20,
123 );
124 }
125 }
126
127 foreach ( (array) get_theme_roots() as $theme_root ) {
128 $root = trailingslashit( wp_normalize_path( get_theme_root( (string) $theme_root ) ) );
129 if ( 0 !== strpos( $path, $root ) ) {
130 continue;
131 }
132 $rel = ltrim( substr( $path, strlen( $root ) ), '/' );
133 $stylesheet = ( false !== strpos( $rel, '/' ) ) ? strtok( $rel, '/' ) : $rel;
134 if ( '' === $stylesheet ) {
135 continue;
136 }
137 $theme = wp_get_theme( $stylesheet );
138 $name = $theme->exists() ? (string) $theme->get( 'Name' ) : '';
139 return array(
140 'id' => 'theme:' . $stylesheet,
141 'label' => '' !== $name ? $name : $stylesheet,
142 'icon' => 'dashicons-admin-appearance',
143 'order' => 30,
144 );
145 }
146
147 return null;
148 }
149
150 /**
151 * Human-readable name for an active plugin folder.
152 *
153 * Matches the folder against `wp_get_active_and_valid_plugins()` — which
154 * returns absolute paths to each active plugin's main file and is
155 * available on every request — then reads the `Plugin Name` header from
156 * that file. Falls back to the folder slug when the plugin isn't active
157 * or carries no header (a CPT registered from an inactive plugin folder
158 * is possible via `require`).
159 *
160 * @param string $folder Plugin folder name.
161 * @return string Display name.
162 */
163 function openstation_my_wordpress_plugin_folder_name( $folder ) {
164 $slug = (string) $folder;
165 if ( '' === $slug ) {
166 return '';
167 }
168
169 static $cache = array();
170 if ( isset( $cache[ $slug ] ) ) {
171 return $cache[ $slug ];
172 }
173
174 $name = '';
175 foreach ( (array) wp_get_active_and_valid_plugins() as $main_file ) {
176 $norm = wp_normalize_path( (string) $main_file );
177 $rel = ltrim( str_replace( trailingslashit( wp_normalize_path( WP_PLUGIN_DIR ) ), '', $norm ), '/' );
178 if ( 0 !== strpos( $rel, $slug . '/' ) ) {
179 continue;
180 }
181 $name = openstation_my_wordpress_plugin_header_name( $norm );
182 break;
183 }
184
185 $cache[ $slug ] = '' !== $name ? $name : $slug;
186 return $cache[ $slug ];
187 }
188
189 /**
190 * Read the `Plugin Name` header from a plugin file.
191 *
192 * @param string $file Absolute path to a PHP file.
193 * @return string Plugin name, or an empty string.
194 */
195 function openstation_my_wordpress_plugin_header_name( $file ) {
196 if ( ! is_string( $file ) || '' === $file || ! is_readable( $file ) ) {
197 return '';
198 }
199 $data = get_file_data( $file, array( 'Name' => 'Plugin Name' ), 'plugin' );
200 return isset( $data['Name'] ) ? trim( (string) $data['Name'] ) : '';
201 }
202
203 /**
204 * Collect the distinct groups referenced by a set of entity
205 * descriptors, in render order.
206 *
207 * The bundle renders one folder tile per entry and drills into it via
208 * the `group` route. Ordering is `order` ascending, then label, so
209 * plugin folders cluster ahead of theme folders.
210 *
211 * @param array[] $entities Entity descriptors.
212 * @return array[] Group descriptors, ordered.
213 */
214 function openstation_my_wordpress_collect_groups( $entities ) {
215 $groups = array();
216
217 foreach ( (array) $entities as $entity ) {
218 if ( empty( $entity['group'] ) ) {
219 continue;
220 }
221 $id = (string) $entity['group'];
222 if ( isset( $groups[ $id ] ) ) {
223 continue;
224 }
225 $groups[ $id ] = array(
226 'id' => $id,
227 'label' => isset( $entity['groupLabel'] ) ? (string) $entity['groupLabel'] : $id,
228 'icon' => isset( $entity['groupIcon'] ) ? (string) $entity['groupIcon'] : 'dashicons-admin-plugins',
229 'order' => isset( $entity['groupOrder'] ) ? (int) $entity['groupOrder'] : 20,
230 );
231 }
232
233 $groups = array_values( $groups );
234 usort(
235 $groups,
236 static function ( $a, $b ) {
237 if ( $a['order'] === $b['order'] ) {
238 return strnatcasecmp( $a['label'], $b['label'] );
239 }
240 return $a['order'] < $b['order'] ? -1 : 1;
241 }
242 );
243
244 /**
245 * Filter the folder groups shown at the root of the site window.
246 *
247 * Each entry declares `id`, `label`, `icon`, and `order`. Removing
248 * an entry does not hide its post types — they fall back to
249 * rendering loose at the root. To move a type between folders,
250 * use `openstation_my_wordpress_post_type_group` instead.
251 *
252 * **Status: Experimental** — the descriptor may gain fields.
253 *
254 * @param array[] $groups Ordered group descriptors.
255 * @param array[] $entities The entity descriptors they were derived from.
256 */
257 $filtered = apply_filters( 'openstation_my_wordpress_post_type_groups', $groups, $entities );
258
259 return is_array( $filtered ) ? array_values( $filtered ) : $groups;
260 }
261