PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.12
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.12
1.1.12 1.1.11 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 All 36 releases
desktop-mode / includes / registries / wallpapers.php

wallpapers.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.12, at includes/registries/wallpapers.php

294 lines 11.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OpenStation — Wallpapers registry.
4 *
5 * Server-side registration API + payload builder + asset enqueue
6 * for the desktop wallpaper picker. Wallpaper definitions live
7 * on `window.openStationWallpapers[ id ]` (set by the plugin's
8 * own JS); this module is the PHP side that announces them to
9 * the shell and ships their script handles into the boot
10 * payload.
11 *
12 * Extracted from `components.php` during the architecture-0.8.1
13 * PHP slicing (phase 6). Behaviour, function names, filter
14 * contracts, and error codes all unchanged.
15 *
16 * @package OpenStation
17 */
18
19 defined( 'ABSPATH' ) || exit;
20
21 /**
22 * Register a server-side desktop wallpaper. Symmetrical to
23 * {@see openstation_register_widget()}. The plugin's JS side
24 * publishes the full `WallpaperDef` (with mount / resolveValue /
25 * renderEditor callbacks as appropriate) on
26 * `window.openStationWallpapers[ <id> ]`; the shell loads the
27 * declared script, reads that global, and registers the def via
28 * the normal wallpaper registry. Deactivation unregisters the
29 * def and re-applies the current selection (which falls back to
30 * a built-in if the user's active wallpaper was the one leaving).
31 *
32 * Example:
33 *
34 * ```php
35 * openstation_register_wallpaper( 'myplugin/snow', array(
36 * 'label' => __( 'Snow', 'my-plugin' ),
37 * 'preview' => 'linear-gradient(#fff, #ddd)',
38 * 'type' => 'canvas',
39 * 'script' => 'my-plugin-snow-wallpaper',
40 * ) );
41 * ```
42 *
43 * ```js
44 * // Inside my-plugin-snow-wallpaper.js
45 * window.openStationWallpapers = window.openStationWallpapers || {};
46 * window.openStationWallpapers[ 'myplugin/snow' ] = {
47 * id: 'myplugin/snow',
48 * label: 'Snow',
49 * type: 'canvas',
50 * preview: 'linear-gradient(#fff, #ddd)',
51 * needs: [ 'pixijs' ],
52 * mount: function ( container, ctx ) { return function () {}; },
53 * };
54 * ```
55 *
56 * @param string $id Wallpaper id. For canvas wallpapers this must
57 * match the `window.openStationWallpapers[<id>]`
58 * key the plugin's JS publishes.
59 * @param array $args {
60 * @type string $label Picker label. Required.
61 * @type string $preview CSS value rendered in the picker
62 * swatch (gradient, color,
63 * `url(...)`, etc.). Required.
64 * @type string $type 'css' | 'canvas'. Default 'canvas'.
65 * @type string $value CSS value applied to the wallpaper
66 * surface (only relevant for `css`
67 * type — canvas wallpapers paint in
68 * JS). Defaults to `preview` so a
69 * single string covers the common
70 * case where swatch and surface are
71 * identical.
72 * @type string $script Enqueued script handle that
73 * publishes the def on the global.
74 * Required for `canvas` type;
75 * optional for `css`.
76 * @type string $description Plain-text description shown in OS
77 * Settings when the wallpaper is the
78 * active selection — what it is, where
79 * its data comes from, the story behind
80 * it. Optional.
81 * @type string $tone 'light' | 'dark'. Whether the desk
82 * paints its icons and labels in
83 * Starlight or in Void. Optional;
84 * unset reads as 'dark'. Declare
85 * 'light' if a user would call your
86 * surface pale. See
87 * docs/desktop-themes.md.
88 * @type string[] $capabilities Gate: ALL caps must match. Any
89 * missed cap returns
90 * `WP_Error openstation_capability_denied`.
91 * }
92 * @return true|WP_Error `true` on success; `WP_Error` otherwise.
93 */
94 function openstation_register_wallpaper( $id, $args = array() ) {
95 $id = (string) $id;
96 if ( '' === $id ) {
97 return openstation_registration_error(
98 'openstation_missing_id',
99 __( 'Wallpaper id is required.', 'desktop-mode' )
100 );
101 }
102
103 $defaults = array(
104 'label' => '',
105 'preview' => '',
106 'type' => 'canvas',
107 'value' => '',
108 'script' => '',
109 'description' => '',
110 'tone' => '',
111 'capabilities' => array(),
112 );
113 $args = wp_parse_args( $args, $defaults );
114
115 foreach ( (array) $args['capabilities'] as $cap ) {
116 if ( ! current_user_can( (string) $cap ) ) {
117 return openstation_registration_error(
118 'openstation_capability_denied',
119 sprintf(
120 /* translators: %s: capability slug. */
121 __( 'Current user lacks the %s capability required to register this wallpaper.', 'desktop-mode' ),
122 (string) $cap
123 ),
124 array(
125 'capability' => (string) $cap,
126 'id' => $id,
127 )
128 );
129 }
130 }
131 if ( '' === (string) $args['label'] ) {
132 return openstation_registration_error(
133 'openstation_missing_label',
134 __( 'Wallpaper registration requires a non-empty `label`.', 'desktop-mode' ),
135 array( 'id' => $id )
136 );
137 }
138 $type = in_array( $args['type'], array( 'css', 'canvas' ), true )
139 ? $args['type']
140 : 'canvas';
141 // Canvas wallpapers always need a script (the def with its
142 // `mount` callback is published on the JS global by that
143 // script). CSS wallpapers can skip the script — the shell can
144 // render from the `value` / `preview` string alone.
145 if ( 'canvas' === $type && '' === (string) $args['script'] ) {
146 return openstation_registration_error(
147 'openstation_missing_script',
148 __( 'Canvas wallpaper registration requires a `script` handle that publishes the def.', 'desktop-mode' ),
149 array( 'id' => $id )
150 );
151 }
152
153 // `value` defaults to `preview` when omitted — the common case
154 // for a plain gradient/solid where the swatch and the surface
155 // render the same CSS. Authors can split them (e.g. static
156 // swatch preview + animated value) by passing both.
157 $value = (string) $args['value'];
158 if ( '' === $value ) {
159 $value = (string) $args['preview'];
160 }
161
162 $entry = array(
163 'id' => $id,
164 // Plain text by contract, same as `description` below. The
165 // shell paints labels through the `html` tagged template, whose
166 // text slots build DOM with `createTextNode()` — never
167 // `innerHTML` — so a label cannot become markup downstream.
168 //
169 // Note this STRIPS rather than ESCAPES, and that distinction is
170 // load-bearing: `esc_html()` here would encode `&` in a
171 // perfectly ordinary label ("Black & White") and the text node
172 // would then render the entity literally as `&amp;`. Escaping
173 // belongs at an HTML boundary; there isn't one on this path.
174 'label' => sanitize_text_field( (string) $args['label'] ),
175 'preview' => (string) $args['preview'],
176 'type' => $type,
177 'value' => $value,
178 'script' => (string) $args['script'],
179 // Plain text by contract — the shell renders it as text, never
180 // as HTML, so strip tags here rather than trusting every caller.
181 'description' => sanitize_textarea_field( (string) $args['description'] ),
182 // Anything else stores empty and reads as 'dark', so a typo
183 // lands on the old look rather than on invisible icons.
184 'tone' => in_array( $args['tone'], array( 'light', 'dark' ), true ) ? (string) $args['tone'] : '',
185 );
186 openstation_desktop_wallpaper_registry( $id, $entry );
187
188 /**
189 * Fires after a desktop wallpaper is successfully registered.
190 *
191 * Does NOT fire when `openstation_register_wallpaper()` returns a
192 * `WP_Error`.
193 *
194 * @param string $id The wallpaper id.
195 * @param array $entry The stored registry entry.
196 */
197 do_action( 'openstation_wallpaper_registered', $id, $entry );
198
199 return true;
200 }
201
202 /**
203 * Internal module-level registry for wallpapers registered via
204 * {@see openstation_register_wallpaper()}. Same static-store
205 * pattern as the widget + native-window registries.
206 *
207 * @internal
208 */
209 function openstation_desktop_wallpaper_registry( $id = '', $entry = null ) {
210 static $store = array();
211
212 if ( '' === (string) $id ) {
213 return $store;
214 }
215 if ( null !== $entry ) {
216 $store[ $id ] = $entry;
217 }
218 return isset( $store[ $id ] ) ? $store[ $id ] : null;
219 }
220
221 /**
222 * Build the wallpaper list for the shell payload. Only metadata +
223 * the resolved script URL cross the wire; the plugin's mount
224 * callback is announced via the JS global the script sets up.
225 *
226 * @return array[]
227 */
228 function openstation_build_desktop_wallpapers_payload() {
229 $registry = openstation_desktop_wallpaper_registry();
230 if ( ! is_array( $registry ) || empty( $registry ) ) {
231 return array();
232 }
233 /**
234 * Filters the server-declared wallpaper list before it ships to
235 * the shell. Mirrors the JS-side `os.wallpapers` filter
236 * so plugins can rearrange, hide, or override entries at boot
237 * without round-tripping through the JS registry.
238 *
239 * @param array[] $registry The registered wallpaper entries.
240 */
241 $registry = apply_filters( 'openstation_wallpapers', $registry );
242 if ( ! is_array( $registry ) ) {
243 return array();
244 }
245 $out = array();
246 foreach ( $registry as $entry ) {
247 if ( ! is_array( $entry ) || empty( $entry['id'] ) ) {
248 continue;
249 }
250 $handle = isset( $entry['script'] ) ? (string) $entry['script'] : '';
251 $payload = openstation_resolve_script_payload( $handle );
252 $out[] = array(
253 'id' => (string) $entry['id'],
254 'label' => isset( $entry['label'] ) ? (string) $entry['label'] : '',
255 'preview' => isset( $entry['preview'] ) ? (string) $entry['preview'] : '',
256 'type' => isset( $entry['type'] ) ? (string) $entry['type'] : 'canvas',
257 'value' => isset( $entry['value'] ) ? (string) $entry['value'] : '',
258 'description' => isset( $entry['description'] ) ? (string) $entry['description'] : '',
259 'tone' => isset( $entry['tone'] ) ? (string) $entry['tone'] : '',
260 'scriptUrl' => $payload['url'],
261 'scriptHandle' => $handle,
262 'scriptBefore' => $payload['before'],
263 'scriptAfter' => $payload['after'],
264 'scriptL10n' => $payload['l10n'],
265 'scriptTranslations' => $payload['translations'],
266 // The handle's dependency closure, replayed before the bundle
267 // on its lazy load — see `openstation_resolve_script_dependencies()`.
268 'scriptDeps' => openstation_resolve_script_dependencies( $handle ),
269 );
270 }
271 return $out;
272 }
273
274
275 /*
276 * Wallpaper scripts are NOT enqueued here, and that is deliberate.
277 *
278 * A canvas wallpaper's bundle IS the wallpaper — Living Tree is 58 KB
279 * of PixiJS scene, Snow is 42 KB — and this file used to
280 * `wp_enqueue_script()` every registered one on every admin page, so
281 * that every user downloaded and parsed every wallpaper in the
282 * install including the ones they were not wearing. The metadata in
283 * the boot payload (label, preview swatch, description) is enough for
284 * the shell to register a stub and paint a picker tile without any of
285 * it.
286 *
287 * The bundle arrives when something needs the callbacks: the shell
288 * hydrates the user's ACTIVE wallpaper during the boot sync, and the
289 * wallpaper picker hydrates the rest when it opens. See
290 * `src/wallpapers/lazy.ts`. `scriptUrl` in the payload (built above)
291 * is what makes that possible; nothing else on the PHP side is
292 * involved.
293 */
294