PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.0.1
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.0.1
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 / themes-tabs.php

themes-tabs.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.0.1, at includes/themes-tabs.php

184 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OpenStation — Appearance window tab adaptations.
4 *
5 * The Appearance dock item opens an iframe of `themes.php` and uses
6 * the in-window submenu strip (built from the WP `$submenu` global)
7 * as its tab bar. WP Core does not register `theme-install.php` as a
8 * submenu of `themes.php`; classic admin instead surfaces it through
9 * the in-page "Add Theme" `.page-title-action` button.
10 *
11 * Inside chromeless we hide that button (it scrolled out of the
12 * iframe's visible area on first paint, leaving no entry point to
13 * the install flow — see assets/css/chromeless.css). This file
14 * compensates by injecting an "Add Theme" tab at the front of the
15 * Appearance window's submenu strip via the `openstation_dock_item`
16 * filter.
17 *
18 * Resulting tab order: Appearance | Add Theme | Editor | Fonts | …
19 *
20 * @package OpenStation
21 */
22
23 defined( 'ABSPATH' ) || exit;
24
25 /**
26 * Prepends an "Add Theme" entry to the Appearance dock item's submenu.
27 *
28 * Called once per dock item via {@see openstation_build_dock_items()}.
29 * Skipped for every menu other than `themes.php` and for users who
30 * lack the `install_themes` capability — matching what classic
31 * admin's "Add Theme" page-title-action enforces.
32 *
33 * @param array $dock_item The dock item data (id, title, icon, url,
34 * badge, submenu, multi, placement, isCore).
35 * @param string $menu_slug The menu slug — e.g. `themes.php`.
36 * @return array Filtered dock item.
37 */
38 function openstation_inject_appearance_tabs( $dock_item, $menu_slug ) {
39 if ( 'themes.php' !== $menu_slug ) {
40 return $dock_item;
41 }
42
43 if ( ! current_user_can( 'install_themes' ) ) {
44 return $dock_item;
45 }
46
47 if ( ! isset( $dock_item['submenu'] ) || ! is_array( $dock_item['submenu'] ) ) {
48 $dock_item['submenu'] = array();
49 }
50
51 // `?browse=popular` lands on the Popular tab (the JS installer
52 // router would default-redirect to this anyway when no sort is
53 // specified — passing it explicitly makes the intent visible
54 // AND avoids the brief flash where WP's Backbone router rewrites
55 // the URL after first paint, which inside the iframe re-arms the
56 // chromeless body class flicker). Other valid values: `new`
57 // (Latest), `block-themes`, `favorites`.
58 $add_theme = array(
59 'title' => __( 'Add Theme', 'desktop-mode' ),
60 'url' => admin_url( 'theme-install.php?browse=popular' ),
61 );
62
63 // Idempotent: skip if a previous filter pass (or another plugin)
64 // already added an entry pointing at theme-install.php.
65 foreach ( $dock_item['submenu'] as $existing ) {
66 if ( ! empty( $existing['url'] ) && false !== strpos( $existing['url'], 'theme-install.php' ) ) {
67 return $dock_item;
68 }
69 }
70
71 array_unshift( $dock_item['submenu'], $add_theme );
72
73 return $dock_item;
74 }
75 add_filter( 'openstation_dock_item', 'openstation_inject_appearance_tabs', 10, 2 );
76
77 /**
78 * Fixes the visible "active tab" state inside chromeless
79 * theme-install.php iframes.
80 *
81 * Background: WP's installer JS uses a Backbone router whose pattern
82 * `theme-install.php?browse=:sort` greedy-captures everything past
83 * `browse=` ([^/?]+ matches `&` too). Inside chromeless the URL also
84 * carries `openstation_chromeless=1`, so `:sort` ends up as
85 * `popular&openstation_chromeless=1`. WP's `view.sort()` then runs
86 * with that polluted value and the `[data-sort]` selector finds
87 * nothing — leaving every tab in the unselected state even though
88 * the popular themes ARE the rendered listing.
89 *
90 * Reordering query params or re-encoding doesn't help: any URL that
91 * exposes `browse=` to the router matches the route, and any URL
92 * that hides it falls through to a redirect that strips the
93 * chromeless flag (which would re-render the iframe with full admin
94 * chrome on next paint).
95 *
96 * The minimum-viable shim: inline JS that reads the real `browse`
97 * value dynamically via URLSearchParams on every check, finds the
98 * matching `[data-sort]` tab, and sets `current` + `aria-current` on it.
99 * A MutationObserver on `.filter-links` keeps the tab synced when WP's
100 * router fires or state changes (e.g. switching tabs to Latest or
101 * returning via pushState).
102 */
103 function openstation_theme_install_active_tab_script() {
104 if ( ! openstation_is_chromeless_request() ) {
105 return;
106 }
107 if ( ! isset( $GLOBALS['pagenow'] ) || 'theme-install.php' !== $GLOBALS['pagenow'] ) {
108 return;
109 }
110
111 $js = <<<'JS'
112 ( function () {
113 function getBrowseParam() {
114 return new URLSearchParams( window.location.search ).get( 'browse' );
115 }
116 if ( ! getBrowseParam() ) {
117 return;
118 }
119 function applyActiveTab() {
120 var browseParam = getBrowseParam();
121 if ( ! browseParam ) {
122 return true;
123 }
124 var match = document.querySelector(
125 '.filter-links li > a[data-sort="' + browseParam + '"]'
126 );
127 if ( ! match ) {
128 return false;
129 }
130 var tabs = document.querySelectorAll( '.filter-links li > a[data-sort]' );
131 var alreadyCorrect =
132 match.classList.contains( 'current' ) &&
133 Array.prototype.every.call( tabs, function ( a ) {
134 return a === match || ! a.classList.contains( 'current' );
135 } );
136 if ( alreadyCorrect ) {
137 return true;
138 }
139 Array.prototype.forEach.call( tabs, function ( a ) {
140 a.classList.remove( 'current' );
141 a.removeAttribute( 'aria-current' );
142 } );
143 match.classList.add( 'current' );
144 match.setAttribute( 'aria-current', 'page' );
145 return true;
146 }
147 function init() {
148 if ( ! applyActiveTab() ) {
149 window.requestAnimationFrame( init );
150 return;
151 }
152 var container = document.querySelector( '.filter-links' );
153 if ( ! container ) {
154 return;
155 }
156 var pending = false;
157 var observer = new MutationObserver( function () {
158 if ( pending ) {
159 return;
160 }
161 pending = true;
162 window.requestAnimationFrame( function () {
163 pending = false;
164 applyActiveTab();
165 } );
166 } );
167 observer.observe( container, {
168 attributes: true,
169 subtree: true,
170 attributeFilter: [ 'class', 'aria-current' ],
171 } );
172 }
173 if ( document.readyState === 'loading' ) {
174 document.addEventListener( 'DOMContentLoaded', init );
175 } else {
176 init();
177 }
178 } )();
179 JS;
180
181 wp_print_inline_script_tag( $js );
182 }
183 add_action( 'admin_footer', 'openstation_theme_install_active_tab_script', 100 );
184