401 ) ); } if ( ! openstation_is_enabled() ) { return new WP_Error( 'rest_forbidden', __( 'OpenStation is not enabled for your account.', 'desktop-mode' ), array( 'status' => 403 ) ); } return true; } // Chromeless / classic admin-bar suppression and the `wp_redirect` // flag-preservation filter pair were moved to // `includes/core/routing.php`. The functions and the // add_filter / add_action hookings live there now; this file // remains the home of `openstation_is_enabled()` (called from the // routing helpers at hook-fire time, after every include has // loaded), which is why `desktop-mode.php` can safely require // routing.php BEFORE helpers.php. /** * `openstation_is_chromeless_request()` and `openstation_is_classic_request()` * were moved to `includes/core/routing.php` — see that * file for the canonical definitions. The function names didn't * change; PHP looks them up by name at call time, so every * existing caller (helpers, render, hooks) keeps working. */ /** * Returns the default wallpaper id used when a user has no saved * selection (or their saved selection was unregistered by a plugin * deactivation). * * Exposed as a filter so themes/plugins can set a site-wide default * without forking the TS build. * * ```php * add_filter( 'openstation_default_wallpaper', function () { * return 'my-plugin/brand'; * } ); * ``` * * The returned string is passed through `sanitize_key()` so a filter * that returns an invalid slug degrades to the empty string (and the * shell falls back to its hard-coded default preset). * * @return string Wallpaper id. Empty string if the filter returns * an invalid value. */ function openstation_get_default_wallpaper() { /** * Filters the wallpaper id loaded on first boot / new user. * * @param string $id Default wallpaper slug. */ $id = apply_filters( 'openstation_default_wallpaper', 'galaxy' ); if ( ! is_string( $id ) ) { return ''; } return sanitize_key( $id ); } /** * The site's own name, ready to use as a window / icon title. * * The desktop shows objects, not the software running it — so the * *root folder* of a site's content is named after the site itself * ("Izzi's Gym"), not after WordPress. That is the breadcrumb root and * the Content Graph's site label; the app that browses it is called WP * Explorer, which is a different string (see * `openstation_my_wordpress_app_title()`). This is the single source * for the site one. * * `get_bloginfo( 'name' )` returns the display-filtered option, which * carries HTML entities (`&`, `'`). Titles land in * `title=` attributes and JS-rendered text nodes, so the entities are * decoded here — leaving them encoded would render a literal * `Ben & Jerry` on the desktop. * * @return string Decoded site title. Falls back to `WordPress` when * the site has no name set. */ function openstation_site_title() { $title = wp_specialchars_decode( (string) get_bloginfo( 'name' ), ENT_QUOTES ); $title = trim( $title ); if ( '' === $title ) { $title = __( 'WordPress', 'desktop-mode' ); } /** * Filters the site title used for openstation window and icon * titles — WP Explorer's breadcrumb root, the Content Graph's site * label, and any "Open in " action. * * Return a different string to label the desktop objects after * something other than `blogname` (a brand, a network name, a * per-user workspace label). * * @param string $title Decoded site title, never empty. */ $filtered = apply_filters( 'openstation_site_title', $title ); return is_string( $filtered ) && '' !== trim( $filtered ) ? $filtered : $title; } /** * Decode a rendered title into the plain text the desktop paints with. * * `wptexturize()` encodes the characters titles are full of — `&` as * `&`, an apostrophe as `’` — and the shell writes titles * into text nodes, where the entity renders as itself. Same reasoning * as {@see openstation_site_title()}, one layer down. * * Decode BEFORE the tag strip, never after: `<script>` decodes * into a real tag, and stripping second is what removes it. * * @param string $rendered A title that has been through a display filter. * @return string Plain text, tag-free. */ function openstation_plain_text_title( $rendered ) { $decoded = html_entity_decode( (string) $rendered, ENT_QUOTES, get_bloginfo( 'charset' ) ); return trim( wp_strip_all_tags( $decoded ) ); } /** * Build a `WP_Error` for a openstation registration failure. * * Centralises the error-code vocabulary used by every * `openstation_register_*()` function so plugin authors see a * consistent contract. The canonical error-code list lives in * `docs/hooks-reference.md`. * * @param string $code Short error slug (e.g. `openstation_missing_title`). * @param string $message Human-readable message. Should be translated. * @param array $data Optional extra context attached to the error. * @return WP_Error */ function openstation_registration_error( $code, $message, $data = array() ) { return new WP_Error( (string) $code, (string) $message, is_array( $data ) ? $data : array() ); } // `openstation_url_is_same_admin()`, // `openstation_resolve_admin_target()` and // `openstation_admin_target_allowlist()` were moved to // `includes/core/routing.php` — see that file for the // canonical definitions. Function names didn't change; PHP's // runtime resolution finds them across the module split. // Dock building, menu / native-windows payload assembly and the // script/style handle resolvers were moved to // `includes/core/payload.php`. Function names didn't // change; existing callers find them via PHP's runtime function // resolution. desktop-mode.php loads payload.php right after // helpers.php so the foundational helpers (openstation_is_enabled // etc.) are present when payload functions are invoked.