id, 'slms' ) ) { return; } // Dismissed for good (older versions stored an expiry time, which counts as dismissed too). $user_id = get_current_user_id(); if ( get_user_meta( $user_id, 'accessnow_slms_dismiss_reg_notice', true ) ) { return; } if ( isset( $_GET['accessnow_slms_dismiss_reg_notice'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ?? '' ) ), 'accessnow_slms_dismiss_reg' ) ) { update_user_meta( $user_id, 'accessnow_slms_dismiss_reg_notice', 1 ); return; } $dismiss_url = wp_nonce_url( add_query_arg( 'accessnow_slms_dismiss_reg_notice', '1' ), 'accessnow_slms_dismiss_reg' ); if ( empty( $slms_settings['allow_registration'] ) ) { $message = __( 'Student registration is disabled. To allow visitors to create accounts and enroll in courses, enable "Allow Student Registration" in', 'accessnow-lms' ); $settings_url = admin_url( 'admin.php?page=slms-settings' ); $link_label = __( 'AccessNow LMS → Settings', 'accessnow-lms' ); } else { $message = __( 'Student registration is enabled in AccessNow LMS, but WordPress registration is turned off. To let visitors create accounts, enable "Anyone can register" in', 'accessnow-lms' ); $settings_url = admin_url( 'options-general.php' ); $link_label = __( 'Settings → General', 'accessnow-lms' ); } printf( '

%s %s %s

%s

', esc_html__( 'AccessNow LMS:', 'accessnow-lms' ), esc_html( $message ), esc_url( $settings_url ), esc_html( $link_label ), esc_url( $dismiss_url ), esc_html__( 'Dismiss', 'accessnow-lms' ) ); } public static function register_menu() { // Shown to anyone who can edit courses (editors, instructors). Its Dashboard stays admin-only, // so for them WordPress opens the menu at Courses. add_menu_page( __( 'AccessNow LMS', 'accessnow-lms' ), __( 'AccessNow LMS', 'accessnow-lms' ), 'edit_slms_courses', 'slms-main', array( __CLASS__, 'render_dashboard' ), 'dashicons-welcome-learn-more', 30 ); add_submenu_page( 'slms-main', __( 'Dashboard', 'accessnow-lms' ), __( 'Dashboard', 'accessnow-lms' ), 'manage_options', 'slms-main', array( __CLASS__, 'render_dashboard' ) ); add_submenu_page( 'slms-main', __( 'Enrollments', 'accessnow-lms' ), __( 'Enrollments', 'accessnow-lms' ), 'manage_slms_enrollments', 'slms-enrollments', array( Enrollment_Admin::class, 'render_page' ) ); add_submenu_page( 'slms-main', __( 'Reports', 'accessnow-lms' ), __( 'Reports', 'accessnow-lms' ), 'manage_slms_reports', 'slms-reports', array( Reports::class, 'render_page' ) ); add_submenu_page( 'slms-main', __( 'Settings', 'accessnow-lms' ), __( 'Settings', 'accessnow-lms' ), 'manage_options', 'slms-settings', array( __CLASS__, 'render_settings' ) ); // WordPress checks access to post-new.php?post_type=... before the page sets its menu parent, // so without a menu entry for it the check falls back to Posts > Add New, which needs // edit_posts. Someone who can create courses but not posts (an instructor) gets an Add New // entry for each type they can create; admins' and editors' menus are unchanged. if ( ! current_user_can( 'edit_posts' ) ) { foreach ( array_keys( Capabilities::POST_TYPES ) as $post_type ) { $type = get_post_type_object( $post_type ); if ( $type && current_user_can( $type->cap->create_posts ) ) { add_submenu_page( 'slms-main', $type->labels->add_new_item, $type->labels->add_new_item, $type->cap->create_posts, 'post-new.php?post_type=' . $post_type ); } } } } public static function render_dashboard() { echo '

' . esc_html__( 'AccessNow LMS Dashboard', 'accessnow-lms' ) . '

'; $total_courses = wp_count_posts( 'slms_course' )->publish; $total_lessons = wp_count_posts( 'slms_lesson' )->publish; $total_enrollments = Enrollment::count_enrollments( array( 'status' => 'active' ) ); echo '
'; printf( '

%s

%s

', esc_html( number_format_i18n( (int) $total_courses ) ), esc_html__( 'Courses', 'accessnow-lms' ) ); printf( '

%s

%s

', esc_html( number_format_i18n( (int) $total_lessons ) ), esc_html__( 'Lessons', 'accessnow-lms' ) ); printf( '

%s

%s

', esc_html( number_format_i18n( (int) $total_enrollments ) ), esc_html__( 'Active Enrollments', 'accessnow-lms' ) ); echo '
'; } public static function render_settings() { if ( isset( $_POST['accessnow_slms_settings_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['accessnow_slms_settings_nonce'] ) ), 'accessnow_slms_save_settings' ) && current_user_can( 'manage_options' ) ) { $settings = array( 'allow_registration' => isset( $_POST['allow_registration'] ) ? 1 : 0, 'header_inherit' => isset( $_POST['header_inherit'] ) ? 1 : 0, 'points_lesson_complete' => absint( $_POST['points_lesson_complete'] ?? 10 ), 'points_quiz_pass' => absint( $_POST['points_quiz_pass'] ?? 25 ), 'points_course_complete' => absint( $_POST['points_course_complete'] ?? 100 ), 'email_enrollment' => isset( $_POST['email_enrollment'] ) ? 1 : 0, 'email_completion' => isset( $_POST['email_completion'] ) ? 1 : 0, 'email_quiz_result' => isset( $_POST['email_quiz_result'] ) ? 1 : 0, 'email_admin_enrollment' => isset( $_POST['email_admin_enrollment'] ) ? 1 : 0, 'delete_data_on_uninstall' => isset( $_POST['delete_data_on_uninstall'] ) ? 1 : 0, ); Settings::save( $settings ); echo '

' . esc_html__( 'Settings saved.', 'accessnow-lms' ) . '

'; } $settings = Settings::all(); $defaults = array( 'allow_registration' => 0, 'header_inherit' => 1, 'points_lesson_complete' => 10, 'points_quiz_pass' => 25, 'points_course_complete' => 100, // As Notifications sends them before the settings are first saved: only the quiz email is on. 'email_enrollment' => 0, 'email_completion' => 0, 'email_quiz_result' => 1, 'email_admin_enrollment' => 0, 'delete_data_on_uninstall' => 0, ); $settings = wp_parse_args( $settings, $defaults ); echo '

' . esc_html__( 'AccessNow LMS Settings', 'accessnow-lms' ) . '

'; echo '
'; wp_nonce_field( 'accessnow_slms_save_settings', 'accessnow_slms_settings_nonce' ); echo '

' . esc_html__( 'Registration', 'accessnow-lms' ) . '

'; echo ''; printf( '', esc_html__( 'Allow Student Registration', 'accessnow-lms' ), checked( $settings['allow_registration'], 1, false ), esc_html__( 'Allow visitors to create student accounts on course pages. The WordPress "Anyone can register" setting (Settings → General) must also be enabled.', 'accessnow-lms' ) ); echo '
%s
'; echo '

' . esc_html__( 'Pages', 'accessnow-lms' ) . '

'; echo ''; printf( '', esc_html__( 'Inherited header images', 'accessnow-lms' ), checked( $settings['header_inherit'], 1, false ), esc_html__( 'Lessons and quizzes without a header image of their own show their unit\'s or course\'s. Untick to show an image only where one is set.', 'accessnow-lms' ) ); echo '
%s
'; echo '

' . esc_html__( 'Gamification Points', 'accessnow-lms' ) . '

'; echo ''; printf( '', esc_html__( 'Points per Lesson Completed', 'accessnow-lms' ), absint( $settings['points_lesson_complete'] ) ); printf( '', esc_html__( 'Points per Quiz Passed', 'accessnow-lms' ), absint( $settings['points_quiz_pass'] ) ); printf( '', esc_html__( 'Points per Course Completed', 'accessnow-lms' ), absint( $settings['points_course_complete'] ) ); echo '
'; echo '

' . esc_html__( 'Email Notifications', 'accessnow-lms' ) . '

'; echo ''; printf( '', esc_html__( 'Enrollment Confirmation', 'accessnow-lms' ), checked( $settings['email_enrollment'], 1, false ) ); printf( '', esc_html__( 'Course Completion', 'accessnow-lms' ), checked( $settings['email_completion'], 1, false ) ); printf( '', esc_html__( 'Quiz Passed', 'accessnow-lms' ), checked( $settings['email_quiz_result'], 1, false ) ); printf( '', esc_html__( 'Admin Enrollment Alert', 'accessnow-lms' ), checked( $settings['email_admin_enrollment'], 1, false ) ); echo '
'; echo '

' . esc_html__( 'Data', 'accessnow-lms' ) . '

'; echo ''; printf( '', esc_html__( 'Delete data on uninstall', 'accessnow-lms' ), checked( $settings['delete_data_on_uninstall'], 1, false ), esc_html__( 'When the plugin is deleted, also delete all courses, units, lessons, quizzes, enrolments, progress, certificates, points, badges, settings and the Student role. Leave unticked to keep everything.', 'accessnow-lms' ) ); echo '
%s
'; submit_button(); echo '
'; } public static function enqueue_assets( $hook ) { $screen = get_current_screen(); if ( ! $screen || false === strpos( $screen->id, 'slms' ) ) { return; } wp_enqueue_style( 'slms-admin', ACCESSNOW_SLMS_PLUGIN_URL . 'assets/css/slms-admin.css', array(), ACCESSNOW_SLMS_VERSION ); wp_enqueue_script( 'slms-admin', ACCESSNOW_SLMS_PLUGIN_URL . 'assets/js/slms-admin.js', array( 'jquery', 'jquery-ui-sortable' ), ACCESSNOW_SLMS_VERSION, true ); wp_localize_script( 'slms-admin', 'slms_admin', array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'accessnow_slms_admin' ), 'i18n' => array( 'select_attachments' => __( 'Select Attachments', 'accessnow-lms' ), /* translators: %s: a student's name. */ 'remove_user' => __( 'Remove %s', 'accessnow-lms' ), 'no_users' => __( 'No matching users.', 'accessnow-lms' ), 'user_found' => __( 'One matching user. Press the down arrow to choose it.', 'accessnow-lms' ), /* translators: %d: number of matching users (2 or more). */ 'users_found' => __( '%d matching users. Use the up and down arrows to choose.', 'accessnow-lms' ), /* translators: %s: a student's name. */ 'user_added' => __( '%s added.', 'accessnow-lms' ), 'correct_answer' => __( 'Correct answer', 'accessnow-lms' ), 'option' => __( 'Option', 'accessnow-lms' ), 'remove_option' => __( 'Remove option', 'accessnow-lms' ), 'remove_answer' => __( 'Remove answer', 'accessnow-lms' ), ), ) ); } /* ─── Lesson Columns: Course + Unit ──────────────────────── */ public static function lesson_columns( $columns ) { $new = array(); foreach ( $columns as $key => $label ) { $new[ $key ] = $label; if ( 'title' === $key ) { $new['slms_course'] = __( 'Course', 'accessnow-lms' ); $new['slms_unit'] = __( 'Unit', 'accessnow-lms' ); } } return $new; } /** * Print an edit link to a post, or a dash when it doesn't exist. * * @param int $target_id Post ID. */ private static function edit_link_or_dash( $target_id ) { $target = $target_id ? get_post( $target_id ) : null; if ( $target ) { printf( '%s', esc_url( get_edit_post_link( $target->ID ) ), esc_html( $target->post_title ) ); } else { echo '—'; } } public static function lesson_column_data( $column, $post_id ) { $unit_id = (int) get_post_meta( $post_id, '_slms_parent_unit', true ); if ( 'slms_unit' === $column ) { self::edit_link_or_dash( $unit_id ); } if ( 'slms_course' === $column ) { self::edit_link_or_dash( $unit_id ? (int) get_post_meta( $unit_id, '_slms_parent_course', true ) : 0 ); } } /* ─── Unit Columns: Course ───────────────────────────────── */ public static function unit_columns( $columns ) { $new = array(); foreach ( $columns as $key => $label ) { $new[ $key ] = $label; if ( 'title' === $key ) { $new['slms_course'] = __( 'Course', 'accessnow-lms' ); } } return $new; } public static function unit_column_data( $column, $post_id ) { if ( 'slms_course' === $column ) { self::edit_link_or_dash( (int) get_post_meta( $post_id, '_slms_parent_course', true ) ); } } /* ─── Quiz Columns: Lesson + Course ──────────────────────── */ public static function quiz_columns( $columns ) { $new = array(); foreach ( $columns as $key => $label ) { $new[ $key ] = $label; if ( 'title' === $key ) { $new['slms_course'] = __( 'Course', 'accessnow-lms' ); $new['slms_lesson'] = __( 'Lesson', 'accessnow-lms' ); } } return $new; } public static function quiz_column_data( $column, $post_id ) { $lesson_id = (int) get_post_meta( $post_id, '_slms_parent_lesson', true ); if ( 'slms_lesson' === $column ) { self::edit_link_or_dash( $lesson_id ); } if ( 'slms_course' === $column ) { $unit_id = $lesson_id ? (int) get_post_meta( $lesson_id, '_slms_parent_unit', true ) : 0; self::edit_link_or_dash( $unit_id ? (int) get_post_meta( $unit_id, '_slms_parent_course', true ) : 0 ); } } /* ─── Course Columns: Units + Lessons count ──────────────── */ public static function course_columns( $columns ) { $new = array(); foreach ( $columns as $key => $label ) { $new[ $key ] = $label; if ( 'title' === $key ) { $new['slms_units'] = __( 'Units', 'accessnow-lms' ); $new['slms_lessons'] = __( 'Lessons', 'accessnow-lms' ); } } return $new; } public static function course_column_data( $column, $post_id ) { if ( 'slms_units' === $column ) { $count = count( get_posts( array( 'post_type' => 'slms_unit', 'posts_per_page' => -1, 'post_status' => 'any', 'meta_key' => '_slms_parent_course', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- course structure is stored as post meta. 'meta_value' => $post_id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- course structure is stored as post meta. 'fields' => 'ids', ) ) ); echo (int) $count; } if ( 'slms_lessons' === $column ) { $units = get_posts( array( 'post_type' => 'slms_unit', 'posts_per_page' => -1, 'post_status' => 'any', 'meta_key' => '_slms_parent_course', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- course structure is stored as post meta. 'meta_value' => $post_id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- course structure is stored as post meta. 'fields' => 'ids', ) ); $count = 0; foreach ( $units as $unit_id ) { $count += count( get_posts( array( 'post_type' => 'slms_lesson', 'posts_per_page' => -1, 'post_status' => 'any', 'meta_key' => '_slms_parent_unit', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- course structure is stored as post meta. 'meta_value' => $unit_id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- course structure is stored as post meta. 'fields' => 'ids', ) ) ); } echo (int) $count; } } }