| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | /** |
| 3 | - * Desktop Mode — Appearance window tab adaptations. | |
| 3 | + * OpenStation — Appearance window tab adaptations. | |
| 4 | 4 | * |
| 5 | 5 | * The Appearance dock item opens an iframe of `themes.php` and uses |
| 6 | 6 | * the in-window submenu strip (built from the WP `$submenu` global) |
| 7 | 7 | * as its tab bar. WP Core does not register `theme-install.php` as a |
| @@ -11,15 +11,14 @@ | ||
| 11 | 11 | * Inside chromeless we hide that button (it scrolled out of the |
| 12 | 12 | * iframe's visible area on first paint, leaving no entry point to |
| 13 | 13 | * the install flow — see assets/css/chromeless.css). This file |
| 14 | 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` | |
| 15 | + * Appearance window's submenu strip via the `openstation_dock_item` | |
| 16 | 16 | * filter. |
| 17 | 17 | * |
| 18 | 18 | * Resulting tab order: Appearance | Add Theme | Editor | Fonts | … |
| 19 | 19 | * |
| 20 | - * @package Desktop_Mode | |
| 21 | - * @since 0.8.2 | |
| 20 | + * @package OpenStation | |
| 22 | 21 | */ |
| 23 | 22 | |
| 24 | 23 | defined( 'ABSPATH' ) || exit; |
| 25 | 24 | |
| @@ -25,21 +24,19 @@ | ||
| 25 | 24 | |
| 26 | 25 | /** |
| 27 | 26 | * Prepends an "Add Theme" entry to the Appearance dock item's submenu. |
| 28 | 27 | * |
| 29 | - * Called once per dock item via {@see desktop_mode_build_dock_items()}. | |
| 28 | + * Called once per dock item via {@see openstation_build_dock_items()}. | |
| 30 | 29 | * Skipped for every menu other than `themes.php` and for users who |
| 31 | 30 | * lack the `install_themes` capability — matching what classic |
| 32 | 31 | * admin's "Add Theme" page-title-action enforces. |
| 33 | 32 | * |
| 34 | - * @since 0.8.2 | |
| 35 | - * | |
| 36 | 33 | * @param array $dock_item The dock item data (id, title, icon, url, |
| 37 | 34 | * badge, submenu, multi, placement, isCore). |
| 38 | 35 | * @param string $menu_slug The menu slug — e.g. `themes.php`. |
| 39 | 36 | * @return array Filtered dock item. |
| 40 | 37 | */ |
| 41 | -function desktop_mode_inject_appearance_tabs( $dock_item, $menu_slug ) { | |
| 38 | +function openstation_inject_appearance_tabs( $dock_item, $menu_slug ) { | |
| 42 | 39 | if ( 'themes.php' !== $menu_slug ) { |
| 43 | 40 | return $dock_item; |
| 44 | 41 | } |
| 45 | 42 | |
| @@ -74,11 +71,88 @@ | ||
| 74 | 71 | array_unshift( $dock_item['submenu'], $add_theme ); |
| 75 | 72 | |
| 76 | 73 | return $dock_item; |
| 77 | 74 | } |
| 78 | -add_filter( 'desktop_mode_dock_item', 'desktop_mode_inject_appearance_tabs', 10, 2 ); | |
| 75 | +add_filter( 'openstation_dock_item', 'openstation_inject_appearance_tabs', 10, 2 ); | |
| 79 | 76 | |
| 80 | 77 | /** |
| 78 | + * Renders the orientation header for the chromeless Themes workspace. | |
| 79 | + * | |
| 80 | + * Core's page heading is intentionally hidden inside OpenStation because the | |
| 81 | + * window chrome already names the screen. The Themes page still needs a little | |
| 82 | + * more context than a generic list screen, though: a theme changes the public | |
| 83 | + * face of the site, and Core's single-theme state otherwise opens directly on | |
| 84 | + * an unexplained details panel. | |
| 85 | + * | |
| 86 | + * The markup is emitted through `admin_notices`, which places it immediately | |
| 87 | + * before the page's `.wrap`. Core continues to own the theme cards, details | |
| 88 | + * overlay, actions, AJAX updates, and keyboard behavior. | |
| 89 | + * | |
| 90 | + * The screen is identified by `get_current_screen()`, not `$pagenow`. Every | |
| 91 | + * page registered with `add_theme_page()` also reports `themes.php` as its | |
| 92 | + * `$pagenow`, but its body class comes from the hook suffix | |
| 93 | + * (`appearance_page_<slug>`) and so matches none of the workspace CSS, which | |
| 94 | + * is scoped to `.themes-php`. Gating on `$pagenow` would emit this header | |
| 95 | + * unstyled on top of unrelated Appearance screens. The screen ID also keeps | |
| 96 | + * network admin (`themes-network`, a different list table entirely) out. | |
| 97 | + */ | |
| 98 | +function openstation_render_themes_workspace_intro() { | |
| 99 | + if ( ! openstation_is_chromeless_request() ) { | |
| 100 | + return; | |
| 101 | + } | |
| 102 | + | |
| 103 | + $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; | |
| 104 | + if ( ! $screen || 'themes' !== $screen->id ) { | |
| 105 | + return; | |
| 106 | + } | |
| 107 | + | |
| 108 | + /* | |
| 109 | + * Mirrors how `wp_prepare_themes_for_js()` builds its own list, without | |
| 110 | + * paying for the preparation itself. Core already called that function | |
| 111 | + * on this request before `admin-header.php` ran, and calling it again | |
| 112 | + * would re-resolve every screenshot, tag and author URL and fire | |
| 113 | + * `pre_prepare_themes_for_js` / `wp_prepare_themes_for_js` a second | |
| 114 | + * time — so any third-party callback hanging off them would run twice | |
| 115 | + * per page load. `wp_get_themes()` is cached, so this is a lookup. | |
| 116 | + */ | |
| 117 | + if ( current_user_can( 'switch_themes' ) ) { | |
| 118 | + $themes = wp_get_themes( array( 'allowed' => true ) ); | |
| 119 | + $stylesheet = get_stylesheet(); | |
| 120 | + if ( ! isset( $themes[ $stylesheet ] ) ) { | |
| 121 | + $themes[ $stylesheet ] = wp_get_theme(); | |
| 122 | + } | |
| 123 | + $theme_count = count( $themes ); | |
| 124 | + } else { | |
| 125 | + $theme_count = 1; | |
| 126 | + } | |
| 127 | + ?> | |
| 128 | + <section class="openstation-themes-intro" aria-labelledby="openstation-themes-intro-title"> | |
| 129 | + <div class="openstation-themes-intro__copy"> | |
| 130 | + <p class="openstation-themes-intro__eyebrow"><?php esc_html_e( 'Site appearance', 'desktop-mode' ); ?></p> | |
| 131 | + <h1 id="openstation-themes-intro-title"><?php esc_html_e( 'Choose how your site greets the world.', 'desktop-mode' ); ?></h1> | |
| 132 | + <p><?php esc_html_e( 'Themes shape your site’s look. Switching keeps your posts and pages in place.', 'desktop-mode' ); ?></p> | |
| 133 | + </div> | |
| 134 | + <?php | |
| 135 | + /* | |
| 136 | + * Left readable rather than labelled: `aria-label` is ignored on a | |
| 137 | + * `<p>` (the paragraph role prohibits an author-supplied name), so | |
| 138 | + * pairing it with `aria-hidden` children hid the count from | |
| 139 | + * assistive tech entirely — and Core's own `.title-count` inside | |
| 140 | + * the page `<h1>` is display:none in chromeless mode, making this | |
| 141 | + * the only count on the screen. Read in DOM order the two spans | |
| 142 | + * announce as "3 Themes installed" on their own. | |
| 143 | + */ | |
| 144 | + ?> | |
| 145 | + <p class="openstation-themes-intro__count"> | |
| 146 | + <strong><?php echo esc_html( number_format_i18n( $theme_count ) ); ?></strong> | |
| 147 | + <span><?php echo esc_html( _n( 'Theme installed', 'Themes installed', $theme_count, 'desktop-mode' ) ); ?></span> | |
| 148 | + </p> | |
| 149 | + </section> | |
| 150 | + <?php | |
| 151 | +} | |
| 152 | +add_action( 'admin_notices', 'openstation_render_themes_workspace_intro', 0 ); | |
| 153 | + | |
| 154 | +/** | |
| 81 | 155 | * Fixes the visible "active tab" state inside chromeless |
| 82 | 156 | * theme-install.php iframes. |
| 83 | 157 | * |
| 84 | 158 | * Background: WP's installer JS uses a Backbone router whose pattern |
| @@ -83,10 +157,10 @@ | ||
| 83 | 157 | * |
| 84 | 158 | * Background: WP's installer JS uses a Backbone router whose pattern |
| 85 | 159 | * `theme-install.php?browse=:sort` greedy-captures everything past |
| 86 | 160 | * `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 | |
| 161 | + * carries `openstation_chromeless=1`, so `:sort` ends up as | |
| 162 | + * `popular&openstation_chromeless=1`. WP's `view.sort()` then runs | |
| 89 | 163 | * with that polluted value and the `[data-sort]` selector finds |
| 90 | 164 | * nothing — leaving every tab in the unselected state even though |
| 91 | 165 | * the popular themes ARE the rendered listing. |
| 92 | 166 | * |
| @@ -96,18 +170,16 @@ | ||
| 96 | 170 | * chromeless flag (which would re-render the iframe with full admin |
| 97 | 171 | * chrome on next paint). |
| 98 | 172 | * |
| 99 | 173 | * 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 | |
| 174 | + * value dynamically via URLSearchParams on every check, finds the | |
| 175 | + * matching `[data-sort]` tab, and sets `current` + `aria-current` on it. | |
| 176 | + * A MutationObserver on `.filter-links` keeps the tab synced when WP's | |
| 177 | + * router fires or state changes (e.g. switching tabs to Latest or | |
| 178 | + * returning via pushState). | |
| 107 | 179 | */ |
| 108 | -function desktop_mode_theme_install_active_tab_script() { | |
| 109 | - if ( ! desktop_mode_is_chromeless_request() ) { | |
| 180 | +function openstation_theme_install_active_tab_script() { | |
| 181 | + if ( ! openstation_is_chromeless_request() ) { | |
| 110 | 182 | return; |
| 111 | 183 | } |
| 112 | 184 | if ( ! isset( $GLOBALS['pagenow'] ) || 'theme-install.php' !== $GLOBALS['pagenow'] ) { |
| 113 | 185 | return; |
| @@ -114,13 +186,19 @@ | ||
| 114 | 186 | } |
| 115 | 187 | |
| 116 | 188 | $js = <<<'JS' |
| 117 | 189 | ( function () { |
| 118 | - var browseParam = new URLSearchParams( window.location.search ).get( 'browse' ); | |
| 119 | - if ( ! browseParam ) { | |
| 190 | + function getBrowseParam() { | |
| 191 | + return new URLSearchParams( window.location.search ).get( 'browse' ); | |
| 192 | + } | |
| 193 | + if ( ! getBrowseParam() ) { | |
| 120 | 194 | return; |
| 121 | 195 | } |
| 122 | 196 | function applyActiveTab() { |
| 197 | + var browseParam = getBrowseParam(); | |
| 198 | + if ( ! browseParam ) { | |
| 199 | + return true; | |
| 200 | + } | |
| 123 | 201 | var match = document.querySelector( |
| 124 | 202 | '.filter-links li > a[data-sort="' + browseParam + '"]' |
| 125 | 203 | ); |
| 126 | 204 | if ( ! match ) { |
| @@ -178,5 +256,5 @@ | ||
| 178 | 256 | JS; |
| 179 | 257 | |
| 180 | 258 | wp_print_inline_script_tag( $js ); |
| 181 | 259 | } |
| 182 | -add_action( 'admin_footer', 'desktop_mode_theme_install_active_tab_script', 100 ); | |
| 260 | +add_action( 'admin_footer', 'openstation_theme_install_active_tab_script', 100 ); | |