PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 0.8.7
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v0.8.7
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 0.8.7, at includes/themes-tabs.php

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