<?php
use Sensei\Admin\Content_Duplicators\Course_Lessons_Duplicator;
use Sensei\Admin\Content_Duplicators\Lesson_Quiz_Duplicator;
use Sensei\Admin\Content_Duplicators\Post_Duplicator;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Handles all admin views, assets and navigation.
*
* @package Views
* @author Automattic
* @since 1.0.0
*/
class Sensei_Admin {
/**
* @var $course_order_page_slug The slug for the Order Courses page.
*/
private $course_order_page_slug;
/**
* @var $lesson_order_page_slug The slug for the Order Lessons page.
*/
private $lesson_order_page_slug;
/**
* Sensei SVG Icon
*
* @var string The menu icon for Sensei LMS.
*/
private $sensei_icon = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04LjkwOCAxNy4xN2MuNDI4LS4zMzUuMjU0LS42OTIuODU2LS44MjMtLjAyNy4yODUuMjA5LjU3My4zNTkuODI0LS4xNTYtLjUzNy0uMDAzLS43ODMuMTEzLTEuMDUzLjQ5Ny0xLjE0Ny0uOTQ1LTEuODktMS45MjgtMi4wNzEuMzM2LS4yNDEuNTctLjQ3OS45OTItLjUyYTMuMzMyIDMuMzMyIDAgMCAwLTEuNTEyLjMzN2MtMS41ODguNzc2LTIuODQ2LS43NjYtMi4wMTQtMi4xNjMuNDQ0LS43NDcuOTUtLjc2IDEuNDA0LTEuMjY1LjEwOS4yNi4xNDYuNTQ0LjEwNi44MjQuMTQyLS4zMDguMTY2LS42NTIuMDctLjk3LS4xNDgtLjQ5MS0uNTcyLS40OTUtLjY1MS0xLjItLjAxNy0uMTQ4LS4xNS4wODItLjM3Ni0uMTEzLS4wNTMtLjA0Ny0uMTYyLS4xMy0uMjMyLS4wOTMtLjEyMy4wNjQtLjI1OC4wMTItLjMzMi4wNDktLjEyLjA2LS4xNTYtLjA2Ni0uMjg2LjAyNC0uMjMuMTU2LS4yNTQtLjAyLS40NzQuMDA4LS4yNzYuMDMzLS40NTguMzgxLS42MTUuMjA5LS4xNDQtLjE1Ny0uMjkzLjExLS4zOTguMTEtLjA3MyAwLS40NDYuNDY0LS42MS4wNDEtLjEyMy0uMzE2LS40LjMwMS0uNTQ2LjA5Mi0uMDQ3LS4wNjctLjE3NS4xNTgtLjI1Ny0uMDY0LS4wMi0uMDUxLjAwMS0uMTE0LS4xNTQtLjA0LS4zMDcuMTQ1LS4xOTgtLjA4OC0uMjk2LS4yMS0uMTE5LS4xNS0uMTYxLS4xNS0uMDk3LS4zOTUuMDUtLjE5My0uMDI3LS40ODIuMjg1LS41MzEuMDgtLjAxMy4xNTQtLjAwOC4yNjMtLjIxNy4xMzctLjI2Mi4zMzEtLjEzNi4yOTYtLjI1NS0uMDUtLjE3NC4yMDUtLjQxLjMyMy0uNTIuMTU3LS4xNS4zNjQtLjM2OC40MzctLjU3OEEuOTIxLjkyMSAwIDAgMSAzLjc4IDYuNGMuMTE2LS4xMjIuMTUtLjAwNC4zMjUtLjA2NC4xMjUtLjA0NS4wMjcuMDc2LjE1MS0uMTAyLjA0LS4wNTcuMTQ5LS4wMDQuMjctLjA1OC4zNy0uMTY0LjQwNS0uMTYxLjQ5Ny0uNTQzLjAxNy0uMDcyLjAzNS0uMTI4LjExMy0uMTIuMTU3LjAxNC4xOTItLjA3Ni4yODgtLjA3NC4wODYuMDAyLjExNy0uMDQ0LjE2My0uMDk5LjI1Ni0uMzE3LjM5Ni0uMDguNTEtLjEwNS4wODctLjAxOS4wMzItLjIwOS4yNjMtLjE1LjMzLjA4NC4yMjItLjA3NC40MzMtLjAyMi4yNDkuMDYuMjY2LjI3Ny40MzcuMzI5LjIwNS4wNjIuMzAyLjI5LjQzOC4yODcuNDgyLS4wMDcuMzM2LjMzNi42NC40NzEtLjIyOC0uMjM1LjAxNy0uNjE0LS41MDItLjY2NC0uMTE2LS4wMS0uMTU3LS4xODUtLjMyOS0uMjY3LS4yMDgtLjA5OS0uMTg3LS4zMDgtLjU2Mi0uMzk0LjA5Ni0uMDYxLjEyNi4wMDYuMTg1LS4xMDguMDgzLS4xNi4xMy0uMDY3LjE2Mi0uMTY2LjAzLS4wOTEuMDA4LS4xNDMuMjM1LS4yMjYuMjM2LS4wODYuNDY0LS4yODUuNjc0LS4yNzQuMTg2LjAwOS4yNjQtLjExOC4yNTMtLjI3Ny0uMDExLS4xNjUtLjA0Ny0uMzkxLjMxOC0uMzIyLjIxOC4wNDIuMjE0LjAwNC4yMTMtLjE0Ni0uMDAyLS4xNS4wNTctLjI0Mi4yNDktLjIzNS4xNC4wMDQuMjc4LS4wMDMuMzMtLjE3MS4wNDItLjEzMy4yOTYtLjE2Ny40MzItLjIxNy40NTMtLjE2Ni41MTYuMDczLjYzOS0uMTM3LjEtLjE3Mi4yNzktLjQ2NS40OC0uMjU2LjA0LjA0Mi4wNzguMDczLjIxNC0uMDgzLjExNC0uMTI4LjM4Ni0uMjkyLjQ4My0uMDcuMDM4LjA4OC4xMjMuMTc4LjE5Ni4wNjVhLjExNC4xMTQgMCAwIDEgLjA3Ni0uMDU3Yy4wNzMtLjAxNi4xMTItLjAzNS4xNS0uMDUzLjMxLS4xNDguNDQ3LS4xMzguMzA0LjE4My0uMjA0LjQ2MiAxLjEzLS4yODEuNzYuMjItLjEzNy4xODQuNzY3LjI4OS44OTkuMzcyLjA3Mi4wNDYuMDcuMDg2LjIwNi4xMTIuMzg4LjA3Mi4zODIuMDM4LjQwNS4zNTUuMDEuMTQzLjE1NC4wNjMuMTU0LjI0NiAwIC43ODIuNjczLjM4OC43ODMuNTgzLjAxOC4wMzIuMDQ2LjA2MS4xMTQuMDYxLjIxMiAwIC4yMjQtLjA0Mi4zMi4xMy4wMzkuMDcuMDcuMTA1LjE4MS4xNC4xNy4wNTUuMTYuMDc0LjA5Ny4yMDItLjA3Ny4xNTIuMDQzLjE1LjE5OC4xNjQuMTQyLjAxNC4yMS4wOTEuMzA4LjIwMi4xNTguMTc4LjQwNS4yMjkuNjE0LjMzMi4xMi4wNTguMDAyLjIwMy4yMDQuMzU1LjE0OS4xMTIuMjI0LjA5Ni4wMjQuMjQtLjg0NC42MTIuMzEzLjQ0Ni42MzcgMS4wMTUuMDcyLjEyNy4xNjkuMjg2LjMwOC40MzIuMzIyLjMzNS4xNDIuNDI4LjMxNi42MzguMS4xMjEuMDIxLjA5Mi4xODYuMi4zODcuMjU3LS4xNS40MjctLjQ1Ni4zMS0uMTM5LS4wNTItLjIzNy0uMTI0LS4yMS0uMDM3LjAyMi4wNzItLjAxNS4xMjItLjA3NC4xNzYtLjE3OS4xNjQuMTc1LjEuMjU5LjA4OC4yNTgtLjAzNy45NDctLjExMi45NDIuMTM2LS4wMDcuMzIyLjE5NS4zOTQuMzU4LjQxNS4yNjQuMDM2LjMzOS4wODMuNDg0LjMzLjEzMi4yMjIuMjA5LjEwMy40MTIuMy4xNC4xMzUuMzEuMzc2LjAyOS40MTItLjA2OC4wMDgtLjE0MSAwLS4xNzMuMDI0YS41OC41OCAwIDAgMS0uMTk5LjA5N2MtLjQyMi4xMzIuMi4xOTkuMzUyLjE2NS4xMzMtLjAyOC4yOTgtLjA3Mi40MjctLjAzOC4wODIuMDIxLjA5LjExNi4zNS4xNTYuMDg2LjAxNC4wNS4xNDMuMDA4LjIzNC0uMTQ5LjMxMi4yNzQuMTQ2LjIzLjM4My0uMDI1LjEzNy4xNTUuMzU4LS4wOTcuMzYzLS4wODEuMDAyLS4xNjYtLjAyMi0uMTY0LjA0OS4wMDUuMTU0LS4yMzYtLjA0My0uNDUzLjE3LS4wOC4wNzgtLjI3MS4xMzMtLjM5LS4wMTQtLjA0NC0uMDU2LS4wOTktLjEtLjMxLS4wMzQtLjIwNi4wNjctLjM0Ni4yOTgtLjQzNy0uMDU5LS4wMjQtLjA5My0uMDcyLS4wOTUtLjIwMy0uMTQtLjE3OS0uMDY0LS4xNjMtLjM3OC0uNDI0LS4xNzZhLjMzNi4zMzYgMCAwIDEtLjMwNi4wNTZjLS4yMDYtLjA1My0uNDMzLjAxOC0uNDUyLS4xNGEuNTEuNTEgMCAwIDAtLjU0LS40MzNjLS4yMS4wMTItLjM5Ni4wNS0uNTA3LS4yLjA4My4yODYuMjk2LjMwNC41NDYuMzA0LjIwNiAwIC4zMjguMTEuMzY1LjMzLjA2NC4zODYuMjkxLjE0My41OC4zMjVhLjc0NS43NDUgMCAwIDEgLjMuMzk5Yy4wNi4xNzIuNTMtLjAzNS40MDMuMzc3LS4wNTQuMTc4LS4xNTMuMjQ1LS4zNDYuMzg5LS41NzMuNDI5LS43MTUuMDA1LS44ODQuMzQ4LS4wODcuMTc4LS4wOS4wNDYtLjI4LjEzMy0uMzE0LjE0My0uNTE0LS4xODgtLjc0NS0uMDY2LS4yNi4xMzctLjI0LS4xMTgtLjUwMy4wNTQtLjIxNy4xNDEtLjQ0LS4xMDgtLjU0NC4yOC0uMDg5LjMzMi0uNDU4LjM0LS42MjcuMTYxLS4yNy0uMjgzLS4xODkuMzM0LS41MTguMDgtLjE3NS0uMTM0LS4zMDQuMDYyLS41Ni4wNjYtLjQ3MS4wMDYtLjY5Ny0uNDEyLS45MjctLjE1OC0uMjc1LjMwMi0uNDc0LS4wNDYtLjQ3OC0uMzU0LS4wMDUtLjMxNC0xLjExLS4yMS0xLjY5Mi0uMzczLS4zMjItLjA5LTEuMzMzLjAyLTEuMDA0LS41MTItLjUzLjU3LjY1Mi41MzYuOTc0LjY2NS4xOTQuMDc3LjM1LjE0LjUzOS4xNjcuMTg2LjQzMi4zMDYuOS4zMjQgMS40NzIuMDYyLS4zMTQuMDc2LS42MzMuMDQtLjk0OS0uMDYtLjU2LjM0Ni4yNzguMzk1LjM5OC4yNjIuNjU5LS4wMzIgMS44NjctLjY5NyAyLjIzYTQuMzQ3IDQuMzQ3IDAgMCAwLS44MDQtMS40NDZjLjM2Ni41NzIuODgxIDEuNzQxLjQ2OCAyLjQxYTEuNDczIDEuNDczIDAgMCAxLS43ODUtLjcwNWMuMTEuNDg3LjU5IDEuMDI2IDEuMTY4Ljg0OCAxLjAxLS4zMTEgMS4wMDUuMzQgMi4zNDQuNDQ4Ljk3NC4xNjMgMi4zNjMgMCAzLjAyNi4wNTUuMzYyLjAyOSAxLjg4NS4yODcgMS44NzguNjc4LS4wMjcgMS42MTYtLjYyNiAxLjcwMS0yLjE5OCAxLjg1NC0uOTE0LjA4OS0xLjYyLjEyNC0yLjYwNC4xNjJhOTMuOTU1IDkzLjk1NSAwIDAgMS02Ljk1NS0uMDAzYy0xLjAzNC0uMDQzLTEuNTMyLS4wNS0yLjYzLS4xOTgtMS4wOTgtLjE0OC0xLjctLjUzMS0xLjY4My0xLjczNi4wMDctLjQ0NCAxLjI5My0uODY4IDEuNzEzLS45MTcuNDk3LS4wNTcgMS4yODkuMDA4IDIuMDEtLjE3Ljc1MS0uMTg1IDEuMDY2LS4zMDcgMS42Mi0uNzQyWm03LjYzNy04Ljg0NGMtLjIzOC0uMDYyLS4wNzItLjIwMi0uNDgzLS4wMi0uMjQzLjEwNi0uMjc3LS4xOS0uNTIyLS4xMzgtLjI0Ni4wNTMtLjM0Ni0uMTgzLS43MTMtLjI0My0uNDU0LS4wNzQtLjEyNi0uMTk0LS44NDYtLjA0Ni0uMTM4LjAyNy0uMjU1LS4xMzItLjY4Ny0uMDc2LS41MDYuMDY3LS4zMjkuMTIyLS42MzUtLjE2OC0uMzg5LS4zNjgtLjY5Ny0uMTIxLTEuMDk2LS4yNy4zMDguMjQzLjUyNS4wMjguODg5LjMyNC0uMzQ2LjItLjc0OCAxLjE3Mi0xLjM3NSAxLjIzMy42ODIuMTU2IDEuMDEzLjUxNyAxLjY0LjYwNC43Mi4wOTggMS4yNi4xNzEgMS42NDUuNzc2LjQzLjY3Ni42MjcuMjg2IDEuMjQ5LjI0NS4zOTItLjAyNS40MDItLjI3LjQ2Ny0uNTMuMDktLjM1Ny4xOC0uMzc0LjQ0NC0uNDIxLjMwNC0uMDU1LjIzMi0uMTM2LjE1Mi0uMjI4LS4yMTQtLjI1MS4wMjctLjMwMi4yMDktLjM1Ni4zMTctLjA5NS4wNi0uMTEuMDYtLjI0NC0uMzk3LS4xMy0uMDktLjMxNi4xMy0uMzk4LjI0Mi4xNTIuMzQ3LjEyNy40NTQuMTA3LjE1NS0uMDMyLjI2LjA0My4zOS0uMDQyLS4xOTMuMDItLjIzMy0uMDU0LS4zOTYtLjAzLS4wNzQuMDEtLjE2MS4wMTYtLjMxNC0uMDc3LS4yNTYtLjE1OC0uMzg2LjA2OS0uNjYyLS4wMDJabS0zLjI1NiAyLjM5Ni0xLjU1OS0uNzk2Yy0xLjg3OS0uOTYtMi43MzQuNDU3LTMuMzM4IDIuMDFsLTEuMDk0IDEuMDY4Yy43ODQtLjQ5NCAxLjQyNC0xLjA0NCAyLjI0OS0xLjAzOS4xMzEgMCAyLjE2NS43MDkgMS44MzgtLjEzMy0uMDU5LS4xNTIuMTI3LS4wNzcuMTc4LS4yOTQuMDU3LS4yNS4wNzgtLjIwNy4yNzktLjA0OC4xMjYuMS4zMDEuMTY1LjI0OC0uMDctLjAyNC0uMTA2LS4wNTctLjIuNC0uMDA2LjIxNS4wOS4xMzUtLjA5NC40MzMtLjEzLjQxNC0uMDUyLjMyNS0uNDU1LjUyLS41MTEuMTA4LS4wMzIuMjE3LjA4My40MjEtLjExMy4wNy0uMDY4LjE1Ny0uMTMuMzMyLS4wODQtLjIwNC0uMTA2LS4zMTQtLjAyLS40MTguMDQ0LS4yMDkuMTMtLjI5LS4wNTMtLjQ5LjEwMlptLTMuMTg2LTIuODFjLjIyOS0uMDMuNDU3LS4wNTMuNjg4LS4wNjhhLjE4Ni4xODYgMCAwIDAgLjEwNi0uMDg2LjE2Ni4xNjYgMCAwIDAgLjAxLS4xMzEuMzkyLjM5MiAwIDAgMSAuMjEtLjMwM2wtLjAwMi0uMDA0Yy0uMzY5LjAwMS0uNjY2LjE0NS0xLjA0My4zMDgtLjA3NS4wMzMtLjAzNS4xNjMtLjE2OS4zMTFhLjM3OC4zNzggMCAwIDEtLjI4OC4zNDQuMTkzLjE5MyAwIDAgMC0uMTI4LjA2Ny4xNjcuMTY3IDAgMCAwLS4wMzcuMTM0LjM1NC4zNTQgMCAwIDEtLjM5Ni4wOWMtLjI4NS0uMTEzLS40OTgtLjA5NS0uNjQuMDUzYS41NS41NSAwIDAgMSAuNTcuMDIuNDU0LjQ1NCAwIDAgMCAuNTU3LS4xMi4xOS4xOSAwIDAgMSAuMTc5LS4xNDRjLjIwMi0uMDc3LjMzLS4yMzUuMzgzLS40NzFaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=';
/**
* Constructor.
*
* @since 1.0.0
*/
public function __construct() {
$this->course_order_page_slug = 'course-order';
$this->lesson_order_page_slug = 'lesson-order';
// register admin styles
add_action( 'admin_enqueue_scripts', array( $this, 'admin_styles_global' ) );
// register admin scripts
add_action( 'admin_enqueue_scripts', array( $this, 'register_scripts' ) );
add_action( 'admin_menu', array( $this, 'add_course_order' ) );
add_action( 'admin_menu', array( $this, 'add_lesson_order' ) );
add_action( 'admin_menu', array( $this, 'admin_menu' ), 9 );
add_action( 'menu_order', array( $this, 'admin_menu_order' ) );
add_action( 'admin_head', array( $this, 'admin_menu_highlight' ) );
add_action( 'admin_init', array( $this, 'sensei_add_custom_menu_items' ) );
add_action( 'admin_print_scripts', array( $this, 'sensei_set_plugin_url' ) );
// Duplicate lesson & courses
add_filter( 'post_row_actions', array( $this, 'duplicate_action_link' ), 10, 2 );
add_action( 'admin_action_duplicate_lesson', array( $this, 'duplicate_lesson_action' ) );
add_action( 'admin_action_duplicate_course', array( $this, 'duplicate_course_action' ) );
add_action( 'admin_action_duplicate_course_with_lessons', array( $this, 'duplicate_course_with_lessons_action' ) );
// Handle course and lesson ordering.
add_action( 'admin_post_order_courses', array( $this, 'handle_order_courses' ) );
add_action( 'admin_post_order_lessons', array( $this, 'handle_order_lessons' ) );
// Handle lessons list table filtering
add_action( 'restrict_manage_posts', array( $this, 'lesson_filter_options' ) );
add_filter( 'request', array( $this, 'lesson_filter_actions' ) );
// Add Sensei items to 'at a glance' widget
add_filter( 'dashboard_glance_items', array( $this, 'glance_items' ), 10, 1 );
// Handle course and lesson deletions
add_action( 'trash_course', array( $this, 'delete_content' ), 10, 2 );
add_action( 'trash_lesson', array( $this, 'delete_content' ), 10, 2 );
// Add notices to WP dashboard
add_action( 'admin_notices', array( $this, 'theme_compatibility_notices' ) );
// warn users in case admin_email is not a real WP_User
add_action( 'admin_notices', array( $this, 'notify_if_admin_email_not_real_admin_user' ) );
// remove a course from course order when trashed
add_action( 'transition_post_status', array( $this, 'remove_trashed_course_from_course_order' ) );
// Add AJAX endpoint for event logging.
add_action( 'wp_ajax_sensei_log_event', array( $this, 'ajax_log_event' ) );
Sensei_Tools::instance()->init();
Sensei_Status::instance()->init();
}
/**
* Add items to admin menu
*
* @since 1.4.0
* @since 4.8.0 Reactivate method since we have a new home page.
*
* @return void
*/
public function admin_menu() {
add_menu_page( 'Sensei LMS', 'Sensei LMS', self::get_top_menu_capability(), 'sensei', '', $this->sensei_icon, '50' );
}
/**
* Get the top menu minimum capability.
*
* @since 4.8.0
*
* @return string
*/
public static function get_top_menu_capability() {
$menu_cap = 'manage_sensei';
if ( ! current_user_can( 'manage_sensei' ) && current_user_can( 'manage_sensei_grades' ) ) {
$menu_cap = 'manage_sensei_grades';
}
return $menu_cap;
}
/**
* Add Course order page to admin panel.
*
* @since 4.0.0
* @access private
*/
public function add_course_order() {
add_submenu_page(
'', // Hide in menu.
__( 'Order Courses', 'sensei-lms' ),
__( 'Order Courses', 'sensei-lms' ),
'manage_sensei',
$this->course_order_page_slug,
array( $this, 'course_order_screen' )
);
}
/**
* Add Lesson order page to admin panel.
*
* @since 4.0.0
* @access private
*/
public function add_lesson_order() {
add_submenu_page(
'',
__( 'Order Lessons', 'sensei-lms' ),
__( 'Order Lessons', 'sensei-lms' ),
'edit_published_lessons',
$this->lesson_order_page_slug,
array( $this, 'lesson_order_screen' )
);
}
/**
* [admin_menu_order description]
*
* @since 1.4.0
* @param array $menu_order Existing menu order
* @return array Modified menu order for Sensei
*/
public function admin_menu_order( $menu_order ) {
// Initialize our custom order array
$sensei_menu_order = array();
// Get the index of our custom separator
$sensei_separator = array_search( 'separator-sensei', $menu_order );
// Loop through menu order and do some rearranging
foreach ( $menu_order as $index => $item ) :
if ( ( ( 'sensei' ) == $item ) ) :
$sensei_menu_order[] = 'separator-sensei';
$sensei_menu_order[] = $item;
unset( $menu_order[ $sensei_separator ] );
elseif ( ! in_array( $item, array( 'separator-sensei' ) ) ) :
$sensei_menu_order[] = $item;
endif;
endforeach;
// Return order
return $sensei_menu_order;
}
/**
* Handle highlighting of admin menu items
*
* @since 1.4.0
* @since 4.8.0 General review after adding the new Sensei Home page.
*
* @return void
*/
public function admin_menu_highlight() {
global $parent_file, $submenu_file, $taxonomy, $_wp_real_parent_file;
$screen = get_current_screen();
if ( empty( $screen ) ) {
return;
}
// phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited -- Only way to highlight our special pages in menu.
if ( 'edit-tags' === $screen->base && 'module' === $taxonomy ) {
$parent_file = 'sensei';
$submenu_file = 'edit-tags.php?taxonomy=module&post_type=course';
} elseif ( in_array( $screen->id, [ 'edit-module', 'admin_page_module-order' ], true ) ) {
// Module pages.
$parent_file = 'sensei';
$_wp_real_parent_file[''] = 'sensei';
$submenu_file = 'edit-tags.php?taxonomy=module&post_type=course';
} elseif ( in_array( $screen->id, [ 'course', 'edit-course-category', 'admin_page_course-order', 'admin_page_' . Sensei_Course::SHOWCASE_COURSES_SLUG ], true ) ) {
// Course pages.
$parent_file = 'sensei';
$_wp_real_parent_file[''] = 'sensei';
$submenu_file = 'edit.php?post_type=course';
} elseif ( in_array( $screen->id, [ 'lesson', 'edit-lesson-tag', 'admin_page_lesson-order' ], true ) ) {
// Lesson pages.
$parent_file = 'sensei';
$_wp_real_parent_file[''] = 'sensei';
$submenu_file = 'edit.php?post_type=lesson';
} elseif ( in_array( $screen->id, [ 'question', 'edit-question-category' ], true ) ) {
// Question pages.
$parent_file = 'sensei';
$_wp_real_parent_file[''] = 'sensei';
$submenu_file = 'edit.php?post_type=question';
} elseif ( in_array( $screen->id, [ 'sensei_message' ], true ) ) {
// Message pages.
$parent_file = 'sensei';
$_wp_real_parent_file[''] = 'sensei';
$submenu_file = 'edit.php?post_type=sensei_message';
} elseif ( in_array( $screen->id, [ 'sensei_email', 'edit-sensei_email' ], true ) ) {
// Message pages.
$parent_file = 'sensei';
$_wp_real_parent_file[''] = 'sensei';
$submenu_file = 'sensei-settings';
}
// phpcs:enable WordPress.WP.GlobalVariablesOverride.Prohibited
}
/**
* Redirect Sensei menu item to Analysis page
*
* @since 1.4.0
* @deprecated 4.0.0
*
* @return void
*/
public function page_redirect() {
_deprecated_function( __METHOD__, '4.0.0' );
if ( isset( $_GET['page'] ) && $_GET['page'] == 'sensei' ) {
wp_safe_redirect( 'admin.php?page=sensei_reports' );
exit;
}
}
/**
* install_pages_output function.
*
* Handles installation of the 2 pages needs for courses and my courses
*
* @deprecated 3.1.0 use Sensei_Setup_Wizard_Pages::create_pages instead.
* @access public
* @return void
*/
function install_pages_output() {
_deprecated_function( __METHOD__, '3.1.0', 'Sensei_Setup_Wizard_Pages::create_pages' );
}
/**
* create_page function.
*
* @deprecated 3.1.0 use Sensei_Setup_Wizard_Pages::create_page instead.
*
* @access public
* @param mixed $slug
* @param mixed $option
* @param string $page_title (default: '')
* @param string $page_content (default: '')
* @param int $post_parent (default: 0)
* @return integer $page_id
*/
function create_page( $slug, $page_title = '', $page_content = '', $post_parent = 0 ) {
_deprecated_function( __METHOD__, '3.1.0', 'Sensei_Setup_Wizard_Pages::create_page' );
return Sensei()->setup_wizard->pages->create_page( $slug, $page_title, $page_content, $post_parent );
}
/**
* create_pages function.
*
* @deprecated 3.1.0 use Sensei_Setup_Wizard_Pages::create_pages instead.
*
* @access public
* @return void
*/
function create_pages() {
_deprecated_function( __METHOD__, '3.1.0', 'Sensei_Setup_Wizard_Pages::create_pages' );
Sensei()->setup_wizard->pages->create_pages();
}
/**
* Load the global admin styles for the menu icon and the relevant page icon.
*
* @access public
* @since 1.0.0
*
* @param string $hook The current admin page.
*/
public function admin_styles_global( $hook ) {
global $post_type;
// Global Styles for icons and menu items
Sensei()->assets->enqueue( 'sensei-global', 'css/global.css', [], 'screen' );
// WordPress component styles with Sensei theming.
Sensei()->assets->register( 'sensei-wp-components', 'shared/styles/wp-components.css', [], 'screen' );
// Select 2 styles
Sensei()->assets->enqueue( 'sensei-core-select2', '../vendor/select2/select2.min.css', [], 'screen' );
Sensei()->assets->register( 'jquery-modal', '../vendor/jquery-modal-0.9.1/jquery.modal.min.css' );
// Test for Write Panel Pages
if ( $this->are_custom_admin_styles_allowed( $post_type, $hook, get_current_screen() ) ) {
Sensei()->assets->enqueue( 'sensei-admin-custom', 'css/admin-custom.css', [], 'screen' );
}
}
/**
* Check if it is allowed to enqueue admin custom styles.
*
* @param string $post_type The post type slug.
* @param string $hook_suffix The current admin page.
* @param WP_Screen|null $screen The current screen.
* @return bool Returns true if admin custom styles are allowed.
*/
private function are_custom_admin_styles_allowed( $post_type, $hook_suffix, $screen ) {
/**
* Filter the list of post types where the admin custom styles should be loaded.
*
* @hook sensei_scripts_allowed_post_types
*
* @param {array} $allowed_post_types The list of post types where the admin custom styles should be loaded.
* @return {array} Filtered list of allowed post types.
*/
$allowed_post_types = apply_filters( 'sensei_scripts_allowed_post_types', array( 'lesson', 'course', 'question' ) );
/**
* Filter the list of admin pages where the admin custom styles should be loaded.
*
* @hook sensei_scripts_allowed_post_type_pages
*
* @param {array} $allowed_post_type_pages The list of admin pages where the admin custom styles should be loaded.
* @return {array} Filtered list of allowed admin pages.
*/
$allowed_post_type_pages = apply_filters( 'sensei_scripts_allowed_post_type_pages', array( 'edit.php', 'post-new.php', 'post.php', 'edit-tags.php' ) );
/**
* Filter the list of admin pages slugs where the admin custom styles should be loaded.
*
* @hook sensei_scripts_allowed_pages
*
* @param {array} $allowed_pages The list of admin pages slugs where the admin custom styles should be loaded.
* @return {array} Filtered list of allowed admin pages.
*/
$allowed_pages = apply_filters( 'sensei_scripts_allowed_pages', array( 'sensei_grading', Sensei_Analysis::PAGE_SLUG, 'sensei_learners', 'sensei_updates', 'sensei-settings', 'sensei_learners', Sensei_Course::SHOWCASE_COURSES_SLUG, $this->lesson_order_page_slug, $this->course_order_page_slug ) );
$module_pages_screen_ids = [ 'edit-module' ];
$is_allowed_type = isset( $post_type ) && in_array( $post_type, $allowed_post_types, true );
$is_allowed_post_type_page = isset( $hook_suffix ) && in_array( $hook_suffix, $allowed_post_type_pages, true );
$is_allowed_page = isset( $_GET['page'] ) && in_array( $_GET['page'], $allowed_pages, true ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_modules_page = $screen && in_array( $screen->id, $module_pages_screen_ids, true );
return ( $is_allowed_type && $is_allowed_post_type_page ) || $is_allowed_page || $is_modules_page;
}
/**
* Globally register all scripts needed in admin.
*
* The script users should enqueue the script when needed.
*
* @since 1.8.2
* @access public
*/
public function register_scripts( $hook ) {
$screen = get_current_screen();
Sensei()->assets->register( 'sensei-dismiss-notices', 'js/admin/sensei-notice-dismiss.js', [] );
// Select2 script used to enhance all select boxes.
Sensei()->assets->register( 'sensei-core-select2', '../vendor/select2/select2.full.js', [ 'jquery' ] );
Sensei()->assets->register( 'jquery-modal', '../vendor/jquery-modal-0.9.1/jquery.modal.js', [ 'jquery' ], true );
Sensei()->assets->register(
'sensei-learners-admin-bulk-actions-js',
'js/learners-bulk-actions.js',
[ 'jquery', 'sensei-core-select2', 'jquery-modal', 'wp-i18n' ],
true
);
$ajax_object = array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
);
wp_localize_script( 'sensei-learners-admin-bulk-actions-js', 'ajax_object', $ajax_object );
Sensei()->assets->register( 'sensei-chosen', '../vendor/chosen/chosen.jquery.min.js', [ 'jquery' ], true );
Sensei()->assets->register( 'sensei-chosen-ajax', '../vendor/chosen/ajax-chosen.jquery.min.js', [ 'jquery', 'sensei-chosen' ], true );
// Load ordering script on Order Courses and Order Lessons pages.
if ( in_array( $screen->id, [ 'admin_page_course-order', 'admin_page_lesson-order' ], true ) ) {
Sensei()->assets->enqueue( 'sensei-ordering', 'js/admin/ordering.js', [ 'jquery', 'jquery-ui-sortable', 'sensei-core-select2' ], true );
}
// Load edit module scripts.
if ( 'edit-module' === $screen->id ) {
wp_enqueue_script( 'sensei-chosen-ajax' );
}
Sensei()->assets->enqueue( 'sensei-message-menu-fix', 'js/admin/message-menu-fix.js', [ 'jquery' ], true );
// Event logging.
Sensei()->assets->enqueue( 'sensei-event-logging', 'js/admin/event-logging.js', [ 'jquery' ], true );
if ( $this->has_custom_navigation( $screen ) ) {
Sensei()->assets->enqueue( 'sensei-admin-custom-navigation', 'js/admin/custom-navigation.js', [], true );
}
wp_localize_script( 'sensei-event-logging', 'sensei_event_logging', [ 'enabled' => Sensei_Usage_Tracking::get_instance()->get_tracking_enabled() ] );
}
/**
* Check if the current screen has a custom navigation.
*
* @param WP_Screen|null $screen The current screen.
* @return bool
*/
private function has_custom_navigation( $screen ) {
$screens_with_custom_navigation = [
'edit-course',
'edit-course-category',
'edit-module',
'edit-lesson',
'edit-lesson-tag',
'edit-question',
'edit-question-category',
'sensei-lms_page_' . Sensei_Analysis::PAGE_SLUG,
'sensei-lms_page_sensei_learners',
];
/**
* Allows modifying the list of screens where the scripts for custom
* navigation (which handles some operations like hiding the
* navigation title that is generated by wp, for example, the name of the
* custom post type that's shown on the list page of custom post type)
* should be loaded.
*
* @since 4.5.0
* @hook sensei_custom_navigation_allowed_screens
*
* @param {array} $screens_with_custom_navigation Screens where custom navigation scrips will be loaded.
*
* @return {array} Screens where custom navigation scrips will be loaded.
*/
$screens_with_custom_navigation = apply_filters(
'sensei_custom_navigation_allowed_screens',
$screens_with_custom_navigation
);
return $screen
&& ( in_array( $screen->id, $screens_with_custom_navigation, true ) )
&& ( 'term' !== $screen->base );
}
/**
* admin_install_notice function.
*
* @deprecated 3.1.0
* @access public
* @return void
*/
function admin_install_notice() {
_deprecated_function( __METHOD__, '3.1.0', 'Sensei_Setup_Wizard::setup_wizard_notice' );
}
/**
* admin_installed_notice function.
*
* @deprecated 3.1.0
* @access public
* @return void
*/
function admin_installed_notice() {
_deprecated_function( __METHOD__, '3.1.0', 'Sensei_Setup_Wizard::setup_wizard_notice' );
}
/**
* admin_notices_styles function.
*
* @deprecated 3.1.0
* @access public
* @return void
*/
function admin_notices_styles() {
_deprecated_function( __METHOD__, '3.1.0', 'Sensei_Setup_Wizard::setup_wizard_notice' );
}
/**
* Add links for duplicating lessons & courses
*
* @param array $actions Default actions
* @param object $post Current post
* @return array Modified actions
*/
public function duplicate_action_link( $actions, $post ) {
switch ( $post->post_type ) {
case 'lesson':
$confirm = __( 'This will duplicate the lesson quiz and all of its questions. Are you sure you want to do this?', 'sensei-lms' );
$actions['duplicate'] = "<a onclick='return confirm(\"" . $confirm . "\");' href='" . $this->get_duplicate_link( $post->ID ) . "' title='" . esc_attr( __( 'Duplicate this lesson', 'sensei-lms' ) ) . "'>" . __( 'Duplicate', 'sensei-lms' ) . '</a>';
break;
case 'course':
$confirm = __( 'This will duplicate the course lessons along with all of their quizzes and questions. Are you sure you want to do this?', 'sensei-lms' );
$actions['duplicate'] = '<a href="' . $this->get_duplicate_link( $post->ID ) . '" title="' . esc_attr( __( 'Duplicate this course', 'sensei-lms' ) ) . '">' . __( 'Duplicate', 'sensei-lms' ) . '</a>';
$actions['duplicate_with_lessons'] = '<a onclick="return confirm(\'' . $confirm . '\');" href="' . $this->get_duplicate_link( $post->ID, true ) . '" title="' . esc_attr( __( 'Duplicate this course with its lessons', 'sensei-lms' ) ) . '">' . __( 'Duplicate (with lessons)', 'sensei-lms' ) . '</a>';
break;
}
return $actions;
}
/**
* Generate duplicationlink
*
* @param integer $post_id Post ID
* @param boolean $with_lessons Include lessons or not
* @return string Duplication link
*/
private function get_duplicate_link( $post_id = 0, $with_lessons = false ) {
$post = get_post( $post_id );
$action = 'duplicate_' . $post->post_type;
if ( 'course' == $post->post_type && $with_lessons ) {
$action .= '_with_lessons';
}
$bare_url = admin_url( 'admin.php?action=' . $action . '&post=' . $post_id );
$url = wp_nonce_url( $bare_url, $action . '_' . $post_id );
return apply_filters( $action . '_link', $url, $post_id );
}
/**
* Duplicate lesson
*
* @return void
*/
public function duplicate_lesson_action() {
$this->duplicate_content( 'lesson' );
}
/**
* Duplicate course
*
* @return void
*/
public function duplicate_course_action() {
$this->duplicate_content( 'course' );
}
/**
* Duplicate course with lessons.
*
* @return void
*/
public function duplicate_course_with_lessons_action() {
$this->duplicate_content( 'course', true );
}
/**
* Redirect the user safely.
*
* @access private
* @param string $redirect_url URL to redirect the user.
* @return void
*/
protected function safe_redirect( $redirect_url ) {
wp_safe_redirect( esc_url_raw( $redirect_url ) );
exit;
}
/**
* Duplicate content.
*
* @param string $post_type Post type being duplicated.
* @param boolean $with_lessons Include lessons or not.
* @return void
*/
private function duplicate_content( $post_type = 'lesson', $with_lessons = false ) {
if ( ! isset( $_GET['post'] ) ) {
// translators: Placeholder is the post type string.
wp_die( esc_html( sprintf( __( 'Please supply a %1$s ID.', 'sensei-lms' ) ), $post_type ) );
}
$post_id = $_GET['post'];
$post = get_post( $post_id );
if ( ! in_array( get_post_type( $post_id ), array( 'lesson', 'course' ), true ) ) {
wp_die( esc_html__( 'Invalid post type. Can duplicate only lessons and courses', 'sensei-lms' ) );
}
$event = false;
if ( 'course' === $post_type ) {
$event = 'course_duplicate';
} elseif ( 'lesson' === $post_type ) {
$event = 'lesson_duplicate';
}
$event_properties = [
$post_type . '_id' => $post_id,
];
$action = 'duplicate_' . $post_type;
if ( $with_lessons ) {
$action .= '_with_lessons';
}
check_admin_referer( $action . '_' . $post_id );
if ( ! current_user_can( 'manage_sensei_grades' ) ) {
wp_die( esc_html__( 'Insufficient permissions', 'sensei-lms' ) );
}
if ( ! is_wp_error( $post ) ) {
$post_duplicator = new Post_Duplicator();
$new_post = $post_duplicator->duplicate( $post );
if ( ! is_wp_error( $new_post ) && $new_post ) {
if ( 'lesson' == $new_post->post_type ) {
$lesson_quiz_duplicator = new Lesson_Quiz_Duplicator();
$lesson_quiz_duplicator->duplicate( $post_id, $new_post->ID );
}
if ( 'course' == $new_post->post_type && $with_lessons ) {
$event = 'course_duplicate_with_lessons';
$course_lessons_duplicator = new Course_Lessons_Duplicator();
$event_properties['lesson_count'] = $course_lessons_duplicator->duplicate( $post_id, $new_post->ID );
}
$redirect_url = admin_url( 'post.php?post=' . $new_post->ID . '&action=edit' );
} else {
$redirect_url = admin_url( 'edit.php?post_type=' . $post->post_type . '&message=duplicate_failed' );
}
// Log event.
if ( $event ) {
sensei_log_event( $event, $event_properties );
}
$this->safe_redirect( $redirect_url );
}
}
/**
* Add options to filter lessons
*
* @return void
*/
public function lesson_filter_options() {
global $typenow;
if ( is_admin() && 'lesson' == $typenow ) {
$args = array(
'post_type' => 'course',
'post_status' => array( 'publish', 'pending', 'draft', 'future', 'private' ),
'posts_per_page' => -1,
'suppress_filters' => 0,
'orderby' => 'title menu_order date',
'order' => 'ASC',
);
$courses = get_posts( $args );
$selected = isset( $_GET['lesson_course'] ) ? $_GET['lesson_course'] : '';
$course_options = '';
foreach ( $courses as $course ) {
$course_options .= '<option value="' . esc_attr( $course->ID ) . '" ' . selected( $selected, $course->ID, false ) . '>' . esc_html( get_the_title( $course->ID ) ) . '</option>';
}
$output = '<select name="lesson_course" id="dropdown_lesson_course">';
$output .= '<option value="">' . esc_html__( 'Show all courses', 'sensei-lms' ) . '</option>';
$output .= $course_options;
$output .= '</select>';
$allowed_html = array(
'option' => array(
'selected' => array(),
'value' => array(),
),
'select' => array(
'id' => array(),
'name' => array(),
),
);
echo wp_kses( $output, $allowed_html );
}
}
/**
* Filter lessons
*
* @param array $request Current request
* @return array Modified request
*/
public function lesson_filter_actions( $request ) {
global $typenow;
if ( is_admin() && 'lesson' == $typenow ) {
$lesson_course = isset( $_GET['lesson_course'] ) ? $_GET['lesson_course'] : '';
if ( $lesson_course ) {
$request['meta_key'] = '_lesson_course';
$request['meta_value'] = $lesson_course;
$request['meta_compare'] = '=';
}
}
return $request;
}
/**
* Adding Sensei items to 'At a glance' dashboard widget
*
* @param array $items Existing items
* @return array Updated items
*/
public function glance_items( $items = array() ) {
$types = array( 'course', 'lesson', 'question' );
foreach ( $types as $type ) {
if ( ! post_type_exists( $type ) ) {
continue;
}
$num_posts = wp_count_posts( $type );
if ( $num_posts ) {
$published = intval( $num_posts->publish );
$post_type = get_post_type_object( $type );
$text = '%s ' . $post_type->labels->singular_name;
$text = sprintf( $text, number_format_i18n( $published ) );
if ( current_user_can( $post_type->cap->edit_posts ) ) {
$items[] = sprintf( '<a class="%1$s-count" href="edit.php?post_type=%1$s">%2$s</a>', $type, $text ) . "\n";
} else {
$items[] = sprintf( '<span class="%1$s-count">%2$s</span>', $type, $text ) . "\n";
}
}
}
return $items;
}
/**
* Process lesson and course deletion
*
* @param integer $post_id Post ID
* @param object $post Post object
* @return void
*/
public function delete_content( $post_id, $post ) {
$type = $post->post_type;
if ( in_array( $type, array( 'lesson', 'course' ) ) ) {
$meta_key = '_' . $type . '_prerequisite';
$args = array(
'post_type' => $type,
'post_status' => 'any',
'posts_per_page' => -1,
'meta_key' => $meta_key,
'meta_value' => $post_id,
);
$posts = get_posts( $args );
foreach ( $posts as $post ) {
delete_post_meta( $post->ID, $meta_key );
}
}
}
/**
* Delete all user activity when user is deleted.
*
* @deprecated 3.0.0 Use `\Sensei_Learner::delete_all_user_activity` instead.
*
* @param integer $user_id User ID.
* @return void
*/
public function delete_user_activity( $user_id = 0 ) {
_deprecated_function( __METHOD__, '3.0.0', 'Sensei_Learner::delete_all_user_activity' );
\Sensei_Learner::instance()->delete_all_user_activity( $user_id );
}
public function render_settings( $settings = array(), $post_id = 0, $group_id = '' ) {
$html = '';
if ( ! $settings ) {
return $html;
}
$html .= '<div class="sensei-options-panel">' . "\n";
$html .= '<div class="options_group" id="' . esc_attr( $group_id ) . '">' . "\n";
foreach ( $settings as $field ) {
$data = '';
if ( $post_id ) {
if ( 'plain-text' !== $field['type'] ) {
$data = get_post_meta( $post_id, '_' . $field['id'], true );
if ( ! $data && isset( $field['default'] ) ) {
$data = $field['default'];
}
} else {
$data = $field['default'];
}
} else {
$option = get_option( $field['id'] );
if ( isset( $field['default'] ) ) {
$data = $field['default'];
if ( $option ) {
$data = $option;
}
}
}
if ( ! isset( $field['disabled'] ) ) {
$field['disabled'] = false;
}
if ( 'hidden' != $field['type'] ) {
$class_tail = '';
if ( isset( $field['class'] ) ) {
$class_tail .= $field['class'];
}
if ( isset( $field['disabled'] ) && $field['disabled'] ) {
$class_tail .= ' disabled';
}
$html .= '<p class="form-field ' . esc_attr( $field['id'] ) . ' ' . esc_attr( $class_tail ) . '">' . "\n";
}
if ( ! in_array( $field['type'], array( 'hidden', 'checkbox_multi', 'radio' ) ) ) {
$html .= '<label for="' . esc_attr( $field['id'] ) . '">' . "\n";
}
if ( $field['label'] ) {
$html .= '<span class="label">' . esc_html( $field['label'] ) . '</span>';
}
switch ( $field['type'] ) {
case 'plain-text':
$html .= '<strong>' . esc_html( $data ) . '</strong>';
break;
case 'text':
case 'password':
$html .= '<input id="' . esc_attr( $field['id'] ) . '" ';
$html .= 'type="' . esc_attr( $field['type'] ) . '" ';
$html .= 'name="' . esc_attr( $field['id'] ) . '" ';
$html .= 'placeholder="' . esc_attr( $field['placeholder'] ) . '" ';
$html .= 'value="' . esc_attr( $data ) . '" ';
$html .= disabled( $field['disabled'], true, false );
$html .= ' />' . "\n";
break;
case 'number':
$min = '';
if ( isset( $field['min'] ) ) {
$min = 'min="' . esc_attr( $field['min'] ) . '"';
}
$max = '';
if ( isset( $field['max'] ) ) {
$max = 'max="' . esc_attr( $field['max'] ) . '"';
}
$html .= '<input id="' . esc_attr( $field['id'] ) . '" ';
$html .= 'type="' . esc_attr( $field['type'] ) . '" ';
$html .= 'name="' . esc_attr( $field['id'] ) . '" ';
$html .= 'placeholder="' . esc_attr( $field['placeholder'] ) . '" ';
$html .= 'value="' . esc_attr( $data ) . '" ';
$html .= $min . ' ' . $max . ' '; // $min and $max are escaped above, for better readibility
$html .= 'class="small-text" ';
$html .= disabled( $field['disabled'], true, false );
$html .= ' />' . "\n";
break;
case 'textarea':
$html .= '<textarea id="' . esc_attr( $field['id'] ) . '" ';
$html .= 'rows="5" cols="50" ';
$html .= 'name="' . esc_attr( $field['id'] ) . '" ';
$html .= 'placeholder="' . esc_attr( $field['placeholder'] ) . '" ';
$html .= disabled( $field['disabled'], true, false );
$html .= '>' . strip_tags( $data ) . '</textarea><br/>' . "\n";
break;
case 'checkbox':
// backwards compatibility
if ( empty( $data ) || 'on' == $data ) {
$checked_value = 'on';
} elseif ( 'yes' == $data ) {
$checked_value = 'yes';
} elseif ( 'auto' == $data ) {
$checked_value = 'auto';
} else {
$checked_value = 1;
$data = intval( $data );
}
$html .= '<input id="' . esc_attr( $field['id'] ) . '" ';
$html .= 'type="' . esc_attr( $field['type'] ) . '" ';
$html .= 'name="' . esc_attr( $field['id'] ) . '" ';
$html .= checked( $checked_value, $data, false );
$html .= disabled( $field['disabled'], true, false );
$html .= " /> \n";
// Input hidden to identify if checkbox is present.
$html .= '<input type="hidden" ';
$html .= 'name="contains_' . esc_attr( $field['id'] ) . '" ';
$html .= 'value="1" ';
$html .= " /> \n";
break;
case 'checkbox_multi':
foreach ( $field['options'] as $k => $v ) {
$checked = false;
if ( in_array( $k, $data ) ) {
$checked = true;
}
$html .= '<label for="' . esc_attr( $field['id'] . '_' . $k ) . '">';
$html .= '<input type="checkbox" ';
$html .= checked( $checked, true, false ) . ' ';
$html .= 'name="' . esc_attr( $field['id'] ) . '[]" ';
$html .= 'value="' . esc_attr( $k ) . '" ';
$html .= 'id="' . esc_attr( $field['id'] . '_' . $k ) . '" ';
$html .= disabled( $field['disabled'], true, false );
$html .= ' /> ' . esc_html( $v );
$html .= "</label> \n";
}
break;
case 'radio':
foreach ( $field['options'] as $k => $v ) {
$checked = false;
if ( $k == $data ) {
$checked = true;
}
$html .= '<label for="' . esc_attr( $field['id'] . '_' . $k ) . '">';
$html .= '<input type="radio" ';
$html .= checked( $checked, true, false ) . ' ';
$html .= 'name="' . esc_attr( $field['id'] ) . '" ';
$html .= 'value="' . esc_attr( $k ) . '" ';
$html .= 'id="' . esc_attr( $field['id'] . '_' . $k ) . '" ';
$html .= disabled( $field['disabled'], true, false );
$html .= ' /> ' . esc_html( $v );
$html .= "</label> \n";
}
break;
case 'select':
$html .= '<select name="' . esc_attr( $field['id'] ) . '" ';
$html .= 'id="' . esc_attr( $field['id'] ) . '" ';
$html .= disabled( $field['disabled'], true, false );
$html .= ">\n";
foreach ( $field['options'] as $k => $v ) {
$selected = false;
if ( $k == $data ) {
$selected = true;
}
$html .= '<option ' . selected( $selected, true, false ) . ' ';
$html .= 'value="' . esc_attr( $k ) . '">' . esc_html( $v ) . "</option>\n";
}
$html .= "</select><br/>\n";
break;
case 'select_multi':
$html .= '<select name="' . esc_attr( $field['id'] ) . '[]" ';
$html .= 'id="' . esc_attr( $field['id'] ) . '" multiple="multiple" ';
$html .= disabled( $field['disabled'], true, false );
$html .= ">\n";
foreach ( $field['options'] as $k => $v ) {
$selected = false;
if ( in_array( $k, $data ) ) {
$selected = true;
}
$html .= '<option ' . selected( $selected, true, false ) . ' ';
$html .= 'value="' . esc_attr( $k ) . '" />' . esc_html( $v ) . "</option>\n";
}
$html .= "</select>\n";
break;
case 'hidden':
$html .= '<input id="' . esc_attr( $field['id'] ) . '" ';
$html .= 'type="' . esc_attr( $field['type'] ) . '" ';
$html .= 'name="' . esc_attr( $field['id'] ) . '" ';
$html .= 'value="' . esc_attr( $data ) . '" ';
$html .= disabled( $field['disabled'], true, false );
$html .= "/>\n";
break;
}
if ( $field['description'] ) {
$html .= ' <span class="description">' . esc_html( $field['description'] ) . '</span>' . "\n";
}
if ( ! in_array( $field['type'], array( 'hidden', 'checkbox_multi', 'radio' ) ) ) {
$html .= '</label>' . "\n";
}
if ( 'hidden' != $field['type'] ) {
$html .= "</p>\n";
}
}
$html .= "</div>\n";
$html .= "</div>\n";
return $html;
}
/**
* Handle the POST request for reordering the Courses.
*
* @since 1.12.2
*/
public function handle_order_courses() {
check_admin_referer( 'order_courses' );
$ordered = null;
if ( isset( $_POST['course-order'] ) && 0 < strlen( $_POST['course-order'] ) ) {
$ordered = $this->save_course_order( esc_attr( $_POST['course-order'] ) );
}
wp_redirect(
esc_url_raw(
add_query_arg(
array(
'page' => $this->course_order_page_slug,
'ordered' => $ordered,
),
admin_url( 'admin.php' )
)
)
);
}
/**
* Dsplay Course Order screen
*
* @return void
*/
public function course_order_screen() {
$should_update_order = false;
$new_course_order = array();
?>
<div id="<?php echo esc_attr( $this->course_order_page_slug ); ?>" class="wrap <?php echo esc_attr( $this->course_order_page_slug ); ?>">
<h1><?php esc_html_e( 'Order Courses', 'sensei-lms' ); ?></h1>
<?php
$html = '';
if ( isset( $_GET['ordered'] ) && $_GET['ordered'] ) {
$html .= '<div class="updated fade">' . "\n";
$html .= '<p>' . esc_html__( 'The course order has been saved.', 'sensei-lms' ) . '</p>' . "\n";
$html .= '</div>' . "\n";
}
$courses = Sensei()->course->get_all_courses();
if ( $courses ) {
// order the courses as set by the users
$all_course_ids = array();
foreach ( $courses as $course ) {
$all_course_ids[] = (string) $course->ID;
}
$order_string = $this->get_course_order();
if ( ! empty( $order_string ) ) {
$ordered_course_ids = explode( ',', $order_string );
$all_course_ids = array_unique( array_merge( $ordered_course_ids, $all_course_ids ) );
}
$html .= '<form id="editgrouping" method="post" action="'
. esc_url( admin_url( 'admin-post.php' ) ) . '" class="validate">' . "\n";
$html .= '<ul class="sortable-course-list">' . "\n";
foreach ( $all_course_ids as $course_id ) {
$course = get_post( $course_id );
if ( empty( $course ) || in_array( $course->post_status, array( 'trash', 'auto-draft' ), true ) ) {
$should_update_order = true;
continue;
}
$new_course_order[] = $course_id;
$title = $course->post_title;
if ( $course->post_status === 'draft' ) {
$title .= ' (Draft)';
}
$html .= '<li class="course"><span rel="' . esc_attr( $course->ID ) . '" style="width: 100%;"> ' . esc_html( $title ) . '</span></li>' . "\n";
}
$html .= '</ul>' . "\n";
$html .= '<input type="hidden" name="action" value="order_courses" />' . "\n";
$html .= wp_nonce_field( 'order_courses', '_wpnonce', true, false ) . "\n";
$html .= '<input type="hidden" name="course-order" value="' . esc_attr( $order_string ) . '" />' . "\n";
$html .= '<input type="submit" class="button-primary" value="' . esc_attr__( 'Save course order', 'sensei-lms' ) . '" />' . "\n";
$html .= '</form>';
}
echo wp_kses(
$html,
array_merge(
wp_kses_allowed_html( 'post' ),
array(
// Explicitly allow form tag for WP.com.
'form' => array(
'action' => array(),
'class' => array(),
'id' => array(),
'method' => array(),
),
'input' => array(
'class' => array(),
'name' => array(),
'type' => array(),
'value' => array(),
),
'span' => array(
'rel' => array(),
'style' => array(),
),
)
)
);
?>
</div>
<?php
if ( $should_update_order ) {
$this->save_course_order( implode( ',', $new_course_order ) );
}
}
public function get_course_order() {
return get_option( 'sensei_course_order', '' );
}
public function save_course_order( $order_string = '' ) {
global $wpdb;
$order = array();
$i = 1;
foreach ( explode( ',', $order_string ) as $course_id ) {
if ( $course_id ) {
$order[] = $course_id;
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Performance improvement.
$wpdb->query(
$wpdb->prepare(
"UPDATE $wpdb->posts SET menu_order = %d WHERE ID = %d",
$i,
absint( $course_id )
)
);
++$i;
}
}
update_option( 'sensei_course_order', implode( ',', $order ) );
return true;
}
/**
* Handle the POST request for reordering the Lessons.
*
* @since 1.12.2
*/
public function handle_order_lessons() {
check_admin_referer( 'order_lessons' );
$course_id = isset( $_POST['course_id'] ) ? intval( $_POST['course_id'] ) : 0;
if (
! current_user_can( 'edit_published_lessons' )
|| ! Sensei_Course::can_current_user_edit_course( $course_id )
) {
wp_die( esc_html__( 'Insufficient permissions', 'sensei-lms' ) );
}
if (
empty( $course_id )
|| empty( $_POST['lessons'] )
) {
_doing_it_wrong(
'handle_order_lessons',
'The handle_order_lessons AJAX call should be a POST request with parameters "course_id" and "lessons".',
'4.1.0'
);
wp_die();
}
$lessons_order = [];
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- The input is sanitized by hand.
foreach ( $_POST['lessons'] as $lesson_id => $lesson_data ) {
$lessons_order[ (int) $lesson_id ] = [
'module' => (int) $lesson_data['module'],
];
}
$ordered = $this->sync_lesson_order(
$lessons_order,
$course_id
);
wp_safe_redirect(
esc_url_raw(
add_query_arg(
array(
'page' => $this->lesson_order_page_slug,
'ordered' => $ordered,
'course_id' => $course_id,
),
admin_url( 'admin.php' )
)
)
);
exit;
}
/**
* Dsplay Lesson Order screen
*
* @return void
*/
public function lesson_order_screen() {
?>
<div id="<?php echo esc_attr( $this->lesson_order_page_slug ); ?>" class="wrap <?php echo esc_attr( $this->lesson_order_page_slug ); ?>">
<h1><?php esc_html_e( 'Order Lessons', 'sensei-lms' ); ?></h1>
<?php
$html = '';
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- No need to unslash or sanitize in this case.
if ( isset( $_GET['ordered'] ) && $_GET['ordered'] ) {
$html .= '<div class="updated fade">' . "\n";
$html .= '<p>' . esc_html__( 'The lesson order has been saved.', 'sensei-lms' ) . '</p>' . "\n";
$html .= '</div>' . "\n";
}
$args = array(
'post_type' => 'course',
'post_status' => array( 'publish', 'draft', 'future', 'private' ),
'posts_per_page' => -1,
'orderby' => 'name',
'order' => 'ASC',
);
// Ensure that the user either has permission to edit other's courses or is the author of the course.
if ( ! current_user_can( 'edit_others_courses' ) ) {
$args['author'] = get_current_user_id();
}
$courses = get_posts( $args );
$html .= '<form action="' . esc_url( admin_url( 'admin.php' ) ) . '" method="get">' . "\n";
$html .= '<input type="hidden" name="post_type" value="course" />' . "\n";
$html .= '<input type="hidden" name="page" value="lesson-order" />' . "\n";
$html .= '<select id="lesson-order-course" name="course_id">' . "\n";
$html .= '<option value="">' . esc_html__( 'Select a course', 'sensei-lms' ) . '</option>' . "\n";
foreach ( $courses as $course ) {
$course_id = '';
if ( isset( $_GET['course_id'] ) ) {
$course_id = intval( $_GET['course_id'] );
}
$html .= '<option value="' . esc_attr( intval( $course->ID ) ) . '" ' . selected( $course->ID, $course_id, false ) . '>' . esc_html( get_the_title( $course->ID ) ) . '</option>' . "\n";
}
$html .= '</select>' . "\n";
$html .= '<input type="submit" class="button-primary lesson-order-select-course-submit" value="' . esc_attr__( 'Select', 'sensei-lms' ) . '" />' . "\n";
$html .= '</form>' . "\n";
if ( isset( $_GET['course_id'] ) ) {
$course_id = intval( $_GET['course_id'] );
if ( $course_id > 0 ) {
$course_structure = $this->get_course_structure( $course_id );
$modules = $this->get_course_structure( $course_structure, 'module' );
$has_lessons = false;
// Form start.
$html .= '<form id="editgrouping" method="post" action="'
. esc_url( admin_url( 'admin-post.php' ) ) . '" class="validate">' . "\n";
foreach ( $modules as $module ) {
// Modules.
$html .= '<h3>' . esc_html( $module['title'] ) . '</h3>' . "\n";
$html .= '<ul class="sortable-lesson-list" data-module-id="' . esc_attr( $module['id'] ) . '">' . "\n";
if ( $module['lessons'] ) {
$has_lessons = true;
foreach ( $module['lessons'] as $lesson ) {
$html .= '<li class="lesson">';
$html .= '<span rel="' . esc_attr( $lesson['id'] ) . '" style="width: 100%;"> ' . esc_html( $lesson['title'] ) . '</span>';
$html .= '<input type="hidden" name="lessons[' . intval( $lesson['id'] ) . '][module]" value="' . intval( $module['id'] ) . '">';
$html .= '</li>' . "\n";
}
}
$html .= '</ul>' . "\n";
}
$other_lessons = $this->get_course_structure( $course_structure, 'lesson' );
$has_lessons = $has_lessons || $other_lessons;
if ( $has_lessons ) {
// Other Lessons (lessons not related to a module).
$html .= '<h3>' . esc_html__( 'Other Lessons', 'sensei-lms' ) . '</h3>' . "\n";
$html .= '<ul class="sortable-lesson-list" data-module-id="0">' . "\n";
foreach ( $other_lessons as $other_lesson ) {
$html .= '<li class="lesson"><span rel="' . esc_attr( $other_lesson['id'] ) . '" style="width: 100%;"> ' . esc_html( $other_lesson['title'] ) . '</span>';
$html .= '<input type="hidden" name="lessons[' . intval( $other_lesson['id'] ) . '][module]" value="">';
$html .= '</li>' . "\n";
}
$html .= '</ul>' . "\n";
// Form end.
$html .= '<input type="hidden" name="action" value="order_lessons" />' . "\n";
$html .= wp_nonce_field( 'order_lessons', '_wpnonce', true, false ) . "\n";
$html .= '<input type="hidden" name="course_id" value="' . esc_attr( $course_id ) . '" />' . "\n";
$html .= '<input type="submit" class="button-primary" value="' . esc_attr__( 'Save lesson order', 'sensei-lms' ) . '" />' . "\n";
$html .= '</form>';
} else {
$html .= '<p><em>' . esc_html__( 'There are no lessons in this course.', 'sensei-lms' ) . '</em></p>';
}
}
}
echo wp_kses(
$html,
array_merge(
wp_kses_allowed_html( 'post' ),
array(
// Explicitly allow form tag for WP.com.
'form' => array(
'action' => array(),
'class' => array(),
'id' => array(),
'method' => array(),
),
'input' => array(
'class' => array(),
'name' => array(),
'type' => array(),
'value' => array(),
),
'option' => array(
'selected' => array(),
'value' => array(),
),
'select' => array(
'id' => array(),
'name' => array(),
),
'span' => array(
'rel' => array(),
'style' => array(),
),
'ul' => array(
'class' => array(),
'data-module-id' => array(),
),
)
)
);
?>
</div>
<?php
}
/**
* Get lesson order.
*
* @deprecated 3.6.0
*
* @param integer $course_id Course ID.
*
* @return string Order string.
*/
public function get_lesson_order( $course_id = 0 ) {
_deprecated_function( __METHOD__, '3.6.0' );
$order_string = get_post_meta( $course_id, '_lesson_order', true );
return $order_string;
}
public function save_lesson_order( $order_string = '', $course_id = 0 ) {
/**
* It is safe to ignore nonce validation here, because this is called
* from `handle_order_lessons` and the nonce validation is done there.
*/
if ( $course_id ) {
remove_filter( 'get_terms', array( Sensei()->modules, 'append_teacher_name_to_module' ), 70 );
$course_structure = $this->get_course_structure( intval( $course_id ) );
add_filter( 'get_terms', array( Sensei()->modules, 'append_teacher_name_to_module' ), 70, 3 );
$order = array_map( 'absint', explode( ',', $order_string ) );
$course_structure = Sensei_Course_Structure::sort_structure( $course_structure, $order, 'lesson' );
// Sort module lessons.
foreach ( $course_structure as $key => $module ) {
if ( 'module' !== $module['type'] ) {
continue;
}
if (
// phpcs:ignore WordPress.Security.NonceVerification
! empty( $_POST[ 'lesson-order-module-' . $module['id'] ] )
&& ! empty( $course_structure[ $key ]['lessons'] )
) {
// phpcs:ignore WordPress.Security.NonceVerification
$order = sanitize_text_field( wp_unslash( $_POST[ 'lesson-order-module-' . $module['id'] ] ) );
$order = array_map( 'absint', explode( ',', $order ) );
$course_structure[ $key ]['lessons'] = Sensei_Course_Structure::sort_structure( $course_structure[ $key ]['lessons'], $order, 'lesson' );
}
}
if ( true === Sensei_Course_Structure::instance( $course_id )->save( $course_structure ) ) {
return true;
}
}
return false;
}
/**
* Sync the course lessons with a list of IDs.
*
* @since 4.1.0
*
* @param array $lesson_ids {
* Arguments that accompany the lesson ids.
*
* @type int $module The module ID of the lesson.
* }
* @param int $course_id
*
* @return bool
*/
private function sync_lesson_order( array $lesson_ids, int $course_id ): bool {
remove_filter( 'get_terms', array( Sensei()->modules, 'append_teacher_name_to_module' ), 70 );
$original_course_structure = $this->get_course_structure( $course_id );
add_filter( 'get_terms', array( Sensei()->modules, 'append_teacher_name_to_module' ), 70, 3 );
$lessons = [];
$modules = [];
// Extract the lessons from the course structure in preparation for the re-ordering.
foreach ( $original_course_structure as $item ) {
if ( 'module' === $item['type'] ) {
foreach ( $item['lessons'] as $lesson ) {
$lessons[ $lesson['id'] ] = $lesson;
}
$item['lessons'] = [];
$modules[ $item['id'] ] = $item;
} elseif ( 'lesson' === $item['type'] ) {
$lessons[ $item['id'] ] = $item;
}
}
// Map the lessons to the modules.
foreach ( $lesson_ids as $lesson_id => $lesson_data ) {
$module_id = (int) $lesson_data['module'];
if ( $module_id ) {
$modules[ $module_id ]['lessons'][] = $lessons[ $lesson_id ];
}
}
$reordered_course_structure = array_values( $modules );
// Map the lessons that don't belong to a module.
foreach ( $lesson_ids as $lesson_id => $lesson_data ) {
if ( ! $lesson_data['module'] ) {
$reordered_course_structure[] = $lessons[ $lesson_id ];
}
}
// Save the new course structure.
$saved = Sensei_Course_Structure::instance( $course_id )
->save( $reordered_course_structure );
return true === $saved;
}
/**
* Get or filter course structure for lesson ordering.
*
* @param int|array $course_structure Structure array or course ID to get the structure.
* @param null|string $type Optional type to filter the content.
*
* @return array Course structure.
*/
private function get_course_structure( $course_structure = null, $type = null ) {
$course_structure = is_array( $course_structure )
? $course_structure
: Sensei_Course_Structure::instance( $course_structure )->get( 'edit', wp_using_ext_object_cache() );
if ( isset( $type ) ) {
$course_structure = array_filter(
$course_structure,
function( $item ) use ( $type ) {
return $type === $item['type'];
}
);
}
return $course_structure;
}
function sensei_add_custom_menu_items() {
global $pagenow;
if ( 'nav-menus.php' == $pagenow ) {
add_meta_box( 'add-sensei-links', 'Sensei LMS', array( $this, 'wp_nav_menu_item_sensei_links_meta_box' ), 'nav-menus', 'side', 'low' );
}
}
function wp_nav_menu_item_sensei_links_meta_box( $object ) {
global $nav_menu_selected_id;
$menu_items = array(
'#senseicourses' => __( 'Courses', 'sensei-lms' ),
'#senseilessons' => __( 'Lessons', 'sensei-lms' ),
'#senseimycourses' => __( 'My Courses', 'sensei-lms' ),
'#senseilearnerprofile' => __( 'My Profile', 'sensei-lms' ),
'#senseimymessages' => __( 'My Messages', 'sensei-lms' ),
'#senseiloginlogout' => __( 'Login', 'sensei-lms' ) . '|' . __( 'Logout', 'sensei-lms' ),
);
$menu_items_obj = array();
foreach ( $menu_items as $value => $title ) {
$menu_items_obj[ $title ] = new stdClass();
$menu_items_obj[ $title ]->object_id = esc_attr( $value );
$menu_items_obj[ $title ]->title = esc_attr( $title );
$menu_items_obj[ $title ]->url = esc_attr( $value );
$menu_items_obj[ $title ]->description = 'description';
$menu_items_obj[ $title ]->db_id = 0;
$menu_items_obj[ $title ]->object = 'sensei';
$menu_items_obj[ $title ]->menu_item_parent = 0;
$menu_items_obj[ $title ]->type = 'custom';
$menu_items_obj[ $title ]->target = '';
$menu_items_obj[ $title ]->attr_title = '';
$menu_items_obj[ $title ]->classes = array();
$menu_items_obj[ $title ]->xfn = '';
}
$walker = new Walker_Nav_Menu_Checklist( array() );
?>
<div id="sensei-links" class="senseidiv taxonomydiv">
<div id="tabs-panel-sensei-links-all" class="tabs-panel tabs-panel-view-all tabs-panel-active">
<ul id="sensei-linkschecklist" class="list:sensei-links categorychecklist form-no-clear">
<?php echo walk_nav_menu_tree( array_map( 'wp_setup_nav_menu_item', $menu_items_obj ), 0, (object) array( 'walker' => $walker ) ); ?>
</ul>
</div>
<p class="button-controls">
<span class="add-to-menu">
<input type="submit"<?php disabled( $nav_menu_selected_id, 0 ); ?> class="button-secondary submit-add-to-menu right" value="<?php esc_attr_e( 'Add to Menu', 'sensei-lms' ); ?>" name="add-sensei-links-menu-item" id="submit-sensei-links" />
<span class="spinner"></span>
</span>
</p>
</div><!-- .senseidiv -->
<?php
}
/**
* Adding admin notice if the current
* installed theme is not compatible
*
* @return void
*/
public function theme_compatibility_notices() {
if ( isset( $_GET['sensei_hide_notice'] ) ) {
switch ( esc_attr( $_GET['sensei_hide_notice'] ) ) {
case 'menu_settings':
add_user_meta( get_current_user_id(), 'sensei_hide_menu_settings_notice', true );
break;
}
}
}
/**
* Hooked onto admin_init. Listens for install_sensei_pages and skip_install_sensei_pages query args
* on the sensei settings page.
*
* @deprecated 3.1.0 use Sensei()->setup_wizard->pages->create_pages() instead
*
* @since 1.8.7
*/
public static function install_pages() {
_deprecated_function( __METHOD__, '3.1.0', 'Sensei_Setup_Wizard_Pages::create_pages' );
}
/**
* Remove a course from course order option when trashed
*
* @since 1.9.8
* @param $new_status null|string
* @param $old_status null|string
* @param $post null|WP_Post
*/
public function remove_trashed_course_from_course_order( $new_status = null, $old_status = null, $post = null ) {
if ( empty( $new_status ) || empty( $old_status ) || $new_status === $old_status ) {
return;
}
if ( empty( $post ) || 'course' !== $post->post_type ) {
return;
}
if ( 'trash' === $new_status ) {
$order_string = $this->get_course_order();
if ( ! empty( $order_string ) ) {
$course_id = $post->ID;
$ordered_course_ids = array_map( 'intval', explode( ',', $order_string ) );
$course_id_position = array_search( $course_id, $ordered_course_ids );
if ( false !== $course_id_position ) {
array_splice( $ordered_course_ids, $course_id_position, 1 );
$this->save_course_order( implode( ',', $ordered_course_ids ) );
}
}
}
}
public function notify_if_admin_email_not_real_admin_user() {
$maybe_admin = get_user_by( 'email', get_bloginfo( 'admin_email' ) );
if ( false === $maybe_admin || false === user_can( $maybe_admin, 'manage_options' ) ) {
$general_settings_url = '<a href="' . esc_url( admin_url( 'options-general.php' ) ) . '">' . esc_html__( 'Settings > General', 'sensei-lms' ) . '</a>';
$add_new_user_url = '<a href="' . esc_url( admin_url( 'user-new.php' ) ) . '">' . esc_html__( 'add a new Administrator', 'sensei-lms' ) . '</a>';
$existing_administrators_link = '<a href="' . esc_url( admin_url( 'users.php?role=administrator' ) ) . '">' . esc_html__( 'existing Administrator', 'sensei-lms' ) . '</a>';
$current_setting = get_bloginfo( 'admin_email' );
/*
* translators: The %s placeholders are as follows:
*
* - A link to the General Settings page with the translated text "Settings > General".
* - A link to add an admin user with the translated text "add a new Administrator".
* - The current admin email address from the Settings.
* - A link to view the existing admin users, with the translated text "existing Administrator".
*/
$warning = __( 'To prevent issues with Sensei LMS module names, your Email Address in %1$s should also belong to an Administrator user. You can either %2$s with the email address %3$s, or change that email address to match the email of an %4$s.', 'sensei-lms' );
?>
<div id="message" class="error sensei-message sensei-connect">
<p>
<strong>
<?php printf( esc_html( $warning ), wp_kses_post( $general_settings_url ), wp_kses_post( $add_new_user_url ), esc_html( $current_setting ), wp_kses_post( $existing_administrators_link ) ); ?>
</strong>
</p>
</div>
<?php
}
}
/**
* Attempt to log a Sensei event.
*
* @since 2.1.0
* @access private
*/
public function ajax_log_event() {
// phpcs:disable WordPress.Security.NonceVerification
if ( ! isset( $_REQUEST['event_name'] ) ) {
wp_die();
}
$event_name = $_REQUEST['event_name'];
$properties = isset( $_REQUEST['properties'] ) ? $_REQUEST['properties'] : [];
if ( is_string( $properties ) ) {
$properties = json_decode( stripslashes( $properties ), true );
}
// Set the source to js-event.
add_filter(
'sensei_event_logging_source',
function() {
return 'js-event';
}
);
sensei_log_event( $event_name, $properties );
// phpcs:enable WordPress.Security.NonceVerification
}
/**
* Set `window.sensei.pluginUrl` to be used from javascript.
*
* @since 4.5.0
* @access private
*/
public function sensei_set_plugin_url() {
$screen = get_current_screen();
if ( ! $screen ) {
return;
}
$screens = [
'course',
'lesson',
Sensei_Home::SCREEN_ID,
'admin_page_' . Sensei_Setup_Wizard::instance()->page_slug,
];
if ( in_array( $screen->id, $screens, true ) ) {
?>
<script>
window.sensei = window.sensei || {};
window.sensei.pluginUrl = '<?php echo esc_url( Sensei()->plugin_url ); ?>';
window.sensei.isCourseThemeInstalled = <?php echo wp_get_theme( 'course' )->exists() ? 'true' : 'false'; ?>;
</script>
<?php
}
}
}
/**
* Legacy Class WooThemes_Sensei_Admin
*
* @ignore only for backward compatibility
* @since 1.9.0
* @ignore
*/
class WooThemes_Sensei_Admin extends Sensei_Admin{ }