| 1 |
<?php |
| 2 |
/** |
| 3 |
* OpenStation — The built-in "Legacy" desktop theme. |
| 4 |
* |
| 5 |
* Legacy is OpenStation's own default look, written down: every |
| 6 |
* design token the shell and the `<os-*>` component kit read, at the |
| 7 |
* value it resolved to with no theme active when the snapshot was |
| 8 |
* taken. Wearing it changes (almost) nothing — that is the point. It |
| 9 |
* exists so a theme author can read the whole palette in one file, |
| 10 |
* fork it, and change the ten values they care about instead of |
| 11 |
* rediscovering 377 fallback literals scattered across the |
| 12 |
* stylesheets. |
| 13 |
* |
| 14 |
* It ships as data, in `assets/desktop-themes/legacy/theme.json` — |
| 15 |
* the same `theme.json` an uploaded ZIP carries, registered through |
| 16 |
* the same public API a plugin would use |
| 17 |
* ({@see openstation_register_desktop_theme()}) and put through the |
| 18 |
* same sanitizer. `bin/package-legacy-theme.sh` zips that directory |
| 19 |
* into the distributable a user could hand to someone else. |
| 20 |
* |
| 21 |
* ## It is frozen |
| 22 |
* |
| 23 |
* The manifest is a snapshot and stays one. It was collected from the |
| 24 |
* stylesheets once and is plain data from here on — nothing |
| 25 |
* regenerates it, not this file, not the build, not CI, and there is |
| 26 |
* deliberately no tool that could. When the shell's own defaults move |
| 27 |
* on, Legacy goes on declaring what it declares today, which is the |
| 28 |
* whole reason someone would wear it: they asked for the old look and |
| 29 |
* they keep it. Drifting it with the code would quietly turn the theme |
| 30 |
* into a no-op again. A second snapshot, if one is ever wanted, is a |
| 31 |
* NEW theme under a new id — never a rewrite of this one. |
| 32 |
* |
| 33 |
* Because it is code-registered rather than uploaded, it is always |
| 34 |
* present and cannot be deleted: the delete route only ever touches |
| 35 |
* the uploaded index ({@see openstation_desktop_theme_delete()}). A |
| 36 |
* site that genuinely does not want it calls |
| 37 |
* `openstation_unregister_desktop_theme( 'desktop-mode/legacy' )` on |
| 38 |
* `init` at a priority above 5. |
| 39 |
* |
| 40 |
* ## What Legacy deliberately does NOT declare |
| 41 |
* |
| 42 |
* Three families, each because naming a literal would make the theme |
| 43 |
* differ from the unthemed shell rather than reproduce it: |
| 44 |
* |
| 45 |
* - Anything that follows `--wp-admin-theme-color`. The accent, the |
| 46 |
* focused title bar, the window-link splines and the selection |
| 47 |
* ring track the user's WordPress admin colour scheme; a hex here |
| 48 |
* would pin every scheme to Fresh blue. |
| 49 |
* - Context-dependent tokens — `--os-fg`, |
| 50 |
* `--os-tooltip-bg` and friends read light on the desk |
| 51 |
* and dark inside a window, so one value breaks one of the two. |
| 52 |
* - Derived sizes (the badge family) and the texture slots, which |
| 53 |
* are written by the manifest's `textures` block, not `tokens`. |
| 54 |
* |
| 55 |
* @package OpenStation |
| 56 |
*/ |
| 57 |
|
| 58 |
defined( 'ABSPATH' ) || exit; |
| 59 |
|
| 60 |
/** |
| 61 |
* Manifest id of the built-in Legacy theme. |
| 62 |
* |
| 63 |
* The VALUE keeps its pre-rebrand spelling on purpose: it is a |
| 64 |
* persisted or externally-visible identifier, so renaming it would |
| 65 |
* orphan data already written by live installs (or break a live |
| 66 |
* URL). The mismatch between this constant's name and its value is |
| 67 |
* deliberate — it is NOT a half-finished rename. |
| 68 |
*/ |
| 69 |
const OPENSTATION_LEGACY_THEME_ID = 'desktop-mode/legacy'; |
| 70 |
|
| 71 |
/** |
| 72 |
* Absolute path of the Legacy theme's `theme.json`. |
| 73 |
* |
| 74 |
* @return string |
| 75 |
*/ |
| 76 |
function openstation_legacy_theme_manifest_path() { |
| 77 |
/** |
| 78 |
* Filters the path the built-in Legacy theme's manifest is read |
| 79 |
* from. A site that has forked the token set can point this at |
| 80 |
* its own `theme.json` without touching the registration. |
| 81 |
* |
| 82 |
* @param string $path Absolute path to a `theme.json`. |
| 83 |
*/ |
| 84 |
return (string) apply_filters( |
| 85 |
'openstation_legacy_theme_manifest_path', |
| 86 |
OPENSTATION_DIR . 'assets/desktop-themes/legacy/theme.json' |
| 87 |
); |
| 88 |
} |
| 89 |
|
| 90 |
/** |
| 91 |
* Read the Legacy theme's token map off disk. |
| 92 |
* |
| 93 |
* Statically cached: the manifest is data that cannot change inside a |
| 94 |
* request, and the file is read at most once even if something calls |
| 95 |
* the registration twice. |
| 96 |
* |
| 97 |
* @return array<string,string> Map of custom property => value, or an |
| 98 |
* empty array when the file is missing |
| 99 |
* or unreadable. |
| 100 |
*/ |
| 101 |
function openstation_legacy_theme_tokens() { |
| 102 |
$manifest = openstation_legacy_theme_manifest(); |
| 103 |
return isset( $manifest['tokens'] ) && is_array( $manifest['tokens'] ) |
| 104 |
? $manifest['tokens'] |
| 105 |
: array(); |
| 106 |
} |
| 107 |
|
| 108 |
/** |
| 109 |
* Read the Legacy theme's manifest off disk. |
| 110 |
* |
| 111 |
* Statically cached: the manifest is data that cannot change inside a |
| 112 |
* request, and the file is read at most once even if something calls |
| 113 |
* the registration twice. |
| 114 |
* |
| 115 |
* @return array Decoded manifest, or an empty array when the file is |
| 116 |
* missing or unreadable. |
| 117 |
*/ |
| 118 |
function openstation_legacy_theme_manifest() { |
| 119 |
static $manifest = null; |
| 120 |
if ( null !== $manifest ) { |
| 121 |
return $manifest; |
| 122 |
} |
| 123 |
|
| 124 |
$manifest = array(); |
| 125 |
$path = openstation_legacy_theme_manifest_path(); |
| 126 |
if ( ! is_readable( $path ) ) { |
| 127 |
return $manifest; |
| 128 |
} |
| 129 |
|
| 130 |
$decoded = wp_json_file_decode( $path, array( 'associative' => true ) ); |
| 131 |
if ( is_array( $decoded ) ) { |
| 132 |
$manifest = $decoded; |
| 133 |
} |
| 134 |
return $manifest; |
| 135 |
} |
| 136 |
|
| 137 |
/** |
| 138 |
* Register the built-in desktop themes. |
| 139 |
* |
| 140 |
* Priority 5 on `init`, the same slot the built-in wallpapers use, so |
| 141 |
* the theme is in the registry before the shell config is built and |
| 142 |
* before any third-party plugin reacting to |
| 143 |
* `openstation_desktop_theme_registered` runs. |
| 144 |
* |
| 145 |
* The name and description are duplicated between here and the |
| 146 |
* manifest on purpose: PHP's copy is translatable, the manifest's is |
| 147 |
* what a user sees if they install the ZIP on a site that does not |
| 148 |
* run OpenStation's own registration. Keep the two in step. |
| 149 |
* |
| 150 |
* @return void |
| 151 |
*/ |
| 152 |
function openstation_register_builtin_desktop_themes() { |
| 153 |
$manifest = openstation_legacy_theme_manifest(); |
| 154 |
$tokens = openstation_legacy_theme_tokens(); |
| 155 |
if ( empty( $tokens ) ) { |
| 156 |
return; |
| 157 |
} |
| 158 |
|
| 159 |
/* |
| 160 |
* The one thing Legacy recommends: the WordPress blue accent it was |
| 161 |
* drawn against. The accent is a user setting the station's palette |
| 162 |
* cannot reach — it is written as an inline style — so without this |
| 163 |
* the old chrome would come back wearing Pulse focus rings, and the |
| 164 |
* "Apply recommended layout and effects" button in OS Settings → |
| 165 |
* Themes would have nothing to offer. |
| 166 |
*/ |
| 167 |
$recommended = isset( $manifest['recommendedOsSettings'] ) && is_array( $manifest['recommendedOsSettings'] ) |
| 168 |
? $manifest['recommendedOsSettings'] |
| 169 |
: array(); |
| 170 |
|
| 171 |
openstation_register_desktop_theme( |
| 172 |
OPENSTATION_LEGACY_THEME_ID, |
| 173 |
array( |
| 174 |
'name' => __( 'Desktop Mode (Legacy)', 'desktop-mode' ), |
| 175 |
'version' => '1.0.0', |
| 176 |
'author' => 'OpenStation', |
| 177 |
'description' => __( 'The look Desktop Mode had before the OpenStation brand: every design token at the value it resolved to then. Wear it to put the old palette back, or fork it as the starting point for a theme of your own.', 'desktop-mode' ), |
| 178 |
// A code theme's assets are URLs it already serves. The |
| 179 |
// artwork is the theme previewing itself: desk, dock and |
| 180 |
// one window, painted in the tokens below. |
| 181 |
'preview' => OPENSTATION_URL . 'assets/desktop-themes/legacy/preview.svg', |
| 182 |
'tokens' => $tokens, |
| 183 |
'recommendedOsSettings' => $recommended, |
| 184 |
) |
| 185 |
); |
| 186 |
} |
| 187 |
|
| 188 |
/* |
| 189 |
* Gated the same way the admin-rendering modules are: nothing on a |
| 190 |
* frontend page view can consult the theme registry, and reading + |
| 191 |
* sanitizing + compiling 377 tokens for a request that will never |
| 192 |
* render the shell is pure waste. |
| 193 |
*/ |
| 194 |
if ( openstation_request_needs_admin_modules() ) { |
| 195 |
add_action( 'init', 'openstation_register_builtin_desktop_themes', 5 ); |
| 196 |
} |
| 197 |
|