diff --git a/.github/actions/install-php/action.yml b/.github/actions/install-php/action.yml index 5c800c9bd8..26267332d5 100644 --- a/.github/actions/install-php/action.yml +++ b/.github/actions/install-php/action.yml @@ -25,7 +25,7 @@ runs: id: composer-cache run: | echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index db6887a996..ec4d38f5ff 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -7,7 +7,7 @@ jobs: hookdocs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: npm install, and build docs run: | npm ci diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f0e196b23c..676310ef6d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,6 @@ name: Plugin Build -on: +on: - pull_request - workflow_call @@ -9,12 +9,12 @@ jobs: name: Plugin Build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Get npm cache directory id: npm-cache run: | echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ${{ steps.npm-cache.outputs.dir }} key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }} @@ -29,7 +29,7 @@ jobs: - name: Decompress plugin run: unzip sensei-lms.zip -d sensei-lms - name: Store Artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: sensei-lms-${{ github.event.pull_request.head.sha }} path: ${{ github.workspace }}/sensei-lms/ @@ -58,7 +58,7 @@ jobs: php: [7.4, 8.2] steps: - name: Download Build Artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: sensei-lms-${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/changelogger.yml b/.github/workflows/changelogger.yml index cee4232bdc..553566e1d6 100644 --- a/.github/workflows/changelogger.yml +++ b/.github/workflows/changelogger.yml @@ -17,7 +17,7 @@ jobs: name: Changelogger used runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/actions/deepen-to-merge-base - uses: ./.github/actions/install-php - name: Check change files are touched for touched projects diff --git a/.github/workflows/dependencies-report.yml b/.github/workflows/dependencies-report.yml index a163e69ef8..dfe5e8704b 100644 --- a/.github/workflows/dependencies-report.yml +++ b/.github/workflows/dependencies-report.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest name: Build trunk for Dependencies Report steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: trunk - shell: bash @@ -16,7 +16,7 @@ jobs: id: trunk-commit run: | echo "commit=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - - uses: actions/cache@v3 + - uses: actions/cache@v4 id: dist-cache with: path: ${{ github.workspace }}/assets/dist @@ -27,7 +27,7 @@ jobs: if: steps.dist-cache.outputs.cache-hit != 'true' run: | echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - uses: actions/cache@v3 + - uses: actions/cache@v4 if: steps.dist-cache.outputs.cache-hit != 'true' with: path: ${{ steps.npm-cache.outputs.dir }} @@ -44,7 +44,7 @@ jobs: run: npm run build:combine-assets if: steps.dist-cache.outputs.cache-hit != 'true' - name: Upload Artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: combined-assets path: ${{ github.workspace }}/assets/dist @@ -53,12 +53,12 @@ jobs: name: WordPress Dependencies Report runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Get npm cache directory id: npm-cache run: | echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ${{ steps.npm-cache.outputs.dir }} key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }} @@ -69,7 +69,7 @@ jobs: - name: Build Combined Assets run: npm run build:combine-assets - name: Download assets (trunk) - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: combined-assets path: dist-trunk diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 39143fa671..2aca6daac9 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -19,17 +19,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: ~/.npm/ key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: node_modules/ key: ${{ runner.os }}-node-modules-${{ hashFiles('package-lock.json') }} - + - name: Install PHP dependencies uses: ./.github/actions/install-php @@ -44,7 +44,7 @@ jobs: run: npx playwright install --with-deps chromium - name: Start wp-env - run: npm run wp-env start + run: npm run wp-env start - name: Run Playwright tests with @setup annotation run: CI=true npm run test:e2e:setup-only @@ -53,7 +53,7 @@ jobs: run: CI=true npm run test:e2e - name: Archive report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: playwright-report diff --git a/.github/workflows/gardening.yml b/.github/workflows/gardening.yml index 5fe4f3dfdb..92978795bd 100644 --- a/.github/workflows/gardening.yml +++ b/.github/workflows/gardening.yml @@ -14,12 +14,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 22 - name: Wait for prior instances of the workflow to finish uses: softprops/turnstyle@v1 diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml index 41ec78db03..829448a1c4 100644 --- a/.github/workflows/js.yml +++ b/.github/workflows/js.yml @@ -9,12 +9,12 @@ jobs: name: JS Linting runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 with: path: ~/.npm/ key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: node_modules/ key: ${{ runner.os }}-node-modules-${{ hashFiles('package-lock.json') }} @@ -29,12 +29,12 @@ jobs: name: TypeScript Checking runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 with: path: ~/.npm/ key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: node_modules/ key: ${{ runner.os }}-node-modules-${{ hashFiles('package-lock.json') }} @@ -48,8 +48,8 @@ jobs: runs-on: ubuntu-latest steps: # clone the repository - - uses: actions/checkout@v3 - - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index cf8768247d..4ac795faab 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -10,16 +10,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Get cached composer directories - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cache/composer/ key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: vendor/ key: ${{ runner.os }}-vendor-${{ hashFiles('composer.lock') }} @@ -84,14 +84,14 @@ jobs: options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Get cached composer directories - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cache/composer/ key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: vendor/ key: ${{ runner.os }}-vendor-${{ hashFiles('composer.lock') }} diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml index 27f31f34ae..20d12b9079 100644 --- a/.github/workflows/psalm.yml +++ b/.github/workflows/psalm.yml @@ -19,16 +19,16 @@ jobs: php: ['7.4', '8.2'] steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Get cached composer directories - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cache/composer/ key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }} - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: path: vendor/ key: ${{ runner.os }}-vendor-${{ hashFiles('composer.lock') }} diff --git a/.github/workflows/release-checklist.yml b/.github/workflows/release-checklist.yml index 300f954deb..a78fc01105 100644 --- a/.github/workflows/release-checklist.yml +++ b/.github/workflows/release-checklist.yml @@ -14,7 +14,7 @@ jobs: name: Sensei release checklist steps: - name: Checkout - uses: actions/checkout@v1 + uses: actions/checkout@v4 - name: Checklist uses: automattic/contextual-qa-checklist-action@master with: diff --git a/.github/workflows/report-build.yml b/.github/workflows/report-build.yml index 2cc69600c2..b0f64b2240 100644 --- a/.github/workflows/report-build.yml +++ b/.github/workflows/report-build.yml @@ -11,7 +11,7 @@ jobs: name: Report Plugin Build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Publish commit status with the link to download the plugin id: plugin_artifact uses: Automattic/github-action-report-artifact@v0 diff --git a/assets/blocks/quiz/style.scss b/assets/blocks/quiz/style.scss new file mode 100644 index 0000000000..75fb36a177 --- /dev/null +++ b/assets/blocks/quiz/style.scss @@ -0,0 +1,3 @@ +.sensei-quiz-action--hidden { + display: none; +} diff --git a/changelog/add-filter-student-management-teacher-ids b/changelog/add-filter-student-management-teacher-ids new file mode 100644 index 0000000000..80c5047049 --- /dev/null +++ b/changelog/add-filter-student-management-teacher-ids @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Allow additional users to manage students diff --git a/changelog/fix-co-teachers-get-students b/changelog/fix-co-teachers-get-students new file mode 100644 index 0000000000..e998138638 --- /dev/null +++ b/changelog/fix-co-teachers-get-students @@ -0,0 +1,4 @@ +Significance: development +Type: added + +Filter course IDs associated with a given teacher diff --git a/changelog/fix-course-horizontal-scroll-with-course-theme b/changelog/fix-course-horizontal-scroll-with-course-theme new file mode 100644 index 0000000000..2fa06e932f --- /dev/null +++ b/changelog/fix-course-horizontal-scroll-with-course-theme @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fix horizontal scroll on Course page when using Course theme diff --git a/changelog/fix-early-translations b/changelog/fix-early-translations new file mode 100644 index 0000000000..ac7a4062f1 --- /dev/null +++ b/changelog/fix-early-translations @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Fix loading some translations too early which generates a warning on WP 6.7 diff --git a/changelog/fix-prevent-rendering-lesson-actions-in-content-drip b/changelog/fix-prevent-rendering-lesson-actions-in-content-drip new file mode 100644 index 0000000000..b28679a3fe --- /dev/null +++ b/changelog/fix-prevent-rendering-lesson-actions-in-content-drip @@ -0,0 +1,4 @@ +Significance: minor +Type: development + +Added a hook to conditionally render the lesson actions in the frontend diff --git a/changelog/fix-timed-quiz-input-not-stopping-when-time-ends b/changelog/fix-timed-quiz-input-not-stopping-when-time-ends new file mode 100644 index 0000000000..dc84758801 --- /dev/null +++ b/changelog/fix-timed-quiz-input-not-stopping-when-time-ends @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Timed quiz not stopping when time ends diff --git a/config/psalm/psalm-baseline.xml b/config/psalm/psalm-baseline.xml index 02fb9eb024..63500b38af 100644 --- a/config/psalm/psalm-baseline.xml +++ b/config/psalm/psalm-baseline.xml @@ -1789,9 +1789,8 @@ $user_id $which - + $file - $name $page_slug @@ -2422,18 +2421,6 @@ get_queried_object()->ID get_queried_object()->post_type - - $course - $lesson - $messages - $question - $quiz - WooThemes_Sensei_PostTypes - WooThemes_Sensei_PostTypes - WooThemes_Sensei_PostTypes - WooThemes_Sensei_PostTypes - WooThemes_Sensei_PostTypes - $post instanceof WP_Post @@ -4181,11 +4168,9 @@ global $sensei_email_data; - - $heading + $learner $recipient - $subject $teacher $template @@ -4432,8 +4417,7 @@ $post $post - - get_edit_post_link( $post ) + get_edit_post_link( $post->ID ) @@ -4970,13 +4954,6 @@ - - WP_HTTP - - - $result[ $student_id ][ $course_id ] - $result[ $student_id ][ $course_id ] - $edit_course_cap @@ -5013,8 +4990,7 @@ WP_HTTP WP_HTTP - - $result[ $user_id ][ $course_id ] + $result[ $user_id ][ $course_id ] @@ -5249,12 +5225,6 @@ - - $post_args - - - $post_args - $question_categories diff --git a/includes/3rd-party/themes/astra.php b/includes/3rd-party/themes/astra.php index fea5a4d201..311c2391e2 100644 --- a/includes/3rd-party/themes/astra.php +++ b/includes/3rd-party/themes/astra.php @@ -12,7 +12,7 @@ function sensei_load_learning_mode_styles_for_astra_theme() { $course_id = Sensei_Utils::get_current_course(); $is_target_theme = 'astra' === strtolower( wp_get_theme()->get_template() ); - if ( empty( $course_id ) || ! $is_target_theme ) { + if ( empty( $course_id ) || ! $is_target_theme || 'course' === get_post_type() ) { return false; } diff --git a/includes/3rd-party/themes/course.php b/includes/3rd-party/themes/course.php index 1b7e1d257d..70c3ecf35b 100644 --- a/includes/3rd-party/themes/course.php +++ b/includes/3rd-party/themes/course.php @@ -12,7 +12,7 @@ function sensei_load_learning_mode_style_for_course_theme() { $course_id = Sensei_Utils::get_current_course(); $is_course_theme = 'course' === wp_get_theme()->get_template(); - if ( empty( $course_id ) || ! $is_course_theme ) { + if ( empty( $course_id ) || ! $is_course_theme || 'course' === get_post_type() ) { return false; } diff --git a/includes/3rd-party/themes/divi.php b/includes/3rd-party/themes/divi.php index 357c4542d0..e63dc1fd43 100644 --- a/includes/3rd-party/themes/divi.php +++ b/includes/3rd-party/themes/divi.php @@ -97,7 +97,7 @@ function sensei_load_learning_mode_style_for_divi_theme() { $course_id = Sensei_Utils::get_current_course(); $is_target_theme = 'divi' === strtolower( wp_get_theme()->get_template() ); - if ( empty( $course_id ) || ! $is_target_theme ) { + if ( empty( $course_id ) || ! $is_target_theme || 'course' === get_post_type() ) { return false; } diff --git a/includes/admin/class-sensei-learner-management.php b/includes/admin/class-sensei-learner-management.php index d14dbf3412..72b2daf56a 100644 --- a/includes/admin/class-sensei-learner-management.php +++ b/includes/admin/class-sensei-learner-management.php @@ -24,30 +24,27 @@ * @since 1.3.0 */ class Sensei_Learner_Management { - /** - * Name of the menu/page. - * - * @var string $name - */ - public $name; /** * Main plugin file name. * * @var string $file */ public $file; + /** * Menu slug name. * * @var string $page_slug */ public $page_slug; + /** * Reference to the class responsible for Bulk Learner Actions. * * @var Sensei_Learners_Admin_Bulk_Actions_Controller $bulk_actions_controller */ public $bulk_actions_controller; + /** * Per page screen option ID. * @@ -63,7 +60,6 @@ class Sensei_Learner_Management { * @param string $file Main plugin file name. */ public function __construct( $file ) { - $this->name = __( 'Students', 'sensei-lms' ); $this->file = $file; $this->page_slug = 'sensei_learners'; @@ -99,6 +95,22 @@ public function __construct( $file ) { } } + /** + * Graceful fallback for deprecated properties. + * + * @since $$next-version$$ + * + * @param string $key The key to get. + * + * @return mixed + */ + public function __get( $key ) { + if ( 'name' === $key ) { + _doing_it_wrong( __CLASS__ . '->name', 'The "name" property is deprecated. Use get_name() instead.', '$$next-version$$' ); + + return $this->get_name(); + } + } /** * Add custom navigation to the admin pages. @@ -148,8 +160,8 @@ public function learners_admin_menu() { if ( current_user_can( 'manage_sensei_grades' ) ) { $learners_page = add_submenu_page( 'sensei', - $this->name, - $this->name, + $this->get_name(), + $this->get_name(), 'manage_sensei_grades', $this->page_slug, array( $this, 'learners_page' ) @@ -274,7 +286,6 @@ public function load_data_table_files() { foreach ( $classes_to_load as $class_file ) { Sensei()->load_class( $class_file ); } - } /** @@ -320,7 +331,6 @@ public function learners_page() { } else { require __DIR__ . '/views/html-admin-page-students-main.php'; } - } /** @@ -340,7 +350,6 @@ public function learners_headers( $args = array( 'nav' => 'default' ) ) { * @hook sensei_learners_after_headers */ do_action( 'sensei_learners_after_headers' ); - } /** @@ -482,14 +491,15 @@ public function edit_date_started() { exit( '' ); } - // validate we can edit date. - $may_edit_date = false; + $post_type = get_post_type( $post_id ); - if ( current_user_can( 'manage_sensei' ) || get_current_user_id() === (int) $post->post_author ) { - $may_edit_date = true; + if ( 'lesson' === $post_type ) { + $can_edit_date = $this->can_user_manage_students( (int) Sensei()->lesson->get_course_id( $post_id ), intval( $post->post_author ) ); + } else { + $can_edit_date = $this->can_user_manage_students( $post_id, intval( $post->post_author ) ); } - if ( ! $may_edit_date ) { + if ( ! $can_edit_date ) { exit( '' ); } @@ -557,28 +567,28 @@ public function handle_user_async_action( $action ) { $post = get_post( intval( $action_data['post_id'] ) ); - if ( empty( $post ) ) { + if ( empty( $post ) || ! is_a( $post, 'WP_Post' ) ) { exit( '' ); } - // validate the user. - $may_remove_user = false; + $can_remove_user = false; + $post_id = $action_data['post_id'] ? intval( $action_data['post_id'] ) : 0; + $post_type = $action_data['post_type'] ? sanitize_text_field( $action_data['post_type'] ) : ''; - // Only teachers and admins can remove users. - if ( current_user_can( 'manage_sensei' ) || get_current_user_id() === intval( $post->post_author ) ) { - $may_remove_user = true; + if ( 'lesson' === $post_type ) { + $can_remove_user = $this->can_user_manage_students( (int) Sensei()->lesson->get_course_id( $post_id ), intval( $post->post_author ) ); + } else { + $can_remove_user = $this->can_user_manage_students( $post_id, intval( $post->post_author ) ); } - if ( ! is_a( $post, 'WP_Post' ) || ! $may_remove_user ) { + if ( ! $can_remove_user ) { exit( '' ); } - if ( $action_data['user_id'] && $action_data['post_id'] && $action_data['post_type'] ) { - $user_id = intval( $action_data['user_id'] ); - $post_id = intval( $action_data['post_id'] ); - $post_type = sanitize_text_field( $action_data['post_type'] ); + if ( $action_data['user_id'] && $post_id && $post_type ) { + $user_id = intval( $action_data['user_id'] ); + $user = get_userdata( $user_id ); - $user = get_userdata( $user_id ); if ( false === $user ) { exit( '' ); } @@ -648,22 +658,22 @@ public function handle_learner_actions() { exit; } - $user_id = intval( $_GET['user_id'] ); $course_id = intval( $_GET['course_id'] ); $post = get_post( $course_id ); - $may_manage_enrolment = false; - - // Only teachers and admins can enrol and withdraw users. - if ( current_user_can( 'manage_sensei' ) || get_current_user_id() === intval( $post->post_author ) ) { - $may_manage_enrolment = true; + if ( ! is_a( $post, 'WP_Post' ) ) { + wp_safe_redirect( esc_url_raw( $failed_redirect_url ) ); + exit; } - if ( ! is_a( $post, 'WP_Post' ) || ! $may_manage_enrolment ) { + $can_manage_enrolment = $this->can_user_manage_students( $course_id, intval( $post->post_author ) ); + + if ( ! $can_manage_enrolment ) { wp_safe_redirect( esc_url_raw( $failed_redirect_url ) ); exit; } + $user_id = intval( $_GET['user_id'] ); $course_enrolment = Sensei_Course_Enrolment::get_course_instance( $course_id ); $result = false; @@ -697,6 +707,39 @@ public function remove_user_from_post() { $this->handle_user_async_action( 'remove' ); } + /** + * Check if a user can manage a student's course. + * + * @param int $course_id Course ID. + * @param int $post_author Author ID for the course (i.e. teacher ID). + * + * @return bool true if the current user can manage a student's course. + */ + private function can_user_manage_students( int $course_id, int $post_author ): bool { + $allowed_ids = []; + $can_manage_student = false; + + /** + * Filter the user IDs that have permission to manage students in a given course. + * + * @since $$next-version$$ + * + * @hook sensei_learners_allowed_user_ids + * + * @param {int[]} $user_ids User IDs that have permission to manage students in a given course. + * Defaults to post author. + * @param {int} $course_id Course ID. + * @return {int[]} Filtered user IDs that have permission to manage students in a given course. + */ + $allowed_ids = apply_filters( 'sensei_learners_allowed_user_ids', [ $post_author ], $course_id ); + + if ( current_user_can( 'manage_sensei' ) || in_array( get_current_user_id(), $allowed_ids, true ) ) { + $can_manage_student = true; + } + + return $can_manage_student; + } + /** * Searches for a Learner by name or username. */ @@ -773,18 +816,12 @@ public function add_new_learners() { $post_type = $_POST['add_post_type']; $user_ids = array_map( 'intval', $_POST['add_user_id'] ); - $course_id = absint( $_POST['add_course_id'] ); - $lesson_id = isset( $_POST['add_lesson_id'] ) ? $_POST['add_lesson_id'] : ''; + $course_id = intval( $_POST['add_course_id'] ); + $lesson_id = intval( $_POST['add_lesson_id'] ); + $course = get_post( $course_id ); $results = []; - $course = get_post( $course_id ); - if ( - ! $course - || ( - ! current_user_can( 'manage_sensei' ) - && get_current_user_id() !== intval( $course->post_author ) - ) - ) { + if ( ! $course || ! $this->can_user_manage_students( $course_id, intval( $course->post_author ) ) ) { return $result; } @@ -944,9 +981,8 @@ public function get_url() { * @return string Name of the menu/page. */ public function get_name() { - return $this->name; + return __( 'Students', 'sensei-lms' ); } - } /** diff --git a/includes/admin/class-sensei-learners-admin-bulk-actions-controller.php b/includes/admin/class-sensei-learners-admin-bulk-actions-controller.php index 5606ad61ab..d77522355a 100644 --- a/includes/admin/class-sensei-learners-admin-bulk-actions-controller.php +++ b/includes/admin/class-sensei-learners-admin-bulk-actions-controller.php @@ -20,13 +20,6 @@ class Sensei_Learners_Admin_Bulk_Actions_Controller { const REMOVE_ENROLMENT = 'remove_enrolment'; const REMOVE_PROGRESS = 'remove_progress'; - /** - * The available bulk actions. - * - * @var array|null - */ - private $known_bulk_actions; - /** * The page slug. * @@ -55,20 +48,13 @@ class Sensei_Learners_Admin_Bulk_Actions_Controller { */ private $learner; - /** - * The name of the page - * - * @var string - */ - private $name; - /** * Get the name of the page. * * @return string|void */ public function get_name() { - return $this->name; + return __( 'Bulk Student Actions', 'sensei-lms' ); } /** @@ -117,16 +103,9 @@ public function learners_admin_menu() { public function __construct( $management, $learner ) { $this->learner_management = $management; $this->learner = $learner; - $this->name = __( 'Bulk Student Actions', 'sensei-lms' ); $this->page_slug = $management->page_slug; $this->view = 'sensei_learner_admin'; - $this->known_bulk_actions = [ - self::ENROL_RESTORE_ENROLMENT => __( 'Add to Course', 'sensei-lms' ), - self::REMOVE_ENROLMENT => __( 'Remove from Course', 'sensei-lms' ), - self::REMOVE_PROGRESS => __( 'Reset Progress', 'sensei-lms' ), - ]; - if ( is_admin() ) { $this->register_hooks(); } @@ -190,7 +169,11 @@ public function get_learner_management_course_url( $course_id ) { * @return array */ public function get_known_bulk_actions() { - $known_bulk_actions = $this->known_bulk_actions; + $known_bulk_actions = [ + self::ENROL_RESTORE_ENROLMENT => __( 'Add to Course', 'sensei-lms' ), + self::REMOVE_ENROLMENT => __( 'Remove from Course', 'sensei-lms' ), + self::REMOVE_PROGRESS => __( 'Reset Progress', 'sensei-lms' ), + ]; /** * Filter the known bulk actions. diff --git a/includes/class-sensei-analysis.php b/includes/class-sensei-analysis.php index 5da93aa95c..440844dfd3 100755 --- a/includes/class-sensei-analysis.php +++ b/includes/class-sensei-analysis.php @@ -11,19 +11,11 @@ * @since 1.0.0 */ class Sensei_Analysis { - /** * The reports' page slug. */ const PAGE_SLUG = 'sensei_reports'; - /** - * The reports' page name (title). - * - * @var string - */ - public $name; - /** * Reference to the main plugin file name. * @@ -38,7 +30,6 @@ class Sensei_Analysis { * @param string $file */ public function __construct( $file ) { - $this->name = __( 'Reports', 'sensei-lms' ); $this->file = $file; // Admin functions. @@ -67,14 +58,31 @@ public function __construct( $file ) { * * @param string $key The key to get. * - * @return string|void + * @return mixed */ public function __get( $key ) { if ( 'page_slug' === $key ) { - _doing_it_wrong( 'Sensei_Analysis->page_slug', 'The "page_slug" property is deprecated. Use the Sensei_Analysis::PAGE_SLUG constant instead.', '4.2.0' ); + _doing_it_wrong( __CLASS__ . '->page_slug', 'The "page_slug" property is deprecated. Use the Sensei_Analysis::PAGE_SLUG constant instead.', '4.2.0' ); return self::PAGE_SLUG; } + + if ( 'name' === $key ) { + _doing_it_wrong( __CLASS__ . '->name', 'The "name" property is deprecated. Use get_name() instead.', '$$next-version$$' ); + + return $this->get_name(); + } + } + + /** + * Get the screen name. + * + * @since $$next-version$$ + * + * @return string + */ + public function get_name() { + return __( 'Reports', 'sensei-lms' ); } /** @@ -144,7 +152,7 @@ private function display_reports_navigation() { * @param {string} $title The page title. * @return {string} Returns filtered page title. */ - $data = apply_filters( 'sensei_analysis_nav_title', $this->name ); + $data = apply_filters( 'sensei_analysis_nav_title', $this->get_name() ); ?>
@@ -175,8 +183,8 @@ public function analysis_admin_menu() { if ( current_user_can( 'manage_sensei_grades' ) ) { add_submenu_page( 'sensei', - $this->name, - $this->name, + $this->get_name(), + $this->get_name(), 'manage_sensei_grades', self::PAGE_SLUG, array( $this, 'analysis_page' ) @@ -581,7 +589,7 @@ public function wrapper_container( $which ) { */ public function analysis_default_nav() { _deprecated_function( __METHOD__, '4.2.0' ); - $title = $this->name; + $title = $this->get_name(); ?>

self::PAGE_SLUG, ); - $title = sprintf( '%s', esc_url( add_query_arg( $analysis_args, admin_url( 'admin.php' ) ) ), esc_html( $this->name ) ); + $title = sprintf( '%s', esc_url( add_query_arg( $analysis_args, admin_url( 'admin.php' ) ) ), esc_html( $this->get_name() ) ); if ( isset( $_GET['user_id'] ) && 0 < intval( $_GET['user_id'] ) ) { $user_id = intval( $_GET['user_id'] ); $url = esc_url( @@ -656,7 +664,7 @@ public function analysis_user_course_nav() { $analysis_args = array( 'page' => self::PAGE_SLUG, ); - $title = sprintf( '%s', esc_url( add_query_arg( $analysis_args, admin_url( 'admin.php' ) ) ), esc_html( $this->name ) ); + $title = sprintf( '%s', esc_url( add_query_arg( $analysis_args, admin_url( 'admin.php' ) ) ), esc_html( $this->get_name() ) ); if ( isset( $_GET['user_id'] ) && 0 < intval( $_GET['user_id'] ) ) { $user_id = intval( $_GET['user_id'] ); $user_data = get_userdata( $user_id ); @@ -711,7 +719,7 @@ public function analysis_course_nav() { $analysis_args = array( 'page' => self::PAGE_SLUG, ); - $title = sprintf( '%s', add_query_arg( $analysis_args, admin_url( 'admin.php' ) ), esc_html( $this->name ) ); + $title = sprintf( '%s', add_query_arg( $analysis_args, admin_url( 'admin.php' ) ), esc_html( $this->get_name() ) ); if ( isset( $_GET['course_id'] ) ) { $course_id = intval( $_GET['course_id'] ); $url = add_query_arg( @@ -753,7 +761,7 @@ public function analysis_course_users_nav() { $analysis_args = array( 'page' => self::PAGE_SLUG, ); - $title = sprintf( '%s', add_query_arg( $analysis_args, admin_url( 'admin.php' ) ), esc_html( $this->name ) ); + $title = sprintf( '%s', add_query_arg( $analysis_args, admin_url( 'admin.php' ) ), esc_html( $this->get_name() ) ); if ( isset( $_GET['course_id'] ) ) { $course_id = intval( $_GET['course_id'] ); $url = add_query_arg( @@ -795,7 +803,7 @@ public function analysis_lesson_users_nav() { $analysis_args = array( 'page' => self::PAGE_SLUG, ); - $title = sprintf( '%s', add_query_arg( $analysis_args, admin_url( 'admin.php' ) ), esc_html( $this->name ) ); + $title = sprintf( '%s', add_query_arg( $analysis_args, admin_url( 'admin.php' ) ), esc_html( $this->get_name() ) ); if ( isset( $_GET['lesson_id'] ) ) { $lesson_id = intval( $_GET['lesson_id'] ); $course_id = intval( get_post_meta( $lesson_id, '_lesson_course', true ) ); diff --git a/includes/class-sensei-customizer.php b/includes/class-sensei-customizer.php index d87ccc0925..57eeba6741 100644 --- a/includes/class-sensei-customizer.php +++ b/includes/class-sensei-customizer.php @@ -10,19 +10,22 @@ * Add customizer settings. */ class Sensei_Customizer { - /** - * Configurable colors. - * - * @var array[] + * Sensei_Customizer constructor. */ - private $colors; + public function __construct() { + add_action( 'customize_register', [ $this, 'add_customizer_settings' ] ); + add_action( 'customize_preview_init', [ $this, 'enqueue_customizer_helper' ] ); + add_action( 'wp_head', [ $this, 'output_custom_settings' ] ); + } /** - * Sensei_Customizer constructor. + * Get the configurable colors. + * + * @return array[] */ - public function __construct() { - $this->colors = [ + private function get_colors() { + return [ 'sensei-course-theme-primary-color' => [ 'label' => __( 'Primary Color', 'sensei-lms' ), 'default' => '#1e1e1e', @@ -36,16 +39,12 @@ public function __construct() { 'default' => '#1e1e1e', ], ]; - - add_action( 'customize_register', [ $this, 'add_customizer_settings' ] ); - add_action( 'customize_preview_init', [ $this, 'enqueue_customizer_helper' ] ); - add_action( 'wp_head', [ $this, 'output_custom_settings' ] ); } /** * Add Sensei section and settings to Customizer. * - * @param WP_Customize_Manager $wp_customize + * @param WP_Customize_Manager $wp_customize The WP_Customize_Manager instance. */ public function add_customizer_settings( WP_Customize_Manager $wp_customize ) { @@ -59,7 +58,7 @@ public function add_customizer_settings( WP_Customize_Manager $wp_customize ) { ] ); - foreach ( $this->colors as $variable => $settings ) { + foreach ( $this->get_colors() as $variable => $settings ) { $wp_customize->add_setting( $variable, @@ -84,7 +83,6 @@ public function add_customizer_settings( WP_Customize_Manager $wp_customize ) { ) ); } - } /** @@ -103,7 +101,7 @@ public function output_custom_settings() { $css = ''; - foreach ( $this->colors as $variable => $settings ) { + foreach ( $this->get_colors() as $variable => $settings ) { $value = get_option( $variable ); if ( $value && $value !== $settings['default'] ) { $css .= sprintf( "--%s: %s;\n", $variable, ( $value ) ); @@ -127,7 +125,7 @@ public function output_customizer_helper() { ?>