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
← All changes | includes/multisite.php +267 -57 1.1.61.1.10 View file →
@@ -11,76 +11,276 @@
11 11
12 12 defined( 'ABSPATH' ) || exit;
13 13
14 14 /**
15 + * How many of the network's sites a super admin's switcher lists beyond
16 + * their own: the network Sites screen's first page. A row, not a
17 + * directory — a larger network picks its set through the filter below.
18 + */
19 +const OPENSTATION_MULTISITE_SWITCHER_SITES = 20;
20 +
21 +/**
15 22 * The multisite block of the shell config.
16 23 *
17 24 * On a network every site is its own OpenStation, and so is the network
18 25 * admin: the block says which instance this shell is, lists every site
19 26 * the user may switch to (the overview's site switcher), and carries
20 - * the Network Admin tile's rows. Null on a single-site install;
21 - * `networkAdmin` null for users who cannot reach the network admin,
22 - * which is what keeps the tile from registering.
27 + * the Network Admin tile's rows. On a single-site install the block
28 + * comes from the OpenStation network the site belongs to, or is the
29 + * hub of (`includes/network/`), and is null outside one. `networkAdmin`
30 + * is null for users who cannot reach the network admin, which is what
31 + * keeps the tile from registering.
23 32 *
24 33 * @return array|null
25 34 */
26 35 function openstation_multisite_payload() {
27 - if ( ! is_multisite() || ! is_user_logged_in() ) {
36 + if ( ! is_user_logged_in() ) {
28 37 return null;
29 38 }
39 + if ( ! is_multisite() ) {
40 + // A single site has a switcher only as a hub or member of an
41 + // OpenStation network, and only while that module is on.
42 + if ( ! openstation_network_on() ) {
43 + return null;
44 + }
45 + $member = openstation_network_member_payload();
46 + return openstation_multisite_with_hop( null !== $member ? $member : openstation_network_hub_payload() );
47 + }
30 48
31 49 $network_admin = null;
32 50 if ( current_user_can( 'manage_network' ) ) {
33 - // Capability gates copied one for one from
34 - // `wp_admin_bar_my_sites_menu()`, so the tile can never offer a
35 - // screen the admin bar would have hidden.
36 - $network = esc_url_raw( network_admin_url() );
37 - $rows = array(
38 - array(
39 - 'title' => __( 'Dashboard', 'desktop-mode' ),
40 - 'url' => $network,
41 - ),
51 + $network_admin = array(
52 + 'url' => esc_url_raw( network_admin_url() ),
53 + 'shellUrl' => esc_url_raw( network_admin_url( 'admin.php?page=' . OPENSTATION_SHELL_PAGE_SLUG ) ),
54 + 'rows' => openstation_multisite_network_admin_rows(),
55 + 'foreign' => false,
42 56 );
43 - $gated = array(
44 - 'manage_sites' => array( 'sites.php', __( 'Sites', 'desktop-mode' ) ),
45 - 'manage_network_users' => array( 'users.php', __( 'Users', 'desktop-mode' ) ),
46 - 'manage_network_themes' => array( 'themes.php', __( 'Themes', 'desktop-mode' ) ),
47 - 'manage_network_plugins' => array( 'plugins.php', __( 'Plugins', 'desktop-mode' ) ),
48 - 'manage_network_options' => array( 'settings.php', __( 'Settings', 'desktop-mode' ) ),
49 - );
57 + }
50 58
51 - foreach ( $gated as $capability => $row ) {
52 - if ( current_user_can( $capability ) ) {
53 - $rows[] = array(
54 - 'title' => $row[1],
55 - 'url' => esc_url_raw( network_admin_url( $row[0] ) ),
56 - );
57 - }
59 + return openstation_multisite_with_hop(
60 + array(
61 + 'isNetworkAdmin' => is_network_admin(),
62 + 'networkAdmin' => $network_admin,
63 + 'current' => is_network_admin() ? 'network' : (string) get_current_blog_id(),
64 + 'sites' => openstation_multisite_sites(),
65 + )
66 + );
67 +}
68 +
69 +/**
70 + * The mint route for hop tokens, on any block that has somewhere to
71 + * hop to. See `includes/network/hop.php`.
72 + *
73 + * @param array|null $block The multisite block.
74 + * @return array|null
75 + */
76 +function openstation_multisite_with_hop( $block ) {
77 + if ( is_array( $block ) && openstation_network_on() ) {
78 + $block['hopUrl'] = esc_url_raw( rest_url( 'desktop-mode/v1/network/hop' ) );
79 + }
80 + return $block;
81 +}
82 +
83 +/**
84 + * Whether the OpenStation Network module is on, from anywhere that
85 + * runs whether or not it loaded. The bootstrap always loads; the
86 + * module does not.
87 + *
88 + * @return bool
89 + */
90 +function openstation_network_on() {
91 + return function_exists( 'openstation_network_enabled' ) && openstation_network_enabled();
92 +}
93 +
94 +/**
95 + * The Network Admin tile's rows: the network dashboard and the screens
96 + * the admin bar's Network Admin node offers, with its capability gates
97 + * copied one for one so the tile can never offer a screen the admin
98 + * bar would have hidden.
99 + *
100 + * @param bool $all Every row regardless of the current user — for the
101 + * list served to a network's members, whose users the
102 + * hub gates on arrival instead.
103 + * @return array<int,array{title:string,url:string}>
104 + */
105 +function openstation_multisite_network_admin_rows( $all = false ) {
106 + $rows = array(
107 + array(
108 + 'title' => __( 'Dashboard', 'desktop-mode' ),
109 + 'url' => esc_url_raw( network_admin_url() ),
110 + ),
111 + );
112 + $gated = array(
113 + 'manage_sites' => array( 'sites.php', __( 'Sites', 'desktop-mode' ) ),
114 + 'manage_network_users' => array( 'users.php', __( 'Users', 'desktop-mode' ) ),
115 + 'manage_network_themes' => array( 'themes.php', __( 'Themes', 'desktop-mode' ) ),
116 + 'manage_network_plugins' => array( 'plugins.php', __( 'Plugins', 'desktop-mode' ) ),
117 + 'manage_network_options' => array( 'settings.php', __( 'Settings', 'desktop-mode' ) ),
118 + );
119 + foreach ( $gated as $capability => $row ) {
120 + if ( $all || current_user_can( $capability ) ) {
121 + $rows[] = array(
122 + 'title' => $row[1],
123 + 'url' => esc_url_raw( network_admin_url( $row[0] ) ),
124 + );
58 125 }
126 + }
127 + return $rows;
128 +}
59 129
60 - $network_admin = array(
61 - 'url' => $network,
62 - 'shellUrl' => esc_url_raw( network_admin_url( 'admin.php?page=' . OPENSTATION_SHELL_PAGE_SLUG ) ),
63 - 'rows' => $rows,
64 - );
130 +/**
131 + * The network's sites by path, up to {@see OPENSTATION_MULTISITE_SWITCHER_SITES},
132 + * minus the archived, spam and deleted.
133 + *
134 + * @return array<int,string> Site names keyed by blog id.
135 + */
136 +function openstation_multisite_network_sites() {
137 + $names = array();
138 + $sites = get_sites(
139 + array(
140 + 'number' => OPENSTATION_MULTISITE_SWITCHER_SITES,
141 + 'archived' => 0,
142 + 'spam' => 0,
143 + 'deleted' => 0,
144 + 'orderby' => 'path',
145 + 'order' => 'ASC',
146 + )
147 + );
148 + foreach ( $sites as $site ) {
149 + $names[ (int) $site->blog_id ] = (string) $site->blogname;
65 150 }
151 + return $names;
152 +}
66 153
67 - return array(
68 - 'isNetworkAdmin' => is_network_admin(),
69 - 'networkAdmin' => $network_admin,
70 - 'current' => is_network_admin() ? 'network' : (string) get_current_blog_id(),
71 - 'sites' => openstation_multisite_sites(),
154 +/**
155 + * Whether OpenStation runs on a site of this network.
156 + *
157 + * @param int $blog_id The site.
158 + * @return bool
159 + */
160 +function openstation_multisite_active_on( $blog_id ) {
161 + $plugin = plugin_basename( OPENSTATION_FILE );
162 + if ( isset( get_site_option( 'active_sitewide_plugins', array() )[ $plugin ] ) ) {
163 + return true;
164 + }
165 + // Running here without being in this site's list means the plugin is
166 + // loaded some other way (an mu-plugin loader), which reaches every site.
167 + if ( ! in_array( $plugin, (array) get_option( 'active_plugins', array() ), true ) ) {
168 + return true;
169 + }
170 + return in_array( $plugin, (array) get_blog_option( $blog_id, 'active_plugins', array() ), true );
171 +}
172 +
173 +/**
174 + * My Sites inside a window: its row links follow the shell instead of
175 + * opening as tabs of the Dashboard window. Visit opens a browser tab;
176 + * this site's Dashboard goes back to Home; another site's hops to its
177 + * shell when OpenStation is active there, or opens its wp-admin in a
178 + * browser tab. The bridge leaves `_blank` and `_top` links to the
179 + * browser, and a top-level navigation is the same hop the shell takes.
180 + *
181 + * @return void
182 + */
183 +function openstation_multisite_my_sites_load() {
184 + if ( ! is_multisite() || ! openstation_is_chromeless_request() ) {
185 + return;
186 + }
187 + // Decided up front: Core runs the filter while switched to each
188 + // site, where `openstation_multisite_active_on()` would read that
189 + // site's plugin list as this one's.
190 + $current = get_current_blog_id();
191 + $active = array();
192 + foreach ( get_blogs_of_user( get_current_user_id() ) as $blog ) {
193 + $active[ (int) $blog->userblog_id ] = openstation_multisite_active_on( (int) $blog->userblog_id );
194 + }
195 + add_filter(
196 + 'myblogs_blog_actions',
197 + static function ( $actions, $user_blog ) use ( $current, $active ) {
198 + $blog_id = (int) $user_blog->userblog_id;
199 + return openstation_multisite_my_sites_actions( $actions, $blog_id, $current, ! empty( $active[ $blog_id ] ) );
200 + },
201 + 10,
202 + 2
72 203 );
73 204 }
205 +add_action( 'load-my-sites.php', 'openstation_multisite_my_sites_load' );
74 206
75 207 /**
76 - * How many of the network's sites a super admin's switcher lists beyond
77 - * their own: the network Sites screen's first page. A row, not a
78 - * directory — a larger network picks its set through the filter below.
208 + * One My Sites row's links, rewritten for the shell. Runs switched to
209 + * that site, as Core's `myblogs_blog_actions` does, and leaves markup
210 + * it does not recognise alone.
211 + *
212 + * @param string $actions The row's links.
213 + * @param int $blog_id The row's site.
214 + * @param int $current_blog_id The site whose shell the window is in.
215 + * @param bool $active Whether OpenStation runs on the row's site.
216 + * @return string
79 217 */
80 -const OPENSTATION_MULTISITE_SWITCHER_SITES = 20;
218 +function openstation_multisite_my_sites_actions( $actions, $blog_id, $current_blog_id, $active ) {
219 + $home = esc_url( home_url() );
220 + $admin = esc_url( admin_url() );
81 221
222 + if ( $blog_id === $current_blog_id ) {
223 + $dashboard = "<a href='" . esc_url( admin_url( 'index.php' ) ) . "'>";
224 + } elseif ( $active ) {
225 + $dashboard = "<a href='" . $admin . "' target='_top'>";
226 + } else {
227 + $dashboard = "<a href='" . $admin . "' target='_blank' rel='noopener'>";
228 + }
229 +
230 + return str_replace(
231 + array( "<a href='" . $home . "'>", "<a href='" . $admin . "'>" ),
232 + array( "<a href='" . $home . "' target='_blank' rel='noopener'>", $dashboard ),
233 + (string) $actions
234 + );
235 +}
236 +
82 237 /**
238 + * The network Sites list inside a window: the same rules as My Sites,
239 + * minus a current site, since the network admin is its own instance.
240 + * Visit opens a browser tab, and Dashboard hops to the site's shell
241 + * when OpenStation is active there, or opens its wp-admin in a tab.
242 + *
243 + * @return void
244 + */
245 +function openstation_multisite_sites_list_load() {
246 + if ( ! is_network_admin() || ! openstation_is_chromeless_request() ) {
247 + return;
248 + }
249 + add_filter(
250 + 'manage_sites_action_links',
251 + static function ( $actions, $blog_id ) {
252 + return openstation_multisite_sites_row_actions( (array) $actions, openstation_multisite_active_on( (int) $blog_id ) );
253 + },
254 + 10,
255 + 2
256 + );
257 +}
258 +add_action( 'load-sites.php', 'openstation_multisite_sites_list_load' );
259 +
260 +/**
261 + * One network Sites row's actions, with a browsing context on the two
262 + * links that leave the network admin. A link that already names one
263 + * is left alone.
264 + *
265 + * @param array<string,string> $actions The row's actions, keyed as Core keys them.
266 + * @param bool $active Whether OpenStation runs on the row's site.
267 + * @return array<string,string>
268 + */
269 +function openstation_multisite_sites_row_actions( array $actions, $active ) {
270 + $targets = array(
271 + 'visit' => '_blank',
272 + 'backend' => $active ? '_top' : '_blank',
273 + );
274 + foreach ( $targets as $key => $target ) {
275 + if ( isset( $actions[ $key ] ) && is_string( $actions[ $key ] ) && false === strpos( $actions[ $key ], 'target=' ) ) {
276 + $actions[ $key ] = (string) preg_replace( '/^<a /', '<a target="' . $target . '" ', $actions[ $key ], 1 );
277 + }
278 + }
279 + return $actions;
280 +}
281 +
282 +/**
83 283 * The sites the user may switch to, each with its own shell screen.
84 284 *
85 285 * The user's own sites first — `get_blogs_of_user()`, the list behind
86 286 * the admin bar's My Sites, minus the archived, spam and deleted, in
@@ -85,11 +285,12 @@
85 285 * The user's own sites first — `get_blogs_of_user()`, the list behind
86 286 * the admin bar's My Sites, minus the archived, spam and deleted, in
87 287 * the order Core keeps them — then, for a super admin, who can reach
88 288 * every site whether or not they are a member, the network's sites by
89 - * path up to {@see OPENSTATION_MULTISITE_SWITCHER_SITES}.
289 + * path up to {@see OPENSTATION_MULTISITE_SWITCHER_SITES}; then the
290 + * members of the OpenStation network this install is the hub of.
90 291 *
91 - * @return array[] Each `id` (the blog id, as a string), `name`, `shellUrl`.
292 + * @return array[] Each `id` (the blog id, or `member:<id>`), `name`, `shellUrl`.
92 293 */
93 294 function openstation_multisite_sites() {
94 295 $names = array();
95 296 foreach ( get_blogs_of_user( get_current_user_id() ) as $blog ) {
@@ -95,22 +296,11 @@
95 296 foreach ( get_blogs_of_user( get_current_user_id() ) as $blog ) {
96 297 $names[ (int) $blog->userblog_id ] = (string) $blog->blogname;
97 298 }
98 299 if ( current_user_can( 'manage_network' ) ) {
99 - $network_sites = get_sites(
100 - array(
101 - 'number' => OPENSTATION_MULTISITE_SWITCHER_SITES,
102 - 'archived' => 0,
103 - 'spam' => 0,
104 - 'deleted' => 0,
105 - 'orderby' => 'path',
106 - 'order' => 'ASC',
107 - )
108 - );
109 - foreach ( $network_sites as $site ) {
110 - $blog_id = (int) $site->blog_id;
300 + foreach ( openstation_multisite_network_sites() as $blog_id => $name ) {
111 301 if ( ! isset( $names[ $blog_id ] ) ) {
112 - $names[ $blog_id ] = (string) $site->blogname;
302 + $names[ $blog_id ] = $name;
113 303 }
114 304 }
115 305 }
116 306
@@ -119,10 +309,23 @@
119 309 $sites[] = array(
120 310 'id' => (string) $blog_id,
121 311 'name' => $name,
122 312 'shellUrl' => esc_url_raw( get_admin_url( $blog_id, 'admin.php?page=' . OPENSTATION_SHELL_PAGE_SLUG ) ),
313 + 'adminUrl' => esc_url_raw( get_admin_url( $blog_id ) ),
314 + 'active' => openstation_multisite_active_on( $blog_id ),
315 + 'kind' => 'local',
316 + 'foreign' => false,
123 317 );
124 318 }
319 + foreach ( openstation_network_on() ? openstation_network_member_entries() : array() as $member ) {
320 + $sites[] = array(
321 + 'id' => $member['id'],
322 + 'name' => $member['name'],
323 + 'shellUrl' => $member['shellUrl'],
324 + 'kind' => 'member',
325 + 'foreign' => true,
326 + );
327 + }
125 328
126 329 /**
127 330 * Filters the sites the overview's site switcher offers.
128 331 *
@@ -129,9 +332,15 @@
129 332 * Trim it on a large network, reorder it, rename an entry, or build
130 333 * a different set. A site dropped here is not offered, though the
131 334 * admin bar still reaches it.
132 335 *
133 - * @param array[] $sites Each `id` (blog id as a string), `name`, `shellUrl`.
336 + * @param array[] $sites Each `id` (blog id as a string, or `member:<id>`), `name`, `shellUrl`,
337 + * `adminUrl` and `active` (whether OpenStation runs there; the switcher
338 + * opens a site without it at `adminUrl` in a browser tab) on a site of
339 + * this network, `kind` (`local` for a site of this network, `member` for an install
340 + * that joined from elsewhere, which the switcher marks as external),
341 + * `foreign` (whether the entry is another install, which a switch to
342 + * it needs a login token for).
134 343 */
135 344 return apply_filters( 'openstation_multisite_sites', $sites );
136 345 }
137 346
@@ -158,8 +367,9 @@
158 367 'desktop_mode_share_user_decisions',
159 368 'desktop_mode_stored_files',
160 369 'desktop_mode_game_scores',
161 370 'desktop_mode_game_challenges',
371 + 'openstation_presence',
162 372 );
163 373 }
164 374
165 375 /**