PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.11
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.11
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 0.8.8 All 35 releases
← All changes | includes/desktop-themes/store.php +132 -88 0.9.8 → 1.1.11 View file →
@@ -1,7 +1,7 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode — Desktop-theme storage + accessors.
3 + * OpenStation — Desktop-theme storage + accessors.
4 4 *
5 5 * Owns the uploads directory, the site option that indexes installed
6 6 * themes, and every filterable knob the rest of the module reads
7 7 * (upload capability, slot allowlists, ZIP caps).
@@ -13,8 +13,9 @@
13 13 * .htaccess <- exec-off, NOT deny-all
14 14 * <slug>/
15 15 * theme.json <- the author's raw manifest
16 16 * theme.css <- compiled by us: custom props +
17 + *
17 18 * @font-face rules we generated
18 19 * icons/… textures/… fonts/… preview.png
19 20 *
20 21 * The `.htaccess` here is deliberately NOT the deny-all one the
@@ -24,25 +25,41 @@
24 25 * installer only ever moves manifest-referenced files whose
25 26 * extension is on the image or font allowlist, so nothing executable
26 27 * lands in the first place.
27 28 *
28 - * @package WPDesktopMode
29 + * @package OpenStation
29 30 */
30 31
31 32 defined( 'ABSPATH' ) || exit;
32 33
33 -/** Site option holding the installed-theme index. Autoload: no. */
34 -const DESKTOP_MODE_DESKTOP_THEMES_OPTION = 'desktop_mode_desktop_themes';
34 +/**
35 + * Site option holding the installed-theme index. Autoload: no.
36 + *
37 + * The VALUE keeps its pre-rebrand spelling on purpose: rows are already
38 + * stored under it on live installs, so renaming it would orphan every
39 + * one. The mismatch between this constant's name and its value is
40 + * deliberate — it is NOT a half-finished rename.
41 + *
42 + * Not to be confused with the `openstation_desktop_themes` filter, which
43 + * once shared this string and is now deliberately decoupled.
44 + */
45 +const OPENSTATION_DESKTOP_THEMES_OPTION = 'desktop_mode_desktop_themes';
35 46
36 47 /**
37 48 * Absolute path of the desktop-themes base dir (no trailing slash),
38 49 * or of one theme's dir when `$slug` is given. Pure path math —
39 - * nothing is created; see {@see desktop_mode_desktop_themes_ensure_dir()}.
50 + * nothing is created; see {@see openstation_desktop_themes_ensure_dir()}.
40 51 *
52 + * The `desktop-mode-themes` segment is the pre-rebrand spelling and is
53 + * frozen: admin-uploaded theme ZIPs already unpack there. Renaming it
54 + * points the plugin at an empty directory and every installed theme
55 + * vanishes from the picker. The mismatch with the function name is
56 + * deliberate.
57 + *
41 58 * @param string $slug Optional. Theme slug.
42 59 * @return string
43 60 */
44 -function desktop_mode_desktop_themes_dir( $slug = '' ) {
61 +function openstation_desktop_themes_dir( $slug = '' ) {
45 62 $uploads = wp_get_upload_dir();
46 63 $base = trailingslashit( $uploads['basedir'] ) . 'desktop-mode-themes';
47 64 /**
48 65 * Filters the desktop-theme storage base directory.
@@ -51,9 +68,9 @@
51 68 * `theme.css` and every image are loaded by the browser.
52 69 *
53 70 * @param string $base Absolute path, no trailing slash.
54 71 */
55 - $base = (string) apply_filters( 'desktop_mode_desktop_themes_base_dir', $base );
72 + $base = (string) apply_filters( 'openstation_desktop_themes_base_dir', $base );
56 73 $slug = sanitize_key( (string) $slug );
57 74 return '' !== $slug ? $base . '/' . $slug : $base;
58 75 }
59 76
@@ -63,18 +80,18 @@
63 80 *
64 81 * @param string $slug Optional. Theme slug.
65 82 * @return string
66 83 */
67 -function desktop_mode_desktop_themes_url( $slug = '' ) {
84 +function openstation_desktop_themes_url( $slug = '' ) {
68 85 $uploads = wp_get_upload_dir();
69 86 $url = untrailingslashit( $uploads['baseurl'] ) . '/desktop-mode-themes';
70 87 /**
71 88 * Filters the desktop-theme storage base URL. Must resolve to the
72 - * same bytes `desktop_mode_desktop_themes_base_dir` points at.
89 + * same bytes `openstation_desktop_themes_base_dir` points at.
73 90 *
74 91 * @param string $url Absolute URL, no trailing slash.
75 92 */
76 - $url = (string) apply_filters( 'desktop_mode_desktop_themes_base_url', $url );
93 + $url = (string) apply_filters( 'openstation_desktop_themes_base_url', $url );
77 94 $slug = sanitize_key( (string) $slug );
78 95 return '' !== $slug ? $url . '/' . $slug : $url;
79 96 }
80 97
@@ -83,13 +100,13 @@
83 100 *
84 101 * @return string|WP_Error Base dir path, or `WP_Error` when the
85 102 * filesystem refuses.
86 103 */
87 -function desktop_mode_desktop_themes_ensure_dir() {
88 - $base = desktop_mode_desktop_themes_dir();
104 +function openstation_desktop_themes_ensure_dir() {
105 + $base = openstation_desktop_themes_dir();
89 106 if ( ! wp_mkdir_p( $base ) ) {
90 107 return new WP_Error(
91 - 'desktop_mode_desktop_theme_mkdir_failed',
108 + 'openstation_desktop_theme_mkdir_failed',
92 109 __( 'Could not create the desktop-themes directory.', 'desktop-mode' ),
93 110 array( 'status' => 500 )
94 111 );
95 112 }
@@ -124,10 +141,10 @@
124 141 * Read the installed-theme index (map of slug => stored entry).
125 142 *
126 143 * @return array<string,array>
127 144 */
128 -function desktop_mode_desktop_themes_index() {
129 - $raw = get_option( DESKTOP_MODE_DESKTOP_THEMES_OPTION, array() );
145 +function openstation_desktop_themes_index() {
146 + $raw = get_option( OPENSTATION_DESKTOP_THEMES_OPTION, array() );
130 147 if ( ! is_array( $raw ) ) {
131 148 return array();
132 149 }
133 150 $out = array();
@@ -149,15 +166,15 @@
149 166 *
150 167 * @param array<string,array> $index Map of slug => stored entry.
151 168 * @return void
152 169 */
153 -function desktop_mode_desktop_themes_put_index( $index ) {
170 +function openstation_desktop_themes_put_index( $index ) {
154 171 $index = is_array( $index ) ? $index : array();
155 - if ( false === get_option( DESKTOP_MODE_DESKTOP_THEMES_OPTION, false ) ) {
156 - add_option( DESKTOP_MODE_DESKTOP_THEMES_OPTION, $index, '', 'no' );
172 + if ( false === get_option( OPENSTATION_DESKTOP_THEMES_OPTION, false ) ) {
173 + add_option( OPENSTATION_DESKTOP_THEMES_OPTION, $index, '', 'no' );
157 174 return;
158 175 }
159 - update_option( DESKTOP_MODE_DESKTOP_THEMES_OPTION, $index, false );
176 + update_option( OPENSTATION_DESKTOP_THEMES_OPTION, $index, false );
160 177 }
161 178
162 179 /**
163 180 * Fetch one installed theme's stored entry.
@@ -164,11 +181,11 @@
164 181 *
165 182 * @param string $slug Theme slug.
166 183 * @return array|null Stored entry, or `null` when not installed.
167 184 */
168 -function desktop_mode_desktop_theme_get( $slug ) {
185 +function openstation_desktop_theme_get( $slug ) {
169 186 $slug = sanitize_key( (string) $slug );
170 - $index = desktop_mode_desktop_themes_index();
187 + $index = openstation_desktop_themes_index();
171 188 return isset( $index[ $slug ] ) ? $index[ $slug ] : null;
172 189 }
173 190
174 191 /**
@@ -175,9 +192,9 @@
175 192 * Capability required to upload / delete desktop themes.
176 193 *
177 194 * @return string
178 195 */
179 -function desktop_mode_desktop_theme_upload_capability() {
196 +function openstation_desktop_theme_upload_capability() {
180 197 /**
181 198 * Filters the capability required to manage the site's desktop
182 199 * theme library. Picking a theme is per-user and never gated.
183 200 *
@@ -182,9 +199,9 @@
182 199 * theme library. Picking a theme is per-user and never gated.
183 200 *
184 201 * @param string $capability Default `manage_options`.
185 202 */
186 - return (string) apply_filters( 'desktop_mode_desktop_theme_upload_capability', 'manage_options' );
203 + return (string) apply_filters( 'openstation_desktop_theme_upload_capability', 'manage_options' );
187 204 }
188 205
189 206 /**
190 207 * Derive the storage slug from a manifest `id`.
@@ -194,9 +211,9 @@
194 211 *
195 212 * @param string $id Manifest id.
196 213 * @return string Slug, or `''` when the id yields nothing usable.
197 214 */
198 -function desktop_mode_desktop_theme_slug_from_id( $id ) {
215 +function openstation_desktop_theme_slug_from_id( $id ) {
199 216 return sanitize_key( str_replace( '/', '-', (string) $id ) );
200 217 }
201 218
202 219 /**
@@ -207,11 +224,11 @@
207 224 * `APP:<slug>` entries are matched by pattern, not by this list.
208 225 *
209 226 * @return string[]
210 227 */
211 -function desktop_mode_desktop_theme_icon_slots() {
228 +function openstation_desktop_theme_icon_slots() {
212 229 $slots = array(
213 - // Window controls — one per `<wpd-window-button>` key.
230 + // Window controls — one per `<os-window-button>` key.
214 231 'WINDOW_CONTROL_MINIMIZE',
215 232 'WINDOW_CONTROL_MAXIMIZE',
216 233 'WINDOW_CONTROL_FULLSCREEN',
217 234 'WINDOW_CONTROL_FULLSCREEN_EXIT',
@@ -222,9 +239,9 @@
222 239 // System tiles.
223 240 'OS_SETTINGS',
224 241 'RECYCLE_BIN',
225 242 'BUG_REPORT',
226 - 'EXIT_DESKTOP_MODE',
243 + 'EXIT_OPENSTATION',
227 244 'PWA_INSTALL',
228 245 // Apps.
229 246 'DEFAULT_APP_ICON',
230 247 // Desktop files.
@@ -250,9 +267,9 @@
250 267 * pattern) are dropped from the manifest during sanitization.
251 268 *
252 269 * @param string[] $slots Slot names.
253 270 */
254 - return (array) apply_filters( 'desktop_mode_desktop_theme_icon_slots', $slots );
271 + return (array) apply_filters( 'openstation_desktop_theme_icon_slots', $slots );
255 272 }
256 273
257 274 /**
258 275 * The texture slots a manifest may address, each mapped to the
@@ -277,9 +294,9 @@
277 294 * must render at one size (the four window
278 295 * corners). First declared wins.
279 296 *
280 297 * **The compiler reads this table and nothing else.** That is what
281 - * makes `desktop_mode_desktop_theme_texture_slots` a complete
298 + * makes `openstation_desktop_theme_texture_slots` a complete
282 299 * extension point: a plugin that adds an entry here, and writes one
283 300 * CSS rule consuming `var( <prop>, none )`, has textured a surface
284 301 * the framework never knew about — no core change, no compiler
285 302 * change. See docs/desktop-themes.md § "Texturing your own surface".
@@ -285,19 +302,19 @@
285 302 * change. See docs/desktop-themes.md § "Texturing your own surface".
286 303 *
287 304 * @return array<string,array{type:string,prop:string}>
288 305 */
289 -function desktop_mode_desktop_theme_texture_slots() {
290 - $corner_size = '--desktop-mode-window-corner-size';
306 +function openstation_desktop_theme_texture_slots() {
307 + $corner_size = '--os-window-corner-size';
291 308 $slots = array(
292 309 // --- Window chrome. ---
293 310 'TITLEBAR' => array(
294 311 'type' => 'image',
295 - 'prop' => '--desktop-mode-titlebar-image',
312 + 'prop' => '--os-titlebar-image',
296 313 ),
297 314 'TITLEBAR_FOCUSED' => array(
298 315 'type' => 'image',
299 - 'prop' => '--desktop-mode-titlebar-image-focused',
316 + 'prop' => '--os-titlebar-image-focused',
300 317 // Shares the base slot's repeat + size; only the image
301 318 // differs, so a theme shipping one strip gets both states.
302 319 'companions' => false,
303 320 ),
@@ -302,32 +319,32 @@
302 319 'companions' => false,
303 320 ),
304 321 'WINDOW_FRAME' => array(
305 322 'type' => 'border-image',
306 - 'prop' => '--desktop-mode-window-border-image',
323 + 'prop' => '--os-window-border-image',
307 324 ),
308 325 'WINDOW_FRAME_FOCUSED' => array(
309 326 'type' => 'border-image',
310 - 'prop' => '--desktop-mode-window-border-image-focused',
327 + 'prop' => '--os-window-border-image-focused',
311 328 ),
312 329 'WINDOW_CORNER_NE' => array(
313 330 'type' => 'image',
314 - 'prop' => '--desktop-mode-window-corner-ne-image',
331 + 'prop' => '--os-window-corner-ne-image',
315 332 'sizeGroup' => $corner_size,
316 333 ),
317 334 'WINDOW_CORNER_NW' => array(
318 335 'type' => 'image',
319 - 'prop' => '--desktop-mode-window-corner-nw-image',
336 + 'prop' => '--os-window-corner-nw-image',
320 337 'sizeGroup' => $corner_size,
321 338 ),
322 339 'WINDOW_CORNER_SE' => array(
323 340 'type' => 'image',
324 - 'prop' => '--desktop-mode-window-corner-se-image',
341 + 'prop' => '--os-window-corner-se-image',
325 342 'sizeGroup' => $corner_size,
326 343 ),
327 344 'WINDOW_CORNER_SW' => array(
328 345 'type' => 'image',
329 - 'prop' => '--desktop-mode-window-corner-sw-image',
346 + 'prop' => '--os-window-corner-sw-image',
330 347 'sizeGroup' => $corner_size,
331 348 ),
332 349 // The control cluster and the individual control faces. Both
333 350 // are TRANSPARENT by default, which is what lets a TITLEBAR
@@ -332,75 +349,75 @@
332 349 // The control cluster and the individual control faces. Both
333 350 // are TRANSPARENT by default, which is what lets a TITLEBAR
334 351 // texture run edge to edge underneath them. A theme that wants
335 352 // the controls to sit on a plate paints one here (and usually
336 - // sets `--desktop-mode-titlebar-controls-radius` +
353 + // sets `--os-titlebar-controls-radius` +
337 354 // `-padding` to give it a shape).
338 355 'TITLEBAR_CONTROLS' => array(
339 356 'type' => 'image',
340 - 'prop' => '--desktop-mode-titlebar-controls-image',
357 + 'prop' => '--os-titlebar-controls-image',
341 358 ),
342 359 'TITLEBAR_BUTTON' => array(
343 360 'type' => 'image',
344 - 'prop' => '--wpd-btn-bg-image',
361 + 'prop' => '--os-ui-btn-bg-image',
345 362 ),
346 363 'WINDOW_BODY' => array(
347 364 'type' => 'image',
348 - 'prop' => '--desktop-mode-window-body-image',
365 + 'prop' => '--os-window-body-image',
349 366 ),
350 367 'TABBAR' => array(
351 368 'type' => 'image',
352 - 'prop' => '--desktop-mode-tabs-image',
369 + 'prop' => '--os-tabs-image',
353 370 ),
354 371 // --- Shell surfaces. ---
355 372 'DOCK' => array(
356 373 'type' => 'image',
357 - 'prop' => '--desktop-mode-dock-bg-image',
374 + 'prop' => '--os-dock-bg-image',
358 375 ),
359 376 'DOCK_ITEM' => array(
360 377 'type' => 'image',
361 - 'prop' => '--desktop-mode-dock-item-image',
378 + 'prop' => '--os-dock-item-image',
362 379 ),
363 380 'DESKTOP' => array(
364 381 'type' => 'image',
365 - 'prop' => '--desktop-mode-desktop-image',
382 + 'prop' => '--os-desktop-image',
366 383 ),
367 384 'ICON_TILE' => array(
368 385 'type' => 'image',
369 - 'prop' => '--desktop-mode-tile-image',
386 + 'prop' => '--os-tile-image',
370 387 ),
371 388 'WIDGET' => array(
372 389 'type' => 'image',
373 - 'prop' => '--desktop-mode-widget-image',
390 + 'prop' => '--os-widget-image',
374 391 ),
375 392 // --- Component-kit surfaces (window bodies + popovers). ---
376 393 'MENU' => array(
377 394 'type' => 'image',
378 - 'prop' => '--wpd-menu-bg-image',
395 + 'prop' => '--os-ui-menu-bg-image',
379 396 ),
380 397 'DIALOG' => array(
381 398 'type' => 'image',
382 - 'prop' => '--wpd-dialog-bg-image',
399 + 'prop' => '--os-ui-dialog-bg-image',
383 400 ),
384 401 'SCRIM' => array(
385 402 'type' => 'image',
386 - 'prop' => '--wpd-scrim-image',
403 + 'prop' => '--os-ui-scrim-image',
387 404 ),
388 405 'PANEL' => array(
389 406 'type' => 'image',
390 - 'prop' => '--wpd-panel-bg-image',
407 + 'prop' => '--os-ui-panel-bg-image',
391 408 ),
392 409 'TOAST' => array(
393 410 'type' => 'image',
394 - 'prop' => '--wpd-toast-bg-image',
411 + 'prop' => '--os-ui-toast-bg-image',
395 412 ),
396 413 'TABLE_HEADER' => array(
397 414 'type' => 'image',
398 - 'prop' => '--wpd-table-header-bg-image',
415 + 'prop' => '--os-ui-table-header-bg-image',
399 416 ),
400 417 'BUTTON' => array(
401 418 'type' => 'image',
402 - 'prop' => '--wpd-button-bg-image',
419 + 'prop' => '--os-ui-button-bg-image',
403 420 ),
404 421 );
405 422 /**
406 423 * Filters the texture slots a desktop theme manifest may address.
@@ -418,9 +435,9 @@
418 435 * @param array<string,array> $slots Map of slot =>
419 436 * `{ type, prop, companions?,
420 437 * sizeGroup? }`.
421 438 */
422 - return (array) apply_filters( 'desktop_mode_desktop_theme_texture_slots', $slots );
439 + return (array) apply_filters( 'openstation_desktop_theme_texture_slots', $slots );
423 440 }
424 441
425 442 /**
426 443 * The OS-settings keys a manifest's `recommendedOsSettings` block may
@@ -437,9 +454,9 @@
437 454 * runtime, by core AND by plugins). PHP checks the
438 455 * charset; the shell drops the key at apply time when
439 456 * nothing is registered under that id, which is the same
440 457 * "resolve at use time" contract
441 - * `desktop_mode_sanitize_os_settings()` already follows
458 + * `openstation_sanitize_os_settings()` already follows
442 459 * for the user's own `dockRailRenderer`.
443 460 * - `int` — a whole number clamped into `{ min, max }`. Clamped
444 461 * rather than dropped: a theme asking for a reveal
445 462 * slower than the shell will play is expressing "slow",
@@ -453,20 +470,37 @@
453 470 * gate, or anything that changes what the shell can do.
454 471 *
455 472 * @return array<string,array{enum?:string[],slug?:bool,int?:array{min:int,max:int}}>
456 473 */
457 -function desktop_mode_desktop_theme_recommended_os_settings_schema() {
474 +function openstation_desktop_theme_recommended_os_settings_schema() {
458 475 $schema = array(
459 - 'dockSize' => array( 'enum' => DESKTOP_MODE_OS_SETTINGS_DOCK_SIZES ),
460 - 'desktopLayout' => array( 'enum' => DESKTOP_MODE_OS_SETTINGS_DESKTOP_LAYOUTS ),
461 - 'windowRadius' => array( 'enum' => DESKTOP_MODE_OS_SETTINGS_WINDOW_RADII ),
462 - 'adminBarMode' => array( 'enum' => DESKTOP_MODE_OS_SETTINGS_ADMIN_BAR_MODES ),
476 + 'dockSize' => array( 'enum' => OPENSTATION_OS_SETTINGS_DOCK_SIZES ),
477 + 'desktopLayout' => array( 'enum' => OPENSTATION_OS_SETTINGS_DESKTOP_LAYOUTS ),
478 + 'dockPlacement' => array( 'enum' => OPENSTATION_OS_SETTINGS_DOCK_PLACEMENTS ),
479 + 'windowRadius' => array( 'enum' => OPENSTATION_OS_SETTINGS_WINDOW_RADII ),
480 + 'adminBarMode' => array( 'enum' => OPENSTATION_OS_SETTINGS_ADMIN_BAR_MODES ),
463 481 'dockRailRenderer' => array( 'slug' => true ),
464 482 'windowReveal' => array( 'slug' => true ),
483 +
484 + /*
485 + * The accent swatch id. A registry lookup rather than an enum
486 + * because the list is filterable
487 + * (`openstation_accent_colors`), so the shell resolves the id
488 + * against whatever swatches the site actually offers and skips
489 + * the key when nothing answers to it.
490 + *
491 + * It earns its place here for the same reason the rest do: a
492 + * theme's palette and the accent are one composition, and a
493 + * dark station wearing the WordPress blue it was never drawn
494 + * against is the most visible way that composition comes
495 + * apart. Still a recommendation — applied once, and the user's
496 + * pick afterwards is theirs.
497 + */
498 + 'accent' => array( 'slug' => true ),
465 499 'windowRevealDuration' => array(
466 500 'int' => array(
467 - 'min' => DESKTOP_MODE_OS_SETTINGS_REVEAL_DURATION_MIN,
468 - 'max' => DESKTOP_MODE_OS_SETTINGS_REVEAL_DURATION_MAX,
501 + 'min' => OPENSTATION_OS_SETTINGS_REVEAL_DURATION_MIN,
502 + 'max' => OPENSTATION_OS_SETTINGS_REVEAL_DURATION_MAX,
469 503 ),
470 504 ),
471 505 );
472 506 /**
@@ -487,9 +521,9 @@
487 521 * @param array<string,array> $schema Map of settings key =>
488 522 * `{ enum }`, `{ slug }`, or `{ int }`.
489 523 */
490 524 $schema = (array) apply_filters(
491 - 'desktop_mode_desktop_theme_recommended_os_settings_schema',
525 + 'openstation_desktop_theme_recommended_os_settings_schema',
492 526 $schema
493 527 );
494 528
495 529 $out = array();
@@ -550,9 +584,9 @@
550 584 *
551 585 * @param string $kind `'image'` or `'font'`.
552 586 * @return string[] Lowercase extensions, no leading dot.
553 587 */
554 -function desktop_mode_desktop_theme_asset_extensions( $kind = 'image' ) {
588 +function openstation_desktop_theme_asset_extensions( $kind = 'image' ) {
555 589 $kind = strtolower( trim( (string) $kind ) );
556 590 $map = array(
557 591 'image' => array( 'png', 'jpg', 'jpeg', 'gif', 'webp', 'avif', 'svg' ),
558 592 'font' => array( 'woff2', 'woff', 'ttf', 'otf' ),
@@ -567,19 +601,24 @@
567 601 * @param string[] $extensions Lowercase extensions, no dot.
568 602 * @param string $kind `'image'` or `'font'`.
569 603 */
570 604 $extensions = (array) apply_filters(
571 - 'desktop_mode_desktop_theme_asset_extensions',
605 + 'openstation_desktop_theme_asset_extensions',
572 606 isset( $map[ $kind ] ) ? $map[ $kind ] : array(),
573 607 $kind
574 608 );
575 609
576 - return array_values( array_filter( array_map(
577 - static function ( $ext ) {
578 - return strtolower( trim( (string) $ext, ". \t\n\r\0\x0B" ) );
579 - },
580 - $extensions
581 - ), 'strlen' ) );
610 + return array_values(
611 + array_filter(
612 + array_map(
613 + static function ( $ext ) {
614 + return strtolower( trim( (string) $ext, ". \t\n\r\0\x0B" ) );
615 + },
616 + $extensions
617 + ),
618 + 'strlen'
619 + )
620 + );
582 621 }
583 622
584 623 /**
585 624 * Maximum number of `@font-face` rules one theme may declare, and
@@ -586,9 +625,9 @@
586 625 * the maximum number of source files per face.
587 626 *
588 627 * @return array{max_faces:int,max_sources:int}
589 628 */
590 -function desktop_mode_desktop_theme_font_caps() {
629 +function openstation_desktop_theme_font_caps() {
591 630 /**
592 631 * Filters the desktop-theme font caps.
593 632 *
594 633 * @param array $caps `{ max_faces, max_sources }`.
@@ -593,9 +632,9 @@
593 632 *
594 633 * @param array $caps `{ max_faces, max_sources }`.
595 634 */
596 635 $caps = (array) apply_filters(
597 - 'desktop_mode_desktop_theme_font_caps',
636 + 'openstation_desktop_theme_font_caps',
598 637 array(
599 638 // A UI font at a few weights, a mono, a display face.
600 639 'max_faces' => 16,
601 640 // woff2 + woff is the realistic ceiling in 2025; four
@@ -613,9 +652,9 @@
613 652 * Hard caps applied while walking an uploaded ZIP.
614 653 *
615 654 * @return array{max_entries:int,max_uncompressed:int,max_file:int,extensions:string[]}
616 655 */
617 -function desktop_mode_desktop_theme_zip_caps() {
656 +function openstation_desktop_theme_zip_caps() {
618 657 $caps = array(
619 658 // Entry count — a theme is a manifest plus a couple of dozen
620 659 // images; anything past this is a zip bomb or a mistake.
621 660 'max_entries' => 256,
@@ -631,10 +670,10 @@
631 670 // font extension — so they are validated, never extracted into
632 671 // the live directory, and discarded with the staging dir.
633 672 'extensions' => array_merge(
634 673 array( 'json', 'txt', 'md' ),
635 - desktop_mode_desktop_theme_asset_extensions( 'image' ),
636 - desktop_mode_desktop_theme_asset_extensions( 'font' )
674 + openstation_desktop_theme_asset_extensions( 'image' ),
675 + openstation_desktop_theme_asset_extensions( 'font' )
637 676 ),
638 677 );
639 678 /**
640 679 * Filters the caps enforced while validating an uploaded desktop
@@ -645,20 +684,25 @@
645 684 * the whole security model of this feature.
646 685 *
647 686 * @param array $caps See the return shape above.
648 687 */
649 - $caps = (array) apply_filters( 'desktop_mode_desktop_theme_zip_caps', $caps );
688 + $caps = (array) apply_filters( 'openstation_desktop_theme_zip_caps', $caps );
650 689
651 690 return array(
652 691 'max_entries' => max( 1, (int) ( $caps['max_entries'] ?? 256 ) ),
653 692 'max_uncompressed' => max( 1, (int) ( $caps['max_uncompressed'] ?? 33554432 ) ),
654 693 'max_file' => max( 1, (int) ( $caps['max_file'] ?? 8388608 ) ),
655 - 'extensions' => array_values( array_filter( array_map(
656 - static function ( $ext ) {
657 - return strtolower( trim( (string) $ext, ". \t\n\r\0\x0B" ) );
658 - },
659 - (array) ( $caps['extensions'] ?? array() )
660 - ), 'strlen' ) ),
694 + 'extensions' => array_values(
695 + array_filter(
696 + array_map(
697 + static function ( $ext ) {
698 + return strtolower( trim( (string) $ext, ". \t\n\r\0\x0B" ) );
699 + },
700 + (array) ( $caps['extensions'] ?? array() )
701 + ),
702 + 'strlen'
703 + )
704 + ),
661 705 );
662 706 }
663 707
664 708 /**
@@ -665,12 +709,12 @@
665 709 * Maximum number of themes the payload ships to the shell.
666 710 *
667 711 * @return int
668 712 */
669 -function desktop_mode_desktop_themes_payload_cap() {
713 +function openstation_desktop_themes_payload_cap() {
670 714 /**
671 715 * Filters how many desktop themes are announced to the shell.
672 716 *
673 717 * @param int $cap Default 24.
674 718 */
675 - return max( 1, (int) apply_filters( 'desktop_mode_desktop_themes_payload_cap', 24 ) );
719 + return max( 1, (int) apply_filters( 'openstation_desktop_themes_payload_cap', 24 ) );
676 720 }