Source: includes/class-sensei-settings.php

<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}

/*
 * Sensei Settings Class
 *
 * All functionality pertaining to the settings in Sensei.
 *
 * @package Core
 * @author Automattic
 *
 * @since 1.0.0
 */
class Sensei_Settings extends Sensei_Settings_API {

	const VISITED_SECTIONS_OPTION_KEY = 'sensei_settings_sections_visited';

	/**
	 * Constructor.
	 *
	 * @access public
	 * @since 1.0.0
	 */
	public function __construct() {
		parent::__construct(); // Required in extended classes.

		add_action( 'init', array( __CLASS__, 'flush_rewrite_rules' ) );

		// Setup Admin Settings data
		if ( is_admin() ) {

			$this->has_tabs   = true;
			$this->name       = __( 'Sensei LMS Settings', 'sensei-lms' );
			$this->menu_label = __( 'Settings', 'sensei-lms' );
			$this->page_slug  = 'sensei-settings';

		}

		$this->register_hook_listener();
		$this->get_settings();

		// Log when settings are updated by the user.
		add_action( 'update_option_sensei-settings', [ $this, 'log_settings_update' ], 10, 2 );

		// Mark settings section as visited on ajax action received.
		add_action( 'wp_ajax_sensei_settings_section_visited', [ $this, 'mark_section_as_visited' ] );
	}

	/**
	 * Get settings value
	 *
	 * @since 1.9.0
	 * @param string $setting_name
	 * @return mixed
	 */
	public function get( $setting_name ) {

		if ( isset( $this->settings[ $setting_name ] ) ) {

			return $this->settings[ $setting_name ];

		}

		return false;
	}

	/**
	 * @since 1.9.0
	 *
	 * @param $setting
	 * @param $new_value
	 */
	public function set( $setting, $new_value ) {

		$settings             = $this->get_settings();
		$settings[ $setting ] = $new_value;

		// Update the cached setting.
		$this->settings[ $setting ] = $new_value;

		return update_option( $this->token, $settings );

	}

	/**
	 * Register the settings screen within the WordPress admin.
	 *
	 * @access public
	 * @since  1.0.0
	 */
	public function register_settings_screen() {
		$this->settings_version = Sensei()->version; // Use the global plugin version on this settings screen.
		$this->hook             = add_submenu_page(
			'sensei',
			$this->name,
			$this->menu_label,
			'manage_sensei',
			$this->page_slug,
			array( $this, 'settings_screen' )
		);

		if ( isset( $_GET['page'] ) && ( $_GET['page'] == $this->page_slug ) ) {
			add_action( 'admin_notices', array( $this, 'settings_errors' ) );
			add_action( 'admin_print_scripts', array( $this, 'enqueue_scripts' ) );
			add_action( 'admin_print_styles', array( $this, 'enqueue_styles' ) );
		}
	}

	/**
	 * Output the settings screen.
	 */
	public function settings_screen() {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification not required.
		$tab_name = sanitize_key( wp_unslash( $_GET['tab'] ?? '' ) );

		/**
		 * Filters content for the settings page.
		 *
		 * @hook  sensei_settings_content
		 * @since 4.12.0
		 *
		 * @param {string} $tab_name The tab slug.
		 *
		 * @return {string} Filtered tab content.
		 */
		$content = apply_filters( 'sensei_settings_content', $tab_name );

		if ( empty( $content ) ) {
			parent::settings_screen();
			return;
		}

		// Render content on the Emails settings page.
		Sensei()->assets->enqueue_style( 'wp-components' );
		?>
		<div id="woothemes-sensei" class="wrap <?php echo esc_attr( $this->token ); ?>">
			<div id="sensei-custom-navigation" class="sensei-custom-navigation">
				<div class="sensei-custom-navigation__heading">
					<div class="sensei-custom-navigation__title">
						<h1>
							<?php
							echo esc_html( $this->name );

							if ( '' !== $this->settings_version ) {
								echo ' <span class="version">' . esc_html( $this->settings_version ) . '</span>';
							}
							?>
						</h1>
					</div>
					<div class="sensei-custom-navigation__links">
						<?php $this->settings_tabs(); ?>
					</div>
				</div>
				<?php
					/**
					 * Fires after the navigation links are displayed.
					 *
					 * @hook  sensei_settings_after_links
					 * @since 4.12.0
					 *
					 * @param {string} $tab_name The tab slug.
					 */
					do_action( 'sensei_settings_after_links', $tab_name );
				?>
			</div>
			<?php
			// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			echo $content;
			?>
		</div>
		<?php
	}

	/**
	 * Add legacy options to alloptions if they don't exist.
	 *
	 * @since 3.0.1
	 * @deprecated 4.13.1
	 *
	 * @param array $alloptions All options that are preloaded by WordPress.
	 *
	 * @return array
	 */
	public function no_special_query_for_legacy_options( $alloptions ) {
		_deprecated_function( __METHOD__, '4.13.1' );

		return $alloptions;
	}

	/**
	 * Add settings sections.
	 *
	 * @access public
	 * @since  1.0.0
	 * @return void
	 */
	public function init_sections() {
		$sections = array();

		$sections['default-settings'] = array(
			'name'        => __( 'General', 'sensei-lms' ),
			'description' => __( 'Settings that apply to the entire plugin.', 'sensei-lms' ),
		);

		$sections['appearance-settings'] = array(
			'name'        => __( 'Appearance', 'sensei-lms' ),
			'description' => __( 'Settings that apply to the entire plugin.', 'sensei-lms' ),
			'badge'       => __( 'new', 'sensei-lms' ),
		);

		$sections['course-settings'] = array(
			'name'        => __( 'Courses', 'sensei-lms' ),
			'description' => __( 'Settings that apply to all Courses.', 'sensei-lms' ),
		);

		$sections['lesson-settings'] = array(
			'name'        => __( 'Lessons', 'sensei-lms' ),
			'description' => __( 'Settings that apply to all Lessons.', 'sensei-lms' ),
		);

		$sections['email-notification-settings'] = array(
			'name'        => __( 'Email Notifications', 'sensei-lms' ),
			'description' => __( 'Settings for email notifications sent from your site.', 'sensei-lms' ),
		);

		$sections['learner-profile-settings'] = array(
			'name'        => __( 'Student Profiles', 'sensei-lms' ),
			'description' => __( 'Settings for public Student Profiles.', 'sensei-lms' ),
		);

		/**
		 * Filters the woocommerce promo settings section.
		 *
		 * @since 4.1.0
		 *
		 * @hook  sensei_settings_woocommerce_hide  Hook used to hide woocommerce promo banner and section.
		 *
		 * @return {boolean}                        Returns a boolean value that defines if the woocommerce promo banner should be hidden.
		 */
		$hide_woocommerce_settings = apply_filters( 'sensei_settings_woocommerce_hide', false );
		if ( ! $hide_woocommerce_settings ) {
			$sections['woocommerce-settings'] = array(
				'name'        => __( 'WooCommerce', 'sensei-lms' ),
				'description' => __( 'Optional settings for WooCommerce functions.', 'sensei-lms' ),
			);
		}

		/**
		 * Filters the content drip promo settings section.
		 *
		 * @since 4.1.0
		 *
		 * @hook  sensei_settings_content_drip_hide  Hook used to hide content drip promo banner and section.
		 *
		 * @return {boolean}                        Returns a boolean value that defines if the content drip promo banner should be hidden.
		 */
		$hide_content_drip_settings = apply_filters( 'sensei_settings_content_drip_hide', false );
		if ( ! $hide_content_drip_settings ) {
			$sections['sensei-content-drip-settings'] = array(
				'name'        => __( 'Content Drip', 'sensei-lms' ),
				'description' => __( 'Optional settings for the Content Drip extension.', 'sensei-lms' ),
			);
		}

		$this->sections = apply_filters( 'sensei_settings_tabs', $sections );
	}

	/**
	 * Add settings fields.
	 *
	 * @access public
	 * @since  1.0.0
	 * @uses   Sensei_Utils::get_slider_types()
	 * @return void
	 */
	public function init_fields() {
		$pages_array          = $this->pages_array();
		$posts_per_page_array = array(
			'0'  => '0',
			'1'  => '1',
			'2'  => '2',
			'3'  => '3',
			'4'  => '4',
			'5'  => '5',
			'6'  => '6',
			'7'  => '7',
			'8'  => '8',
			'9'  => '9',
			'10' => '10',
			'11' => '11',
			'12' => '12',
			'13' => '13',
			'14' => '14',
			'15' => '15',
			'16' => '16',
			'17' => '17',
			'18' => '18',
			'19' => '19',
			'20' => '20',
		);
		$complete_settings    = array(
			'passed'   => __( 'Once all the course lessons have been completed', 'sensei-lms' ),
			'complete' => __( 'At any time (by clicking the \'Complete Course\' button)', 'sensei-lms' ),
		);
		$quiz_points_formats  = array(
			'none'     => __( "Don't show quiz question points", 'sensei-lms' ),
			'number'   => __( 'Number (e.g. 1. Default)', 'sensei-lms' ),
			'brackets' => __( 'Brackets (e.g. [1])', 'sensei-lms' ),
			'text'     => __( 'Text (e.g. Points: 1)', 'sensei-lms' ),
			'full'     => __( 'Text and Brackets (e.g. [Points: 1])', 'sensei-lms' ),
		);
		$fields               = array();

		$fields['access_permission'] = array(
			'name'        => __( 'Access Permissions', 'sensei-lms' ),
			'description' => __( 'Users must be logged in to view lesson content.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => true,
			'section'     => 'default-settings',
		);

		$fields['messages_disable'] = array(
			'name'        => __( 'Disable Private Messages', 'sensei-lms' ),
			'description' => __( 'Disable the private message functions between students and teachers.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => false,
			'section'     => 'default-settings',
		);

		$fields['course_page'] = array(
			'name'        => __( 'Course Archive Page', 'sensei-lms' ),
			'description' => __( 'The page to use to display courses. If you leave this blank the default custom post type archive will apply.', 'sensei-lms' ),
			'type'        => 'select',
			'default'     => 0,
			'section'     => 'default-settings',
			'required'    => 0,
			'options'     => $pages_array,
		);

		$fields['my_course_page'] = array(
			'name'        => __( 'My Courses Page', 'sensei-lms' ),
			'description' => __( 'The page to use to display the courses that a user is currently taking as well as the courses a user has complete.', 'sensei-lms' ),
			'type'        => 'select',
			'default'     => 0,
			'section'     => 'default-settings',
			'required'    => 0,
			'options'     => $pages_array,
		);

		$fields['course_completed_page'] = array(
			'name'        => __( 'Course Completed Page', 'sensei-lms' ),
			'description' => __( 'The page that is displayed after a student completes a course.', 'sensei-lms' ),
			'type'        => 'select',
			'default'     => 0,
			'section'     => 'default-settings',
			'required'    => 0,
			'options'     => $pages_array,
		);

		$fields['placeholder_images_enable'] = array(
			'name'        => __( 'Use placeholder images', 'sensei-lms' ),
			'description' => __( 'Output a placeholder image when no featured image has been specified for Courses and Lessons.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => false,
			'section'     => 'default-settings',
		);

		$fields['styles_disable']              = array(
			'name'        => __( 'Disable Sensei LMS Styles', 'sensei-lms' ),
			'description' => __( 'Prevent the frontend stylesheets from loading. This will remove the default styles for all Sensei LMS elements.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => false,
			'section'     => 'default-settings',
		);
		$fields['quiz_question_points_format'] = array(
			'name'        => __( 'Quiz question points format', 'sensei-lms' ),
			'description' => __( 'Set the quiz question points format', 'sensei-lms' ),
			'type'        => 'select',
			'default'     => 'number',
			'section'     => 'default-settings',
			'options'     => $quiz_points_formats,
		);

		$fields['js_disable'] = array(
			'name'        => __( 'Disable Sensei LMS Javascript', 'sensei-lms' ),
			'description' => __( 'Prevent the frontend javascript from loading. This affects the progress bars and the My Courses tabs.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => false,
			'section'     => 'default-settings',
		);

		$fields['sensei_video_embed_html_sanitization_disable'] = array(
			'name'        => __( 'Disable HTML security', 'sensei-lms' ),
			'description' => __( 'Allow any HTML tags in the Video Embed field. Warning: Enabling this may leave your site more vulnerable to XSS attacks', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => false,
			'section'     => 'default-settings',
		);

		$fields['sensei_delete_data_on_uninstall'] = array(
			'name'        => __( 'Delete data on uninstall', 'sensei-lms' ),
			'description' => __( 'Delete Sensei LMS data when the plugin is deleted. Once removed, this data cannot be restored.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => false,
			'section'     => 'default-settings',
		);

		$fields['course_completion'] = array(
			'name'        => __( 'Courses are complete:', 'sensei-lms' ),
			'description' => __( 'This will determine when courses are marked as complete.', 'sensei-lms' ),
			'type'        => 'select',
			'default'     => 'passed',
			'section'     => 'course-settings',
			'required'    => 0,
			'options'     => $complete_settings,
		);

		$fields['course_author'] = array(
			'name'        => __( 'Display Course Author', 'sensei-lms' ),
			'description' => __( 'Output the Course Author on Course archive and My Courses page.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => true,
			'section'     => 'course-settings',
		);

		$fields['my_course_amount'] = array(
			'name'        => __( 'My Courses Pagination', 'sensei-lms' ),
			'description' => __( 'The number of courses to output for the my courses page.', 'sensei-lms' ),
			'type'        => 'range',
			'default'     => '0',
			'section'     => 'course-settings',
			'required'    => 0,
			'options'     => $posts_per_page_array,
		);

		$fields['course_archive_image_enable'] = array(
			'name'        => __( 'Course Archive Image', 'sensei-lms' ),
			'description' => __( 'Output the Course Image on the Course Archive Page.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => true,
			'section'     => 'course-settings',
		);

		$fields['course_archive_image_width'] = array(
			'name'        => __( 'Image Width - Archive', 'sensei-lms' ),
			'description' => __( 'The width in pixels of the featured image for the Course Archive page.', 'sensei-lms' ),
			'type'        => 'text',
			'default'     => '100',
			'section'     => 'course-settings',
			'required'    => 0,
		);

		$fields['course_archive_image_height'] = array(
			'name'        => __( 'Image Height - Archive', 'sensei-lms' ),
			'description' => __( 'The height in pixels of the featured image for the Course Archive page.', 'sensei-lms' ),
			'type'        => 'text',
			'default'     => '100',
			'section'     => 'course-settings',
			'required'    => 0,
		);

		$fields['course_archive_image_hard_crop'] = array(
			'name'        => __( 'Image Hard Crop - Archive', 'sensei-lms' ),
			// translators: Placeholders are an opening and closing <a> tag linking to the documentation page.
			'description' => sprintf( __( 'After changing this setting, you may need to %1$sregenerate your thumbnails%2$s.', 'sensei-lms' ), '<a href="' . esc_url( 'http://wordpress.org/extend/plugins/regenerate-thumbnails/' ) . '">', '</a>' ),
			'type'        => 'checkbox',
			'default'     => false,
			'section'     => 'course-settings',
		);

		$fields['course_single_image_enable'] = array(
			'name'        => __( 'Single Course Image', 'sensei-lms' ),
			'description' => __( 'Output the Course Image on the Single Course Page.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => false,
			'section'     => 'course-settings',
		);

		$fields['course_single_image_width'] = array(
			'name'        => __( 'Image Width - Single', 'sensei-lms' ),
			'description' => __( 'The width in pixels of the featured image for the Course single post page.', 'sensei-lms' ),
			'type'        => 'text',
			'default'     => '100',
			'section'     => 'course-settings',
			'required'    => 0,
		);

		$fields['course_single_image_height'] = array(
			'name'        => __( 'Image Height - Single', 'sensei-lms' ),
			'description' => __( 'The height in pixels of the featured image for the Course single post page.', 'sensei-lms' ),
			'type'        => 'text',
			'default'     => '100',
			'section'     => 'course-settings',
			'required'    => 0,
		);

		$fields['course_single_image_hard_crop'] = array(
			'name'        => __( 'Image Hard Crop - Single', 'sensei-lms' ),
			// translators: Placeholders are an opening and closing <a> tag linking to the documentation page.
			'description' => sprintf( __( 'After changing this setting, you may need to %1$sregenerate your thumbnails%2$s.', 'sensei-lms' ), '<a href="' . esc_url( 'http://wordpress.org/extend/plugins/regenerate-thumbnails/' ) . '">', '</a>' ),
			'type'        => 'checkbox',
			'default'     => false,
			'section'     => 'course-settings',
		);

		$fields['course_archive_featured_enable'] = array(
			'name'        => __( 'Featured Courses Panel', 'sensei-lms' ),
			'description' => __( 'Output the Featured Courses Panel on the Course Archive Page.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => true,
			'section'     => 'course-settings',
		);

		$fields['course_archive_more_link_text'] = array(
			'name'        => __( 'More link text', 'sensei-lms' ),
			'description' => __( 'The text that will be displayed on the Course Archive for the more courses link.', 'sensei-lms' ),
			'type'        => 'text',
			'default'     => __( 'More', 'sensei-lms' ),
			'section'     => 'course-settings',
			'required'    => 0,
		);

		// Course Settings.
		$fields['sensei_learning_mode_all'] = array(
			'name'        => __( 'Learning Mode', 'sensei-lms' ),
			'description' => __( 'Show an immersive and distraction-free view for lessons and quizzes.', 'sensei-lms' ),
			'form'        => 'render_learning_mode_setting',
			'type'        => 'checkbox',
			'default'     => \Sensei()->install_version && version_compare( \Sensei()->install_version, '4.7.0', '>=' ),
			'section'     => 'appearance-settings',
		);

		// Course Settings.
		$fields['sensei_learning_mode_template'] = array(
			'name'        => __( 'Learning Mode Templates', 'sensei-lms' ),
			'description' => __( 'Choose a learning mode template that is most suited for your type of content and the style you want to offer to your students.', 'sensei-lms' ),
			'form'        => 'render_learning_mode_templates',
			'type'        => 'radio',
			'default'     => Sensei_Course_Theme_Template_Selection::DEFAULT_TEMPLATE_NAME,
			'section'     => 'appearance-settings',
			'options'     => $this->get_learning_mode_template_options(),
		);

		// Lesson Settings.
		$fields['lesson_comments'] = array(
			'name'        => __( 'Allow Comments for Lessons', 'sensei-lms' ),
			'description' => __( 'This will allow students to post comments on the single Lesson page, only student who have access to the Lesson will be allowed to comment.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => true,
			'section'     => 'lesson-settings',
		);

		$fields['lesson_author'] = array(
			'name'        => __( 'Display Lesson Author', 'sensei-lms' ),
			'description' => __( 'Output the Lesson Author on Course single page & Lesson archive page.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => true,
			'section'     => 'lesson-settings',
		);

		$fields['course_lesson_image_enable'] = array(
			'name'        => __( 'Course Lesson Images', 'sensei-lms' ),
			'description' => __( 'Output the Lesson Image on the Single Course Page.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => false,
			'section'     => 'lesson-settings',
		);

		$fields['lesson_archive_image_width'] = array(
			'name'        => __( 'Image Width - Course Lessons', 'sensei-lms' ),
			'description' => __( 'The width in pixels of the featured image for the Lessons on the Course Single page.', 'sensei-lms' ),
			'type'        => 'text',
			'default'     => '100',
			'section'     => 'lesson-settings',
			'required'    => 0,
		);

		$fields['lesson_archive_image_height'] = array(
			'name'        => __( 'Image Height - Course Lessons', 'sensei-lms' ),
			'description' => __( 'The height in pixels of the featured image for the Lessons on the Course Single page.', 'sensei-lms' ),
			'type'        => 'text',
			'default'     => '100',
			'section'     => 'lesson-settings',
			'required'    => 0,
		);

		$fields['lesson_archive_image_hard_crop'] = array(
			'name'        => __( 'Image Hard Crop - Course Lessons', 'sensei-lms' ),
			// translators: Placeholders are an opening and closing <a> tag linking to the documentation page.
			'description' => sprintf( __( 'After changing this setting, you may need to %1$sregenerate your thumbnails%2$s.', 'sensei-lms' ), '<a href="' . esc_url( 'http://wordpress.org/extend/plugins/regenerate-thumbnails/' ) . '">', '</a>' ),
			'type'        => 'checkbox',
			'default'     => false,
			'section'     => 'lesson-settings',
		);

		$fields['lesson_single_image_enable'] = array(
			'name'        => __( 'Single Lesson Images', 'sensei-lms' ),
			'description' => __( 'Output the Lesson Image on the Single Lesson Page.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => false,
			'section'     => 'lesson-settings',
		);

		$fields['lesson_single_image_width'] = array(
			'name'        => __( 'Image Width - Single', 'sensei-lms' ),
			'description' => __( 'The width in pixels of the featured image for the Lessons single post page.', 'sensei-lms' ),
			'type'        => 'text',
			'default'     => '100',
			'section'     => 'lesson-settings',
			'required'    => 0,
		);

		$fields['lesson_single_image_height'] = array(
			'name'        => __( 'Image Height - Single', 'sensei-lms' ),
			'description' => __( 'The height in pixels of the featured image for the Lessons single post page.', 'sensei-lms' ),
			'type'        => 'text',
			'default'     => '100',
			'section'     => 'lesson-settings',
			'required'    => 0,
		);

		$fields['lesson_single_image_hard_crop'] = array(
			'name'        => __( 'Image Hard Crop - Single', 'sensei-lms' ),
			// translators: Placeholders are an opening and closing <a> tag linking to the documentation page.
			'description' => sprintf( __( 'After changing this setting, you may need to %1$sregenerate your thumbnails%2$s.', 'sensei-lms' ), '<a href="' . esc_url( 'http://wordpress.org/extend/plugins/regenerate-thumbnails/' ) . '">', '</a>' ),
			'type'        => 'checkbox',
			'default'     => false,
			'section'     => 'lesson-settings',
		);

		// Learner Profile settings
		$profile_url_base    = apply_filters( 'sensei_learner_profiles_url_base', __( 'learner', 'sensei-lms' ) );
		$profile_url_example = trailingslashit( get_home_url() ) . $profile_url_base . '/%username%';

		$fields['learner_profile_enable'] = array(
			'name'        => __( 'Public student profiles', 'sensei-lms' ),
			// translators: Placeholder is a profile URL example.
			'description' => sprintf( __( 'Enable public student profiles that will be accessible to everyone. Profile URL format: %s', 'sensei-lms' ), $profile_url_example ),
			'type'        => 'checkbox',
			'default'     => true,
			'section'     => 'learner-profile-settings',
		);

		$fields['learner_profile_show_courses'] = array(
			'name'        => __( 'Show student\'s courses', 'sensei-lms' ),
			'description' => __( 'Display the student\'s active and completed courses on their profile.', 'sensei-lms' ),
			'type'        => 'checkbox',
			'default'     => true,
			'section'     => 'learner-profile-settings',
		);

		// Email notifications
		$learner_email_options = array(
			'learner-graded-quiz'      => __( 'Their quiz is graded (auto and manual grading)', 'sensei-lms' ),
			'learner-completed-course' => __( 'They complete a course', 'sensei-lms' ),
		);

		$teacher_email_options = array(
			'teacher-started-course'   => __( 'A student starts their course', 'sensei-lms' ),
			'teacher-completed-course' => __( 'A student completes their course', 'sensei-lms' ),
			'teacher-completed-lesson' => __( 'A student completes a lesson', 'sensei-lms' ),
			'teacher-quiz-submitted'   => __( 'A student submits a quiz for grading', 'sensei-lms' ),
			'teacher-new-message'      => __( 'A student sends a private message to a teacher', 'sensei-lms' ),
		);

		$global_email_options = array(
			'new-message-reply' => __( 'They receive a reply to their private message', 'sensei-lms' ),
		);

		$fields['email_learners'] = array(
			'name'        => __( 'Emails Sent to Students', 'sensei-lms' ),
			'description' => __( 'Select the notifications that will be sent to students.', 'sensei-lms' ),
			'type'        => 'multicheck',
			'options'     => $learner_email_options,
			'defaults'    => array( 'learner-graded-quiz', 'learner-completed-course' ),
			'section'     => 'email-notification-settings',
		);

		$fields['email_teachers'] = array(
			'name'        => __( 'Emails Sent to Teachers', 'sensei-lms' ),
			'description' => __( 'Select the notifications that will be sent to teachers.', 'sensei-lms' ),
			'type'        => 'multicheck',
			'options'     => $teacher_email_options,
			'defaults'    => array( 'teacher-completed-course', 'teacher-started-course', 'teacher-quiz-submitted', 'teacher-new-message' ),
			'section'     => 'email-notification-settings',
		);

		$fields['email_global'] = array(
			'name'        => __( 'Emails Sent to All Users', 'sensei-lms' ),
			'description' => __( 'Select the notifications that will be sent to all users.', 'sensei-lms' ),
			'type'        => 'multicheck',
			'options'     => $global_email_options,
			'defaults'    => array( 'new-message-reply' ),
			'section'     => 'email-notification-settings',
		);

		$fields['email_from_name'] = array(
			'name'        => __( '"From" Name', 'sensei-lms' ),
			'description' => __( 'The name from which all emails will be sent.', 'sensei-lms' ),
			'type'        => 'text',
			'default'     => get_bloginfo( 'name' ),
			'section'     => 'email-notification-settings',
			'required'    => 1,
		);

		$fields['email_from_address'] = array(
			'name'        => __( '"From" Address', 'sensei-lms' ),
			'description' => __( 'The address from which all emails will be sent.', 'sensei-lms' ),
			'type'        => 'email',
			'default'     => get_bloginfo( 'admin_email' ),
			'section'     => 'email-notification-settings',
			'required'    => 1,
		);

		$fields['email_reply_to_name'] = [
			'name'     => __( '"Reply To" Name', 'sensei-lms' ),
			'type'     => 'input',
			'default'  => '',
			'section'  => 'email-notification-settings',
			'required' => 0,
		];

		$fields['email_reply_to_address'] = [
			'name'     => __( '"Reply To" Address', 'sensei-lms' ),
			'type'     => 'email',
			'default'  => get_bloginfo( 'admin_email' ),
			'section'  => 'email-notification-settings',
			'required' => 0,
		];

		$fields['email_cc'] = array(
			'name'          => __( 'CC', 'sensei-lms' ),
			'description'   => __( 'Enter email addresses to CC on all emails. Separate multiple email addresses with commas.', 'sensei-lms' ),
			'type'          => 'email',
			'multiple'      => true,
			'default'       => '',
			'section'       => 'email-notification-settings',
			'required'      => 0,
			'error_message' => __( 'One or more of the email addresses entered for CC is invalid.', 'sensei-lms' ),
		);

		$fields['email_bcc'] = array(
			'name'          => __( 'BCC', 'sensei-lms' ),
			'description'   => __( 'Enter email addresses to BCC on all emails. Separate multiple email addresses with commas.', 'sensei-lms' ),
			'type'          => 'email_list',
			'type'          => 'email',
			'multiple'      => true,
			'default'       => '',
			'section'       => 'email-notification-settings',
			'required'      => 0,
			'error_message' => __( 'One or more of the email addresses entered for BCC is invalid.', 'sensei-lms' ),
		);

		$fields['email_header_image'] = array(
			'name'        => __( 'Header Image', 'sensei-lms' ),
			// translators: Placeholders are opening and closing <a> tags linking to the media uploader.
			'description' => sprintf( __( 'Enter a URL to an image you want to show in the email\'s header. Upload your image using the %1$smedia uploader%2$s.', 'sensei-lms' ), '<a href="' . admin_url( 'media-new.php' ) . '">', '</a>' ),
			'type'        => 'text',
			'default'     => '',
			'section'     => 'email-notification-settings',
			'required'    => 0,
		);

		$fields['email_footer_text'] = array(
			'name'        => __( 'Email Footer Text', 'sensei-lms' ),
			'description' => __( 'The text to appear in the footer of Sensei LMS emails.', 'sensei-lms' ),
			'type'        => 'textarea',
			// translators: Placeholder is the blog name.
			'default'     => sprintf( __( '%1$s - Powered by Sensei LMS', 'sensei-lms' ), get_bloginfo( 'name' ) ),
			'section'     => 'email-notification-settings',
			'required'    => 0,
		);

		$fields['email_base_color'] = array(
			'name'        => __( 'Base Colour', 'sensei-lms' ),
			// translators: Placeholders are opening and closing <code> tags.
			'description' => sprintf( __( 'The base colour for Sensei LMS email templates. Default %1$s#557da1%2$s.', 'sensei-lms' ), '<code>', '</code>' ),
			'type'        => 'color',
			'default'     => '#557da1',
			'section'     => 'email-notification-settings',
			'required'    => 1,
		);

		$fields['email_background_color'] = array(
			'name'        => __( 'Background Colour', 'sensei-lms' ),
			// translators: Placeholders are opening and closing <code> tags.
			'description' => sprintf( __( 'The background colour for Sensei LMS email templates. Default %1$s#f5f5f5%2$s.', 'sensei-lms' ), '<code>', '</code>' ),
			'type'        => 'color',
			'default'     => '#f5f5f5',
			'section'     => 'email-notification-settings',
			'required'    => 1,
		);

		$fields['email_body_background_color'] = array(
			'name'        => __( 'Body Background Colour', 'sensei-lms' ),
			// translators: Placeholders are opening and closing <code> tags.
			'description' => sprintf( __( 'The main body background colour for Sensei LMS email templates. Default %1$s#fdfdfd%2$s.', 'sensei-lms' ), '<code>', '</code>' ),
			'type'        => 'color',
			'default'     => '#fdfdfd',
			'section'     => 'email-notification-settings',
			'required'    => 1,
		);

		$fields['email_text_color'] = array(
			'name'        => __( 'Body Text Colour', 'sensei-lms' ),
			// translators: Placeholders are opening and closing <code> tags.
			'description' => sprintf( __( 'The main body text colour for Sensei LMS email templates. Default %1$s#505050%2$s.', 'sensei-lms' ), '<code>', '</code>' ),
			'type'        => 'color',
			'default'     => '#505050',
			'section'     => 'email-notification-settings',
			'required'    => 1,
		);

		$this->fields = apply_filters( 'sensei_settings_fields', $fields );

	}

	/**
	 * Get options for the learning mode templates.
	 */
	private function get_learning_mode_template_options() {
		return Sensei_Course_Theme_Template_Selection::get_templates();
	}

	/**
	 * Get options for the duration fields.
	 *
	 * @since  1.0.0
	 * @param  $include_milliseconds (default: true) Whether or not to include milliseconds between 0 and 1.
	 * @return array Options between 0.1 and 10 seconds.
	 */
	private function get_duration_options( $include_milliseconds = true ) {
		$numbers = array( '1.0', '1.5', '2.0', '2.5', '3.0', '3.5', '4.0', '4.5', '5.0', '5.5', '6.0', '6.5', '7.0', '7.5', '8.0', '8.5', '9.0', '9.5', '10.0' );
		$options = array();

		if ( true == (bool) $include_milliseconds ) {
			$milliseconds = array( '0.1', '0.2', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9' );
			foreach ( $milliseconds as $k => $v ) {
				$options[ $v ] = $v;
			}
		} else {
			$options['0.5'] = '0.5';
		}

		foreach ( $numbers as $k => $v ) {
			$options[ $v ] = $v;
		}

		return $options;
	}

	/**
	 * Return an array of pages.
	 *
	 * @access private
	 * @since  1.0.0
	 * @return array
	 */
	private function pages_array() {
		if ( ! is_admin() ) {
			return [];
		}

		// REFACTOR - Transform this into a field type instead.
		// Setup an array of portfolio gallery terms for a dropdown.
		$pages_dropdown = wp_dropdown_pages(
			array(
				'echo'         => 0,
				'hierarchical' => 1,
				'sort_column'  => 'post_title',
				'sort_order'   => 'ASC',
			)
		);
		$page_items     = array();

		// Quick string hack to make sure we get the pages with the indents.
		$pages_dropdown = str_replace( "<select class='' name='page_id' id='page_id'>", '', $pages_dropdown );
		$pages_dropdown = str_replace( '</select>', '', $pages_dropdown );
		$pages_split    = explode( '</option>', $pages_dropdown );

		$page_items[] = __( 'Select a Page:', 'sensei-lms' );

		foreach ( $pages_split as $k => $v ) {
			$id = '';
			// Get the ID value.
			preg_match( '/value="(.*?)"/i', $v, $matches );

			if ( isset( $matches[1] ) ) {
				$id                = $matches[1];
				$page_items[ $id ] = trim( strip_tags( $v ) );
			}
		}

		$pages_array = $page_items;

		return $pages_array;
	}

	/**
	 * Flush the rewrite rules after the settings have been updated.
	 * This is to ensure that the proper permalinks are set up for archive pages.
	 *
	 * @since 1.9.0
	 */
	public static function flush_rewrite_rules() {

		/*
		 * Skipping nonce check because it is already done by WordPress for the Settings page.
		 * phpcs:disable WordPress.Security.NonceVerification
		 */
		if ( isset( $_POST['option_page'] ) && 'sensei-settings' === $_POST['option_page']
			&& isset( $_POST['action'] ) && 'update' === $_POST['action'] ) {
			// phpcs:enable WordPress.Security.NonceVerification

			Sensei()->initiate_rewrite_rules_flush();

		}

	}

	/**
	 * Logs settings update from the Settings form.
	 *
	 * @access private
	 * @since 2.1.0
	 *
	 * @param array $old_value The old settings value.
	 * @param array $value     The new settings value.
	 */
	public function log_settings_update( $old_value, $value ) {
		// Only process user-initiated settings updates.
		if ( ! ( 'POST' === $_SERVER['REQUEST_METHOD'] && ! defined( 'REST_REQUEST' ) && 'options' === get_current_screen()->id ) ) {
			return;
		}

		// Find changed fields.
		$changed = [];
		foreach ( $this->fields as $field => $field_config ) {
			// Handle absent fields.
			$old_field_value = isset( $old_value[ $field ] ) ? $old_value[ $field ] : '';
			$new_field_value = isset( $value[ $field ] ) ? $value[ $field ] : '';

			// phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison -- Loose comparison is okay for checking for changes.
			if ( $new_field_value != $old_field_value ) {
				// Create an array for this section of settings if needed.
				$section = $field_config['section'];
				if ( ! isset( $changed[ $section ] ) ) {
					$changed[ $section ] = [];
				}

				// Get changed setting values to be logged. In most cases, this
				// will be an array containing only the name of the field.
				$changed_values      = $this->get_changed_setting_values(
					$field,
					$new_field_value,
					$old_field_value
				);
				$changed[ $section ] = array_merge( $changed[ $section ], $changed_values );
			}
		}

		// Log changed sections.
		foreach ( $changed as $section => $fields ) {
			if ( empty( $fields ) ) {
				continue;
			}

			sensei_log_event(
				'settings_update',
				[
					'view'     => $section,
					'settings' => implode( ',', $fields ),
				]
			);
		}
	}

	/**
	 * Get an array of setting values which were changed. In most cases, this
	 * will simply be the name of the setting. However, if the setting is an
	 * array of strings, then this will return an array of the string values
	 * that were changed. These values returned will be of the form
	 * "field_name[value_name]".
	 *
	 * @since 2.1.0
	 *
	 * @param string $field     The name of the setting field.
	 * @param array  $new_value The new value.
	 * @param array  $old_value The old value.
	 *
	 * @return array The array of strings representing the field that was
	 *               changed, or an array containing the field name.
	 */
	private function get_changed_setting_values( $field, $new_value, $old_value ) {
		// If the old and new values are not arrays, return the field name.
		if ( ! is_array( $new_value ) || ! is_array( $old_value ) ) {
			return [ $field ];
		}

		// Now, make sure they are both string arrays.
		foreach ( array_merge( $new_value, $old_value ) as $value ) {
			if ( ! is_string( $value ) ) {
				return [ $field ];
			}
		}

		// We have two string arrays. Return the difference in their values.
		$added   = array_diff( $new_value, $old_value );
		$removed = array_diff( $old_value, $new_value );

		return array_filter( array_merge( $added, $removed ) );
	}

	/**
	 * Learning mode setting.
	 *
	 * @param array $args The field arguments.
	 */
	public function render_learning_mode_setting( $args ) {
		$options = $this->get_settings();
		$key     = $args['key'];
		$value   = $options[ $key ];

		$color_customizer_url = '';
		if ( ! function_exists( 'wp_is_block_theme' ) || ! wp_is_block_theme() ) {
			$color_customizer_url = Sensei_Course_Theme::get_learning_mode_customizer_url();
		}

		?>
		<label for="<?php echo esc_attr( $key ); ?>">
			<input id="<?php echo esc_attr( $key ); ?>" name="<?php echo esc_attr( "{$this->token}[{$key}]" ); ?>" type="checkbox" value="1" <?php checked( $value, '1' ); ?> />
			<?php esc_html_e( 'Enable for all courses', 'sensei-lms' ); ?>
		</label>
		<p>
			<span class="description"><?php echo esc_html( $args['data']['description'] ); ?></span>
		</p>

		<?php if ( $color_customizer_url ) : ?>
			<p class="extra-content">
				<a href="<?php echo esc_url( $color_customizer_url ); ?>">
					<?php esc_html_e( 'Customize Colors', 'sensei-lms' ); ?>
				</a>
			</p>
		<?php endif; ?>

		<?php
	}

	/**
	 * Renders the Learning Mode template selection setting.
	 *
	 * @param array $args The field arguments.
	 */
	public function render_learning_mode_templates( $args ) {
		$settings = $this->get_settings();
		$key      = $args['key'];
		$value    = $settings[ $key ];
		?>
			<p> <?php echo esc_html( $args['data']['description'] ); ?></p>
			<ul class="sensei-lm-block-template__options" id="sensei-lm-block-template__options">
			<?php foreach ( $args['data']['options'] as $template ) : ?>
				<?php
					$upsell   = isset( $template->upsell ) ? $template->upsell : false;
					$title    = isset( $template->title ) || empty( $template->title ) ? $template->title : $template->name;
					$disabled = (bool) $upsell;
				?>
				<li class="sensei-lm-block-template__option">
					<label class="sensei-lm-block-template__option-label">
						<div class="sensei-lm-block-template__option-header">
							<input
								type="radio"
								name="<?php echo esc_attr( "{$this->token}[{$key}]" ); ?>"
								value="<?php echo esc_attr( $template->name ); ?>"
								<?php disabled( true, $disabled, true ); ?>
								<?php checked( $template->name, $value, true ); ?>
							/>
							<h4 class="sensei-lm-block-template__option-title <?php echo $disabled ? 'sensei-lm-block-template__option-title--disabled' : ''; ?>">
								<?php echo esc_html( $title ); ?>
							</h4>
							<?php if ( $disabled ) : ?>
								<a href="<?php echo esc_attr( $upsell['url'] ); ?>" target="_blank" rel="noopener">
									<?php echo esc_attr( $upsell['title'] ); ?>
								</a>
							<?php endif; ?>
						</div>

						<img alt="<?php esc_attr( $template->title ); ?>" src="<?php echo esc_attr( $template->screenshots['thumbnail'] ); ?>" />
					</label>
				</li>
			<?php endforeach; ?>
			</ul>
		<?php

		$inline_data = [
			'name'         => "{$this->token}[{$key}]",
			'value'        => $value,
			'options'      => $args['data']['options'],
			'customizeUrl' => Sensei_Course_Theme::get_learning_mode_fse_url(),
			'formId'       => "{$this->token}-form",
			'section'      => $args['data']['section'],
		];
		Sensei()->assets->enqueue( 'learning-mode-templates-styles', 'course-theme/learning-mode-templates/styles.css', [ 'wp-components' ] );
		Sensei()->assets->enqueue( 'learning-mode-templates-script', 'course-theme/learning-mode-templates/index.js', [ 'wp-element', 'wp-components' ], true );
		wp_add_inline_script(
			'learning-mode-templates-script',
			sprintf( 'window.sensei = window.sensei || {}; window.sensei.learningModeTemplateSetting = %s;', wp_json_encode( $inline_data ) ),
			'before'
		);
	}

	/**
	 * Enqueue javascript.
	 */
	public function enqueue_scripts() {
		parent::enqueue_scripts();

		// Generate nonce for marking section visited action.
		wp_add_inline_script(
			'sensei-settings',
			'window.senseiSettingsSectionVisitNonce  = "' . wp_create_nonce( 'sensei-mark-settings-section-visited' ) . '";',
			'before'
		);
	}

	/**
	 * Marks the given section as visited.
	 */
	public function mark_section_as_visited() {
		if ( isset( $_POST['nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'sensei-mark-settings-section-visited' ) ) {
			if ( isset( $_POST['section_id'] ) ) {
				$section_id = sanitize_key( $_POST['section_id'] );
				$visited    = get_option( self::VISITED_SECTIONS_OPTION_KEY, [] );
				if ( ! in_array( $section_id, $visited, true ) ) {
					$visited[] = $section_id;
					update_option( self::VISITED_SECTIONS_OPTION_KEY, $visited );
				}
			}
		}
	}
}

/**
 * Class WooThemes_Sensei_Settings
 *
 * @ignore only for backward compatibility
 * @since 1.9.0
 */
class WooThemes_Sensei_Settings extends Sensei_Settings{}