PluginProbe
Adminify – White Label, Admin Menu Editor, Login Customizer / 4.2.17
Adminify – White Label, Admin Menu Editor, Login Customizer v4.2.17
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 / Modules / MenuEditor / MenuEditor.php

MenuEditor.php in Adminify – White Label, Admin Menu Editor, Login Customizer 4.2.17, at Inc/Modules/MenuEditor/MenuEditor.php

1,931 lines 85.8 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\Modules\MenuEditor;
4
5 use PXLBSAdminify\Inc\Utils;
6 use PXLBSAdminify\Inc\Admin\AdminSettings;
7 use PXLBSAdminify\Inc\Modules\MenuEditor\MenuEditorAssets;
8 use enshrined\svgSanitize\Sanitizer;
9 // no direct access allowed
10 if ( !defined( 'ABSPATH' ) ) {
11 exit;
12 }
13 /**
14 * WP Adminify
15 *
16 * @package WP Adminify: Menu Editor
17 *
18 * @author WP Adminify <support@wpadminify.com>
19 */
20 if ( !class_exists( 'MenuEditor' ) ) {
21 class MenuEditor extends MenuEditorModel {
22 public $url;
23
24 public $menu;
25
26 public $users;
27
28 public $roles;
29
30 public $submenu;
31
32 public $options = [];
33
34 public $menu_settings = [];
35
36 public static $instance;
37
38 public static function get_instance() {
39 if ( self::$instance === null ) {
40 self::$instance = new self();
41 }
42 return self::$instance;
43 }
44
45 public function __construct() {
46 $this->menu_settings = ( new MenuEditorOptions() )->get();
47 $this->options = (array) AdminSettings::get_instance()->get();
48 add_filter( 'upload_mimes', [$this, 'custom_icon_mime_types'] );
49 add_filter( 'wp_handle_upload_prefilter', [$this, 'sanitize_svg_file'] );
50 add_filter( 'admin_body_class', [$this, 'menu_editor_body_class'] );
51 // add_filter('parent_file', [$this, 'set_menu'], 800);
52 // add_filter('parent_file', [$this, 'apply_menu'], 900);
53 // add_action('modules_menu_editor_render', [$this, 'modules_menu_editor_render']);
54 if ( apply_filters( 'pxlbsadminify_enable_menu_editor_render', true ) ) {
55 add_filter( 'parent_file', [$this, 'set_menu'], 800 );
56 add_filter( 'parent_file', [$this, 'apply_menu'], 900 );
57 }
58 add_action( 'wp_ajax_pxlbsadminify_save_menu_settings', [$this, 'save_menu_settings'] );
59 add_action( 'wp_ajax_pxlbsadminify_reset_menu_settings', [$this, 'reset_menu_settings'] );
60 add_action( 'wp_ajax_pxlbsadminify_export_menu_settings', [$this, 'export_menu_settings'] );
61 add_action( 'wp_ajax_pxlbsadminify_import_menu_settings', [$this, 'import_menu_settings'] );
62 add_action( 'wp_ajax_pxlbsadminify_file_upload', [$this, 'file_upload_callback'] );
63 add_action( 'wp_ajax_pxlbsadminify_load_custom_icons', [$this, 'load_custom_icons_callback'] );
64 add_action( 'wp_ajax_pxlbsadminify_search_users', [$this, 'search_users_callback'] );
65 new MenuEditorAssets();
66 }
67
68 public function custom_icon_mime_types( $mimes ) {
69 if ( current_user_can( 'administrator' ) ) {
70 $mimes['svg'] = 'image/svg+xml';
71 }
72 return $mimes;
73 }
74
75 public function sanitize_svg_file( $file ) {
76 // Step 1: Validate File Type
77 $file_type = wp_check_filetype( $file['name'] );
78 // Ensure the file is an SVG
79 if ( $file_type['ext'] === 'svg' ) {
80 // Read the contents of the SVG file
81 $file_contents = file_get_contents( $file['tmp_name'] );
82 // Validate if it's an actual SVG by checking if it contains the <svg> tag
83 if ( strpos( $file_contents, '<svg' ) === false ) {
84 $file['error'] = 'Invalid SVG file';
85 return $file;
86 // Stop further processing if it's not a valid SVG
87 }
88 // Step 2: Sanitize the SVG File
89 $sanitizer = new Sanitizer();
90 $clean_svg = $sanitizer->sanitize( $file_contents );
91 if ( $clean_svg ) {
92 // Save the sanitized SVG back to the file
93 file_put_contents( $file['tmp_name'], $clean_svg );
94 } else {
95 $file['error'] = 'Failed to sanitize the SVG file';
96 }
97 }
98 return $file;
99 }
100
101 public function filter_attachment( $value ) {
102 // return $value->post_title == 'adminify-custom-icon';
103 return strpos( $value->guid, 'adminify-custom-icon' ) !== false;
104 }
105
106 public function load_custom_icons_callback() {
107 // Security check - verify nonce and capability
108 check_ajax_referer( 'pxlbsadminify-menu-editor-security-nonce', 'security' );
109 if ( !current_user_can( 'manage_options' ) ) {
110 wp_send_json_error( array(
111 'message' => __( 'You do not have permission to perform this action.', 'adminify' ),
112 ) );
113 }
114 $result['images'] = null;
115 $query = get_posts( [
116 'post_type' => 'attachment',
117 'numberposts' => -1,
118 ] );
119 $filtered = array_filter( $query, [$this, 'filter_attachment'] );
120 foreach ( $filtered as $key => $value ) {
121 $result['images'][$value->ID] = $value->guid;
122 }
123 echo wp_json_encode( $result );
124 die;
125 }
126
127 public function file_upload_callback() {
128 $result['status'] = false;
129 check_ajax_referer( 'pxlbsadminify-menu-editor-security-nonce', 'security' );
130 if ( !current_user_can( 'manage_options' ) ) {
131 wp_send_json_error( array(
132 'message' => __( 'You do not have permission to perform this action.', 'adminify' ),
133 ) );
134 }
135 $upload_dir = wp_upload_dir();
136 $targeted_dir = $upload_dir['basedir'] . '/adminify-custom-icons';
137 if ( !is_dir( $targeted_dir ) ) {
138 wp_mkdir_p( $targeted_dir );
139 }
140 add_filter( 'upload_dir', [$this, 'icon_custom_upload_dir'] );
141 if ( !isset( $_FILES['my_file_upload'] ) ) {
142 wp_send_json_error( array(
143 'message' => __( 'No file was uploaded.', 'adminify' ),
144 ) );
145 }
146 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- $_FILES is sanitized by the WP upload handler.
147 $files = wp_kses_post_deep( wp_unslash( $_FILES['my_file_upload'] ) );
148 foreach ( $files['name'] as $key => $value ) {
149 if ( $files['name'][$key] ) {
150 $file = [
151 'name' => $files['name'][$key],
152 'type' => $files['type'][$key],
153 'tmp_name' => $files['tmp_name'][$key],
154 'error' => $files['error'][$key],
155 'size' => $files['size'][$key],
156 ];
157 $_FILES = [
158 'upload_file' => $file,
159 ];
160 $attachment_id = media_handle_upload( 'upload_file', 0 );
161 if ( is_wp_error( $attachment_id ) ) {
162 // There was an error uploading the image.
163 $result['status'] = false;
164 $result['message'] = __( 'Error uploading file.', 'adminify' );
165 } else {
166 // The image was uploaded successfully!
167 $result['status'] = true;
168 $result['message'] = __( 'File Uploaded successfully.', 'adminify' );
169 $result['images'][$attachment_id] = wp_get_attachment_url( $attachment_id );
170 }
171 }
172 }
173 remove_filter( 'upload_dir', [$this, 'icon_custom_upload_dir'] );
174 echo wp_json_encode( $result );
175 wp_die();
176 }
177
178 public function icon_custom_upload_dir( $dir_data ) {
179 // $dir_data already you might want to use
180 $custom_dir = 'adminify-custom-icons';
181 return [
182 'path' => $dir_data['basedir'] . '/' . $custom_dir,
183 'url' => $dir_data['baseurl'] . '/' . $custom_dir,
184 'subdir' => '/' . $custom_dir,
185 'basedir' => $dir_data['basedir'],
186 'baseurl' => $dir_data['baseurl'],
187 'error' => $dir_data['error'],
188 ];
189 }
190
191 /**
192 * AJAX callback to search users for Select2
193 * Uses 'fields' parameter for optimized database query performance
194 */
195 public function search_users_callback() {
196 check_ajax_referer( 'pxlbsadminify-menu-editor-security-nonce', 'security' );
197 if ( !current_user_can( 'manage_options' ) ) {
198 wp_send_json_error( array(
199 'message' => __( 'You do not have permission to perform this action.', 'adminify' ),
200 ) );
201 }
202 $search = ( isset( $_GET['search'] ) ? sanitize_text_field( wp_unslash( $_GET['search'] ) ) : '' );
203 // If no search term or less than 3 chars, return first 3 users
204 if ( strlen( $search ) < 3 ) {
205 $args = array(
206 'number' => 3,
207 'orderby' => 'ID',
208 'order' => 'DESC',
209 'fields' => array('ID', 'user_login', 'display_name'),
210 );
211 $users = get_users( $args );
212 $results = array();
213 foreach ( $users as $user ) {
214 $results[] = array(
215 'id' => $user->user_login,
216 'text' => $user->user_login . ' (' . $user->display_name . ')',
217 );
218 }
219 // Get total user count efficiently
220 $user_count = count_users();
221 $total_users = $user_count['total_users'];
222 wp_send_json( array(
223 'results' => $results,
224 'pagination' => array(
225 'more' => $total_users > 3,
226 ),
227 'total_users' => $total_users,
228 ) );
229 return;
230 }
231 // Search users when 3+ characters typed
232 $args = array(
233 'search' => '*' . $search . '*',
234 'search_columns' => array('user_login', 'user_email', 'display_name'),
235 'number' => 20,
236 'orderby' => 'display_name',
237 'order' => 'ASC',
238 'fields' => array('ID', 'user_login', 'display_name'),
239 );
240 $users = get_users( $args );
241 $results = array();
242 foreach ( $users as $user ) {
243 $results[] = array(
244 'id' => $user->user_login,
245 'text' => $user->user_login . ' (' . $user->display_name . ')',
246 );
247 }
248 wp_send_json( array(
249 'results' => $results,
250 'pagination' => array(
251 'more' => false,
252 ),
253 ) );
254 }
255
256 // Menu Editor Body Class
257 public function menu_editor_body_class( $classes ) {
258 $classes .= ' adminify_menu_editor ';
259 return $classes;
260 }
261
262 /**
263 * Sanitises and strips tags of input from ajax
264 *
265 * @since 1.0.0
266 * @variables $values = item to clean (array or string)
267 */
268 public function clean_ajax_input( $values ) {
269 if ( is_array( $values ) ) {
270 foreach ( $values as $index => $in ) {
271 if ( is_array( $in ) ) {
272 $values[$index] = $this->clean_ajax_input( $in );
273 } else {
274 $values[$index] = wp_strip_all_tags( $in );
275 }
276 }
277 } else {
278 $values = wp_strip_all_tags( $values );
279 }
280 return $values;
281 }
282
283 /**
284 * Returns ajax error
285 *
286 * @since 1.4
287 * @variables $message = error message to send back to user (string)
288 */
289 public function ajax_error_message( $message ) {
290 $returndata = [];
291 $returndata['error'] = true;
292 $returndata['error_message'] = $message;
293 return wp_json_encode( $returndata );
294 }
295
296 public function save_menu_settings() {
297 if ( defined( 'DOING_AJAX' ) && DOING_AJAX && check_ajax_referer( 'pxlbsadminify-menu-editor-security-nonce', 'security' ) > 0 ) {
298 if ( !current_user_can( 'manage_options' ) ) {
299 wp_send_json_error( array(
300 'message' => __( 'You do not have permission to perform this action.', 'adminify' ),
301 ) );
302 }
303 $options = ( isset( $_POST['options'] ) ? wp_kses_post_deep( wp_unslash( $_POST['options'] ) ) : '' );
304 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized via wp_kses_post_deep() and clean_ajax_input() below.
305 $options = $this->clean_ajax_input( $options );
306 if ( $options == '' || !is_array( $options ) ) {
307 $message = __( 'No options supplied to save', 'adminify' );
308 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- kses_custom() is a wp_kses() wrapper; output already escaped.
309 echo Utils::kses_custom( $this->ajax_error_message( $message ) );
310 die;
311 }
312 if ( is_array( $options ) ) {
313 update_option( $this->prefix, $options );
314 $returndata = [];
315 $returndata['success'] = true;
316 $returndata['message'] = __( 'Settings saved', 'adminify' );
317 echo wp_json_encode( $returndata );
318 die;
319 } else {
320 $message = __( 'Something went wrong', 'adminify' );
321 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- kses_custom() is a wp_kses() wrapper; output already escaped.
322 echo Utils::kses_custom( $this->ajax_error_message( $message ) );
323 die;
324 }
325 }
326 die;
327 }
328
329 /**
330 * Menu Editor Settings Reset
331 *
332 * @return void
333 */
334 public function reset_menu_settings() {
335 if ( defined( 'DOING_AJAX' ) && DOING_AJAX && check_ajax_referer( 'pxlbsadminify-menu-editor-security-nonce', 'security' ) > 0 ) {
336 if ( !current_user_can( 'manage_options' ) ) {
337 wp_send_json_error( array(
338 'message' => __( 'You do not have permission to perform this action.', 'adminify' ),
339 ) );
340 }
341 update_option( $this->prefix, [] );
342 $menu_editor_options = get_option( $this->prefix );
343 if ( !$menu_editor_options ) {
344 $returndata = [];
345 $returndata['success'] = true;
346 $returndata['message'] = __( 'Settings reset', 'adminify' );
347 echo wp_json_encode( $returndata );
348 die;
349 } else {
350 $message = __( 'Something went wrong', 'adminify' );
351 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- kses_custom() is a wp_kses() wrapper; output already escaped.
352 echo Utils::kses_custom( $this->ajax_error_message( $message ) );
353 die;
354 }
355 }
356 die;
357 }
358
359 /**
360 * Export Menu Editor Settings
361 *
362 * @return void
363 */
364 public function export_menu_settings() {
365 if ( defined( 'DOING_AJAX' ) && DOING_AJAX && check_ajax_referer( 'pxlbsadminify-menu-editor-security-nonce', 'security' ) > 0 ) {
366 if ( !current_user_can( 'manage_options' ) ) {
367 wp_send_json_error( array(
368 'message' => __( 'You do not have permission to perform this action.', 'adminify' ),
369 ) );
370 }
371 $menu_editor_options = get_option( $this->prefix );
372 echo wp_json_encode( $menu_editor_options );
373 }
374 die;
375 }
376
377 /**
378 * Import Menu Editor Settings
379 *
380 * @since 1.0.0
381 */
382 public function import_menu_settings() {
383 if ( defined( 'DOING_AJAX' ) && DOING_AJAX && check_ajax_referer( 'pxlbsadminify-menu-editor-security-nonce', 'security' ) > 0 ) {
384 if ( !current_user_can( 'manage_options' ) ) {
385 wp_send_json_error( array(
386 'message' => __( 'You do not have permission to perform this action.', 'adminify' ),
387 ) );
388 }
389 $new_options = ( isset( $_POST['settings'] ) ? wp_kses_post_deep( wp_unslash( $_POST['settings'] ) ) : '' );
390 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized via wp_kses_post_deep().
391 if ( $new_options == '' || !is_array( $new_options ) ) {
392 $message = __( 'No options supplied to save', 'adminify' );
393 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- kses_custom() is a wp_kses() wrapper; output already escaped.
394 echo Utils::kses_custom( $this->ajax_error_message( $message ) );
395 die;
396 }
397 if ( is_array( $new_options ) ) {
398 update_option( $this->prefix, $new_options );
399 $returndata = [];
400 $returndata['success'] = true;
401 $returndata['message'] = __( 'Menu Imported', 'adminify' );
402 echo wp_json_encode( $returndata );
403 die;
404 } else {
405 $message = __( 'Something went wrong', 'adminify' );
406 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- kses_custom() is a wp_kses() wrapper; output already escaped.
407 echo Utils::kses_custom( $this->ajax_error_message( $message ) );
408 die;
409 }
410 }
411 die;
412 }
413
414 /**
415 * Find menu settings for a given slug.
416 * Some plugins (e.g. Yoast SEO) register different top-level menu slugs
417 * depending on user capabilities. The admin sees slug 'wpseo_dashboard'
418 * but the editor sees 'wpseo_tools'. This method handles that mismatch
419 * by falling back to checking if the slug appears as a submenu key.
420 *
421 * @param string $slug The menu slug to look up.
422 * @return array|null The matching settings array, or null if not found.
423 */
424 public function find_menu_settings_by_slug( $slug ) {
425 // Direct match
426 if ( isset( $this->menu_settings[$slug] ) ) {
427 return $this->menu_settings[$slug];
428 }
429 // Fallback: check if this slug is a submenu of any saved parent menu
430 if ( is_array( $this->menu_settings ) ) {
431 foreach ( $this->menu_settings as $parent_slug => $parent_settings ) {
432 if ( !is_array( $parent_settings ) || !isset( $parent_settings['submenu'] ) || !is_array( $parent_settings['submenu'] ) ) {
433 continue;
434 }
435 if ( isset( $parent_settings['submenu'][$slug] ) ) {
436 return $parent_settings;
437 }
438 }
439 }
440 return null;
441 }
442
443 /**
444 * Get menu items
445 *
446 * @param [type] $parent_file
447 *
448 * @return void
449 */
450 public function set_menu( $parent_file ) {
451 global $menu, $submenu;
452 $this->menu = $this->sort_menu_settings( $menu );
453 $this->submenu = $this->sort_sub_menu_settings( $this->menu, $submenu );
454 return $parent_file;
455 }
456
457 /**
458 * Sorts Menu's for Option settings
459 */
460 public function sort_menu_settings( $thismenu ) {
461 $menu_settings = $this->menu_settings;
462 $tempmenu = [];
463 foreach ( $thismenu as $key => $current_menu_item ) {
464 if ( $current_menu_item[4] === "wp-menu-separator" ) {
465 unset($thismenu[$key]);
466 continue;
467 }
468 if ( !current_user_can( 'administrator' ) && $current_menu_item[2] === 'vc-welcome' ) {
469 unset($thismenu[$key]);
470 continue;
471 }
472 $next_menu_item = next( $thismenu );
473 $optiongroup = [];
474 $order = $key;
475 $separator = 0;
476 $external_link = 0;
477 $icon = null;
478 $hidden_for = [];
479 if ( !empty( $next_menu_item ) && strpos( $next_menu_item[2], 'separator' ) !== false ) {
480 $separator = 1;
481 }
482 if ( is_array( $menu_settings ) ) {
483 // Direct slug match first, then fallback for dynamic-slug plugins (e.g. Yoast SEO)
484 if ( isset( $menu_settings[$current_menu_item[2]] ) ) {
485 $optiongroup = $menu_settings[$current_menu_item[2]];
486 } else {
487 $fallback = $this->find_menu_settings_by_slug( $current_menu_item[2] );
488 if ( $fallback !== null ) {
489 $optiongroup = $fallback;
490 }
491 }
492 if ( !empty( $optiongroup ) ) {
493 if ( isset( $optiongroup['order'] ) ) {
494 $order = $optiongroup['order'];
495 }
496 if ( isset( $optiongroup['separator'] ) ) {
497 $separator = $optiongroup['separator'];
498 }
499 if ( isset( $optiongroup['external_link'] ) ) {
500 $external_link = $optiongroup['external_link'];
501 }
502 if ( !empty( $optiongroup['icon'] ) ) {
503 $icon = $optiongroup['icon'];
504 }
505 if ( !empty( $optiongroup['hidden_for'] ) ) {
506 $hidden_for = $optiongroup['hidden_for'];
507 }
508 }
509 }
510 $current_menu_item['order'] = $order;
511 $current_menu_item['hidden_for'] = $hidden_for;
512 $current_menu_item['separator'] = $separator;
513 $current_menu_item['external_link'] = $external_link;
514 // if (empty($this->options['admin_ui'])) {
515 if ( $icon !== null ) {
516 if ( strpos( $icon, 'dashicons ' ) !== false ) {
517 $menu_icon = str_replace( 'dashicons ', '', $icon );
518 $current_menu_item[6] = $menu_icon;
519 } else {
520 if ( strpos( $icon, ',' ) !== false ) {
521 $menu_icon = explode( ',', $icon )[1];
522 } else {
523 $menu_icon = 'dashicons-adminify- ' . $icon;
524 }
525 $current_menu_item[6] = $menu_icon;
526 }
527 }
528 if ( $separator == 1 ) {
529 $current_menu_item[4] = $current_menu_item[4] . ' adminify-menu-separator';
530 }
531 // }
532 array_push( $tempmenu, $current_menu_item );
533 if ( isset( $menu_settings[$current_menu_item[2]] ) ) {
534 unset($menu_settings[$current_menu_item[2]]);
535 }
536 }
537 if ( is_array( $menu_settings ) && !array_key_exists( 0, $menu_settings ) ) {
538 foreach ( $menu_settings as $key => $menu_s ) {
539 // Skip if $menu_s is not an array
540 if ( !is_array( $menu_s ) ) {
541 continue;
542 }
543 // Ensure required keys exist with defaults
544 $menu_s = wp_parse_args( $menu_s, [
545 'name' => '',
546 'link' => '',
547 'icon' => '',
548 'order' => 0,
549 'separator' => 0,
550 'external_link' => 0,
551 'hidden_for' => [],
552 ] );
553 $new_array = [
554 $menu_s['name'],
555 'read',
556 ( !empty( $menu_s['link'] ) ? $menu_s['link'] : '' ),
557 '',
558 'menu-top menu-icon-custom',
559 $key,
560 ( !empty( $menu_s['icon'] ) ? $menu_s['icon'] : '' ),
561 'order' => $menu_s['order'],
562 'separator' => ( !empty( $menu_s['separator'] ) ? $menu_s['separator'] : 0 ),
563 'external_link' => ( !empty( $menu_s['external_link'] ) ? $menu_s['external_link'] : 0 ),
564 'hidden_for' => ( !empty( $menu_s['hidden_for'] ) ? $menu_s['hidden_for'] : [] )
565 ];
566 // if (empty($this->options['admin_ui'])) {
567 if ( strpos( $menu_s['icon'], 'dashicons ' ) !== false ) {
568 $menu_icon = str_replace( 'dashicons ', '', $menu_s['icon'] );
569 } else {
570 if ( empty( $menu_s['icon'] ) || strpos( $menu_s['icon'], ',' ) !== false ) {
571 if ( empty( $menu_s['icon'] ) ) {
572 $menu_icon = PXLBSADMINIFY_ASSETS_IMAGE . 'logos/menu-icon.svg';
573 } else {
574 $menu_icon = explode( ',', $menu_s['icon'] )[1];
575 }
576 } else {
577 $menu_icon = 'dashicons-adminify- ' . $menu_s['icon'];
578 }
579 }
580 if ( !empty( $menu_s['separator'] ) && $menu_s['separator'] == 1 ) {
581 $new_array[4] = $new_array[4] . ' adminify-menu-separator';
582 }
583 // if (!empty($menu_s['external_link']) && $menu_s['external_link'] == 1) {
584 // $new_array[4] = $new_array[4] . ' adminify-menu-external_link';
585 // }
586 $new_array[6] = $menu_icon;
587 // }
588 array_push( $tempmenu, $new_array );
589 }
590 }
591 return $this->sort_array( $tempmenu );
592 }
593
594 /**
595 * usort function for menu arrays
596 */
597 public function sort_array_helper( $a, $b ) {
598 $result = 0;
599 if ( !isset( $a['order'] ) ) {
600 return $result;
601 }
602 if ( $a['order'] > $b['order'] ) {
603 $result = 1;
604 } elseif ( $a['order'] < $b['order'] ) {
605 $result = -1;
606 }
607 return $result;
608 }
609
610 /**
611 * Sorts arrays by key 'order'
612 */
613 public function sort_array( $tosort ) {
614 usort( $tosort, [$this, 'sort_array_helper'] );
615 return $tosort;
616 }
617
618 /**
619 * Sorts Sub Menu for settings
620 *
621 * @since 1.4
622 */
623 public function sort_sub_menu_settings( $themenu, $thesubmenu ) {
624 $menu_settings = $this->menu_settings;
625 $tempsubmenu = [];
626 foreach ( $themenu as $current_menu_item ) {
627 $optiongroup = [];
628 $submenu_items = [];
629 if ( isset( $thesubmenu[$current_menu_item[2]] ) ) {
630 $submenuitems = $thesubmenu[$current_menu_item[2]];
631 foreach ( $submenuitems as $key => $subitem ) {
632 $subitem['order'] = $key;
633 if ( is_array( $menu_settings ) && isset( $menu_settings[$current_menu_item[2]] ) && isset( $menu_settings[$current_menu_item[2]]['submenu'] ) ) {
634 $submenugroup = $menu_settings[$current_menu_item[2]]['submenu'];
635 if ( isset( $submenugroup[$subitem[2]] ) ) {
636 $itemoptions = $submenugroup[$subitem[2]];
637 if ( isset( $itemoptions['order'] ) ) {
638 $subitem['order'] = $itemoptions['order'];
639 }
640 }
641 }
642 array_push( $submenu_items, $subitem );
643 }
644 $submenu_items = $this->sort_array( $submenu_items );
645 $tempsubmenu[$current_menu_item[2]] = $submenu_items;
646 }
647 }
648 if ( is_array( $menu_settings ) ) {
649 $tmp_menu = [];
650 foreach ( $menu_settings as $key => $menu_s ) {
651 if ( isset( $menu_s['submenu'] ) && is_array( $menu_s['submenu'] ) ) {
652 $tmp_sub_menu = [];
653 $i = 0;
654 foreach ( $menu_s['submenu'] as $k => $sub_menu ) {
655 if ( strpos( $k, 'adminify-custom-submenu' ) === false ) {
656 continue;
657 }
658 $order = ( $sub_menu['order'] ? $sub_menu['order'] : $i );
659 $new_array = [
660 ( $sub_menu['name'] ? $sub_menu['name'] : '' ),
661 'read',
662 ( $sub_menu['link'] ? $sub_menu['link'] : '' ),
663 'order' => $order,
664 'key' => $k,
665 'hidden_for' => ( isset( $sub_menu['hidden_for'] ) ? $sub_menu['hidden_for'] : [] )
666 ];
667 // processing submenu array
668 $tmp_sub_menu[$order] = $new_array;
669 $i++;
670 }
671 if ( !empty( $tmp_sub_menu ) ) {
672 // adding submenu into temp variable
673 $tmp_menu[$key] = $tmp_sub_menu;
674 }
675 }
676 }
677 if ( !empty( $tmp_menu ) ) {
678 $tempsubmenu = array_merge_recursive( $tempsubmenu, $tmp_menu );
679 }
680 foreach ( $tempsubmenu as $m_k => $m_menu ) {
681 $tempsubmenu[$m_k] = $this->sort_array( $tempsubmenu[$m_k] );
682 }
683 }
684 return $tempsubmenu;
685 }
686
687 /**
688 * Applies menu settings
689 */
690 public function apply_menu( $parent_file ) {
691 // Not applicable for Network Admin
692 if ( is_multisite() && is_network_admin() ) {
693 return;
694 }
695 global $menu, $submenu;
696 $tempmenu = [];
697 $tempsub = [];
698 $submenu = $this->sort_sub_menu_settings( $menu, $submenu );
699 if ( $this->menu && is_array( $this->menu ) ) {
700 foreach ( $this->menu as $key => $menu_item ) {
701 if ( empty( $menu_item[2] ) ) {
702 continue;
703 }
704 if ( strpos( $menu_item[2], 'separator' ) !== false && !$menu_item[0] ) {
705 // Build Separator
706 $newitem = $this->apply_separator_settings( $menu_item, $key );
707 if ( $newitem ) {
708 array_push( $tempmenu, $newitem );
709 }
710 } else {
711 // Build Top Level
712 $newitem = $this->apply_top_level_settings( $menu_item, $key );
713 if ( $newitem ) {
714 array_push( $tempmenu, $newitem );
715 // Menu Editor Custom Separator
716 if ( !empty( $newitem['separator'] ) ) {
717 $separator_order = $this->menu[$key]['order'];
718 $empty_separator = array(
719 0 => '',
720 1 => 'read',
721 2 => 'separator_' . wp_rand( 200, 1000 ) * 1000,
722 3 => '',
723 4 => 'wp-menu-separator',
724 'order' => $separator_order + 0.5,
725 );
726 array_push( $tempmenu, $empty_separator );
727 }
728 $parent_key = $newitem[2];
729 if ( strpos( $newitem[5], 'adminify-custom-menu-' ) !== false ) {
730 $parent_key = $newitem[5];
731 }
732 if ( isset( $this->submenu[$parent_key] ) ) {
733 $subitem = $this->apply_sub_level_settings( $this->submenu[$parent_key], $parent_key );
734 if ( $subitem ) {
735 $tempsub[$newitem[2]] = $this->apply_sub_level_settings( $this->submenu[$parent_key], $parent_key );
736 }
737 }
738 }
739 }
740 }
741 }
742 $submenu = $tempsub;
743 $menu = $this->sort_array( $tempmenu );
744 return $parent_file;
745 }
746
747 /**
748 * Applies sub level menu item settings
749 *
750 * @return $tempsub | false
751 *
752 * @since 1.4
753 */
754 public function apply_sub_level_settings( $subitems, $parentname ) {
755 if ( !is_array( $this->menu_settings ) ) {
756 return $subitems;
757 }
758 if ( !isset( $this->menu_settings[$parentname]['submenu'] ) ) {
759 return $subitems;
760 }
761 $submenu_settings = $this->menu_settings[$parentname]['submenu'];
762 $tempsub = [];
763 foreach ( $subitems as $current_menu_item ) {
764 $sub_level = apply_filters( 'pxlbsadminify/menu_editor', [
765 'sub_level' => false,
766 ] );
767 if ( !empty( $sub_level['sub_level'] ) ) {
768 if ( empty( $current_menu_item[0] ) || $current_menu_item[2] === 'wp-adminify-settings-pricing' ) {
769 continue;
770 }
771 }
772 $name = '';
773 $link = '';
774 $disabled_for = [];
775 $optiongroup = [];
776 // NO SETTINGS
777 if ( isset( $submenu_settings[$current_menu_item[2]] ) ) {
778 $optiongroup = $submenu_settings[$current_menu_item[2]];
779 if ( isset( $optiongroup['name'] ) ) {
780 $name = $optiongroup['name'];
781 if ( $name != '' ) {
782 $current_menu_item[0] = $name;
783 }
784 }
785 if ( isset( $optiongroup['link'] ) ) {
786 $link = $optiongroup['link'];
787 if ( $link != '' ) {
788 $current_menu_item[2] = $link;
789 $current_menu_item['link'] = $link;
790 }
791 }
792 if ( isset( $optiongroup['external_link'] ) ) {
793 $external_link = $optiongroup['external_link'];
794 }
795 if ( isset( $optiongroup['hidden_for'] ) ) {
796 $disabled_for = $optiongroup['hidden_for'];
797 if ( $this->is_hidden( $disabled_for ) ) {
798 $current_menu_item['hidden_for'] = true;
799 continue;
800 }
801 }
802 }
803 $custom_menu = ( isset( $current_menu_item['key'] ) ? $current_menu_item['key'] : '' );
804 if ( strpos( $custom_menu, 'adminify-custom-submenu-' ) !== false ) {
805 if ( isset( $submenu_settings[$current_menu_item['key']]['hidden_for'] ) ) {
806 $disabled_for = $submenu_settings[$current_menu_item['key']]['hidden_for'];
807 }
808 if ( isset( $submenu_settings[$current_menu_item['key']]['external_link'] ) ) {
809 $external_link = $submenu_settings[$current_menu_item['key']]['external_link'];
810 }
811 if ( $this->is_hidden( $disabled_for ) ) {
812 $current_menu_item['hidden_for'] = true;
813 continue;
814 }
815 }
816 array_push( $tempsub, $current_menu_item );
817 }
818 if ( count( $tempsub ) < 1 ) {
819 return false;
820 } else {
821 return $tempsub;
822 }
823 }
824
825 /**
826 * Applies top level menu item settings
827 *
828 * @since 1.4
829 */
830 public function apply_top_level_settings( $current_menu_item, $key ) {
831 $name = '';
832 $link = '';
833 $icon = '';
834 $disabled_for = [];
835 $optiongroup = [];
836 $order = $key;
837 if ( is_array( $this->menu_settings ) ) {
838 // Direct slug match first, then fallback for dynamic-slug plugins (e.g. Yoast SEO)
839 if ( isset( $this->menu_settings[$current_menu_item[2]] ) ) {
840 $optiongroup = $this->menu_settings[$current_menu_item[2]];
841 } else {
842 $fallback = $this->find_menu_settings_by_slug( $current_menu_item[2] );
843 if ( $fallback !== null ) {
844 $optiongroup = $fallback;
845 }
846 }
847 if ( !empty( $optiongroup ) ) {
848 if ( isset( $optiongroup['name'] ) ) {
849 $name = $optiongroup['name'];
850 if ( $name != '' ) {
851 $current_menu_item[0] = $name;
852 }
853 }
854 if ( isset( $optiongroup['link'] ) ) {
855 $link = $optiongroup['link'];
856 if ( $link != '' ) {
857 $current_menu_item[2] = $link;
858 $current_menu_item['link'] = $link;
859 }
860 }
861 if ( isset( $optiongroup['external_link'] ) ) {
862 $external_link = ( $optiongroup['external_link'] ? $optiongroup['external_link'] : '' );
863 }
864 if ( isset( $optiongroup['icon'] ) ) {
865 $icon = $optiongroup['icon'];
866 if ( $icon != '' ) {
867 $current_menu_item['icon'] = $icon;
868 }
869 }
870 if ( isset( $optiongroup['order'] ) ) {
871 $order = $optiongroup['order'];
872 }
873 if ( isset( $optiongroup['hidden_for'] ) ) {
874 $disabled_for = $optiongroup['hidden_for'];
875 if ( $this->is_hidden( $disabled_for ) ) {
876 $current_menu_item['hidden_for'] = true;
877 } else {
878 $current_menu_item['hidden_for'] = false;
879 }
880 }
881 }
882 $custom_menu = ( isset( $current_menu_item[5] ) ? $current_menu_item[5] : '' );
883 if ( strpos( $custom_menu, 'adminify-custom-menu-' ) !== false ) {
884 if ( isset( $this->menu_settings[$current_menu_item[5]]['hidden_for'] ) && !empty( $this->menu_settings[$current_menu_item[5]]['hidden_for'] ) ) {
885 $disabled_for = $this->menu_settings[$current_menu_item[5]]['hidden_for'];
886 if ( $this->is_hidden( $disabled_for ) ) {
887 $current_menu_item['hidden_for'] = true;
888 }
889 }
890 if ( isset( $this->menu_settings[$current_menu_item[5]]['external_link'] ) ) {
891 $external_link = $this->menu_settings[$current_menu_item[5]]['external_link'];
892 }
893 }
894 }
895 $current_menu_item['order'] = $order;
896 // Check hidden_for: boolean true means hide, array means check via is_hidden
897 if ( isset( $current_menu_item['hidden_for'] ) ) {
898 if ( $current_menu_item['hidden_for'] === true ) {
899 return false;
900 } elseif ( is_array( $current_menu_item['hidden_for'] ) && !empty( $current_menu_item['hidden_for'] ) ) {
901 // Fallback: hidden_for is still an array (settings lookup may have missed it)
902 if ( $this->is_hidden( $current_menu_item['hidden_for'] ) ) {
903 return false;
904 }
905 }
906 return $current_menu_item;
907 } else {
908 return $current_menu_item;
909 }
910 }
911
912 /**
913 * Hidden for method
914 *
915 * @param [type] $disabled_for
916 *
917 * @return boolean
918 */
919 public function is_hidden( $disabled_for ) {
920 if ( !is_array( $disabled_for ) ) {
921 return false;
922 }
923 $current_user = wp_get_current_user();
924 $current_roles = $current_user->roles;
925 $all_roles = wp_roles()->get_names();
926 foreach ( $current_roles as $current_role ) {
927 if ( isset( $all_roles[$current_role] ) && in_array( $all_roles[$current_role], $disabled_for ) ) {
928 return true;
929 }
930 }
931 // Normalize disabled_for array
932 $disabled_for_arr = [];
933 foreach ( $disabled_for as $v ) {
934 $disabled_for_arr[] = Utils::sluggify_with_underscores( $v );
935 }
936 $slugify_user_login = Utils::sluggify_with_underscores( $current_user->user_login );
937 // Check Username
938 if ( in_array( $current_user->user_login, $disabled_for_arr ) || in_array( $slugify_user_login, $disabled_for_arr ) ) {
939 return true;
940 }
941 // Check User Roles and Capabilities
942 foreach ( $current_roles as $role ) {
943 $role_obj = get_role( $role );
944 if ( !$role_obj ) {
945 continue;
946 }
947 // Check if role slug is in disabled array
948 if ( in_array( $role, $disabled_for_arr ) ) {
949 return true;
950 }
951 // Check if role display name is in disabled array
952 if ( isset( $disabled_for_arr[$role] ) && (in_array( $all_roles[$role], $disabled_for ) || in_array( strtolower( $all_roles[$role] ), array_map( 'strtolower', $disabled_for ) )) ) {
953 return true;
954 }
955 // Check for custom roles
956 foreach ( $disabled_for as $disabled_item ) {
957 // Convert role names to match WordPress format
958 $disabled_role = str_replace( ' ', '_', strtolower( $disabled_item ) );
959 if ( $role === $disabled_role ) {
960 return true;
961 }
962 }
963 }
964 // Handle Super Admin specially
965 if ( is_super_admin( $current_user->ID ) ) {
966 if ( in_array( 'Super Admin', $disabled_for_arr ) || in_array( 'super_admin', $disabled_for_arr ) || in_array( 'administrator', $disabled_for_arr ) ) {
967 return true;
968 }
969 // Super admin sees everything by default unless explicitly disabled
970 return false;
971 }
972 // Special check for user ID 1 (default admin)
973 if ( $current_user->ID === 1 ) {
974 if ( in_array( 'Super Admin', $disabled_for_arr ) || in_array( 'super_admin', $disabled_for_arr ) || in_array( 'administrator', $disabled_for_arr ) ) {
975 return true;
976 }
977 return false;
978 }
979 return false;
980 }
981
982 /**
983 * Applies separator menu item settings
984 *
985 * @since 1.0.0
986 */
987 public function apply_separator_settings( $current_menu_item, $key ) {
988 $name = '';
989 $disabled_for = [];
990 $optiongroup = [];
991 $order = $key;
992 if ( is_array( $this->menu_settings ) ) {
993 if ( isset( $this->menu_settings[$current_menu_item[2]] ) ) {
994 $optiongroup = $this->menu_settings[$current_menu_item[2]];
995 if ( isset( $optiongroup['name'] ) ) {
996 $name = $optiongroup['name'];
997 if ( $name != '' ) {
998 $current_menu_item['name'] = $name;
999 }
1000 }
1001 if ( isset( $optiongroup['order'] ) ) {
1002 $order = $optiongroup['order'];
1003 }
1004 if ( isset( $optiongroup['hidden_for'] ) ) {
1005 $disabled_for = $optiongroup['hidden_for'];
1006 if ( $this->is_hidden( $disabled_for ) ) {
1007 $current_menu_item['hidden_for'] = true;
1008 }
1009 }
1010 }
1011 }
1012 $current_menu_item['order'] = $order;
1013 if ( isset( $current_menu_item['hidden_for'] ) ) {
1014 if ( $current_menu_item['hidden_for'] == true ) {
1015 return false;
1016 } else {
1017 return $current_menu_item;
1018 }
1019 } else {
1020 return $current_menu_item;
1021 }
1022 }
1023
1024 public function get_saved_sub_menu() {
1025 $menu_settings = $this->menu_settings;
1026 $menu = $this->menu;
1027 $submenu = $this->submenu;
1028 if ( is_array( $menu_settings ) && !array_key_exists( 0, $menu_settings ) ) {
1029 foreach ( $menu as $menu_item ) {
1030 if ( isset( $menu_settings[$menu_item[2]] ) ) {
1031 unset($menu_settings[$menu_item[2]]);
1032 }
1033 }
1034 $tmp_menu = [];
1035 foreach ( $menu_settings as $key => $menu_s ) {
1036 if ( isset( $menu_s['submenu'] ) && is_array( $menu_s['submenu'] ) ) {
1037 $tmp_sub_menu = [];
1038 $i = 0;
1039 foreach ( $menu_s['submenu'] as $k => $sub_menu ) {
1040 $new_array = [
1041 $sub_menu['name'],
1042 'read',
1043 $sub_menu['link'],
1044 'order' => $i
1045 ];
1046 $tmp_sub_menu[$i] = $new_array;
1047 $i++;
1048 }
1049 if ( !empty( $tmp_sub_menu ) ) {
1050 $tmp_menu[$key] = $tmp_sub_menu;
1051 }
1052 }
1053 }
1054 if ( !empty( $tmp_menu ) ) {
1055 $submenu = $submenu + $tmp_menu;
1056 }
1057 }
1058 return $submenu;
1059 }
1060
1061 /**
1062 * Render Menu Editor
1063 */
1064 public function render_menu_editor() {
1065 global $wp_roles;
1066 $this->roles = $wp_roles->roles;
1067 if ( $this->menu && is_array( $this->menu ) ) {
1068 foreach ( $this->menu as $menu_item ) {
1069 $next_menu_item = next( $this->menu );
1070 if ( strpos( $menu_item[2], 'separator' ) !== false && !$menu_item[0] ) {
1071 // Render Separator
1072 // $this->render_menu_separator($menu_item);
1073 } else {
1074 // Render Top Level menu
1075 $this->render_top_level_menu_item(
1076 $this->menu,
1077 $menu_item,
1078 $this->submenu,
1079 $next_menu_item
1080 );
1081 }
1082 }
1083 $this->render_add_new_menu_item();
1084 }
1085 }
1086
1087 public function get_icon( $value, $default ) {
1088 if ( empty( $value ) ) {
1089 return $default;
1090 }
1091 return $value;
1092 }
1093
1094 /**
1095 * Generate User Rules Select box
1096 */
1097 public function generate_user_rules_select_field( $menu_id, $disabled_for, $menu_type = 'menu' ) {
1098 ob_start();
1099 ?>
1100 <select class="adminify-menu-settings adminify-user-role-select <?php
1101 echo esc_attr( $menu_type );
1102 ?>_setting" name="hidden_for" id="<?php
1103 echo esc_attr( $menu_id );
1104 ?>-user-role-types" multiple>
1105 <optgroup label="<?php
1106 esc_attr_e( 'Roles', 'adminify' );
1107 ?>">
1108 <?php
1109 foreach ( $this->roles as $role ) {
1110 $rolename = $role['name'];
1111 if ( 'administrator' == strtolower( $rolename ) ) {
1112 continue;
1113 }
1114 $sel = '';
1115 if ( in_array( $rolename, $disabled_for ) ) {
1116 $sel = 'selected';
1117 }
1118 ?>
1119 <option value="<?php
1120 echo esc_attr( $rolename );
1121 ?>" <?php
1122 echo esc_attr( $sel );
1123 ?>><?php
1124 echo esc_html( $rolename );
1125 ?></option>
1126 <?php
1127 }
1128 ?>
1129 </optgroup>
1130
1131 <optgroup label="<?php
1132 esc_attr_e( 'Users', 'adminify' );
1133 ?>" class="adminify-users-optgroup">
1134 <?php
1135 // Only include pre-selected users in HTML (AJAX will populate dropdown)
1136 foreach ( $disabled_for as $item ) {
1137 // Check if this is a user (not a role)
1138 $is_role = false;
1139 foreach ( $this->roles as $role ) {
1140 if ( $role['name'] === $item ) {
1141 $is_role = true;
1142 break;
1143 }
1144 }
1145 if ( !$is_role ) {
1146 $user = get_user_by( 'login', $item );
1147 if ( $user ) {
1148 ?>
1149 <option value="<?php
1150 echo esc_attr( $user->user_login );
1151 ?>" selected><?php
1152 echo esc_html( $user->user_login . ' (' . $user->display_name . ')' );
1153 ?></option>
1154 <?php
1155 }
1156 }
1157 }
1158 ?>
1159 </optgroup>
1160 </select>
1161 <?php
1162 return ob_get_clean();
1163 }
1164
1165 /**
1166 * Render Top Level menu Item
1167 *
1168 * @return void
1169 */
1170 public function render_top_level_menu_item(
1171 $master_menu,
1172 $current_menu_item,
1173 $master_sub_menu,
1174 $next_menu_item
1175 ) {
1176 if ( empty( $current_menu_item[2] ) ) {
1177 return;
1178 }
1179 // $disabled_for = '';
1180 $menu_id = preg_replace( '/[^A-Za-z0-9 ]/', '', $current_menu_item[5] );
1181 $name = '';
1182 $link = '';
1183 $external_link = '';
1184 $icon = '';
1185 $separator = '';
1186 $disabled_for = [];
1187 $optiongroup = [];
1188 $menu_options = $this->menu_settings;
1189 if ( !empty( $next_menu_item ) && strpos( $next_menu_item[2], 'separator' ) !== false ) {
1190 $separator = 1;
1191 }
1192 if ( is_array( $menu_options ) ) {
1193 if ( isset( $menu_options[$current_menu_item[2]] ) ) {
1194 $optiongroup = $menu_options[$current_menu_item[2]];
1195 if ( isset( $optiongroup['name'] ) ) {
1196 $name = $optiongroup['name'];
1197 }
1198 if ( isset( $optiongroup['link'] ) ) {
1199 $link = $optiongroup['link'];
1200 }
1201 if ( isset( $optiongroup['icon'] ) ) {
1202 $icon = $optiongroup['icon'];
1203 }
1204 if ( isset( $optiongroup['hidden_for'] ) ) {
1205 $disabled_for = $optiongroup['hidden_for'];
1206 }
1207 if ( isset( $optiongroup['separator'] ) ) {
1208 $separator = $optiongroup['separator'];
1209 }
1210 if ( isset( $optiongroup['external_link'] ) ) {
1211 $external_link = $optiongroup['external_link'];
1212 }
1213 }
1214 }
1215 $name_attr = $current_menu_item[2];
1216 if ( strpos( $current_menu_item[5], 'adminify-custom-menu-' ) !== false ) {
1217 $name_attr = $current_menu_item[5];
1218 $name = $current_menu_item[0];
1219 $link = $current_menu_item[2];
1220 if ( isset( $menu_options[$current_menu_item[5]]['hidden_for'] ) ) {
1221 $disabled_for = $menu_options[$current_menu_item[5]]['hidden_for'];
1222 }
1223 if ( isset( $menu_options[$current_menu_item[5]] ) ) {
1224 $optiongroup = $menu_options[$current_menu_item[5]];
1225 }
1226 if ( isset( $menu_options[$current_menu_item[5]]['icon'] ) ) {
1227 $icon = $menu_options[$current_menu_item[5]]['icon'];
1228 }
1229 if ( isset( $current_menu_item['separator'] ) ) {
1230 $separator = $current_menu_item['separator'];
1231 }
1232 if ( isset( $current_menu_item['external_link'] ) ) {
1233 $external_link = $current_menu_item['external_link'];
1234 }
1235 }
1236 if ( !is_array( $disabled_for ) ) {
1237 $disabled_for = [];
1238 }
1239 // LIST OF AVAILABLE MENU ICONS
1240 $icons = [
1241 'dashicons-admin-multisite' => 'dashicons dashicons-admin-multisite',
1242 'dashicons-dashboard' => 'dashicons dashicons-dashboard',
1243 'dashicons-admin-post' => 'dashicons dashicons-admin-post',
1244 'dashicons-database' => 'dashicons dashicons-database',
1245 'dashicons-admin-media' => 'dashicons dashicons-admin-media',
1246 'dashicons-admin-page' => 'dashicons dashicons-admin-page',
1247 'dashicons-admin-comments' => 'dashicons dashicons-admin-comments',
1248 'dashicons-admin-appearance' => 'dashicons dashicons-admin-appearance',
1249 'dashicons-admin-plugins' => 'dashicons dashicons-admin-plugins',
1250 'dashicons-admin-users' => 'dashicons dashicons-admin-users',
1251 'dashicons-admin-tools' => 'dashicons dashicons-admin-tools',
1252 'dashicons-chart-bar' => 'dashicons dashicons-chart-bar',
1253 'dashicons-admin-settings' => 'dashicons dashicons-admin-settings',
1254 ];
1255 $default_icons = ( isset( $current_menu_item ) ? $current_menu_item[6] : 'dashicons dashicons-external' );
1256 ?>
1257 <div class="accordion adminify_menu_item" name="<?php
1258 echo esc_attr( $name_attr );
1259 ?>" id="wp-adminify-top-menu-<?php
1260 echo esc_attr( $menu_id );
1261 ?>">
1262 <input type="number" class="top_level_order" value="" style="display:none;">
1263 <a class="menu-editor-title accordion-button adminify-flex adminify-flex-center adminify-gap-2" href="#">
1264 <svg class="drag-icon is-pulled-left mr-2" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1265 <path d="M12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10C10.8954 10 10 10.8954 10 12C10 13.1046 10.8954 14 12 14Z" fill="#4E4B66" fill-opacity="0.72" />
1266 <path d="M12 7C13.1046 7 14 6.10457 14 5C14 3.89543 13.1046 3 12 3C10.8954 3 10 3.89543 10 5C10 6.10457 10.8954 7 12 7Z" fill="#4E4B66" fill-opacity="0.72" />
1267 <path d="M12 21C13.1046 21 14 20.1046 14 19C14 17.8954 13.1046 17 12 17C10.8954 17 10 17.8954 10 19C10 20.1046 10.8954 21 12 21Z" fill="#4E4B66" fill-opacity="0.72" />
1268 <path d="M5 14C6.10457 14 7 13.1046 7 12C7 10.8954 6.10457 10 5 10C3.89543 10 3 10.8954 3 12C3 13.1046 3.89543 14 5 14Z" fill="#4E4B66" fill-opacity="0.72" />
1269 <path d="M5 7C6.10457 7 7 6.10457 7 5C7 3.89543 6.10457 3 5 3C3.89543 3 3 3.89543 3 5C3 6.10457 3.89543 7 5 7Z" fill="#4E4B66" fill-opacity="0.72" />
1270 <path d="M5 21C6.10457 21 7 20.1046 7 19C7 17.8954 6.10457 17 5 17C3.89543 17 3 17.8954 3 19C3 20.1046 3.89543 21 5 21Z" fill="#4E4B66" fill-opacity="0.72" />
1271 <path d="M19 14C20.1046 14 21 13.1046 21 12C21 10.8954 20.1046 10 19 10C17.8954 10 17 10.8954 17 12C17 13.1046 17.8954 14 19 14Z" fill="#4E4B66" fill-opacity="0.72" />
1272 <path d="M19 7C20.1046 7 21 6.10457 21 5C21 3.89543 20.1046 3 19 3C17.8954 3 17 3.89543 17 5C17 6.10457 17.8954 7 19 7Z" fill="#4E4B66" fill-opacity="0.72" />
1273 <path d="M19 21C20.1046 21 21 20.1046 21 19C21 17.8954 20.1046 17 19 17C17.8954 17 17 17.8954 17 19C17 20.1046 17.8954 21 19 21Z" fill="#4E4B66" fill-opacity="0.72" />
1274 </svg>
1275 <?php
1276 echo wp_kses_post( preg_replace( '/\\<span.*?>.*?\\<\\/span><\\/span>/s', '', $current_menu_item[0] ) );
1277 ?>
1278 </a>
1279
1280 <div class="accordion-body adminify_top_level_settings">
1281 <div class="tabs tabbable">
1282 <ul class="adminify-flex adminify-flex-center adminify-m-0 nav nav-tabs">
1283 <li class="nav-item active">
1284 <a class="nav-link is-clickable active" href="#tab-<?php
1285 echo esc_attr( $menu_id );
1286 ?>-1">
1287 <?php
1288 esc_html_e( 'Settings', 'adminify' );
1289 ?>
1290 </a>
1291 </li>
1292 <li class="nav-item" role="presentation">
1293 <a class="nav-link is-clickable" href="#tab-<?php
1294 echo esc_attr( $menu_id );
1295 ?>-2">
1296 <?php
1297 esc_html_e( 'Submenu', 'adminify' );
1298 ?>
1299 </a>
1300 </li>
1301 </ul>
1302 </div>
1303
1304 <div class="tab-content tab-panel panel adminify-p-4">
1305 <div id="tab-<?php
1306 echo esc_attr( $menu_id );
1307 ?>-1" class="tab-pane">
1308 <div class="menu-editor-form">
1309 <div class="columns">
1310 <div class="column">
1311 <label for="<?php
1312 echo esc_attr( $current_menu_item[2] );
1313 ?>">
1314 <?php
1315 esc_html_e( 'Rename as', 'adminify' );
1316 ?>
1317 </label>
1318 <input class="menu_setting" type="text" name="name" data-top-menu-id="<?php
1319 echo esc_attr( $menu_id );
1320 ?>" placeholder="<?php
1321 echo esc_attr( $current_menu_item[0] );
1322 ?>" value='<?php
1323 echo esc_attr( $name );
1324 ?>' />
1325 </div>
1326 <div class="column">
1327 <label for="<?php
1328 echo esc_attr( $current_menu_item[2] );
1329 ?>">
1330 <?php
1331 esc_html_e( 'Hidden For Roles or Users', 'adminify' );
1332 ?>
1333 </label>
1334
1335 <div class="select is-small">
1336
1337 <?php
1338 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- generate_user_rules_select_field() returns a trusted <select> template with form inputs that wp_kses_post() would strip; values inside are escaped.
1339 echo $this->generate_user_rules_select_field( $menu_id, $disabled_for );
1340 ?>
1341
1342 </div>
1343 </div>
1344 </div>
1345
1346 <div class="columns">
1347 <!-- <div class="column">
1348 <label for="">
1349 <?php
1350 esc_html_e( 'Change Link', 'adminify' );
1351 ?>
1352 </label>
1353 <input class="menu_setting" name="link" type="url" placeholder="<?php
1354 esc_html_e( 'New link', 'adminify' );
1355 ?>" value="<?php
1356 echo esc_attr( ( $name_attr != ltrim( $link, '#' ) ? $link : '' ) );
1357 ?>" onchange="checkForLinkTarget(this)" >
1358
1359 <label for="external_link-<?php
1360 echo esc_attr( $name_attr );
1361 ?>">
1362 <?php
1363 esc_html_e( 'New Window?', 'adminify' );
1364 ?>
1365 <input class="menu_setting" id="external_link-<?php
1366 echo esc_attr( $name_attr );
1367 ?>" name="external_link" type="checkbox" value="0" <?php
1368 checked( (int) $external_link, 1 );
1369 ?> disabled>
1370
1371 </label>
1372 <script>
1373 function checkForLinkTarget(inputField) {
1374 var checkbox = inputField.closest('.column').querySelector('input[type="checkbox"]');
1375
1376 if (inputField.value.trim() !== "") {
1377 checkbox.disabled = false; // Enable checkbox if input is not empty
1378 } else {
1379 checkbox.disabled = true; // Disable checkbox if input is empty
1380 }
1381 }
1382 </script>
1383 </div> -->
1384
1385 <div class="column">
1386 <label for="">
1387 <?php
1388 esc_html_e( 'Change Link', 'adminify' );
1389 ?>
1390 </label>
1391 <input class="menu_setting" name="link" type="url"
1392 placeholder="<?php
1393 esc_html_e( 'New link', 'adminify' );
1394 ?>"
1395 value="<?php
1396 echo esc_attr( ( $name_attr != ltrim( $link, '#' ) ? $link : '' ) );
1397 ?>"
1398 oninput="checkForLinkTarget(this)">
1399 <div class="new-tab-settings" style="padding-top: 10px;">
1400 <label for="external_link-<?php
1401 echo esc_attr( $name_attr );
1402 ?>" style="display: flex; align-items: center; gap: 8px;">
1403
1404 <input class="menu_setting" id="external_link-<?php
1405 echo esc_attr( $name_attr );
1406 ?>"
1407 name="external_link" type="checkbox" value="1"
1408 <?php
1409 checked( (int) $external_link, 1 );
1410 ?> disabled style="margin-top: 0;" >
1411 <span><?php
1412 esc_html_e( 'New Window?', 'adminify' );
1413 ?></span>
1414 </label>
1415 </div>
1416
1417 <script>
1418 // Function to validate URL
1419 function isValidURL(url) {
1420 // Allow absolute URLs (with http:// or https://)
1421 var absolutePattern = /^(https?:\/\/)[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}.*$/;
1422
1423 // Allow relative URLs (starting with "/")
1424 var relativePattern = /^\/[a-zA-Z0-9-_\/]+$/;
1425
1426 return absolutePattern.test(url) || relativePattern.test(url);
1427 }
1428
1429
1430 function checkForLinkTarget(inputField) {
1431 var checkbox = inputField.closest('.column').querySelector('input[type="checkbox"]');
1432
1433 if (isValidURL(inputField.value.trim())) {
1434 checkbox.disabled = false; // Enable if URL is valid
1435 } else {
1436 checkbox.disabled = true; // Disable if invalid
1437 checkbox.checked = false; // Also uncheck if disabled
1438 }
1439 }
1440
1441 // Run on page load
1442 document.addEventListener("DOMContentLoaded", function() {
1443 document.querySelectorAll('.column input[name="link"]').forEach(function(inputField) {
1444 checkForLinkTarget(inputField);
1445 });
1446 });
1447 </script>
1448 </div>
1449
1450 <div class="column">
1451 <label for="">
1452 <?php
1453 esc_html_e( 'Set Custom Icon', 'adminify' );
1454 ?>
1455 </label>
1456 <div class="icon-picker-wrap wp-adminify-menu-icon-picker adminify-icon-picker-input icon-select-button is-clickable is-pulled-left">
1457 <ul class="icon-picker">
1458 <li class="icon-none" title="None"><i class="dashicons dashicons-dismiss"></i></li>
1459 <li class="select-icon <?php
1460 echo ( !empty( $icon ) && preg_match( '/http(s?)\\:\\/\\//i', $icon ) || empty( $icon ) && empty( $icons[$default_icons] ) ? 'custom-icon' : '' );
1461 ?>" title="Icon Library">
1462 <?php
1463 if ( !empty( $icon ) ) {
1464 if ( preg_match( '/http(s?)\\:\\/\\//i', $icon ) ) {
1465 $image = explode( ',', $icon );
1466 echo '<i class=""><img width="24" height="24" src=' . esc_url( $image[1] ) . ' ></i>';
1467 } else {
1468 ?>
1469 <i class="<?php
1470 echo esc_attr( $this->get_icon( esc_attr( $icon ), '' ) );
1471 ?>"></i>
1472 <?php
1473 }
1474 } else {
1475 $adminify_icon = PXLBSADMINIFY_ASSETS_IMAGE . 'logos/menu-icon.svg';
1476 if ( empty( $icons[$default_icons] ) ) {
1477 echo '<i class=""><img width="24" height="24" src=' . esc_url( $adminify_icon ) . ' ></i>';
1478 } else {
1479 ?>
1480 <i class="<?php
1481 echo wp_kses_post( $this->get_icon( esc_attr( $icon ), $icons[$default_icons] ) );
1482 ?>"></i>
1483 <?php
1484 }
1485 }
1486 ?>
1487
1488 </li>
1489 <input type="hidden" class="menu_setting" name="icon" value="<?php
1490 echo esc_attr( $icon );
1491 ?>">
1492 </ul>
1493 </div>
1494 </div>
1495 </div>
1496 <div class='columns'>
1497 <?php
1498 $upgrade_pro = '';
1499 $separator_name_attr = '';
1500 $upgrade_pro = 'upgrade-pro';
1501 $separator_name_attr = Utils::upgrade_pro_notice( 'Add Separator' );
1502 ?>
1503
1504 <div class='column <?php
1505 echo esc_attr( $upgrade_pro );
1506 ?>'>
1507 <label for="seperator-<?php
1508 echo esc_attr( $name_attr );
1509 ?>">
1510 <?php
1511 $separator_content = apply_filters( 'pxlbsadminify/menu_editor/add_separator', $separator_name_attr, $separator );
1512 // Apply the filter
1513 echo wp_kses_post( $separator_content || "" );
1514 ?>
1515 </label>
1516 </div>
1517 </div>
1518 </div>
1519 <?php
1520 if ( strpos( $current_menu_item[5], 'adminify-custom-menu-' ) !== false ) {
1521 ?>
1522 <div class="remove-add-new-menu"><span data-id="<?php
1523 echo esc_attr( $current_menu_item[5] );
1524 ?>"><i class="icon-close"></i> Delete</span></div>
1525 <?php
1526 }
1527 ?>
1528 </div>
1529 <div id="tab-<?php
1530 echo esc_attr( $menu_id );
1531 ?>-2" class="tab-pane tab-pane--submenu">
1532 <?php
1533 // Sub Menu Items Check
1534 $link = $current_menu_item[2];
1535 if ( isset( $master_sub_menu[$name_attr] ) && is_array( $master_sub_menu[$name_attr] ) ) {
1536 foreach ( $master_sub_menu[$name_attr] as $sub_menu_item ) {
1537 $this->build_sub_menu_item( $sub_menu_item, $optiongroup, $name_attr );
1538 }
1539 } else {
1540 ?>
1541 <span><?php
1542 esc_html_e( 'No sub menu items', 'adminify' );
1543 ?></span>
1544 <?php
1545 }
1546 $this->render_add_new_menu_item( true );
1547 // true for submenu item
1548 ?>
1549
1550 </div>
1551 </div>
1552
1553 </div>
1554 </div>
1555
1556 <?php
1557 }
1558
1559 public function render_menu_separator( $current_menu_item ) {
1560 $disabled_for = [];
1561 $name = '';
1562 $menu_id = preg_replace( '/[^A-Za-z0-9 ]/', '', $current_menu_item[2] );
1563 $menu_options = $this->menu_settings;
1564 if ( is_array( $menu_options ) ) {
1565 if ( isset( $menu_options[$current_menu_item[2]] ) ) {
1566 $optiongroup = $menu_options[$current_menu_item[2]];
1567 if ( isset( $optiongroup['name'] ) ) {
1568 $name = $optiongroup['name'];
1569 }
1570 if ( isset( $optiongroup['hidden_for'] ) ) {
1571 $disabled_for = $optiongroup['hidden_for'];
1572 }
1573 }
1574 }
1575 if ( !is_array( $disabled_for ) ) {
1576 $disabled_for = [];
1577 }
1578 ?>
1579 <div class="accordion adminify_menu_item" name="<?php
1580 echo esc_attr( $current_menu_item[2] );
1581 ?>" id="<?php
1582 echo esc_attr( $menu_id );
1583 ?>">
1584 <input type="number" class="top_level_order" value="" style="display:none;">
1585 <a class="menu-editor-title accordion-button p-4" href="#">
1586 <svg class="drag-icon is-pulled-left mr-2" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1587 <path d="M12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10C10.8954 10 10 10.8954 10 12C10 13.1046 10.8954 14 12 14Z" fill="#4E4B66" fill-opacity="0.72" />
1588 <path d="M12 7C13.1046 7 14 6.10457 14 5C14 3.89543 13.1046 3 12 3C10.8954 3 10 3.89543 10 5C10 6.10457 10.8954 7 12 7Z" fill="#4E4B66" fill-opacity="0.72" />
1589 <path d="M12 21C13.1046 21 14 20.1046 14 19C14 17.8954 13.1046 17 12 17C10.8954 17 10 17.8954 10 19C10 20.1046 10.8954 21 12 21Z" fill="#4E4B66" fill-opacity="0.72" />
1590 <path d="M5 14C6.10457 14 7 13.1046 7 12C7 10.8954 6.10457 10 5 10C3.89543 10 3 10.8954 3 12C3 13.1046 3.89543 14 5 14Z" fill="#4E4B66" fill-opacity="0.72" />
1591 <path d="M5 7C6.10457 7 7 6.10457 7 5C7 3.89543 6.10457 3 5 3C3.89543 3 3 3.89543 3 5C3 6.10457 3.89543 7 5 7Z" fill="#4E4B66" fill-opacity="0.72" />
1592 <path d="M5 21C6.10457 21 7 20.1046 7 19C7 17.8954 6.10457 17 5 17C3.89543 17 3 17.8954 3 19C3 20.1046 3.89543 21 5 21Z" fill="#4E4B66" fill-opacity="0.72" />
1593 <path d="M19 14C20.1046 14 21 13.1046 21 12C21 10.8954 20.1046 10 19 10C17.8954 10 17 10.8954 17 12C17 13.1046 17.8954 14 19 14Z" fill="#4E4B66" fill-opacity="0.72" />
1594 <path d="M19 7C20.1046 7 21 6.10457 21 5C21 3.89543 20.1046 3 19 3C17.8954 3 17 3.89543 17 5C17 6.10457 17.8954 7 19 7Z" fill="#4E4B66" fill-opacity="0.72" />
1595 <path d="M19 21C20.1046 21 21 20.1046 21 19C21 17.8954 20.1046 17 19 17C17.8954 17 17 17.8954 17 19C17 20.1046 17.8954 21 19 21Z" fill="#4E4B66" fill-opacity="0.72" />
1596 </svg>
1597 <?php
1598 esc_html_e( 'Separator', 'adminify' );
1599 ?>
1600 </a>
1601
1602 <div class="accordion-body adminify_top_level_settings">
1603 <div class="tab-content tab-panel panel p-4">
1604 <div class="menu-editor-form">
1605 <div class="columns">
1606 <div class="column">
1607 <label for=""><?php
1608 esc_html_e( 'Rename as', 'adminify' );
1609 ?></label>
1610 <input class="menu_setting" type="text" name="name" placeholder="<?php
1611 esc_html_e( 'New Name', 'adminify' );
1612 ?>" value="<?php
1613 echo esc_attr( $name );
1614 ?>">
1615 </div>
1616 <div class="column">
1617 <label for=""><?php
1618 esc_html_e( 'Hidden For Roles or Users', 'adminify' );
1619 ?></label>
1620
1621 <div class="select is-small">
1622 <?php
1623 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- generate_user_rules_select_field() returns a trusted <select> template with form inputs that wp_kses_post() would strip; values inside are escaped.
1624 echo $this->generate_user_rules_select_field( $menu_id, $disabled_for );
1625 ?>
1626 </div>
1627 </div>
1628 </div>
1629 </div>
1630
1631 </div>
1632 </div>
1633 </div>
1634
1635 <?php
1636 }
1637
1638 public function build_sub_menu_item( $current_menu_item, $optiongroup, $name_attr ) {
1639 $name = '';
1640 $link = '';
1641 $icon = '';
1642 $external_link = '';
1643 $disabled_for = [];
1644 $suboptiongroup = [];
1645 $menu_options = $this->menu_settings;
1646 if ( isset( $optiongroup['submenu'] ) ) {
1647 if ( isset( $optiongroup['submenu'][$current_menu_item[2]] ) ) {
1648 $suboptiongroup = $optiongroup['submenu'][$current_menu_item[2]];
1649 if ( isset( $suboptiongroup['name'] ) ) {
1650 $name = $suboptiongroup['name'];
1651 }
1652 if ( isset( $suboptiongroup['link'] ) ) {
1653 $link = $suboptiongroup['link'];
1654 }
1655 if ( isset( $suboptiongroup['external_link'] ) ) {
1656 $external_link = $suboptiongroup['external_link'];
1657 }
1658 if ( isset( $suboptiongroup['icon'] ) ) {
1659 $icon = $suboptiongroup['icon'];
1660 }
1661 if ( isset( $suboptiongroup['hidden_for'] ) ) {
1662 $disabled_for = $suboptiongroup['hidden_for'];
1663 }
1664 }
1665 }
1666 $sub_menu_name_attr = $current_menu_item[2];
1667 if ( isset( $current_menu_item['key'] ) ) {
1668 $sub_menu_name_attr = $current_menu_item['key'];
1669 }
1670 if ( strpos( $sub_menu_name_attr, 'adminify-custom-submenu-' ) !== false ) {
1671 $name = $current_menu_item[0];
1672 $link = $current_menu_item[2];
1673 if ( isset( $optiongroup['submenu'][$sub_menu_name_attr]['hidden_for'] ) ) {
1674 $disabled_for = $optiongroup['submenu'][$sub_menu_name_attr]['hidden_for'];
1675 }
1676 if ( isset( $optiongroup['submenu'][$sub_menu_name_attr]['icon'] ) ) {
1677 $icon = $optiongroup['submenu'][$sub_menu_name_attr]['icon'];
1678 }
1679 if ( isset( $optiongroup['submenu'][$sub_menu_name_attr]['external_link'] ) ) {
1680 $external_link = $optiongroup['submenu'][$sub_menu_name_attr]['external_link'];
1681 }
1682 }
1683 if ( !is_array( $disabled_for ) ) {
1684 $disabled_for = [];
1685 }
1686 $menu_id = preg_replace( '/[^A-Za-z0-9 ]/', '', $sub_menu_name_attr );
1687 ?>
1688 <div class="accordion adminify_sub_menu_item" name="<?php
1689 echo esc_attr( $sub_menu_name_attr );
1690 ?>" id="wp-adminify-sub-menu-<?php
1691 echo esc_attr( $menu_id );
1692 ?>">
1693 <input type="number" class="top_level_order" value="" style="display:none;">
1694 <a class="menu-editor-title accordion-button adminify-flex adminify-flex-center adminify-gap-2" href="#">
1695 <svg class="drag-icon is-pulled-left mr-2" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1696 <path d="M12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10C10.8954 10 10 10.8954 10 12C10 13.1046 10.8954 14 12 14Z" fill="#4E4B66" fill-opacity="0.72" />
1697 <path d="M12 7C13.1046 7 14 6.10457 14 5C14 3.89543 13.1046 3 12 3C10.8954 3 10 3.89543 10 5C10 6.10457 10.8954 7 12 7Z" fill="#4E4B66" fill-opacity="0.72" />
1698 <path d="M12 21C13.1046 21 14 20.1046 14 19C14 17.8954 13.1046 17 12 17C10.8954 17 10 17.8954 10 19C10 20.1046 10.8954 21 12 21Z" fill="#4E4B66" fill-opacity="0.72" />
1699 <path d="M5 14C6.10457 14 7 13.1046 7 12C7 10.8954 6.10457 10 5 10C3.89543 10 3 10.8954 3 12C3 13.1046 3.89543 14 5 14Z" fill="#4E4B66" fill-opacity="0.72" />
1700 <path d="M5 7C6.10457 7 7 6.10457 7 5C7 3.89543 6.10457 3 5 3C3.89543 3 3 3.89543 3 5C3 6.10457 3.89543 7 5 7Z" fill="#4E4B66" fill-opacity="0.72" />
1701 <path d="M5 21C6.10457 21 7 20.1046 7 19C7 17.8954 6.10457 17 5 17C3.89543 17 3 17.8954 3 19C3 20.1046 3.89543 21 5 21Z" fill="#4E4B66" fill-opacity="0.72" />
1702 <path d="M19 14C20.1046 14 21 13.1046 21 12C21 10.8954 20.1046 10 19 10C17.8954 10 17 10.8954 17 12C17 13.1046 17.8954 14 19 14Z" fill="#4E4B66" fill-opacity="0.72" />
1703 <path d="M19 7C20.1046 7 21 6.10457 21 5C21 3.89543 20.1046 3 19 3C17.8954 3 17 3.89543 17 5C17 6.10457 17.8954 7 19 7Z" fill="#4E4B66" fill-opacity="0.72" />
1704 <path d="M19 21C20.1046 21 21 20.1046 21 19C21 17.8954 20.1046 17 19 17C17.8954 17 17 17.8954 17 19C17 20.1046 17.8954 21 19 21Z" fill="#4E4B66" fill-opacity="0.72" />
1705 </svg>
1706 <?php
1707 if ( !empty( $current_menu_item[0] ) ) {
1708 echo wp_kses_post( preg_replace( '/\\<span.*?>.*?\\<\\/span><\\/span>/s', '', $current_menu_item[0] ) );
1709 }
1710 ?>
1711 </a>
1712
1713 <div class="accordion-body">
1714 <div class="tab-content tab-panel panel p-4">
1715 <div class="tab-pane">
1716 <div class="menu-editor-form">
1717 <div class="columns">
1718 <div class="column">
1719 <label for=""><?php
1720 esc_html_e( 'Rename as', 'adminify' );
1721 ?></label>
1722 <input class="sub_menu_setting" type="text" data-sub-menu-id="<?php
1723 echo esc_attr( $menu_id );
1724 ?>" name="name" placeholder="<?php
1725 esc_html_e( 'New Menu name...', 'adminify' );
1726 ?>" value="<?php
1727 echo esc_attr( $name );
1728 ?>">
1729 </div>
1730 <div class="column">
1731 <label for=""><?php
1732 esc_html_e( 'Hidden For Roles or Users', 'adminify' );
1733 ?></label>
1734
1735 <div class="select is-small">
1736 <?php
1737 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- generate_user_rules_select_field() returns a trusted <select> template with form inputs that wp_kses_post() would strip; values inside are escaped.
1738 echo $this->generate_user_rules_select_field( $menu_id, $disabled_for, 'sub_menu' );
1739 ?>
1740 </div>
1741 </div>
1742 </div>
1743 <div class="columns">
1744 <div class="column">
1745 <label for=""><?php
1746 esc_html_e( 'Change Link', 'adminify' );
1747 ?></label>
1748 <input class="sub_menu_setting" name="link" type="url" placeholder="New link" value="<?php
1749 echo esc_attr( ( $sub_menu_name_attr != ltrim( $link, '#' ) ? $link : '' ) );
1750 ?>" onchange="checkForLinkTarget(this)" >
1751
1752 <label for="external_link-<?php
1753 echo esc_attr( $sub_menu_name_attr );
1754 ?>">
1755 <?php
1756 esc_html_e( 'New Window?', 'adminify' );
1757 ?>
1758 <input class="sub_menu_setting" id="external_link-<?php
1759 echo esc_attr( $sub_menu_name_attr );
1760 ?>" name="external_link" type="checkbox" value="0" <?php
1761 checked( (int) $external_link, 1 );
1762 ?>disabled />
1763
1764 </label>
1765 </div>
1766 <div class="column">
1767 <label for="" style="margin-top:25px">
1768 <?php
1769 esc_html_e( 'Custom Icon is not allowed for Submenu', 'adminify' );
1770 ?>
1771 </label>
1772 </div>
1773 </div>
1774 </div>
1775 <?php
1776 if ( strpos( $sub_menu_name_attr, 'adminify-custom-submenu-' ) !== false ) {
1777 ?>
1778 <div class="remove-add-new-menu"><span data-id="<?php
1779 echo esc_attr( $sub_menu_name_attr );
1780 ?>"><i class="icon-close"></i> Delete</span></div>
1781 <?php
1782 }
1783 ?>
1784 </div>
1785 </div>
1786 </div>
1787 </div>
1788
1789 <?php
1790 }
1791
1792 /**
1793 * Add New Menu Item.
1794 */
1795 public function render_add_new_menu_item( $submenu = false ) {
1796 $add_item = apply_filters( 'pxlbsadminify/menu_editor', [
1797 'upgrade_pro' => Utils::upgrade_pro_notice( ' ' ),
1798 'upgrade_class' => 'upgrade-pro',
1799 ] );
1800 ?>
1801 <div class="adminify-accordion adminify-add-new-menu-editor-item upgrade-pro adminify-field adminify-field-switcher adminify-depend-visible adminify-depend-on adminify-pro-notice <?php
1802 echo esc_attr( ( $submenu ? 'submenu' : '' ) );
1803 ?> <?php
1804 echo esc_attr( $add_item['upgrade_class'] );
1805 ?>">
1806 <div class="inner-text">
1807 <i class="dashicons dashicons-plus-alt"></i>
1808 <span class="title"><?php
1809 esc_html_e( 'Add Item', 'adminify' );
1810 ?></span>
1811 </div>
1812 <?php
1813 echo wp_kses( $add_item['upgrade_pro'], Utils::kses_allowed_html() );
1814 ?>
1815 </div>
1816 <?php
1817 }
1818
1819 /**
1820 * Menu Editor Header
1821 *
1822 * @return void
1823 */
1824 public static function render_menu_editor_header() {
1825 ?>
1826
1827 <div class="adminify-flex adminify-flex-center adminify-justify-between">
1828 <div class="adminify-menu-editor-help-urls adminify-flex adminify-flex-center adminify-gap-2">
1829 <?php
1830 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- help_urls() returns a trusted template containing inline SVG icons that wp_kses_post() would strip; URLs inside are passed through esc_url().
1831 echo Utils::help_urls(
1832 __( 'Menu Editor', 'adminify' ),
1833 'https://wpadminify.com/kb/wordpress-dashboard-menu-editor/',
1834 'https://www.youtube.com/playlist?list=PLqpMw0NsHXV-EKj9Xm1DMGa6FGniHHly8',
1835 'https://www.facebook.com/groups/jeweltheme',
1836 esc_url( \PXLBSAdminify\Inc\Admin\AdminSettings::support_url() )
1837 );
1838 ?>
1839 </div>
1840
1841
1842 <div class="wp-adminify--page--title--actions adminify-flex adminify-flex-center adminify-gap-2">
1843
1844 <button class="adminify-btn adminify-btn-outline-primary adminify_menu_save_settings">
1845 <?php
1846 esc_html_e( 'Save', 'adminify' );
1847 ?>
1848 </button>
1849
1850 <div class="adminify-dropdown">
1851 <div class="adminify-dropdown-trigger">
1852 <button class="adminify-btn adminify-btn-outline-primary" aria-haspopup="true" aria-controls="dropdown-menu">
1853 <svg class="icon" width="16" height="12" viewBox="0 0 16 12" fill="none" xmlns="http://www.w3.org/2000/svg">
1854 <path d="M5 12C3.63144 12.0007 2.43589 11.0751 2.09375 9.75H0.5V8.25H2.0945C2.48423 6.74059 3.96509 5.78119 5.50196 6.04243C7.03883 6.30366 8.11953 7.69847 7.98865 9.25188C7.85776 10.8053 6.55892 11.9996 5 12ZM5 7.5C4.18055 7.50083 3.51342 8.15914 3.50167 8.97851C3.48993 9.79788 4.13792 10.475 4.95702 10.4993C5.77611 10.5237 6.46312 9.88613 6.5 9.0675V9.3675V9C6.5 8.17158 5.82843 7.5 5 7.5ZM15.5 9.75H8.75V8.25H15.5V9.75ZM8.75 6C7.38172 6.00035 6.18657 5.07483 5.8445 3.75H0.5V2.25H5.8445C6.23423 0.740588 7.71509 -0.218809 9.25196 0.0424253C10.7888 0.30366 11.8695 1.69847 11.7386 3.25188C11.6078 4.80529 10.3089 5.99961 8.75 6ZM8.75 1.5C7.93055 1.50083 7.26342 2.15914 7.25167 2.97851C7.23993 3.79788 7.88792 4.47503 8.70702 4.49934C9.52611 4.52365 10.2131 3.88613 10.25 3.0675V3.3675V3C10.25 2.17158 9.57843 1.5 8.75 1.5ZM15.5 3.75H12.5V2.25H15.5V3.75Z" fill="#0347FF" />
1855 </svg>
1856 </button>
1857 </div>
1858 <div class="adminify-dropdown-menu" id="dropdown-menu" role="menu">
1859 <div class="adminify-dropdown-content adminify-flex adminify-flex-col adminify-gap-2">
1860 <a href="#" class="adminify-dropdown-item adminify_export_menu_settings">
1861 <?php
1862 esc_html_e( 'Export menu', 'adminify' );
1863 ?>
1864 </a>
1865 <input accept=".json" type="file" single="" id="adminify_import_menu" class="hidden">
1866 <a href="#" class="adminify-dropdown-item adminify_import_menu_settings">
1867 <?php
1868 esc_html_e( 'Import menu', 'adminify' );
1869 ?>
1870 </a>
1871 <a href="#" class="adminify-dropdown-item adminify_reset_menu_settings">
1872 <?php
1873 esc_html_e( 'Reset menu', 'adminify' );
1874 ?>
1875 </a>
1876 </div>
1877 </div>
1878 </div>
1879 </div>
1880 </div>
1881
1882 <div class="adminify-text-wrap">
1883 <p><?php
1884 esc_html_e( 'Edit each menu item\'s name, link, icon and visibility. Drag and drop to rearange the menu. Changes will take effect after page refresh.', 'adminify' );
1885 ?></p>
1886 <p class="adminify-flex adminify-flex-center adminify-gap-1">
1887 <svg class="" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
1888 <path d="M7.33398 7.32002C7.33398 7.14321 7.40422 6.97364 7.52925 6.84861C7.65427 6.72359 7.82384 6.65335 8.00065 6.65335C8.17746 6.65335 8.34703 6.72359 8.47206 6.84861C8.59708 6.97364 8.66732 7.14321 8.66732 7.32002V11.32C8.66732 11.4968 8.59708 11.6664 8.47206 11.7914C8.34703 11.9164 8.17746 11.9867 8.00065 11.9867C7.82384 11.9867 7.65427 11.9164 7.52925 11.7914C7.40422 11.6664 7.33398 11.4968 7.33398 11.32V7.32002Z" fill="#4E4B66" />
1889 <path d="M8.00065 4.034C7.82384 4.034 7.65427 4.10423 7.52925 4.22926C7.40422 4.35428 7.33398 4.52385 7.33398 4.70066C7.33398 4.87747 7.40422 5.04704 7.52925 5.17207C7.65427 5.29709 7.82384 5.36733 8.00065 5.36733C8.17746 5.36733 8.34703 5.29709 8.47206 5.17207C8.59708 5.04704 8.66732 4.87747 8.66732 4.70066C8.66732 4.52385 8.59708 4.35428 8.47206 4.22926C8.34703 4.10423 8.17746 4.034 8.00065 4.034Z" fill="#4E4B66" />
1890 <path fill-rule="evenodd" clip-rule="evenodd" d="M8.00065 1.33334C4.31865 1.33334 1.33398 4.31801 1.33398 8.00001C1.33398 11.682 4.31865 14.6667 8.00065 14.6667C11.6827 14.6667 14.6673 11.682 14.6673 8.00001C14.6673 4.31801 11.6827 1.33334 8.00065 1.33334ZM2.66732 8.00001C2.66732 9.4145 3.22922 10.7711 4.22942 11.7712C5.22961 12.7714 6.58616 13.3333 8.00065 13.3333C9.41514 13.3333 10.7717 12.7714 11.7719 11.7712C12.7721 10.7711 13.334 9.4145 13.334 8.00001C13.334 6.58552 12.7721 5.22897 11.7719 4.22877C10.7717 3.22858 9.41514 2.66668 8.00065 2.66668C6.58616 2.66668 5.22961 3.22858 4.22942 4.22877C3.22922 5.22897 2.66732 6.58552 2.66732 8.00001V8.00001Z" fill="#4E4B66" />
1891 </svg>
1892
1893 <?php
1894 esc_html_e( 'If you have Adminify Menu Module disabled, icons and label dividers won\'t change.', 'adminify' );
1895 ?>
1896
1897 </p>
1898 </div>
1899
1900
1901 <?php
1902 }
1903
1904 public function menu_editor_contents() {
1905 ?>
1906
1907 <div class="wrap">
1908 <div class="wp-adminify--menu--editor--container">
1909
1910 <!-- <div id="adminify-data-saved-message"></div> -->
1911
1912 <?php
1913 //self::render_menu_editor_header();
1914 ?>
1915
1916 <div class="wp-adminify--menu--editor--settings adminify-flex adminify-flex-col adminify-gap-4">
1917 <div class="wp-adminify-menu-editor-loader"></div>
1918 <?php
1919 $this->render_menu_editor();
1920 ?>
1921 </div>
1922
1923 </div>
1924 </div>
1925 <a href="#" id="adminify_download_settings" style="display: none;" ?></a>
1926 <?php
1927 }
1928
1929 }
1930
1931 }