$item ) { $key = $item['key']; if ( ! isset( $items[ $key ] ) ) { continue; } $items[ $key ]['priority'] = ( $index + 1 ) * 10; if ( '' !== $item['label'] ) { $items[ $key ]['label'] = $item['label']; } if ( empty( $item['enabled'] ) && ! in_array( $key, Settings::REQUIRED_MENU, true ) ) { $items[ $key ]['public'] = false; } } return $items; } /** * Pages added to the menu on Customize → Dashboard. Each one shows a * WordPress page inside the dashboard, so it can hold any block or * shortcode. They are registered like any other item, so they can be * reordered, renamed and switched off with the rest of the menu. * * @param array $items Menu items. * @return array */ public static function add_pages( $items ) { if ( ! is_array( $items ) ) { return $items; } foreach ( Settings::get()['pages'] as $index => $page ) { if ( isset( $items[ $page['key'] ] ) ) { continue; } $items[ $page['key'] ] = [ 'label' => $page['label'], 'area' => $page['area'], 'icon' => 'academy-icon academy-icon--' . $page['icon'], 'public' => true, 'priority' => 90 + $index, ]; add_action( 'academy_frontend_dashboard_' . $page['key'] . '_endpoint', [ __CLASS__, 'render_page' ] ); } return $items; } /** * A page added to the menu, found by its menu key. * * @param string $key Menu key. * @return array|null */ public static function find_page( $key ) { foreach ( Settings::get()['pages'] as $page ) { if ( $page['key'] === $key ) { return $page; } } return null; } /** * Show a page added to the menu: its content, blocks and shortcodes * included, inside the dashboard. * * @param string $key Menu key. * @return void */ public static function render_page( $key ) { static $depth = 0; $page = self::find_page( (string) $key ); $post = $page && $page['page'] ? get_post( $page['page'] ) : null; // The dashboard page itself would draw the dashboard again, and again. $usable = $post && 'page' === $post->post_type && in_array( $post->post_status, [ 'publish', 'private' ], true ) && (int) Helper::get_settings( 'frontend_dashboard_page' ) !== (int) $post->ID; if ( ! $usable || $depth > 0 ) { if ( current_user_can( 'manage_options' ) ) { echo '

' . esc_html__( 'Choose a page for this menu item on Academy → Customize → Dashboard.', 'academy' ) . '

'; } return; } ++$depth; // Blocks that read the current post (title, featured image…) see this page. $previous = $GLOBALS['post'] ?? null; $GLOBALS['post'] = $post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited setup_postdata( $post ); $content = apply_filters( 'the_content', $post->post_content ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- core filter. $GLOBALS['post'] = $previous; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited if ( $previous instanceof \WP_Post ) { setup_postdata( $previous ); } --$depth; ?>
card. * @param string $view learning|teaching|family. * @return array */ public static function apply_cards( $cards, $view = '' ) { $saved = Settings::get()['cards']; if ( ! $saved || ! is_array( $cards ) ) { return $cards; } $ordered = []; foreach ( $saved as $item ) { if ( isset( $cards[ $item['key'] ] ) ) { if ( $item['enabled'] ) { $ordered[ $item['key'] ] = $cards[ $item['key'] ]; } unset( $cards[ $item['key'] ] ); } } // Cards an add-on added since the list was saved go last. return $ordered + $cards; } /** * Boxed or full width. "Auto" keeps Academy's behaviour: full width for * instructors, boxed for students. * * @param string $class Container class. * @param int $page_id Page ID. * @return string */ public static function container_class( $class, $page_id ) { if ( (int) Helper::get_settings( 'frontend_dashboard_page' ) !== (int) $page_id ) { return $class; } $width = Settings::get()['width']; if ( 'full' === $width ) { return 'academy-container-fluid'; } if ( 'boxed' === $width ) { return 'academy-container'; } return $class; } /** * The dashboard's own container, inside the page: full width when set. * * @return string */ public static function inner_container_class() { // The dashboard's layout rules hang on .academy-container, so full width // is a modifier on it rather than another container. return self::is_full_width() ? 'academy-container academy-dashboard-container--full' : 'academy-container'; } /** * Classes for the dashboard's outer wrapper. * * Block themes cap everything in the post content at their content width * (often 600-700px), far too narrow for the dashboard. WordPress's own * alignment classes lift that cap using the theme's sizes: `alignwide` * takes the theme's wide width, `alignfull` the whole page. Classic * themes don't apply those rules the same way, so they're left as before. * * @return string */ public static function wrapper_class() { $classes = array( 'academy-frontend-dashboard' ); if ( self::is_full_width() ) { $classes[] = 'academy-frontend-dashboard--full'; } if ( wp_is_block_theme() ) { $classes[] = self::is_full_width() ? 'alignfull' : 'alignwide'; } /** * Filters the dashboard wrapper's classes. * * @param string[] $classes Classes. */ $classes = (array) apply_filters( 'academy/frontend_dashboard/wrapper_classes', $classes ); return implode( ' ', array_map( 'sanitize_html_class', $classes ) ); } /** * Whether the dashboard runs the full width of the page. * * @return bool */ public static function is_full_width() { // "Automatic" keeps the dashboard as it has always been. return 'full' === Settings::get()['width']; } /** * Classes the dashboard's styles hang on. * * @param string[] $classes Body classes. * @return string[] */ public static function body_class( $classes ) { if ( ! self::is_page() ) { return $classes; } $settings = Settings::get(); $classes[] = 'academy-dashboard-body'; if ( 'bare' === $settings['layout'] && is_user_logged_in() ) { $classes[] = 'academy-dashboard-body--bare'; } if ( $settings['sidebarCollapsed'] ) { $classes[] = 'academy-dashboard-body--collapsed'; } return $classes; } /** * Sidebar width and the colours set on Customize, for light and dark mode. * * @return void */ public static function print_styles() { if ( ! self::is_page() ) { return; } $settings = Settings::get(); $css = sprintf( 'body.academy-dashboard-body{--academy-dashboard-sidebar-width:%dpx;}', $settings['sidebarWidth'] ); // Dark mode reaches the page around the dashboard too. On the "theme" // layout that's only the `.academy-frontend-dashboard` wrapper, or text // outside the cards (the welcome line) turns light on the theme's light // page. Printed before the Customize colours so a set one still wins. $css .= 'body.academy-dashboard-body[data-academy-user-theme="dark"] .academy-canvas,body.academy-dashboard-body--bare[data-academy-user-theme="dark"],body.academy-dashboard-body[data-academy-user-theme="dark"] .academy-frontend-dashboard{background:var(--academy-body-background-color);color:var(--academy-text-color)}'; // Only hex colours get here (Settings::colors()). foreach ( [ 'light', 'dark' ] as $mode ) { $scope = 'dark' === $mode ? 'body.academy-dashboard-body[data-academy-user-theme="dark"]' : 'body.academy-dashboard-body'; $colors = $settings['colors'][ $mode ]; $rules = [ // `.academy-canvas` only exists on the "academy"/"bare" page templates // (`template()` above); `--bare` covers the bare layout's own body // modifier. Neither is present on the default "theme" layout, where // the shortcode's own `.academy-frontend-dashboard` wrapper is the // only element behind the whole dashboard — include it too, or this // colour never has anything to paint on the most common layout. 'background' => '%1$s .academy-canvas,%1$s.academy-dashboard-body--bare,%1$s .academy-frontend-dashboard{background:%2$s}', 'sidebar' => '%1$s .academy-dashboard-menu,%1$s .academy-frontend-dashboard__user{background:%2$s}', 'active' => '%1$s .academy-dashboard-menu__item-current>a{background:%2$s}', 'topbar' => '%1$s .academy-frontend-dashboard .academy-topbar{background:%2$s}', 'card' => '%1$s .academy-analytics-cards--card,%1$s .academy-dashboard-continue__course{background:%2$s}', ]; foreach ( $rules as $key => $rule ) { if ( '' !== $colors[ $key ] ) { $css .= sprintf( $rule, $scope, $colors[ $key ] ); } } }//end foreach printf( "\n", wp_strip_all_tags( $css ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS body is built from colour settings validated above; wp_strip_all_tags guards against a stray tag ever reaching the