-
Notifications
You must be signed in to change notification settings - Fork 17
Moodle 45 #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Moodle 45 #49
Changes from all commits
b896597
b746976
f0705bc
b0d8ff3
50aa60d
c657ffd
50d2892
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| name: Moodle Plugin CI | ||
|
|
||
| on: [push, pull_request] | ||
|
|
||
| jobs: | ||
| test: | ||
| runs-on: ubuntu-22.04 | ||
|
|
||
| services: | ||
| postgres: | ||
| image: postgres:15 | ||
| env: | ||
| POSTGRES_USER: 'postgres' | ||
| POSTGRES_HOST_AUTH_METHOD: 'trust' | ||
| ports: | ||
| - 5432:5432 | ||
| options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3 | ||
|
|
||
| mariadb: | ||
| image: mariadb:10 | ||
| env: | ||
| MYSQL_USER: 'root' | ||
| MYSQL_ALLOW_EMPTY_PASSWORD: "true" | ||
| MYSQL_CHARACTER_SET_SERVER: "utf8mb4" | ||
| MYSQL_COLLATION_SERVER: "utf8mb4_unicode_ci" | ||
| ports: | ||
| - 3306:3306 | ||
| options: --health-cmd="mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 3 | ||
|
|
||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| include: | ||
| - php: '8.3' | ||
| # Main job. Run all checks that do not require setup and only need to be run once. | ||
| runchecks: 'all' | ||
| moodle-branch: 'MOODLE_405_STABLE' | ||
| database: 'mariadb' | ||
| - php: '8.4' | ||
| moodle-branch: 'MOODLE_500_STABLE' | ||
| database: 'mariadb' | ||
|
|
||
| steps: | ||
| - name: Check out repository code | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| path: plugin | ||
|
|
||
| - name: Setup PHP ${{ matrix.php }} | ||
| uses: shivammathur/setup-php@v2 | ||
| with: | ||
| php-version: ${{ matrix.php }} | ||
| extensions: ${{ matrix.extensions }} | ||
| ini-values: max_input_vars=5000 | ||
| # If you are not using code coverage, keep "none". Otherwise, use "pcov" (Moodle 3.10 and up) or "xdebug". | ||
| # If you try to use code coverage with "none", it will fallback to phpdbg (which has known problems). | ||
| coverage: none | ||
|
|
||
| - name: Initialise moodle-plugin-ci | ||
| run: | | ||
| composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^4 | ||
| echo $(cd ci/bin; pwd) >> $GITHUB_PATH | ||
| echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH | ||
| sudo locale-gen en_AU.UTF-8 | ||
| echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV | ||
|
|
||
| - name: Install moodle-plugin-ci | ||
| run: moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1 | ||
| env: | ||
| DB: ${{ matrix.database }} | ||
| MOODLE_BRANCH: ${{ matrix.moodle-branch }} | ||
| # Uncomment this to run Behat tests using the Moodle App. | ||
| # MOODLE_APP: 'true' | ||
|
|
||
| - name: PHP Lint | ||
| if: ${{ !cancelled() && matrix.runchecks == 'all' }} | ||
| run: moodle-plugin-ci phplint | ||
|
|
||
| - name: PHP Mess Detector | ||
| continue-on-error: true # This step will show errors but will not fail | ||
| if: ${{ !cancelled() && matrix.runchecks == 'all' }} | ||
| run: moodle-plugin-ci phpmd | ||
|
|
||
| - name: Moodle Code Checker | ||
| if: ${{ !cancelled() && matrix.runchecks == 'all' }} | ||
| run: moodle-plugin-ci phpcs --max-warnings 0 | ||
|
|
||
| - name: Moodle PHPDoc Checker | ||
| if: ${{ !cancelled() && matrix.runchecks == 'all' }} | ||
| run: moodle-plugin-ci phpdoc --max-warnings 0 | ||
|
|
||
| - name: Validating | ||
| if: ${{ !cancelled() }} | ||
| run: moodle-plugin-ci validate | ||
|
|
||
| - name: Check upgrade savepoints | ||
| if: ${{ !cancelled() && matrix.runchecks == 'all' }} | ||
| run: moodle-plugin-ci savepoints | ||
|
|
||
| - name: Mustache Lint | ||
| if: ${{ !cancelled() && matrix.runchecks == 'all' }} | ||
| run: moodle-plugin-ci mustache | ||
|
|
||
| - name: Grunt | ||
| if: ${{ !cancelled() && matrix.runchecks == 'all' }} | ||
| run: moodle-plugin-ci grunt --max-lint-warnings 0 | ||
|
|
||
| - name: PHPUnit tests | ||
| if: ${{ !cancelled() }} | ||
| run: moodle-plugin-ci phpunit --fail-on-warning | ||
|
|
||
| - name: Behat features | ||
| id: behat | ||
| if: ${{ !cancelled() }} | ||
| run: moodle-plugin-ci behat --profile chrome --scss-deprecations | ||
|
|
||
| - name: Upload Behat Faildump | ||
| if: ${{ failure() && steps.behat.outcome == 'failure' }} | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: Behat Faildump (${{ join(matrix.*, ', ') }}) | ||
| path: ${{ github.workspace }}/moodledata/behat_dump | ||
| retention-days: 7 | ||
| if-no-files-found: ignore | ||
|
|
||
| - name: Mark cancelled jobs as failed. | ||
| if: ${{ cancelled() }} | ||
| run: exit 1 | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -27,6 +27,29 @@ | |||||
| defined('MOODLE_INTERNAL') || die(); | ||||||
| require_once($CFG->libdir . '/completionlib.php'); | ||||||
|
|
||||||
| /** | ||||||
| * Other course completion condition. | ||||||
| * | ||||||
| * This plugin restricts access based on completion of OTHER COURSES (not activities). | ||||||
| * | ||||||
| * ARCHITECTURAL NOTE: | ||||||
| * The Moodle availability framework (core_availability) is designed to handle dependencies | ||||||
| * on course modules (activities), not courses. This creates a naming inconsistency: | ||||||
| * | ||||||
| * - JSON property is named "cm" (suggesting course module) but stores a COURSE ID | ||||||
| * - Internal property $courseid correctly identifies it as a course ID | ||||||
| * - The framework's update_dependency_id() expects 'course_modules' table references | ||||||
| * - This plugin checks the 'course_completions' table instead | ||||||
| * | ||||||
| * This means: | ||||||
| * 1. Backup/restore will NOT automatically remap course IDs (courses are global, not course-specific) | ||||||
| * 2. The "cm" property name is misleading but required for framework compatibility | ||||||
| * 3. Tests must create actual courses and course completions, not just activities | ||||||
| * | ||||||
| * @package availability_othercompleted | ||||||
| * @copyright MU DOT MY PLT <support@mu.my> | ||||||
| * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | ||||||
| */ | ||||||
| class condition extends \core_availability\condition { | ||||||
| /** @var int ID of course that this depends on */ | ||||||
| protected $courseid; | ||||||
|
|
@@ -35,7 +58,7 @@ class condition extends \core_availability\condition { | |||||
| protected $expectedcompletion; | ||||||
|
|
||||||
| /** @var array Array of modules used in these conditions for course */ | ||||||
| protected static $modsusedincondition = array(); | ||||||
| protected static $modsusedincondition = []; | ||||||
|
|
||||||
| /** | ||||||
| * Constructor. | ||||||
|
|
@@ -52,17 +75,29 @@ public function __construct($structure) { | |||||
| } | ||||||
|
|
||||||
| // Get expected completion. | ||||||
| if (isset($structure->e) && in_array($structure->e, | ||||||
| array(COMPLETION_COMPLETE, COMPLETION_INCOMPLETE))) { | ||||||
| if ( | ||||||
| isset($structure->e) && in_array( | ||||||
| $structure->e, | ||||||
| [COMPLETION_COMPLETE, COMPLETION_INCOMPLETE] | ||||||
| ) | ||||||
| ) { | ||||||
| $this->expectedcompletion = $structure->e; | ||||||
| } else { | ||||||
| throw new \coding_exception('Missing or invalid ->e for completion condition'); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Saves tree data back to a structure object. | ||||||
| * | ||||||
| * NOTE: Returns 'course' property containing a COURSE ID (not module ID). | ||||||
| * Constructor accepts 'cm' property for framework compatibility. | ||||||
| * | ||||||
| * @return \stdClass Structure object (ready to be made into JSON format) | ||||||
| */ | ||||||
| public function save() { | ||||||
| return (object)array('type' => 'othercompleted', | ||||||
| 'course' => $this->courseid, 'e' => $this->expectedcompletion); | ||||||
| return (object)['type' => 'othercompleted', | ||||||
| 'course' => $this->courseid, 'e' => $this->expectedcompletion]; | ||||||
|
Comment on lines
98
to
+100
|
||||||
| } | ||||||
|
|
||||||
| /** | ||||||
|
|
@@ -75,24 +110,43 @@ public function save() { | |||||
| * @param int $expectedcompletion Expected completion value (COMPLETION_xx) | ||||||
| */ | ||||||
| public static function get_json($courseid, $expectedcompletion) { | ||||||
| return (object)array('type' => 'othercompleted', 'course' => (int)$courseid, | ||||||
| 'e' => (int)$expectedcompletion); | ||||||
| return (object)['type' => 'othercompleted', 'course' => (int)$courseid, | ||||||
|
||||||
| return (object)['type' => 'othercompleted', 'course' => (int)$courseid, | |
| return (object)['type' => 'othercompleted', 'cm' => (int)$courseid, |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The is_available() method doesn't handle the $not parameter correctly for the expected completion state. The logic should first check if the actual completion matches the expected completion ($this->expectedcompletion), then apply the $not inversion.
Currently, the code always checks if the course is complete (regardless of $this->expectedcompletion), then applies the $not inversion. This means when expectedcompletion is COMPLETION_INCOMPLETE, the condition logic is incorrect.
Suggested fix:
// Check if course is completed (has a completion time set).
$iscomplete = ($completion && $completion->timecompleted > 0);
// Check if the completion state matches what we expect.
$allow = ($this->expectedcompletion === COMPLETION_COMPLETE) ? $iscomplete : !$iscomplete;
// Handle NOT condition.
if ($not) {
$allow = !$allow;
}
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The get_courses() function retrieves ALL courses in the system, which can be very inefficient and cause performance issues on sites with many courses. This is especially problematic since get_description() is called frequently when displaying availability conditions.
Recommended fix: Fetch only the specific course needed:
public function get_description($full, $not, \core_availability\info $info) {
global $DB;
// Get course name.
$course = $DB->get_record('course', ['id' => $this->courseid], 'fullname', IGNORE_MISSING);
$modname = $course ? $course->fullname : get_string('missingcourse', 'availability_othercompleted');
// ... rest of the method
}Note: You'll need to add a language string for 'missingcourse' to handle deleted courses gracefully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] This comment references Moodle 3.10 which is quite old. Since the plugin now requires Moodle 4.5+ (based on version.php line 28 requiring 2024100100), this comment should be updated to reflect current supported versions.
Suggested update:
# If you are not using code coverage, keep "none". Otherwise, use "pcov" (Moodle 4.0 and up).