PluginProbe
404 Solution / trunk
404 Solution vtrunk
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / admin / AdminAssetEnqueuer.php

AdminAssetEnqueuer.php in 404 Solution trunk, at includes/admin/AdminAssetEnqueuer.php

449 lines 25.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * Enqueues admin scripts, styles, and support-request bootstrap assets.
9 */
10 class ABJ_404_Solution_AdminAssetEnqueuer {
11
12 /**
13 * Include things necessary for ajax.
14 *
15 * @param string $hook
16 * @param callable(string, Throwable): void $errorReporter
17 * @return void
18 */
19 public static function addScripts($hook, callable $errorReporter): void {
20 // only load this stuff for this plugin.
21 // thanks to https://pippinsplugins.com/loading-scripts-correctly-in-the-wordpress-admin/
22 if (!array_key_exists('abj404_settingsPageName', $GLOBALS) ||
23 $hook != $GLOBALS['abj404_settingsPageName']) {
24 return;
25 }
26
27 try {
28 $includesUrl = ABJ404_URL . 'includes/';
29 $subpage = self::requestStringParam('subpage');
30 // Default plugin landing is redirects when subpage is not specified.
31 if ($subpage === '') {
32 $subpage = 'abj404_redirects';
33 }
34 $isEditPage = self::isEditPageRequest($subpage);
35
36 $isOptionsPage = ($subpage === 'abj404_options');
37 $isStatsPage = ($subpage === 'abj404_stats');
38 $isToolsPage = ($subpage === 'abj404_tools');
39 $isCardAccordionPage = in_array($subpage, array('abj404_options', 'abj404_tools', 'abj404_stats'), true);
40 $isLogsPage = ($subpage === 'abj404_logs');
41 $isListPage = !$isEditPage && in_array($subpage, array('abj404_redirects', 'abj404_captured', 'abj404_logs'), true);
42 $needsDestinationAutocomplete = in_array($subpage, array('abj404_redirects', 'abj404_captured', 'abj404_options', 'abj404_edit'), true);
43
44 // remove the "thank you for creating with wordpress" message
45 add_filter('admin_footer_text',
46 'ABJ_404_Solution_WordPress_Connector::remove_admin_footer_text');
47 // remove the version number message
48 add_filter('update_footer',
49 'ABJ_404_Solution_WordPress_Connector::remove_admin_footer_text', 11);
50
51 // jquery is used for the searchable dropdown list of pages for adding a redirect and other things.
52 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('jquery');
53 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('jquery-ui-autocomplete');
54 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('jquery-effects-core');
55 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('jquery-effects-highlight');
56 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('jquery-color');
57
58 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-admin-ajax',
59 ABJ404_URL . 'includes/js/abj404-admin-ajax.js', array('jquery'));
60
61 $redirectToScriptUrl = $includesUrl . 'ajax/redirect_to_ajax.js';
62 wp_register_script(
63 'abj404-redirect_to_ajax',
64 $redirectToScriptUrl,
65 array('jquery', 'jquery-ui-autocomplete'),
66 ABJ_404_Solution_WPUtils::createUpdatedVersionNumber($redirectToScriptUrl)
67 );
68 wp_register_script('abj404-exclude_pages_ajax', $includesUrl . 'ajax/exclude_pages_ajax.js',
69 array('jquery', 'jquery-ui-autocomplete', 'abj404-redirect_to_ajax'));
70 $translation_array = array(
71 'type_a_page_name' => __('(Type a page name or an external URL)', '404-solution'),
72 'a_page_has_been_selected' => __('(A page has been selected.)', '404-solution'),
73 'an_external_url_will_be_used' => __('(An external URL will be used.)', '404-solution')
74 );
75 wp_localize_script('abj404-redirect_to_ajax', 'abj404localization', $translation_array );
76 if ($needsDestinationAutocomplete) {
77 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-redirect_to_ajax');
78 wp_localize_script('abj404-exclude_pages_ajax', 'abj404localization', $translation_array );
79 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-exclude_pages_ajax');
80 }
81
82 wp_register_script('abj404-enable_disable_apply_button_js',
83 ABJ404_URL . 'includes/js/enableDisableApplyButton.js');
84 $translation_array = array('{altText}' => __('Choose at least one URL', '404-solution'));
85 wp_localize_script('abj404-enable_disable_apply_button_js', 'abj404localization', $translation_array);
86 if ($isListPage) {
87 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-enable_disable_apply_button_js');
88 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-trash_link_ajax', $includesUrl . 'ajax/trash_link_ajax.js',
89 array('jquery', 'abj404-admin-ajax'));
90 }
91 if ($isListPage || $isEditPage) {
92 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-table-interactions', $includesUrl . 'js/tableInteractions.js',
93 array('jquery'));
94 }
95
96 if ($isListPage || $isStatsPage) {
97 self::enqueueViewUpdaterModules($includesUrl . 'ajax/');
98 }
99
100 if ($isStatsPage) {
101 // Chart.js is bundled with the plugin (includes/js/lib/) and enqueued
102 // as a hard dependency of both chart scripts. It used to be fetched from a
103 // third-party CDN at runtime, which is a WordPress.org guideline violation
104 // and a supply-chain risk in the authenticated admin context.
105 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-chartjs',
106 ABJ404_URL . 'includes/js/lib/chart.umd.min.js', array());
107 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-stats-confidence-chart',
108 ABJ404_URL . 'includes/js/statsConfidenceChart.js', array('abj404-chartjs'));
109 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-stats-trends',
110 ABJ404_URL . 'includes/js/statsTrends.js', array('abj404-chartjs'));
111 }
112
113 if ($isToolsPage) {
114 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-tools-migrate-plugin',
115 ABJ404_URL . 'includes/js/toolsMigratePlugin.js', array());
116 }
117
118 if ($isLogsPage) {
119 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-search_logs_ajax', $includesUrl . 'ajax/search_logs_ajax.js',
120 array('jquery', 'jquery-ui-autocomplete'));
121 }
122
123 if ($isOptionsPage) {
124 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-general-js', $includesUrl . 'js/general.js',
125 array('jquery', 'abj404-admin-ajax'));
126 wp_localize_script('abj404-general-js', 'abj404General', array(
127 'savingSettings' => __('Saving settings...', '404-solution'),
128 ));
129 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-theme-preview', $includesUrl . 'js/themePreview.js',
130 array('jquery'));
131 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-settings-mode-toggle', $includesUrl . 'ajax/SettingsModeToggle.js',
132 array('jquery', 'abj404-admin-ajax'));
133 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-restore-defaults', $includesUrl . 'ajax/RestoreDefaults.js',
134 array('jquery', 'abj404-admin-ajax'));
135 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-diagnostic-data-card',
136 $includesUrl . 'js/diagnosticDataCard.js', array('jquery', 'abj404-admin-ajax'));
137 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-behavior-tiles', ABJ404_URL . 'includes/js/behaviorTiles.js',
138 array());
139 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-settings-deferred', ABJ404_URL . 'includes/js/settingsDeferred.js',
140 array('jquery'));
141 }
142
143 if ($isCardAccordionPage) {
144 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-options-accordion', $includesUrl . 'js/optionsAccordion.js',
145 array('jquery'));
146 wp_localize_script('abj404-options-accordion', 'abj404Accordion', array(
147 'expandAll' => __('Expand All', '404-solution'),
148 'collapseAll' => __('Collapse All', '404-solution'),
149 ));
150 }
151
152 if ($isOptionsPage) {
153 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-engine-profiles',
154 $includesUrl . 'ajax/ajax-engine-profiles.js',
155 array('jquery'));
156 wp_localize_script('abj404-engine-profiles', 'abj404EngineProfiles', array(
157 'nonce' => wp_create_nonce('abj404_engine_profiles_nonce'),
158 'ajaxUrl' => admin_url('admin-ajax.php'),
159 'i18n' => array(
160 'edit' => __('Edit', '404-solution'),
161 'delete' => __('Delete', '404-solution'),
162 'addProfile' => __('Add Engine Profile', '404-solution'),
163 'editProfile' => __('Edit Engine Profile', '404-solution'),
164 'nameRequired' => __('Profile name is required.', '404-solution'),
165 'patternRequired' => __('URL pattern is required.', '404-solution'),
166 'saved' => __('Profile saved.', '404-solution'),
167 'saveFailed' => __('Failed to save profile.', '404-solution'),
168 'confirmDelete' => __('Delete this engine profile?', '404-solution'),
169 ),
170 ));
171 }
172
173 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt(
174 'abj404-review-feedback',
175 $includesUrl . 'js/reviewFeedback.js',
176 array()
177 );
178
179 self::registerSupportRequestAssets();
180
181 // Every translated script has been registered by this point, so one call
182 // wires wp.i18n for whichever of them this screen actually enqueued.
183 ABJ_404_Solution_WPUtils::registerScriptTranslations();
184
185 ABJ_404_Solution_WPUtils::my_wp_enq_style('abj404solution-styles', ABJ404_URL . 'includes/html/404solutionStyles.css',
186 array());
187 ABJ_404_Solution_WPUtils::my_wp_enq_style('abj404solution-themes', ABJ404_URL . 'includes/html/adminThemes.css',
188 array());
189
190 if (is_rtl()) {
191 ABJ_404_Solution_WPUtils::my_wp_enq_style('abj404solution-rtl', ABJ404_URL . 'includes/html/404solutionStyles-rtl.css',
192 array('abj404solution-styles'));
193 }
194 } catch (Throwable $e) {
195 call_user_func($errorReporter, 'admin_enqueue_scripts', $e);
196 }
197 }
198
199 /**
200 * Read a scalar GET parameter as sanitized text.
201 *
202 * @param string $name
203 * @return string
204 */
205 private static function requestStringParam(string $name): string {
206 return array_key_exists($name, $_GET)
207 ? sanitize_text_field(ABJ_404_Solution_RequestInputNormalizer::normalizeScalar($_GET[$name]))
208 : '';
209 }
210
211 /**
212 * Identify request shapes that need edit-form assets instead of list-table assets.
213 *
214 * @param string $subpage
215 * @return bool
216 */
217 private static function isEditPageRequest(string $subpage): bool {
218 if ($subpage === 'abj404_edit') {
219 return true;
220 }
221
222 if (self::requestStringParam('action') !== 'edit') {
223 return false;
224 }
225
226 if (!in_array($subpage, array('abj404_redirects', 'abj404_captured'), true)) {
227 return false;
228 }
229
230 return array_key_exists('id', $_GET) || array_key_exists('idnum', $_GET);
231 }
232
233 /**
234 * Enqueue the support-request button assets specifically for wp-admin/plugins.php.
235 *
236 * @param string $hook
237 * @param callable(string, Throwable): void $errorReporter
238 * @return void
239 */
240 public static function enqueueSupportRequestAssetsOnPluginsPage($hook, callable $errorReporter): void {
241 if ($hook !== 'plugins.php') {
242 return;
243 }
244 try {
245 self::registerSupportRequestAssets();
246 ABJ_404_Solution_WPUtils::registerScriptTranslations();
247 } catch (Throwable $e) {
248 call_user_func($errorReporter, 'admin_enqueue_scripts:plugins.php', $e);
249 }
250 }
251
252 /**
253 * @param string $vuBase URL prefix for the ajax/ assets directory.
254 * @return void
255 */
256 private static function enqueueViewUpdaterModules(string $vuBase): void {
257 $enq = array('ABJ_404_Solution_WPUtils', 'my_wp_enq_scrpt');
258 // Client build identity (Bruno timeout cause matrix, gap GF). Loaded
259 // ahead of every diagnostic module so each can hand it the source of
260 // its own executing body; a module that ran before it would silently
261 // shrink the manifest, which is the blind spot this closes.
262 $enq('abj404-view-updater-client-build-registry',
263 $vuBase . 'view_updater_client_build_registry.js', array());
264 $enq('abj404-view-updater-nonce-refresh',
265 $vuBase . 'view_updater_nonce_refresh.js', array('jquery'));
266 $enq('abj404-view-updater-stage-diagnostics',
267 $vuBase . 'view_updater_stage_diagnostics.js',
268 array('jquery', 'abj404-view-updater-client-build-registry'));
269 $enq('abj404-view-updater-compare',
270 $vuBase . 'view_updater_compare.js', array('jquery'));
271 $enq('abj404-view-updater-refresh-pill',
272 $vuBase . 'view_updater_refresh_pill.js', array('jquery'));
273 $enq('abj404-view-updater-stats', $vuBase . 'view_updater_stats.js',
274 array('jquery', 'abj404-view-updater-refresh-pill', 'abj404-view-updater-nonce-refresh'));
275 $enq('abj404-view-updater-table-init', $vuBase . 'view_updater_table_init.js',
276 array('jquery', 'abj404-view-updater-refresh-pill', 'abj404-view-updater-stats',
277 'abj404-view-updater-nonce-refresh'));
278 $enq('abj404-view-updater-pagination-request',
279 $vuBase . 'view_updater_pagination_request.js',
280 array('jquery', 'abj404-view-updater-compare', 'abj404-view-updater-client-build-registry'));
281 // Client transport telemetry (tab identity -> cross-tab presence and
282 // page AJAX activity -> storage -> page observations -> attempt
283 // records -> delivery). Loaded before the transport that records
284 // through them; each degrades to a no-op if it fails to load.
285 $enq('abj404-view-updater-client-tab-identity',
286 $vuBase . 'view_updater_client_tab_identity.js', array('abj404-view-updater-client-build-registry'));
287 $enq('abj404-view-updater-client-tab-presence',
288 $vuBase . 'view_updater_client_tab_presence.js',
289 array('abj404-view-updater-client-tab-identity', 'abj404-view-updater-client-build-registry'));
290 $enq('abj404-view-updater-page-ajax-activity',
291 $vuBase . 'view_updater_page_ajax_activity.js',
292 array('jquery', 'abj404-view-updater-client-build-registry'));
293 $enq('abj404-view-updater-client-attempt-buffer',
294 $vuBase . 'view_updater_client_attempt_buffer.js', array('abj404-view-updater-client-build-registry'));
295 $enq('abj404-view-updater-client-telemetry-store',
296 $vuBase . 'view_updater_client_telemetry_store.js',
297 array('abj404-view-updater-client-tab-identity',
298 'abj404-view-updater-client-tab-presence',
299 'abj404-view-updater-client-attempt-buffer', 'abj404-view-updater-client-build-registry'));
300 $enq('abj404-view-updater-client-main-thread-observations',
301 $vuBase . 'view_updater_client_main_thread_observations.js', array('abj404-view-updater-client-build-registry'));
302 $enq('abj404-view-updater-client-resource-timing',
303 $vuBase . 'view_updater_client_resource_timing.js', array('abj404-view-updater-client-build-registry'));
304 $enq('abj404-view-updater-response-body-shape',
305 $vuBase . 'view_updater_response_body_shape.js', array('abj404-view-updater-client-build-registry'));
306 $enq('abj404-view-updater-client-telemetry-env',
307 $vuBase . 'view_updater_client_telemetry_env.js',
308 array('abj404-view-updater-client-telemetry-store',
309 'abj404-view-updater-client-tab-identity',
310 'abj404-view-updater-client-tab-presence',
311 'abj404-view-updater-page-ajax-activity',
312 'abj404-view-updater-client-main-thread-observations', 'abj404-view-updater-client-build-registry'));
313 $enq('abj404-view-updater-transport-telemetry',
314 $vuBase . 'view_updater_transport_telemetry.js',
315 array('jquery', 'abj404-view-updater-client-telemetry-store',
316 'abj404-view-updater-client-telemetry-env',
317 'abj404-view-updater-client-resource-timing',
318 'abj404-view-updater-response-body-shape', 'abj404-view-updater-client-build-registry'));
319 $enq('abj404-view-updater-transport-telemetry-delivery',
320 $vuBase . 'view_updater_transport_telemetry_delivery.js',
321 array('abj404-view-updater-client-telemetry-store',
322 'abj404-view-updater-transport-telemetry', 'abj404-view-updater-client-build-registry'));
323 $enq('abj404-view-updater-health-bar', $vuBase . 'view_updater_health_bar.js',
324 array('jquery', 'abj404-view-updater-nonce-refresh',
325 'abj404-view-updater-transport-telemetry',
326 'abj404-view-updater-transport-telemetry-delivery',
327 'abj404-view-updater-client-build-registry'));
328 $enq('abj404-view-updater-pagination-transport',
329 $vuBase . 'view_updater_pagination_transport.js',
330 array('jquery', 'abj404-view-updater-nonce-refresh',
331 'abj404-view-updater-transport-telemetry',
332 'abj404-view-updater-transport-telemetry-delivery', 'abj404-view-updater-client-build-registry'));
333 $enq('abj404-view-updater-lazy-backfill',
334 $vuBase . 'view_updater_lazy_backfill.js',
335 array('jquery', 'abj404-view-updater-nonce-refresh'));
336 $enq('abj404-view-updater-pagination-response-apply',
337 $vuBase . 'view_updater_pagination_response_apply.js',
338 array('jquery', 'abj404-view-updater-table-init', 'abj404-view-updater-health-bar',
339 'abj404-view-updater-lazy-backfill'));
340 $enq('abj404-view-updater-pagination-error-notice',
341 $vuBase . 'view_updater_pagination_error_notice.js',
342 array('jquery', 'abj404-view-updater-stage-diagnostics',
343 'abj404-view-updater-table-init', 'abj404-view-updater-nonce-refresh'));
344 $enq('abj404-view-updater-canary-measurements',
345 $vuBase . 'view_updater_canary_measurements.js',
346 array('abj404-view-updater-client-resource-timing',
347 'abj404-view-updater-transport-telemetry',
348 'abj404-view-updater-client-build-registry'));
349 $enq('abj404-view-updater-concurrent-control-evidence',
350 $vuBase . 'view_updater_concurrent_control_evidence.js',
351 array('abj404-view-updater-client-telemetry-store',
352 'abj404-view-updater-canary-measurements',
353 'abj404-view-updater-client-build-registry'));
354 $enq('abj404-view-updater-canary-cooldown',
355 $vuBase . 'view_updater_canary_cooldown.js',
356 array('abj404-view-updater-client-build-registry'));
357 $enq('abj404-view-updater-canary-ladder',
358 $vuBase . 'view_updater_canary_ladder.js',
359 array('jquery', 'abj404-view-updater-nonce-refresh',
360 'abj404-view-updater-canary-cooldown',
361 'abj404-view-updater-client-telemetry-env',
362 'abj404-view-updater-transport-telemetry',
363 'abj404-view-updater-canary-measurements',
364 'abj404-view-updater-client-build-registry'));
365 $enq('abj404-view-updater-pagination', $vuBase . 'view_updater_pagination.js',
366 array('jquery', 'abj404-view-updater-compare', 'abj404-view-updater-stage-diagnostics',
367 'abj404-view-updater-table-init', 'abj404-view-updater-health-bar',
368 'abj404-view-updater-refresh-pill',
369 'abj404-view-updater-nonce-refresh',
370 'abj404-view-updater-pagination-request',
371 'abj404-view-updater-transport-telemetry-delivery',
372 'abj404-view-updater-pagination-transport',
373 'abj404-view-updater-pagination-response-apply',
374 'abj404-view-updater-pagination-error-notice',
375 'abj404-view-updater-concurrent-control-evidence',
376 'abj404-view-updater-canary-ladder'));
377 $enq('abj404-view-updater', $vuBase . 'view_updater.js',
378 array('jquery', 'jquery-ui-autocomplete',
379 'abj404-view-updater-stage-diagnostics',
380 'abj404-view-updater-compare',
381 'abj404-view-updater-refresh-pill', 'abj404-view-updater-stats',
382 'abj404-view-updater-table-init',
383 'abj404-view-updater-pagination',
384 'abj404-view-updater-lazy-backfill',
385 'abj404-view-updater-nonce-refresh'));
386 }
387
388 /** @return void */
389 private static function registerSupportRequestAssets(): void {
390 // The support client drains the client transport telemetry buffer, so
391 // the storage adapter has to be present wherever the button is -- the
392 // plugins-page button included, since the records it reads outlive the
393 // admin screen that produced them. It reads every tab's buffer, and it
394 // keys its own by tab, so the identity module rides along; presence is
395 // not needed here (nothing on this screen writes a record).
396 // The build registry rides along for the same reason it does on the
397 // table screens: the support client is itself one of the modules whose
398 // shipped bytes have to be provable (gap GF), and it is the module
399 // that collects the evidence, so a stale copy of it is the worst case.
400 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-view-updater-client-build-registry',
401 ABJ404_URL . 'includes/ajax/view_updater_client_build_registry.js', array());
402 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-view-updater-client-tab-identity',
403 ABJ404_URL . 'includes/ajax/view_updater_client_tab_identity.js',
404 array('abj404-view-updater-client-build-registry'));
405 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-view-updater-client-attempt-buffer',
406 ABJ404_URL . 'includes/ajax/view_updater_client_attempt_buffer.js',
407 array('abj404-view-updater-client-build-registry'));
408 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-view-updater-client-telemetry-store',
409 ABJ404_URL . 'includes/ajax/view_updater_client_telemetry_store.js',
410 array('abj404-view-updater-client-tab-identity',
411 'abj404-view-updater-client-attempt-buffer',
412 'abj404-view-updater-client-build-registry'));
413 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-support-request-client',
414 ABJ404_URL . 'includes/ajax/SupportRequest.js',
415 array('abj404-view-updater-client-telemetry-store', 'abj404-view-updater-client-build-registry'));
416 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-support-request-transport',
417 ABJ404_URL . 'includes/js/support-request-transport.js',
418 array('abj404-support-request-client'));
419 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-support-request-modal-view',
420 ABJ404_URL . 'includes/js/support-request-modal-view.js', array());
421 ABJ_404_Solution_WPUtils::my_wp_enq_scrpt('abj404-support-request-button',
422 ABJ404_URL . 'includes/js/support-request-button.js',
423 array('abj404-support-request-client', 'abj404-support-request-transport', 'abj404-support-request-modal-view'));
424 if (!function_exists('wp_add_inline_script')) {
425 return;
426 }
427 $supportNonce = wp_create_nonce(ABJ_404_Solution_Ajax_SupportRequest::NONCE_ACTION);
428 $previewNonce = wp_create_nonce(ABJ_404_Solution_Ajax_SupportRequestPreview::NONCE_ACTION);
429 $ajaxUrl = function_exists('admin_url') ? admin_url('admin-ajax.php') : '/wp-admin/admin-ajax.php';
430 $payload = wp_json_encode(array(
431 'ajaxurl' => $ajaxUrl,
432 'nonces' => array(
433 'support_request' => $supportNonce,
434 'support_request_preview' => $previewNonce,
435 ),
436 // Canary ladder step 1 (Bruno matrix req. 7): a same-host static
437 // asset URL the client fetches directly, no admin-ajax involved,
438 // to test delivery with no PHP in the path at all.
439 'canaryStaticAssetUrl' => ABJ404_URL . 'includes/diagnostics/assets/canary-1kb.txt',
440 // Gap-hunt iteration 3: the repeated ladder baseline belongs only
441 // to the same pre-release diagnostic that consumes its result.
442 'detachAbDiagnosticEnabled' => ABJ_404_Solution_DetachAbExperiment::isEnabled(),
443 ));
444 $bootstrap = 'window.ABJ404=window.ABJ404||{};Object.assign(window.ABJ404,'
445 . (is_string($payload) ? $payload : '{}') . ');';
446 wp_add_inline_script('abj404-support-request-client', $bootstrap, 'before');
447 }
448 }
449