PluginProbe ʕ •ᴥ•ʔ
Admin Help Docs / 2.0.0
Admin Help Docs v2.0.0
2.0.1.1 trunk 1.4.3.2 2.0.0 2.0.0.1 2.0.0.2 2.0.1
admin-help-docs / inc / menu.php
admin-help-docs / inc Last commit date
css 3 months ago docs 3 months ago img 3 months ago js 3 months ago tabs 3 months ago api.php 3 months ago cleanup.php 3 months ago colors.php 3 months ago deprecated.php 3 months ago header.php 3 months ago helpers.php 3 months ago index.php 3 months ago menu.php 3 months ago plugins-page.php 3 months ago post-type-help-doc-imports.php 3 months ago post-type-help-docs.php 3 months ago shortcodes.php 3 months ago taxonomy-folders.php 3 months ago
menu.php
595 lines
1 <?php
2 /**
3 * Menu Loader
4 */
5
6 namespace PluginRx\AdminHelpDocs;
7
8 if ( ! defined( 'ABSPATH' ) ) exit;
9
10 class Menu {
11
12 /**
13 * Defaults
14 */
15 public static $default_menu_position = 2;
16
17
18 /**
19 * Tab slugs and labels
20 *
21 * @param string $tab The current tab slug.
22 * @return array
23 */
24 public static function tabs( $tab = null ) : array {
25 $post_new_link = Bootstrap::admin_url( 'post-new.php' );
26 $author_uri = Bootstrap::author_uri();
27 $text_domain = Bootstrap::textdomain();
28
29 $import_header = isset( $_GET[ 'id' ] ) ? __( 'Edit Import', 'admin-help-docs' ) : __( 'Add New Import', 'admin-help-docs' ); // phpcs:ignore
30
31 $tabs = [
32 'documentation' => [
33 'label' => __( 'Documentation', 'admin-help-docs' ),
34 'buttons' => [
35 [
36 'link' => add_query_arg( 'post_type', HelpDocs::$post_type, $post_new_link ),
37 'text' => '<span class="dashicons dashicons-plus"></span> ' . __( 'Add New', 'admin-help-docs' ),
38 ],
39 ],
40 'class' => Documentation::class,
41 ],
42 'manage' => [
43 'label' => __( 'Manage Docs', 'admin-help-docs' ),
44 'title' => __( 'Manage Help Docs', 'admin-help-docs' ),
45 'buttons' => [
46 [
47 'link' => add_query_arg( 'post_type', HelpDocs::$post_type, $post_new_link ),
48 'text' => '<span class="dashicons dashicons-plus"></span> ' . __( 'Add New', 'admin-help-docs' ),
49 ],
50 ],
51 'link' => Bootstrap::tab_url( 'manage' ),
52 ],
53 'folders' => [
54 'label' => __( 'Folders', 'admin-help-docs' ),
55 'title' => __( 'Folders', 'admin-help-docs' ),
56 'link' => Bootstrap::tab_url( 'folders' ),
57 ],
58 'imports' => [
59 'label' => __( 'Imports', 'admin-help-docs' ),
60 'title' => __( 'Imported Docs', 'admin-help-docs' ),
61 'buttons' => [
62 [
63 'link' => Bootstrap::tab_url( 'import' ),
64 'text' => '<span class="dashicons dashicons-plus"></span> ' . __( 'Add New', 'admin-help-docs' ),
65 ],
66 ],
67 'link' => Bootstrap::tab_url( 'imports' ),
68 ],
69 'import' => [
70 'label' => $import_header,
71 'buttons' => [
72 [
73 'id' => 'save_import_settings',
74 'link' => '#',
75 'text' => __( 'Save Import Settings', 'admin-help-docs' ),
76 'form' => 'helpdocs_import_form',
77 'disabled' => true,
78 ],
79 ],
80 'class' => ImportEditor::class,
81 'hidden' => true,
82 ],
83 'admin-menu' => [
84 'label' => __( 'Admin Menu', 'admin-help-docs' ),
85 'buttons' => [
86 [
87 'link' => '#',
88 'text' => __( 'Save', 'admin-help-docs' ),
89 ],
90 ],
91 'class' => AdminMenu::class,
92 ],
93 'settings' => [
94 'label' => __( 'Settings', 'admin-help-docs' ),
95 'buttons' => [
96 [
97 'link' => '#',
98 'text' => __( 'Save', 'admin-help-docs' ),
99 ],
100 ],
101 'class' => Settings::class,
102 ],
103 'faq' => [
104 'label' => __( 'FAQ', 'admin-help-docs' ),
105 'title' => __( 'Frequently Asked Questions', 'admin-help-docs' ),
106 'buttons' => [
107 [
108 'link' => $author_uri . 'guide/plugin/' . $text_domain,
109 'text' => __( 'Help Guides', 'admin-help-docs' ),
110 'target' => '_blank',
111 ],
112 [
113 'link' => $author_uri . 'docs/plugin/' . $text_domain,
114 'text' => __( 'Developer Docs', 'admin-help-docs' ),
115 'target' => '_blank',
116 ],
117 [
118 'link' => $author_uri . 'support/plugin/' . $text_domain,
119 'text' => __( 'Support', 'admin-help-docs' ),
120 'target' => '_blank',
121 ],
122 ],
123 'class' => FAQ::class,
124 ],
125 'support' => [
126 'label' => __( 'Contact Support', 'admin-help-docs' ),
127 'title' => __( 'Contact Support', 'admin-help-docs' ),
128 'class' => Support::class,
129 ],
130 ];
131
132 if ( $tab ) {
133 return $tabs[ $tab ] ?? [];
134 }
135 return $tabs;
136 } // End tabs()
137
138
139 /**
140 * The single instance of the class
141 *
142 * @var self|null
143 */
144 private static ?Menu $instance = null;
145
146
147 /**
148 * Get the singleton instance
149 *
150 * @return self
151 */
152 public static function instance() : self {
153 return self::$instance ??= new self();
154 } // End instance()
155
156
157 /**
158 * Constructor
159 */
160 private function __construct() {
161 add_action( is_network_admin() ? 'network_admin_menu' : 'admin_menu', [ $this, 'register_menu' ] );
162 add_filter( 'parent_file', [ $this, 'submenus' ], 999 );
163 add_action( 'admin_body_class', [ $this, 'admin_body_class' ] );
164 add_filter( 'admin_title', [ $this, 'admin_title' ], 10, 2 );
165 add_action( 'in_admin_header', [ $this, 'admin_header' ] );
166 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
167 } // End __construct()
168
169
170 /**
171 * Register admin menu
172 *
173 * @return void
174 */
175 public function register_menu() : void {
176 $title = sanitize_text_field( get_option( 'helpdocs_menu_title', Helpers::get_menu_title() ) );
177
178 $icon = Helpers::get_icon();
179 $position = absint( get_option( 'helpdocs_menu_position', self::$default_menu_position ) );
180
181 $parent_slug = Bootstrap::textdomain();
182
183 add_menu_page(
184 Bootstrap::name(),
185 $title,
186 'read',
187 $parent_slug,
188 [ $this, 'render_tab' ],
189 $icon,
190 $position
191 );
192
193 if ( ! Helpers::user_can_view() ) {
194 remove_menu_page( $parent_slug );
195 return;
196 }
197
198 $user_can_edit = Helpers::user_can_edit();
199
200 global $submenu;
201
202 foreach ( self::tabs() as $slug => $tab ) {
203
204 if ( isset( $tab[ 'hidden' ] ) && $tab[ 'hidden' ] ) {
205 continue;
206 }
207
208 // Documentation tab is always added for users with view access, even if they don't have edit access.
209 // Other tabs are only added if the user has edit access.
210 if ( $slug === 'documentation' ) {
211 $link = Bootstrap::tab_url( $slug );
212 $submenu[ $parent_slug ][] = [ $tab[ 'label' ] ?? '', 'read', $link ];
213 continue;
214 }
215
216 if ( ! $user_can_edit ) {
217 continue;
218 }
219
220 if ( $slug === 'support' && ! get_option( 'helpdocs_contact_form' ) ) {
221 continue;
222 }
223
224 if ( isset( $tab[ 'link' ] ) && $tab[ 'link' ] ) {
225 $link = $tab[ 'link' ];
226 } else {
227 $link = Bootstrap::tab_url( $slug );
228 }
229
230 $submenu[ $parent_slug ][] = [ $tab[ 'label' ] ?? '', 'read', $link ];
231 }
232
233 // Register the hidden dashboard replacement page
234 add_submenu_page(
235 'index.php',
236 __( 'Dashboard', 'admin-help-docs' ),
237 __( 'Dashboard', 'admin-help-docs' ),
238 'read',
239 'admin-help-dashboard',
240 [ WPDashboard::class, 'render_replacement_page' ]
241 );
242 } // End register_menu()
243
244
245 /**
246 * Set the correct submenu file for our pages
247 *
248 * @param string $parent_file The current parent file slug.
249 * @return string The modified parent file slug.
250 */
251 public function submenus( $parent_file ) {
252 global $submenu_file, $current_screen;
253
254 if ( ( 'admin_page_admin-help-dashboard' ) === $current_screen->id ) {
255 $parent_file = 'index.php';
256 $submenu_file = 'index.php';
257 return $parent_file;
258 }
259
260 $textdomain = Bootstrap::textdomain();
261 $options_page = 'toplevel_page_' . $textdomain;
262
263 if ( is_network_admin() ) {
264 $options_page .= '-network';
265 }
266
267 // Help Docs Tabs
268 if ( $current_screen->id === $options_page ) {
269 $tab = self::get_current_tab();
270 if ( ! $tab ) {
271 return $parent_file;
272 }
273
274 if ( $tab === 'import' ) {
275 $parent_file = $textdomain;
276 $submenu_file = Bootstrap::tab_url( 'imports' );
277 return $parent_file;
278 }
279
280 $submenu_file = Bootstrap::tab_url( $tab );
281
282 // Folder taxonomy
283 } elseif ( $current_screen->id === 'edit-' . Folders::$taxonomy ) {
284 $parent_file = $textdomain;
285 $submenu_file = Bootstrap::tab_url( 'folders' );
286
287 // Post Type Submenus
288 } elseif ( isset( $current_screen->post_type ) && $current_screen->post_type === HelpDocs::$post_type ) {
289 $parent_file = $textdomain;
290 $submenu_file = Bootstrap::tab_url( 'manage' );
291
292 } elseif ( isset( $current_screen->post_type ) && $current_screen->post_type === Imports::$post_type ) {
293 $parent_file = $textdomain;
294 $submenu_file = Bootstrap::tab_url( 'imports' );
295 }
296
297 return $parent_file;
298 } // End submenus()
299
300
301 /**
302 * Add custom body class on our plugin pages
303 *
304 * @param string $classes Existing body classes.
305 * @return string Modified body classes.
306 */
307 public function admin_body_class( $classes ) {
308 $current_screen = get_current_screen();
309
310 $helpdocs_screen_ids = [
311 'toplevel_page_' . Bootstrap::textdomain(),
312 'toplevel_page_' . Bootstrap::textdomain() . '-network',
313 'edit-' . HelpDocs::$post_type,
314 'edit-' . Imports::$post_type,
315 'edit-' . Folders::$taxonomy,
316 ];
317
318 if ( in_array( $current_screen->id, $helpdocs_screen_ids, true ) ) {
319 $classes .= ' helpdocs-admin-screen';
320 }
321
322 return $classes;
323 } // End admin_body_class()
324
325
326 /**
327 * Admin title filter
328 *
329 * @param string $title Current admin title.
330 * @param string $page Current page slug.
331 *
332 * @return string
333 */
334 public function admin_title( $title, $page ) {
335 if ( $page === 'toplevel_page_' . Bootstrap::textdomain() || $page === 'toplevel_page_' . Bootstrap::textdomain() . '-network' ) {
336 $current_tab = self::get_current_tab();
337 $tab = self::tabs( $current_tab );
338 if ( ! empty( $tab ) ) {
339 return $tab[ 'label' ] . '' . Bootstrap::name();
340 }
341 return Bootstrap::name();
342 }
343
344 return $title;
345 } // End admin_title()
346
347
348 /**
349 * Admin header action
350 *
351 * @return void
352 */
353 public function admin_header() {
354 $current_screen = get_current_screen();
355 if ( $current_screen->id === 'toplevel_page_' . Bootstrap::textdomain() || $current_screen->id === 'toplevel_page_' . Bootstrap::textdomain() . '-network' ) {
356 include Bootstrap::path( 'inc/header.php' );
357 }
358 } // End admin_header()
359
360
361 public static function is_our_tab( $tab ) : bool {
362 // Check if it's one of our pages first
363 $is_current_page = self::get_current_page() === Bootstrap::textdomain();
364 if ( ! $is_current_page ) {
365 return false;
366 }
367
368 // Then check if the tab matches
369 $is_current_tab = self::get_current_tab() === $tab;
370 if ( ! $is_current_tab ) {
371 return false;
372 }
373
374 // Finally, check if the tab exists in our tabs array
375 $tabs = self::tabs();
376 return isset( $tabs[ $tab ] );
377 } // End is_our_tab()
378
379
380 /**
381 * Get the current page slug
382 *
383 * @return string
384 */
385 public static function get_current_page() : string {
386 return isset( $_GET[ 'page' ] ) ? sanitize_text_field( wp_unslash( $_GET[ 'page' ] ) ) : ''; // phpcs:ignore
387 } // End get_current_page()
388
389
390 /**
391 * Get the current tab slug
392 *
393 * @return string
394 */
395 public static function get_current_tab() : string {
396 return isset( $_GET[ 'tab' ] ) ? sanitize_key( wp_unslash( $_GET[ 'tab' ] ) ) : ''; // phpcs:ignore
397 } // End get_current_tab()
398
399
400 /**
401 * Render tab content
402 *
403 * @return void
404 */
405 public function render_tab() : void {
406 $tabs = self::tabs();
407 $current_tab_slug = self::get_current_tab();
408 if ( ! $current_tab_slug || ! isset( $tabs[ $current_tab_slug ] ) || ( $current_tab_slug === 'support' && ! get_option( 'helpdocs_contact_form' ) ) ) {
409 wp_safe_redirect( Bootstrap::tab_url( 'documentation' ) );
410 exit;
411 }
412 ?>
413 <div id="<?php echo esc_attr( Bootstrap::textdomain() ); ?>">
414
415 <div class="tab-content">
416 <?php
417 foreach ( $tabs as $key => $tab ) {
418 if ( $current_tab_slug === $key ) {
419
420 if ( isset( $tab[ 'class' ] ) && class_exists( $tab[ 'class' ] ) ) {
421 ?>
422 <div id="<?php echo esc_attr( $key ); ?>">
423 <?php
424 $class_name = $tab[ 'class' ];
425 $instance = $class_name::instance();
426 $instance->render_tab();
427 ?>
428 </div>
429 <?php
430 } else {
431 echo 'Tab content not available.';
432 }
433 }
434 }
435 ?>
436 </div>
437
438 </div>
439 <?php
440 } // End render_tab()
441
442
443 /**
444 * Enqueue admin assets
445 *
446 * @return void
447 */
448 public function enqueue_assets() : void {
449 $text_domain = Bootstrap::textdomain();
450 $script_version = Bootstrap::script_version();
451
452 // All pages in admin area
453 wp_enqueue_style( $text_domain . '-docs', Bootstrap::url( 'inc/css/docs.css' ), [], $script_version );
454
455 $colors = Colors::get();
456 if ( ! empty( $colors ) ) {
457 $inline_css = ':root {';
458 foreach ( $colors as $key => $color ) {
459 $key = str_replace( '_', '-', $key );
460 $inline_css .= "--helpdocs-color-{$key}: {$color};";
461 }
462 $inline_css .= '}';
463 wp_add_inline_style( $text_domain . '-docs', $inline_css );
464 }
465
466 // Click to copy script
467 wp_enqueue_script( $text_domain . '-click-to-copy', Bootstrap::url( 'inc/js/click-to-copy.js' ), [ 'jquery' ], $script_version, true );
468 wp_localize_script( $text_domain . "-click-to-copy", "helpdocs_click_to_copy", [
469 'copied_text' => __( 'Copied!', 'admin-help-docs' ),
470 ] );
471
472 // Limited pages in admin area
473 $current_screen = get_current_screen();
474 $is_helpdocs_screen = $current_screen->id === 'toplevel_page_' . $text_domain || $current_screen->id === 'toplevel_page_' . $text_domain . '-network';
475 $is_post_type_list_screen = $current_screen->id === 'edit-' . HelpDocs::$post_type || $current_screen->id === 'edit-' . Imports::$post_type;
476 $is_taxonomy_list_screen = $current_screen->id === 'edit-' . Folders::$taxonomy;
477 $is_our_dashboard = $current_screen->id === 'dashboard_page_admin-help-dashboard';
478
479 // Only on our dashboard
480 if ( $is_our_dashboard ) {
481 wp_enqueue_style( $text_domain . '-our-dashboard', Bootstrap::url( 'inc/css/our-dashboard.css' ), [], $script_version );
482 }
483
484 // Only on our plugin pages and related post type list screens
485 if ( $is_helpdocs_screen || $is_post_type_list_screen || $is_taxonomy_list_screen ) {
486 wp_enqueue_style( $text_domain . '-header', Bootstrap::url( 'inc/css/header.css' ), [], $script_version );
487 }
488
489 // Only on our tab pages
490 if ( $is_helpdocs_screen ) {
491 wp_enqueue_style( $text_domain . '-content', Bootstrap::url( 'inc/css/content.css' ), [], $script_version );
492 }
493
494 // Only on our post types and taxonomies list screens
495 if ( $is_post_type_list_screen || $is_taxonomy_list_screen ) {
496 wp_enqueue_style( $text_domain . '-pt-tax', Bootstrap::url( 'inc/css/pt-tax.css' ), [], $script_version );
497 }
498
499 // Only on our post types list screens
500 if ( $is_post_type_list_screen ) {
501 wp_enqueue_style( $text_domain . '-post-types', Bootstrap::url( 'inc/css/post-types.css' ), [], $script_version );
502 }
503
504 // Only on our taxonomy list screens
505 if ( $is_taxonomy_list_screen ) {
506 wp_enqueue_style( $text_domain . '-taxonomies', Bootstrap::url( 'inc/css/taxonomies.css' ), [], $script_version );
507 }
508
509 // Load tab-specific assets
510 $current_tab = self::get_current_tab();
511 if ( $is_helpdocs_screen && $current_tab ) {
512 if ( file_exists( Bootstrap::path( "inc/tabs/js/{$current_tab}.js" ) ) ) {
513 wp_enqueue_script( $text_domain . "-tab-{$current_tab}", Bootstrap::url( "inc/tabs/js/{$current_tab}.js" ), [ 'jquery' ], $script_version, true );
514 }
515 if ( file_exists( Bootstrap::path( "inc/tabs/css/{$current_tab}.css" ) ) ) {
516 wp_enqueue_style( $text_domain . "-tab-{$current_tab}", Bootstrap::url( "inc/tabs/css/{$current_tab}.css" ), [], $script_version );
517 }
518
519 // Settings tab
520 if ( $current_tab === 'settings' ) {
521 $editor_settings = wp_enqueue_code_editor( [ 'type' => 'text/css' ] );
522 wp_localize_script( $text_domain . "-tab-{$current_tab}", "helpdocs_{$current_tab}", [
523 'nonce' => wp_create_nonce( "helpdocs_{$current_tab}_nonce" ),
524 'settings' => Settings::setting_fields( true ),
525 'wp_version' => get_bloginfo( 'version' ),
526 'themes' => Colors::themes(),
527 'default_logo' => Helpers::get_default_logo_url(),
528 'editor_settings' => $editor_settings,
529 'saving_text' => __( 'Saving', 'admin-help-docs' ),
530 'saved_text' => __( 'Settings saved successfully.', 'admin-help-docs' ),
531 'error_text' => __( 'Error saving settings. Please try again.', 'admin-help-docs' ),
532 'flushing_text' => __( 'Clearing Cache', 'admin-help-docs' ),
533 ] );
534 }
535
536 // Admin Menu tab
537 if ( $current_tab === 'admin-menu' ) {
538 $current__tab = str_replace( '-', '_', $current_tab );
539 wp_localize_script( $text_domain . "-tab-{$current_tab}", "helpdocs_{$current__tab}", [
540 'nonce' => wp_create_nonce( "helpdocs_{$current__tab}_nonce" ),
541 'settings' => AdminMenu::setting_fields( true ),
542 ] );
543 }
544
545 // Documentation tab
546 if ( $current_tab === 'documentation' ) {
547
548 $main_docs_css = get_option( 'helpdocs_main_docs_css' );
549 if ( ! empty( $main_docs_css ) ) {
550 wp_add_inline_style( $text_domain . "-tab-{$current_tab}", wp_strip_all_tags( $main_docs_css ) );
551 }
552
553 wp_localize_script( $text_domain . "-tab-{$current_tab}", "helpdocs_{$current_tab}", [
554 'nonce' => wp_create_nonce( "helpdocs_{$current_tab}_nonce" ),
555 ] );
556 }
557
558 // Support tab
559 if ( $current_tab === 'support' ) {
560 wp_localize_script( $text_domain . "-tab-{$current_tab}", "helpdocs_{$current_tab}", [
561 'nonce' => wp_create_nonce( "helpdocs_{$current_tab}_nonce" ),
562 'clear_logs_confirm' => __( 'Are you sure you want to delete all support logs?', 'admin-help-docs' ),
563 'max_attachment_mb' => Support::$max_attachment_mb,
564 'files_too_large' => sprintf( __( 'Total uploads exceed the maximum size of %dMB.', 'admin-help-docs' ), Support::$max_attachment_mb ),
565 'log_date_format' => Support::$log_date_format,
566 'required_fields' => __( 'Please fill in all required fields.', 'admin-help-docs' ),
567 ] );
568 }
569
570 // Import tab
571 if ( $current_tab === 'import' ) {
572 wp_localize_script( $text_domain . "-tab-{$current_tab}", "helpdocs_{$current_tab}", [
573 'fetch_nonce' => wp_create_nonce( "helpdocs_{$current_tab}_fetch_nonce" ),
574 'clone_nonce' => wp_create_nonce( "helpdocs_{$current_tab}_clone_nonce" ),
575 'fetching_text' => __( 'Fetching', 'admin-help-docs' ),
576 'importing_text' => __( 'Cloning', 'admin-help-docs' ),
577 'imported_text' => __( 'Copy Complete!', 'admin-help-docs' ),
578 'error_text' => __( 'An error occurred during cloning.', 'admin-help-docs' ),
579 'saving_text' => __( 'Saving', 'admin-help-docs' ),
580 ] );
581 }
582 }
583 } // End enqueue_assets()
584
585
586 /**
587 * Prevent cloning and unserializing
588 */
589 public function __clone() {}
590 public function __wakeup() {}
591
592 }
593
594
595 Menu::instance();