enqueue_assets();
// Check permission
$user_id = get_current_user_id();
$userModel = UserModel::find( $user_id, true );
if ( ! $userModel || ! $userModel->is_instructor() ) {
throw new Exception( __( "Sorry, you don't have permission to access Course Builder", 'learnpress' ) );
}
$data = [
'userModel' => $userModel,
];
$layout = [
'wrapper' => '
',
'header' => $this->html_header( $data ),
'body' => '
',
'sidebar' => $this->html_sidebar( $data ),
'content' => $this->html_content( $data ),
'body_end' => '
',
'wrapper_end' => '
',
];
echo Template::combine_components( $layout );
} catch ( Throwable $e ) {
Template::print_message(
wp_kses_post( $e->getMessage() ),
'error'
);
}
}
/**
* Enqueue scripts, styles and localize data for Course Builder.
*
* @since 4.3.x
* @version 1.0.0
*/
protected function enqueue_assets() {}
/**
* Auto-detect and dequeue all theme/child-theme stylesheets.
* Prevents theme CSS from interfering with Course Builder styles.
*
* Hooked to `wp_enqueue_scripts` at priority 9999 so it runs DURING wp_head(),
* after themes have enqueued their styles but before they are printed.
*
* Only removes styles whose source URL is within the theme or child-theme directory.
* WP core styles, plugin styles, and other assets remain untouched.
*
* @since 4.3.0
* @version 1.0.0
*/
/*public function dequeue_theme_styles() {
global $wp_styles, $wp_scripts;
if ( ! LP_Page_Controller::is_page_course_builder() ) {
return;
}
$allowed_styles = apply_filters(
'learn-press/course-builder/allowed-styles',
[
'dashicons',
'admin-bar',
'buttons',
'media-views',
'wp-components',
'wp-block-library',
'wp-editor',
'wp-edit-post',
'wp-block-editor',
'wp-components',
'wp-editor',
'wp-nux',
'wp-notices',
]
);
$allowed_scripts = apply_filters(
'learn-press/course-builder/allowed-scripts',
[
'jquery',
'jquery-core',
'jquery-migrate',
'jquery-ui-core',
'jquery-ui-widget',
'wp-api-fetch',
'wp-i18n',
'wp-components',
'wp-element',
'react',
'react-dom',
'wp-polyfill',
'wp-hooks',
'lodash',
'moment',
'heartbeat',
'wp-data',
'wp-core-data',
'wp-url',
'wp-api',
'wp-block-editor',
'wp-blocks',
'wp-media-utils',
'wp-compose',
'regenerator-runtime',
'wp-a11y',
]
);
if ( ! empty( $wp_styles->queue ) ) {
foreach ( $wp_styles->queue as $handle ) {
if ( ! in_array( $handle, $allowed_styles ) && strpos( $handle, 'lp-' ) !== 0 && strpos( $handle, 'learn-press' ) !== 0 && strpos( $handle, 'learnpress' ) !== 0 ) {
wp_dequeue_style( $handle );
}
}
}
if ( ! empty( $wp_scripts->queue ) ) {
foreach ( $wp_scripts->queue as $handle ) {
if ( ! in_array( $handle, $allowed_scripts ) && strpos( $handle, 'lp-' ) !== 0 && strpos( $handle, 'learn-press' ) !== 0 && strpos( $handle, 'learnpress' ) !== 0 ) {
wp_dequeue_script( $handle );
}
}
}
}*/
/**
* Header with logo and user profile
*
* @param array $data
*
* @return string
* @throws Exception
* @version 1.0.0
* @since 4.3.6
*/
protected function html_header( array $data = [] ): string {
/** @var UserModel $userModel */
$userModel = $data['userModel'] ?? false;
if ( ! $userModel ) {
return '';
}
$avatar = $userModel->get_avatar_url();
$display_name = $userModel->get_display_name();
$profile = LP_Profile::instance( $userModel->get_id() );
$profile_url = $profile->get_tab_link();
$logout_url = wp_logout_url( home_url() );
$logo_id = absint( LP_Settings::get_option( 'course_builder_logo_id', 0 ) );
if ( $logo_id ) {
$custom_logo = wp_get_attachment_image(
$logo_id,
'full',
false,
[
'class' => 'lp-cb-top-header__logo-image',
'alt' => __( 'Course Builder', 'learnpress' ),
'loading' => 'eager',
'decoding' => 'async',
]
);
}
$header = [
'wrapper' => '',
];
return Template::combine_components( $header );
}
/**
* HTML Sidebar
*
* @param array $data
*
* @return string
*/
public function html_sidebar( array $data = [] ): string {
$userModel = $data['userModel'] ?? false;
if ( ! $userModel ) {
return '';
}
$tabs = CourseBuilder::get_menus_arr();
$nav_content = '';
$is_admin = current_user_can( ADMIN_ROLE );
$tabs = array_filter(
$tabs,
static function ( array $tab ) use ( $is_admin ): bool {
if ( ! empty( $tab['admin_only'] ) && ! $is_admin ) {
return false;
}
return true;
}
);
usort(
$tabs,
static function ( array $a, array $b ): int {
$a_priority = isset( $a['priority'] ) && is_numeric( $a['priority'] ) ? (int) $a['priority'] : PHP_INT_MAX;
$b_priority = isset( $b['priority'] ) && is_numeric( $b['priority'] ) ? (int) $b['priority'] : PHP_INT_MAX;
return $a_priority <=> $b_priority;
}
);
foreach ( $tabs as $tab ) {
$slug = $tab['slug'];
$nav_item = $this->html_nav_item_main( $slug, $tab );
$nav_content .= $nav_item;
}
$nav = [
'wrapper' => '',
];
$sidebar_toggle_icon = LP_WP_Filesystem::get_icon_svg( 'ico-cb-sidebar-toggle.svg' );
$toggle = sprintf(
'',
esc_attr__( 'Toggle Sidebar', 'learnpress' ),
esc_attr__( 'Toggle Sidebar', 'learnpress' ),
$sidebar_toggle_icon
);
$sidebar = [
'wrapper' => '',
];
return Template::combine_components( $sidebar );
}
/**
* HTML main content area
*
* @param array $data
*
* @return string
* @throws Exception
* @version 1.0.1
* @since 4.3.6
*/
public function html_content( array $data = [] ): string {
$userModel = $data['userModel'] ?? false;
if ( ! $userModel ) {
return '';
}
$menu_current = CourseBuilder::get_menu_current();
//Switch layout display by menu, via model, @since 4.3.7
switch ( $menu_current ) {
case self::MENU_COURSES:
$content = BuilderCourseTemplate::instance()->layout( $data );
break;
default:
if ( has_action( "learn-press/course-builder/{$menu_current}/layout" ) ) {
// Hook old @since 4.3.6.
ob_start();
do_action( "learn-press/course-builder/{$menu_current}/layout", $data );
$content = ob_get_clean();
} else {
// Hook new @since 4.3.7.
$content = apply_filters( 'learn-press/course-builder/content/layout', '', $menu_current, $data );
}
break;
}
$output = [
'wrapper' => '',
'content' => $content,
'wrapper_end' => '
',
];
return Template::combine_components( $output );
}
/**
* Sidebar footer with "Back to Dashboard" link
*
* @param array $data
*
* @return string
* @since 4.3.0
*/
protected function sidebar_footer( array $data = [] ): string {
$userModel = $data['userModel'] ?? false;
if ( ! $userModel instanceof UserModel ) {
return '';
}
$hide_instructor_access_admin_screen = LP_Settings::is_hide_instructor_access_admin_screen();
$wp_user = new WP_User( $userModel );
$is_instructor = user_can( $wp_user, UserModel::ROLE_INSTRUCTOR );
$dashboard_url = admin_url();
$footer = [
'wrapper' => '';
return Template::combine_components( $footer );
}
/**
* Render main navigation item (persistent sidebar)
*
* @param string $slug
* @param array $tab_data
*
* @return string
* @since 4..0
*/
protected function html_nav_item_main( $slug, $tab_data ) {
$tab_current = CourseBuilder::get_menu_current();
$sub_menu_items = $this->get_nav_item_sub_menu_items( $slug, $tab_data );
$has_sub_menu = ! empty( $sub_menu_items );
$has_active_submenu = $has_sub_menu && ! empty(
array_filter(
$sub_menu_items,
static function ( array $item ): bool {
return ! empty( $item['is_active'] );
}
)
);
$is_active = $slug === $tab_current || ! empty( $has_active_submenu );
$classes = [ 'lp-cb-sidebar__item', $slug ];
if ( $has_sub_menu ) {
$classes[] = 'has-sub-menu';
}
if ( $is_active ) {
$classes[] = 'is-active';
}
if ( $has_sub_menu && $is_active ) {
$classes[] = 'is-expanded';
}
$icon = isset( $tab_data['icon'] ) ? $tab_data['icon'] : '';
$title = $tab_data['title'];
$link = CourseBuilder::get_tab_link( $slug );
$sub_menu_id = 'lp-cb-sidebar-sub-menu-' . sanitize_html_class( $slug );
$aria_current = $is_active && empty( $has_active_submenu ) ? ' aria-current="page"' : '';
if ( $has_sub_menu ) {
$content = sprintf(
'',
esc_url( $link ),
esc_attr( $title ),
$aria_current,
$icon,
esc_html( $title ),
esc_attr( sprintf( __( 'Toggle %s submenu', 'learnpress' ), $title ) ),
$is_active ? 'true' : 'false',
esc_attr( $sub_menu_id )
);
} else {
$content = sprintf(
'',
esc_url( $link ),
esc_attr( $title ),
$aria_current,
$icon,
esc_html( $title )
);
}
$item = [
'wrapper' => sprintf( '', implode( ' ', $classes ) ),
'content' => $content,
'wrapper_end' => '',
];
if ( $has_sub_menu ) {
$item['content'] .= $this->html_nav_item_sub_menu( $sub_menu_items, $sub_menu_id, $title );
}
return Template::combine_components( $item );
}
protected function get_nav_item_sub_menu_items( string $parent_slug, array $tab_data ): array {
$items = [];
if ( ! empty( $tab_data['sub_menu'] ) && is_array( $tab_data['sub_menu'] ) ) {
$items = $this->normalize_nav_sub_menu_items( $parent_slug, $tab_data['sub_menu'] );
}
return $items;
}
protected function normalize_nav_sub_menu_items( string $parent_slug, array $sub_menu ): array {
$is_admin = current_user_can( ADMIN_ROLE );
$items = [];
foreach ( $sub_menu as $key => $item ) {
if ( ! is_array( $item ) || ( ! empty( $item['admin_only'] ) && ! $is_admin ) ) {
continue;
}
$title = $item['title'] ?? '';
if ( '' === $title ) {
continue;
}
$slug = ! empty( $item['slug'] ) ? (string) $item['slug'] : ( is_string( $key ) ? $key : sanitize_title( $title ) );
if ( '' === $slug ) {
continue;
}
$url = $item['url'] ?? '';
if ( '' === $url ) {
$url = CourseBuilder::get_tab_link( $parent_slug, $slug );
}
if ( '' === $url ) {
continue;
}
$is_active = false;
if ( $parent_slug === CourseBuilder::get_menu_current() ) {
$is_active = $slug === (string) CourseBuilder::get_item_id();
}
$items[] = [
'class' => is_string( $key ) ? $key : sanitize_title( $title ),
'icon' => $item['icon'] ?? '',
'is_active' => $is_active,
'rel' => ! empty( $item['target'] ) && '_blank' === $item['target'] ? 'noopener noreferrer' : '',
'slug' => $slug,
'target' => $item['target'] ?? '',
'title' => $title,
'url' => $url,
];
}
return $items;
}
protected function html_nav_item_sub_menu( array $sub_menu_items, string $sub_menu_id, string $parent_title = '' ): string {
if ( empty( $sub_menu_items ) ) {
return '';
}
$content = '';
foreach ( $sub_menu_items as $item ) {
$target = '' !== $item['target'] ? sprintf( ' target="%s"', esc_attr( $item['target'] ) ) : '';
$rel = '' !== $item['rel'] ? sprintf( ' rel="%s"', esc_attr( $item['rel'] ) ) : '';
$active_class = ! empty( $item['is_active'] ) ? ' is-active' : '';
$aria_current = ! empty( $item['is_active'] ) ? ' aria-current="page"' : '';
$content .= sprintf(
'',
esc_attr( sanitize_html_class( $item['class'] ) ),
esc_attr( $active_class ),
esc_url( $item['url'] ),
$target,
$rel,
$aria_current,
$item['icon'],
esc_html( $item['title'] )
);
}
if ( '' === $content ) {
return '';
}
return sprintf(
'',
esc_attr( $sub_menu_id ),
esc_attr( sprintf( __( '%s submenu', 'learnpress' ), $parent_title ) ),
$content
);
}
public function html_btn_add_new() {
$tab_current = CourseBuilder::get_menu_current();
$map_title = [
'courses' => __( 'Course', 'learnpress' ),
'lessons' => __( 'Lesson', 'learnpress' ),
'quizzes' => __( 'Quiz', 'learnpress' ),
'questions' => __( 'Question', 'learnpress' ),
];
$map_type = [
'lessons' => 'lesson',
'quizzes' => 'quiz',
'questions' => 'question',
];
$title = isset( $map_title[ $tab_current ] ) ? $map_title[ $tab_current ] : '';
$type = isset( $map_type[ $tab_current ] ) ? $map_type[ $tab_current ] : '';
$add_new = 'data-add-new-' . esc_attr( $type );
$template_html = '';
$template_attr = '';
$btn_add_new = sprintf( '';
if ( 'lessons' === $tab_current ) {
$template_id = 'lp-tmpl-builder-popup-lesson-tab-new';
$template_attr = sprintf(
' data-template="#%1$s" data-popup-type="lesson" data-popup-id="0"',
esc_attr( $template_id )
);
$template_html = sprintf(
'',
esc_attr( $template_id ),
TemplateAJAX::load_content_via_ajax(
[
'id_url' => 'builder-popup-lesson-tab-new',
'lesson_id' => 0,
'html_no_load_ajax_first' => sprintf(
'',
esc_html__( 'Loading...', 'learnpress' )
),
],
[
'class' => BuilderPopupTemplate::class,
'method' => 'render_lesson_popup',
]
)
);
}
if ( 'courses' === $tab_current ) {
$btn_add_new = sprintf(
'',
esc_url( CourseBuilder::get_link_add_new( 'courses' ) )
);
$btn_close = '';
}
if ( 'quizzes' === $tab_current ) {
$btn_add_new = sprintf(
'',
esc_url( CourseBuilder::get_link_add_new( 'quizzes' ) )
);
$btn_close = '';
}
if ( 'questions' === $tab_current ) {
$btn_add_new = sprintf(
'',
esc_url( CourseBuilder::get_link_add_new( 'questions' ) )
);
$btn_close = '';
}
$btn = [
'wrapper' => str_replace( '>', $template_attr . '>', $btn_add_new ),
'content' => sprintf( '%s %s', __( 'Add New', 'learnpress' ), $title ),
'wrapper_end' => $btn_close,
'template' => $template_html,
];
$btn = apply_filters( 'learn-press/course-builder/button-add-new', $btn, $tab_current, $type );
return Template::combine_components( $btn );
}
/**
* Show link to Course Builder in admin bar
*/
public function add_admin_bar_menu( $wp_admin_bar ) {
$href = CourseBuilder::get_link_course_builder();
$title = esc_html__( 'Course Builder', 'learnpress' );
// Check if on frontend single course page
if ( is_singular( LP_COURSE_CPT ) && get_the_ID() ) {
$title = esc_html__( 'Edit with Course Builder', 'learnpress' );
$href = BuilderCourseTemplate::instance()->get_link_edit( get_the_ID() );
}
// Check if on admin edit course page (post.php or post-new.php)
if ( is_admin() ) {
global $post, $pagenow;
if ( in_array( $pagenow, array( 'post.php', 'post-new.php' ), true ) ) {
$post_type = '';
if ( isset( $_GET['post_type'] ) ) {
$post_type = sanitize_text_field( wp_unslash( $_GET['post_type'] ) );
} elseif ( isset( $_GET['post'] ) ) {
$post_id = absint( $_GET['post'] );
$post_type = get_post_type( $post_id );
} elseif ( $post && isset( $post->post_type ) ) {
$post_type = $post->post_type;
}
if ( LP_COURSE_CPT === $post_type ) {
$title = esc_html__( 'Edit with Course Builder', 'learnpress' );
if ( isset( $_GET['post'] ) ) {
$href = BuilderCourseTemplate::instance()->get_link_edit( $_GET['post'] );
} else {
$href = CourseBuilder::get_link_add_new( 'courses' );
}
}
}
}
$admin_bar_icon = LP_WP_Filesystem::get_icon_svg( 'ico-cb-admin-bar.svg' );
$wp_admin_bar->add_node(
array(
'id' => 'lp-course-builder',
'title' => sprintf(
'%1$s%2$s',
$admin_bar_icon,
$title
),
'href' => $href,
)
);
}
}