PluginProbe
Adminify – White Label, Admin Menu Editor, Login Customizer / 4.2.9
Adminify – White Label, Admin Menu Editor, Login Customizer v4.2.9
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.9, at Inc/Admin/AdminSettings.php

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