PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.8
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.8
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 0.8.7 0.8.6 All 33 releases
desktop-mode / includes / render / asset-guard.php

asset-guard.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.8, at includes/render/asset-guard.php

229 lines 8.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OpenStation — Asset guard.
4 *
5 * Guarantees that every OpenStation style and script that was
6 * enqueued for the current admin page actually prints, even when a
7 * third-party plugin force-dequeues foreign assets on its own
8 * screens.
9 *
10 * The canonical offender is MailPoet's `ConflictResolver`: on every
11 * MailPoet page it dequeues each style and script whose `src` does
12 * not match an internal allowlist, hooked at `PHP_INT_MAX` on the
13 * enqueue hooks and `PHP_INT_MIN` on the print hooks. Inside a
14 * chromeless iframe that strips `os-chromeless` (so the raw admin
15 * chrome — the sidebar menu, the classic layout — reappears inside
16 * the window), and on a full shell page it would strip the desktop
17 * itself. Several "admin cleanup" plugins ship the same pattern, so
18 * this is a class of conflict, not a single bug — and no enqueue
19 * priority can win a war both sides fight with `PHP_INT_MAX`.
20 *
21 * The guard therefore doesn't fight at enqueue time at all. It works
22 * in two moves:
23 *
24 * 1. Snapshot. On `admin_enqueue_scripts` it records every queued
25 * handle whose registered `src` lives under `OPENSTATION_URL` —
26 * "these are ours, and this page meant to load them". The
27 * snapshot runs twice: right after our own enqueue callback
28 * (priority 11), and again at `PHP_INT_MAX` so late legitimate
29 * enqueues are seen too. Within the `PHP_INT_MAX` bucket our
30 * callback is registered at plugin load, long before any
31 * stripper registers its own, so it observes the queue first.
32 *
33 * 2. Re-assert. The `print_styles_array` / `print_scripts_array`
34 * filters run inside `WP_Dependencies::do_items()` — after
35 * every dequeue at every priority has already had its say.
36 * Any snapshotted handle missing from the to-print list (and
37 * not already printed) is appended there, dependencies first.
38 *
39 * Appending re-asserted styles at the end of the list also puts them
40 * after the stripper plugin's own stylesheets in the cascade, which
41 * is exactly where the chromeless overrides want to be.
42 *
43 * The guard never touches assets that aren't ours: a stripper is
44 * usually stripping for a reason (its screens break under foreign
45 * CSS), and re-asserting the whole queue would recreate the very
46 * conflicts it resolves. OpenStation's chromeless and shell sheets
47 * are scoped to the `os-chromeless` / `os-active` body classes, so
48 * re-asserting them cannot bleed into the host page's own UI.
49 *
50 * @package OpenStation
51 */
52
53 defined( 'ABSPATH' ) || exit;
54
55 /**
56 * Accessor for the guard's snapshot of expected handles.
57 *
58 * @param array|null $set When given, replaces the stored snapshot.
59 * @return array {
60 * Handles the current page enqueued from this plugin.
61 *
62 * @type string[] $styles Style handles.
63 * @type string[] $scripts Script handles.
64 * }
65 */
66 function openstation_asset_guard_store( $set = null ) {
67 static $expected = array(
68 'styles' => array(),
69 'scripts' => array(),
70 );
71 if ( null !== $set ) {
72 $expected = $set;
73 }
74 return $expected;
75 }
76
77 /**
78 * Records every queued OpenStation handle as expected to print.
79 *
80 * A handle is ours when its registered `src` sits under
81 * `OPENSTATION_URL`. Third-party chromeless overrides (enqueued on
82 * `openstation_chromeless_styles`) deliberately don't match — a
83 * plugin that wants its own handles guarded adds them via the
84 * `openstation_guarded_styles` / `openstation_guarded_scripts`
85 * filters instead.
86 */
87 function openstation_asset_guard_snapshot() {
88 $store = openstation_asset_guard_store();
89 $pools = array(
90 'styles' => wp_styles(),
91 'scripts' => wp_scripts(),
92 );
93 foreach ( $pools as $type => $dependencies ) {
94 foreach ( $dependencies->queue as $handle ) {
95 if ( in_array( $handle, $store[ $type ], true ) ) {
96 continue;
97 }
98 if ( ! isset( $dependencies->registered[ $handle ] ) ) {
99 continue;
100 }
101 $src = $dependencies->registered[ $handle ]->src;
102 if ( is_string( $src ) && 0 === strpos( $src, OPENSTATION_URL ) ) {
103 $store[ $type ][] = $handle;
104 }
105 }
106 }
107 openstation_asset_guard_store( $store );
108 }
109 add_action( 'admin_enqueue_scripts', 'openstation_asset_guard_snapshot', 11 );
110 add_action( 'admin_enqueue_scripts', 'openstation_asset_guard_snapshot', PHP_INT_MAX );
111
112 /**
113 * Collects `$handle` (dependencies first) into `$missing` unless it
114 * is already queued to print, already printed, or unregistered.
115 *
116 * @param WP_Dependencies $dependencies The styles or scripts registry.
117 * @param string $handle Handle to collect.
118 * @param string[] $to_do Handles already queued to print.
119 * @param string[] $missing Collected handles, in print order. Passed by reference.
120 */
121 function openstation_asset_guard_collect( $dependencies, $handle, $to_do, &$missing ) {
122 if (
123 in_array( $handle, $to_do, true )
124 || in_array( $handle, $missing, true )
125 || in_array( $handle, $dependencies->done, true )
126 || ! isset( $dependencies->registered[ $handle ] )
127 ) {
128 return;
129 }
130 foreach ( $dependencies->registered[ $handle ]->deps as $dep ) {
131 openstation_asset_guard_collect( $dependencies, $dep, $to_do, $missing );
132 }
133 $missing[] = $handle;
134 }
135
136 /**
137 * Appends any expected-but-missing handles to a to-print list.
138 *
139 * @param WP_Dependencies $dependencies The styles or scripts registry.
140 * @param string[] $to_do Handles about to be printed.
141 * @param string[] $handles Handles that must be among them.
142 * @return string[] The to-print list with missing handles appended.
143 */
144 function openstation_asset_guard_merge( $dependencies, $to_do, $handles ) {
145 $missing = array();
146 foreach ( $handles as $handle ) {
147 if ( is_string( $handle ) && '' !== $handle ) {
148 openstation_asset_guard_collect( $dependencies, $handle, $to_do, $missing );
149 }
150 }
151 if ( empty( $missing ) ) {
152 return $to_do;
153 }
154 return array_merge( $to_do, $missing );
155 }
156
157 /**
158 * Re-asserts snapshotted OpenStation styles into the to-print list.
159 *
160 * Runs inside `WP_Styles::all_deps()`, after any force-dequeue has
161 * already happened — the last word before output.
162 *
163 * @param string[] $to_do Style handles about to be printed.
164 * @return string[]
165 */
166 function openstation_asset_guard_print_styles( $to_do ) {
167 $store = openstation_asset_guard_store();
168
169 /**
170 * Filters the style handles the asset guard re-asserts at print
171 * time.
172 *
173 * Defaults to every style this plugin enqueued for the current
174 * page. A plugin whose chromeless overrides are stripped by an
175 * asset-cleanup plugin can append its own handles here.
176 *
177 * @param string[] $handles Style handles expected to print.
178 */
179 $handles = apply_filters( 'openstation_guarded_styles', $store['styles'] );
180
181 if ( empty( $handles ) || ! is_array( $handles ) ) {
182 return $to_do;
183 }
184 return openstation_asset_guard_merge( wp_styles(), $to_do, $handles );
185 }
186 add_filter( 'print_styles_array', 'openstation_asset_guard_print_styles' );
187
188 /**
189 * Re-asserts snapshotted OpenStation scripts into the to-print list.
190 *
191 * Only acts during the admin footer pass: every OpenStation bundle
192 * is registered `in_footer`, and handles appended during the head
193 * pass would print there without their group bookkeeping. Re-added
194 * handles are stamped into the footer group so
195 * `WP_Scripts::do_item()` finds the entry it expects.
196 *
197 * @param string[] $to_do Script handles about to be printed.
198 * @return string[]
199 */
200 function openstation_asset_guard_print_scripts( $to_do ) {
201 if ( ! doing_action( 'admin_print_footer_scripts' ) ) {
202 return $to_do;
203 }
204 $store = openstation_asset_guard_store();
205
206 /**
207 * Filters the script handles the asset guard re-asserts at print
208 * time.
209 *
210 * Defaults to every script this plugin enqueued for the current
211 * page. A plugin whose iframe-side scripts are stripped by an
212 * asset-cleanup plugin can append its own handles here.
213 *
214 * @param string[] $handles Script handles expected to print.
215 */
216 $handles = apply_filters( 'openstation_guarded_scripts', $store['scripts'] );
217
218 if ( empty( $handles ) || ! is_array( $handles ) ) {
219 return $to_do;
220 }
221 $scripts = wp_scripts();
222 $merged = openstation_asset_guard_merge( $scripts, $to_do, $handles );
223 foreach ( array_diff( $merged, $to_do ) as $handle ) {
224 $scripts->groups[ $handle ] = 1;
225 }
226 return $merged;
227 }
228 add_filter( 'print_scripts_array', 'openstation_asset_guard_print_scripts' );
229