PluginProbe
Adminify – White Label, Admin Menu Editor, Login Customizer / 4.2.13
Adminify – White Label, Admin Menu Editor, Login Customizer v4.2.13
4.3.1 4.3.0 4.2.26 4.2.25 4.2.24 4.2.23 4.2.22 4.2.21 4.2.20 4.2.19 4.2.18 4.2.17 4.2.16 4.2.15 4.2.14 4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 4.1.17 All 164 releases
adminify / Inc / Admin / AdminSettings.php

AdminSettings.php in Adminify – White Label, Admin Menu Editor, Login Customizer 4.2.13, at Inc/Admin/AdminSettings.php

536 lines 26.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace PXLBSAdminify\Inc\Admin;
4
5 use PXLBSAdminify\Inc\Utils;
6 use PXLBSAdminify\Inc\Admin\AdminSettingsModel;
7 use PXLBSAdminify\Inc\Admin\Options\Customize;
8 use PXLBSAdminify\Inc\Admin\Options\Productivity;
9 use PXLBSAdminify\Inc\Admin\Options\CustomCSSJS;
10 use PXLBSAdminify\Inc\Admin\Options\Performance;
11 use PXLBSAdminify\Inc\Admin\Options\MenuLayout;
12 use PXLBSAdminify\Inc\Admin\Options\Security;
13 use PXLBSAdminify\Inc\Admin\Options\White_Label;
14 if ( !defined( 'ABSPATH' ) ) {
15 die;
16 }
17 // Cannot access directly.
18 if ( !class_exists( 'AdminSettings' ) ) {
19 class AdminSettings extends AdminSettingsModel {
20 // AdminSettings cannot be extended by creating instances
21 public static $instance = null;
22
23 public $defaults = [];
24
25 private $message = [];
26
27 // Snapshot of the settings admin menu taken just before Freemius runs,
28 // used to restore it afterwards (see pxlbsadminify_capture_settings_menu).
29 private $captured_menu_item = null;
30
31 private $captured_submenu = null;
32
33 private $captured_page_cb = null;
34
35 public function __construct() {
36 // this should be first so the default values get stored
37 $this->pxlbsadminify_options();
38 parent::__construct( (array) get_option( $this->prefix ) );
39 add_action( 'network_admin_menu', [$this, 'network_panel'] );
40 // Freemius's _prepare_admin_menu (admin_menu @ 999999999, x2 via
41 // parallel_activation) strips the per-site "wp-adminify-settings" menu on
42 // multisite (SDK 2.13.1+), so individual site admins hit
43 // "Cannot load wp-adminify-settings". Snapshot the menu just before Freemius
44 // runs and restore it just after — this keeps the menu registered at the
45 // normal priority (so submenu URLs stay correct) yet survives Freemius's
46 // removal pass.
47 add_action( 'admin_menu', [$this, 'pxlbsadminify_capture_settings_menu'], 999999998 );
48 add_action( 'admin_menu', [$this, 'pxlbsadminify_restore_settings_menu'], PHP_INT_MAX );
49 add_filter(
50 'pxlbsadminify_render_field_html',
51 array($this, 'render_pro_locked_field'),
52 10,
53 2
54 );
55 // System mode shows BOTH header logos (see system_mode_logo_styles). Run after
56 // the framework registers its 'adminify' stylesheet (default priority 10).
57 add_action( 'admin_enqueue_scripts', array($this, 'system_mode_logo_styles'), 100 );
58 }
59
60 /**
61 * Fix the settings-header logo in System mode.
62 *
63 * The header prints both a light and a dark logo; the framework CSS hides one
64 * based on the body's .adminify-light-mode / .adminify-dark-mode class. System
65 * mode adds NEITHER class, so neither hide-rule fires and both logos show.
66 *
67 * These fallbacks are scoped under .wp-adminify-settings with lower specificity
68 * than the mode-class rules, so they only take effect when no mode class is
69 * present (i.e. System) and pick a single logo from the OS prefers-color-scheme.
70 */
71 public function system_mode_logo_styles() {
72 if ( !wp_style_is( 'adminify', 'enqueued' ) && !wp_style_is( 'adminify', 'registered' ) ) {
73 return;
74 }
75 $css = '.wp-adminify-settings img.adminify-settings-dark-logo{display:none}' . '.wp-adminify-settings img.adminify-settings-light-logo{display:inline-block}' . '@media (prefers-color-scheme: dark){' . '.wp-adminify-settings img.adminify-settings-light-logo{display:none}' . '.wp-adminify-settings img.adminify-settings-dark-logo{display:inline-block}' . '}';
76 wp_add_inline_style( 'adminify', $css );
77 }
78
79 /**
80 * Snapshot the wp-adminify-settings menu, submenu and page callback right
81 * before Freemius's _prepare_admin_menu (priority 999999999) can remove them.
82 */
83 public function pxlbsadminify_capture_settings_menu() {
84 global $menu, $submenu, $wp_filter;
85 $slug = 'wp-adminify-settings';
86 $hook = 'toplevel_page_' . $slug;
87 if ( !empty( $menu ) ) {
88 foreach ( $menu as $item ) {
89 if ( isset( $item[2] ) && $item[2] === $slug ) {
90 $this->captured_menu_item = $item;
91 break;
92 }
93 }
94 }
95 if ( isset( $submenu[$slug] ) ) {
96 $this->captured_submenu = $submenu[$slug];
97 }
98 if ( isset( $wp_filter[$hook] ) ) {
99 foreach ( $wp_filter[$hook]->callbacks as $callbacks ) {
100 foreach ( $callbacks as $callback ) {
101 $this->captured_page_cb = $callback['function'];
102 break 2;
103 }
104 }
105 }
106 }
107
108 /**
109 * Restore anything Freemius removed: the top-level menu entry, its submenus
110 * and the page render callback. No-op when nothing was removed.
111 */
112 public function pxlbsadminify_restore_settings_menu() {
113 global $menu, $submenu;
114 $slug = 'wp-adminify-settings';
115 $hook = 'toplevel_page_' . $slug;
116 // Re-attach the page render callback so the page is loadable again.
117 if ( !has_action( $hook ) && $this->captured_page_cb ) {
118 add_action( $hook, $this->captured_page_cb );
119 }
120 // Re-add the top-level menu entry if it was removed.
121 $has_top = false;
122 if ( !empty( $menu ) ) {
123 foreach ( $menu as $item ) {
124 if ( isset( $item[2] ) && $item[2] === $slug ) {
125 $has_top = true;
126 break;
127 }
128 }
129 }
130 if ( !$has_top && $this->captured_menu_item ) {
131 $menu[] = $this->captured_menu_item;
132 }
133 // Re-add submenus if they were removed (preserves their original URLs).
134 if ( empty( $submenu[$slug] ) && $this->captured_submenu ) {
135 $submenu[$slug] = $this->captured_submenu;
136 }
137 }
138
139 public function network_panel() {
140 add_menu_page(
141 $this->get_plugin_menu_label(),
142 $this->get_plugin_menu_label(),
143 'manage_options',
144 'wp-adminify-settings',
145 [$this, 'network_panel_display'],
146 PXLBSADMINIFY_ASSETS_IMAGE . 'logos/menu-icon.svg',
147 30
148 );
149 }
150
151 public function get_bloginfo( $blog_id, $fields = [] ) {
152 switch_to_blog( $blog_id );
153 $_fields = [];
154 foreach ( $fields as $field ) {
155 $_fields[$field] = get_bloginfo( $field );
156 }
157 restore_current_blog();
158 return $_fields;
159 }
160
161 public function get_sites() {
162 $sites = get_sites();
163 foreach ( $sites as $site ) {
164 $info = $this->get_bloginfo( $site->blog_id, ['name'] );
165 $site->name = $info['name'];
166 }
167 return $sites;
168 }
169
170 public function get_sites_option_empty() {
171 /* translators: 1: option value attribute, 2: option label text */
172 return sprintf( __( '<option value="%1$s">%2$s</option>', 'adminify' ), 0, __( 'Select', 'adminify' ) );
173 }
174
175 public function get_sites_option( $sites = [], $add_empty_slot = false ) {
176 if ( empty( $sites ) ) {
177 $sites = $this->get_sites();
178 }
179 $_sites = [];
180 if ( $add_empty_slot ) {
181 $_sites[] = $this->get_sites_option_empty();
182 }
183 foreach ( $sites as $site ) {
184 /* translators: 1: site blog ID used as option value, 2: site name shown as option label */
185 $_sites[] = sprintf( __( '<option value="%1$s">%2$s</option>', 'adminify' ), $site->blog_id, $site->name );
186 }
187 return implode( '', $_sites );
188 }
189
190 public function maybe_display_message() {
191 if ( empty( $this->message ) ) {
192 return;
193 }
194 $classes = 'adminify-status adminify-status--' . esc_attr( $this->message['type'] );
195 echo '<div class="' . esc_attr( $classes ) . '"><p>' . wp_kses_post( $this->message['message'] ) . '</p></div>';
196 }
197
198 public function network_panel_display() {
199 $multisite_settings = sprintf(
200 wp_kses_post( '<div class="%1$s"><h2>%2$s</h2> <a href="%3$s" target="_blank">%4$s</a></div>', 'adminify' ),
201 Utils::upgrade_pro_notice_class(),
202 esc_html__( 'Network Settings', 'adminify' ),
203 esc_url( 'https://wpadminify.com/pricing' ),
204 Utils::upgrade_pro_notice_class( 'Please Upgrade or Activate License' )
205 );
206 // Initialize the multisite_settings variable
207 $multisite_settings = apply_filters( 'pxlbsadminify/admin_settings/network', $multisite_settings );
208 // Apply the filter
209 echo wp_kses_post( $multisite_settings );
210 }
211
212 public function option_modules() {
213 $extra_data_merge = [];
214 $clonable_data = [
215 'pxlbsadminify_settings' => __( 'Adminify Options', 'adminify' ),
216 '_adminify_admin_columns_adminify_admin_page' => __( 'Admin Page Columns Data', 'adminify' ),
217 ];
218 // Activity Logs Active
219 if ( Utils::is_plugin_active( 'adminify-activity-logs/adminify-activity-logs.php' ) ) {
220 $extra_data_merge['adminify_activity_logs'] = __( 'Activity Logs Data', 'adminify' );
221 $clonable_data = array_merge( $clonable_data, $extra_data_merge );
222 }
223 // Quick Circle Menu Active
224 if ( Utils::is_plugin_active( 'adminify-quick-circle-menu/adminify-quick-circle-menu.php' ) ) {
225 $extra_data_merge['pxlbsadminify_quick_circle_menu'] = __( 'Quick Circle Menu', 'adminify' );
226 $clonable_data = array_merge( $clonable_data, $extra_data_merge );
227 }
228 // Google Pagespeed Active
229 if ( Utils::is_plugin_active( 'adminify-google-pagespeed/adminify-google-pagespeed.php' ) ) {
230 $extra_data_merge['adminify_page_speed'] = __( 'Google Pagespeed', 'adminify' );
231 $clonable_data = array_merge( $clonable_data, $extra_data_merge );
232 }
233 // Login Customizer Active
234 if ( Utils::is_plugin_active( 'loginfy/loginfy.php' ) ) {
235 $extra_data_merge['pxlbsadminify_login'] = __( 'Loginify Data', 'adminify' );
236 $clonable_data = array_merge( $clonable_data, $extra_data_merge );
237 }
238 // Header Footer Scripts Active
239 if ( Utils::is_plugin_active( 'adminify-sidebar-generator/adminify-sidebar-generator.php' ) ) {
240 $extra_data_merge['pxlbsadminify_sidebar_settings'] = __( 'Sidebar Generator', 'adminify' );
241 $clonable_data = array_merge( $clonable_data, $extra_data_merge );
242 }
243 // Sidebar Generator Active
244 if ( Utils::is_plugin_active( 'adminify-header-footer-scripts/adminify-header-footer-scripts.php' ) ) {
245 $extra_data_merge['pxlbsadminify_custom_js_css'] = __( 'Custom JS/CSS', 'adminify' );
246 $clonable_data = array_merge( $clonable_data, $extra_data_merge );
247 }
248 // Admin Columns Active
249 if ( Utils::is_plugin_active( 'adminify-admin-columns/adminify-admin-columns.php' ) ) {
250 $extra_data_merge['_adminify_admin_columns_page'] = __( 'Admin Columns Page Data', 'adminify' );
251 $extra_data_merge['_adminify_admin_columns_post'] = __( 'Admin Columns Post Data', 'adminify' );
252 $clonable_data = array_merge( $clonable_data, $extra_data_merge );
253 }
254 return (array) apply_filters( 'adminify_clone_blog_option_modules', $clonable_data );
255 }
256
257 public function get_pagespeed_data( $copy_from ) {
258 switch_to_blog( $copy_from );
259 global $wpdb;
260 $table_name = $wpdb->prefix . 'adminify_page_speed';
261 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter -- table name derived from a sanitized internal value, no user input; direct query required for reading a custom plugin table; not cached intentionally.
262 $histories = $wpdb->get_results( "SELECT * FROM {$table_name}", ARRAY_A );
263 restore_current_blog();
264 return $histories;
265 }
266
267 public function clone_pagespeed_data( $histories, $copy_to ) {
268 switch_to_blog( $copy_to );
269 global $wpdb;
270 $table_name = $wpdb->prefix . 'adminify_page_speed';
271 foreach ( $histories as $history ) {
272 unset($history['id']);
273 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- direct query required for writing to a custom plugin table; not cached intentionally.
274 $wpdb->insert( "{$table_name}", $history, [
275 'url' => '%s',
276 'score_desktop' => '%d',
277 'score_mobile' => '%d',
278 'data_desktop' => '%s',
279 'data_mobile' => '%s',
280 'screenshot' => '%s',
281 'time' => '%s',
282 ] );
283 }
284 restore_current_blog();
285 }
286
287 public function get_admin_columns_options( $copy_from ) {
288 $options = [];
289 switch_to_blog( $copy_from );
290 $args = [
291 'public' => true,
292 ];
293 $types = get_post_types( $args );
294 unset($types['attachment']);
295 restore_current_blog();
296 foreach ( $types as $type ) {
297 $options[] = '_adminify_admin_columns_meta_' . esc_attr( $type );
298 }
299 return $options;
300 }
301
302 public static function get_instance() {
303 if ( !is_null( self::$instance ) ) {
304 return self::$instance;
305 }
306 self::$instance = new self();
307 return self::$instance;
308 }
309
310 protected function get_defaults() {
311 return $this->defaults;
312 }
313
314 public static function get_pro_label() {
315 $is_pro = "";
316 $is_pro = esc_html__( 'Adminify', 'adminify' );
317 return $is_pro;
318 }
319
320 public function get_plugin_menu_icon() {
321 $menu_icon = PXLBSADMINIFY_ASSETS_IMAGE . 'logos/menu-icon-light.svg';
322 $saved_data = get_option( $this->prefix );
323 if ( isset( $saved_data['white_label']['adminify']['menu_icon'] ) && !empty( $saved_data['white_label']['adminify']['menu_icon']['url'] ) ) {
324 $menu_icon = $saved_data['white_label']['adminify']['menu_icon']['url'];
325 }
326 return $menu_icon;
327 }
328
329 public function get_plugin_menu_label() {
330 $plugin_menu_label = self::get_pro_label();
331 $saved_data = get_option( $this->prefix );
332 if ( isset( $saved_data['white_label']['adminify']['menu_label'] ) && !empty( $saved_data['white_label']['adminify']['menu_label'] ) ) {
333 $plugin_menu_label = $saved_data['white_label']['adminify']['menu_label'];
334 }
335 return $plugin_menu_label;
336 }
337
338 public static function support_url() {
339 $support_url = '';
340 $support_url = 'https://wordpress.org/support/plugin/adminify/#new-topic-0';
341 return $support_url;
342 }
343
344 public function pxlbsadminify_options() {
345 if ( !class_exists( 'ADMINIFY' ) ) {
346 return;
347 }
348 $submenu_position = apply_filters( 'pxlbsadminify_submenu_position', 30 );
349 $saved_data = get_option( $this->prefix );
350 $global_admin_ui_mode = ( empty( $saved_data['light_dark_mode']['admin_ui_mode'] ) ? 'light' : sanitize_text_field( $saved_data['light_dark_mode']['admin_ui_mode'] ) );
351 $admin_ui_mode = ( empty( get_user_meta( get_current_user_id(), 'color_mode', true ) ) ? $global_admin_ui_mode : get_user_meta( get_current_user_id(), 'color_mode', true ) );
352 $light_logo_image_url = PXLBSADMINIFY_ASSETS_IMAGE . 'logos/logo-text-light.svg';
353 $dark_logo_image_url = PXLBSADMINIFY_ASSETS_IMAGE . 'logos/logo-text-dark.svg';
354 $plugin_author_name = PXLBSADMINIFY_AUTHOR;
355 // WP Adminify Options
356 \ADMINIFY::createOptions( $this->prefix, [
357 'framework_title' => '<img class="wp-adminify-settings-logo adminify-settings-light-logo" src=' . esc_url( $light_logo_image_url ) . '><img class="wp-adminify-settings-logo adminify-settings-dark-logo" src=' . esc_url( $dark_logo_image_url ) . '>' . ' <small>by ' . esc_html( $plugin_author_name ) . '</small>',
358 'framework_class' => '',
359 'menu_title' => $this->get_plugin_menu_label(),
360 'menu_slug' => 'wp-adminify-settings',
361 'menu_capability' => 'manage_options',
362 'menu_icon' => $this->get_plugin_menu_icon(),
363 'menu_position' => 30,
364 'menu_hidden' => false,
365 'menu_parent' => 'admin.php?page=wp-adminify-settings',
366 'show_bar_menu' => true,
367 'show_sub_menu' => false,
368 'show_in_network' => false,
369 'show_in_customizer' => false,
370 'show_search' => false,
371 'show_reset_all' => true,
372 'show_reset_section' => true,
373 'show_footer' => true,
374 'show_all_options' => false,
375 'show_form_warning' => true,
376 'sticky_header' => false,
377 'save_defaults' => false,
378 'ajax_save' => true,
379 'admin_bar_menu_icon' => '',
380 'admin_bar_menu_priority' => 80,
381 'footer_text' => ' ',
382 'footer_after' => ' ',
383 'footer_credit' => ' ',
384 'database' => 'options',
385 'transient_time' => 0,
386 'contextual_help' => [],
387 'contextual_help_sidebar' => '',
388 'enqueue_webfont' => true,
389 'async_webfont' => false,
390 'output_css' => true,
391 'nav' => 'normal',
392 'has_nav' => false,
393 'theme' => 'dark',
394 'class' => 'wp-adminify-settings',
395 'defaults' => [],
396 ] );
397 $this->defaults = array_merge( $this->defaults, ( new Customize() )->get_defaults() );
398 $this->defaults = array_merge( $this->defaults, ( new MenuLayout() )->get_defaults() );
399 $this->defaults = array_merge( $this->defaults, ( new Productivity() )->get_defaults() );
400 $this->defaults = array_merge( $this->defaults, ( new Security() )->get_defaults() );
401 $this->defaults = array_merge( $this->defaults, ( new Performance() )->get_defaults() );
402 $this->defaults = array_merge( $this->defaults, ( new CustomCSSJS() )->get_defaults() );
403 $this->defaults = array_merge( $this->defaults, ( new White_Label() )->get_defaults() );
404 // Fix Missing keys on save
405 add_filter( "adminify_{$this->prefix}_save", function ( $data ) {
406 $data = $this->fix_missing_keys( $this->defaults, $data );
407 return $data;
408 } );
409 // Backup Settings
410 \ADMINIFY::createSection( $this->prefix, [
411 'title' => __( 'Backup', 'adminify' ),
412 'icon' => 'fas fa-database',
413 'fields' => [[
414 'type' => 'subheading',
415 'content' => Utils::help_urls(
416 __( 'Backup Config Settings', 'adminify' ),
417 'https://wpadminify.com/docs/adminify/export-import/backup',
418 'https://www.youtube.com/playlist?list=PLqpMw0NsHXV-EKj9Xm1DMGa6FGniHHly8',
419 'https://www.facebook.com/groups/jeweltheme',
420 self::support_url()
421 ),
422 ], [
423 'type' => 'backup',
424 'class' => 'adminify-block',
425 ]],
426 ] );
427 }
428
429 function fix_missing_keys( $defaults, $data ) {
430 if ( is_array( $defaults ) ) {
431 foreach ( $defaults as $key => $value ) {
432 if ( is_array( $value ) ) {
433 if ( !isset( $data[$key] ) || gettype( $data[$key] ) !== 'array' ) {
434 $data[$key] = [];
435 }
436 $data[$key] = $this->fix_missing_keys( $value, $data[$key] );
437 }
438 }
439 }
440 return $data;
441 }
442
443 public function get_prefix() {
444 return $this->prefix;
445 }
446
447 /**
448 * Render-time hook that neutralizes Pro-locked controls in the
449 * free build. Bypassed when the Pro plugin is active so the Pro
450 * UI is unchanged.
451 *
452 * Two operating modes, both detected from the rendered HTML:
453 *
454 * 1. Field-level lock (the field's class array contains
455 * 'adminify-pro-locked'): strip name="..." from every input/
456 * select/textarea, add disabled + tabindex="-1", and wrap the
457 * entire field with a <div class="adminify-pro-locked-wrapper">
458 * that carries a lock-icon + "Upgrade to Pro" CTA overlay.
459 *
460 * 2. Option-level lock (a checkbox/radio option label contains
461 * <span class="adminify-pro-locked-option">): for every <li>
462 * that contains the marker span, strip name="..." from the
463 * enclosed input and add disabled + tabindex="-1". Inline lock
464 * icon is rendered by CSS off the marker class itself.
465 *
466 * @param string $html Rendered field HTML from the framework.
467 * @param array $field Field config array.
468 * @return string Possibly transformed HTML.
469 */
470 public function render_pro_locked_field( $html, $field ) {
471 // Pro plugin active: leave HTML untouched.
472 if ( function_exists( 'jltwp_adminify' ) && jltwp_adminify()->can_use_premium_code__premium_only() ) {
473 return $html;
474 }
475 $field_class = '';
476 if ( is_array( $field ) && isset( $field['class'] ) ) {
477 $field_class = (string) $field['class'];
478 }
479 // Detect field-level lock from either the new marker class
480 // (adminify-pro-locked) or the legacy Pro-feature classes that
481 // pre-date the render-disable hook. Both signal a Pro-only
482 // field that must not submit a value in the free build.
483 $has_field_lock = strpos( $field_class, 'adminify-pro-locked' ) !== false || strpos( $field_class, 'adminify-pro-fieldset' ) !== false || strpos( $field_class, 'adminify-pro-feature' ) !== false || strpos( $field_class, 'adminify-pro-notice' ) !== false;
484 // Per-option lock signal: the helper Utils::upgrade_pro_class()
485 // emits a <span class="adminify-pro-checkbox"> next to a Pro-only
486 // checkbox label. Match that span (or the historical
487 // adminify-pro-locked-option marker) inside <li> blocks.
488 $has_option_lock = strpos( $html, 'adminify-pro-checkbox' ) !== false || strpos( $html, 'adminify-pro-locked-option' ) !== false;
489 if ( !$has_field_lock && !$has_option_lock ) {
490 return $html;
491 }
492 if ( $has_field_lock ) {
493 // Strip name attrs and disable inputs across the whole field.
494 // The visual treatment is the inline <span class="adminify-pro-badge">Pro</span>
495 // rendered by the helper — no extra wrapper / overlay.
496 $html = $this->adminify_strip_inputs( $html );
497 return $html;
498 }
499 // Option-level: only strip inputs inside <li> blocks that
500 // carry the per-option marker. Match each <li> ... </li>
501 // non-greedily.
502 $html = preg_replace_callback( '#<li\\b[^>]*>(.*?)</li>#is', function ( $li_match ) {
503 $li_inner = $li_match[1];
504 if ( strpos( $li_inner, 'adminify-pro-checkbox' ) === false && strpos( $li_inner, 'adminify-pro-locked-option' ) === false ) {
505 return $li_match[0];
506 }
507 $open_tag_end = strpos( $li_match[0], '>' );
508 $open_tag = substr( $li_match[0], 0, $open_tag_end + 1 );
509 return $open_tag . $this->adminify_strip_inputs( $li_inner ) . '</li>';
510 }, $html );
511 return $html;
512 }
513
514 /**
515 * Strip name="..." from inputs/selects/textareas in the given
516 * HTML and add disabled + tabindex="-1" if missing. Pure string
517 * transform — the framework emits a known shape.
518 */
519 private function adminify_strip_inputs( $html ) {
520 $html = preg_replace( '/\\s+name="[^"]*"/i', '', $html );
521 $html = preg_replace_callback( '/<(input|select|textarea)\\b([^>]*)>/i', function ( $m ) {
522 $attrs = $m[2];
523 if ( strpos( $attrs, 'disabled' ) === false ) {
524 $attrs .= ' disabled="disabled"';
525 }
526 if ( strpos( $attrs, 'tabindex=' ) === false ) {
527 $attrs .= ' tabindex="-1"';
528 }
529 return '<' . $m[1] . $attrs . '>';
530 }, $html );
531 return $html;
532 }
533
534 }
535
536 }