diff --git a/.github/workflows/moodle-plugin-ci.yml b/.github/workflows/moodle-plugin-ci.yml new file mode 100644 index 00000000..f77a8dda --- /dev/null +++ b/.github/workflows/moodle-plugin-ci.yml @@ -0,0 +1,119 @@ +name: Moodle plugin CI +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - php: '8.0' + moodle-branch: 'master' + database: 'pgsql' + - php: '8.0' + moodle-branch: 'master' + database: 'mariadb' + + services: + postgres: + image: postgres:14 + env: + POSTGRES_USER: 'postgres' + POSTGRES_HOST_AUTH_METHOD: 'trust' + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 3 + ports: + - 5432:5432 + + 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 + + steps: + - name: Checkout + uses: actions/checkout@v3 + 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 + coverage: none + + - name: Deploy moodle-plugin-ci + run: | + composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3 + # Add dirs to $PATH + echo $(cd ci/bin; pwd) >> $GITHUB_PATH + echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH + # PHPUnit depends on en_AU.UTF-8 locale + sudo locale-gen en_AU.UTF-8 + echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV + + - name: Install Moodle + # Need explicit IP to stop mysql client fail on attempt to use unix socket. + run: moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1 + env: + DB: ${{ matrix.database }} + MOODLE_BRANCH: ${{ matrix.moodle-branch }} + IGNORE_PATHS: 'templates/local/mobile' + + - name: PHP Lint + if: ${{ always() }} + run: moodle-plugin-ci phplint + + - name: PHP Copy/Paste Detector + continue-on-error: true # This step will show errors but will not fail + if: ${{ always() }} + run: moodle-plugin-ci phpcpd + + - name: PHP Mess Detector + continue-on-error: true # This step will show errors but will not fail + if: ${{ always() }} + run: moodle-plugin-ci phpmd + + - name: Moodle Code Checker + if: ${{ always() }} + run: moodle-plugin-ci codechecker + + - name: Moodle PHPDoc Checker + if: ${{ always() }} + run: moodle-plugin-ci phpdoc + + - name: Validating + if: ${{ always() }} + run: moodle-plugin-ci validate + + - name: Check upgrade savepoints + if: ${{ always() }} + run: moodle-plugin-ci savepoints + + - name: Mustache Lint + if: ${{ always() }} + run: moodle-plugin-ci mustache + + - name: Grunt + if: ${{ always() }} + run: moodle-plugin-ci grunt --max-lint-warnings 0 + + - name: PHPUnit tests + if: ${{ always() }} + run: moodle-plugin-ci phpunit + + - name: Behat features + if: ${{ always() }} + run: moodle-plugin-ci behat --profile chrome diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 86c8878a..00000000 --- a/.travis.yml +++ /dev/null @@ -1,48 +0,0 @@ -language: php - -sudo: true - -addons: - firefox: "47.0.1" - postgresql: "9.4" - apt: - packages: - - openjdk-8-jre-headless - -cache: - directories: - - $HOME/.composer/cache - - $HOME/.npm - -php: - - 7.1 - -env: - global: - - MOODLE_BRANCH=MOODLE_37_STABLE - matrix: - - DB=pgsql - -before_install: - - phpenv config-rm xdebug.ini - - nvm install 8.9 - - nvm use 8.9 - - cd ../.. - - composer create-project -n --no-dev --prefer-dist blackboard-open-source/moodle-plugin-ci ci ^2 - - export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH" - -install: - - moodle-plugin-ci install - -script: - - moodle-plugin-ci phplint - #- moodle-plugin-ci phpcpd - #- moodle-plugin-ci phpmd - - moodle-plugin-ci codechecker - - moodle-plugin-ci validate - - moodle-plugin-ci savepoints - - moodle-plugin-ci mustache - - moodle-plugin-ci grunt - - moodle-plugin-ci phpdoc - - moodle-plugin-ci phpunit - #- moodle-plugin-ci behat diff --git a/README.txt b/README.md similarity index 83% rename from README.txt rename to README.md index 4fecc482..3d0cbd95 100644 --- a/README.txt +++ b/README.md @@ -1,4 +1,12 @@ -Appointment Scheduler for Moodle +moodle-mod_scheduler +==================== + + +[![Latest Release](https://img.shields.io/github/v/release/bostelm/moodle-mod_scheduler?sort=semver&color=orange)](https://github.com/bostelm/moodle-mod_scheduler/releases) +[![Moodle Plugin CI](https://github.com/bostelm/moodle-mod_scheduler/workflows/Moodle%20plugin%20CI/badge.svg?branch=master)](https://github.com/bostelm/moodle-mod_scheduler/actions?query=workflow%3A%22Moodle+plugin+CI%22+branch%3Amaster) + +Appointment +Scheduler for Moodle This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -13,7 +21,8 @@ GNU General Public License for more details: http://www.gnu.org/copyleft/gpl.html -=== Description === +Description +----------- The Scheduler module helps you to schedule appointments with your students. Teachers specify time slots for meetings, students then choose one of them on Moodle. @@ -21,24 +30,26 @@ Teacher in turn can record the outcome of the meeting - and optionally a grade - within the scheduler. For further information, please see: - http://docs.moodle.org/37/en/Scheduler_module + https://docs.moodle.org/en/Scheduler_module (Note that the information there may refer to a previous version of the module.) -=== Installation instructions === +Installation instructions +------------------------- Place the code of the module into the mod/scheduler directory of your Moodle directory root. That is, the present file should be located at: mod/scheduler/README.txt For further installation instructions please see: - http://docs.moodle.org/en/Installing_contributed_modules_or_plugins + https://docs.moodle.org/en/Installing_contributed_modules_or_plugins This module is intended for Moodle 3.7 and above. -=== Authors === +Authors +------- Current maintainer: Henning Bostelmann, University of York @@ -56,15 +67,23 @@ With further contributions taken from: * Various authors of the core Moodle code -=== Release notes === +Release notes +------------- + +### Version 4.0 ---- Version 3.7 --- +Intended for Moodle 4.0 and later. -New features / improvements: +This release has no major new features, but only adaptions to API changes +in Moodle 4.x, along with bugfixes. + +### Version 3.7 + +#### New features / improvements: Basic support for the completion API (completion on grade) -Feature changes: +#### Feature changes: The permissions / capabilities on the teacher side have been altered. In particular, teachers now strictly need the capability @@ -76,20 +95,19 @@ to edit the grades, the teacher notes, or the attended flag in all appointments of all teachers, in the appointment screen only. The configuration setting 'allteachersgrading' is no longer in use. ---- Version 3.5 --- +### Version 3.5 Intended for Moodle 3.5 and later. -New features / improvements: +#### New features / improvements: Support for Moodle's Privacy API. - ---- Version 3.3 --- +### Version 3.3 Intended for Moodle 3.3 and later. -New features / improvements: +#### New features / improvements: Optionally, before making an appointment, students now see a booking screen in which they need to enter text, upload a file, and/or solve a captcha. @@ -99,7 +117,7 @@ course fullname, and location fields. Export files can now include custom profile fields of students. -Feature changes: +#### Feature changes: For booking in groups, students now need to select explicitly which group they are booking for, or whether they want to make an individual booking. @@ -111,17 +129,17 @@ moodle/site:viewuseridentity is now required. When allowing an "unlimited" number of appointments, students will no longer be included in reminder e-mails if they have booked at least one slot. -Refactoring / API changes: +#### Refactoring / API changes: The function scheduler_get_user_fields() in customlib.php has changed signature. If you have customized it in an earlier version, you will want to edit your code. ---- Version 3.1 --- +### Version 3.1 Intended for Moodle 3.1 and later. -New features / improvements: +#### New features / improvements: An additional "confidential note" field is supplied for appointments; the contents can be read by teachers only. @@ -131,7 +149,7 @@ Slot notes and appointment notes can now contain attachments. Students can now be allowed to see existing bookings of other students. See https://docs.moodle.org/31/en/Scheduler_Module_capabilities#Student_side -Feature changes: +#### Feature changes: Sending of invitations and reminders is no longer handled via a "mailto" link but rather via a webform, using Moodle's messaging system. @@ -139,7 +157,7 @@ but rather via a webform, using Moodle's messaging system. The conflict detection feature (when creating new slots) has been reworked slightly. See https://docs.moodle.org/31/en/Scheduler:_Conflicts -Refactoring / API changes: +#### Refactoring / API changes: All email-related features now use the Messaging API. @@ -148,11 +166,11 @@ the Scheduled Tasks API. The new Search API is supported for the activity description only. ---- Version 2.9 --- +### Version 2.9 Intended for Moodle 2.9 and later. -New features / improvements: +#### New features / improvements: The export screen now allows users to choose the format of the output file, as well as the data fields to include in the export. File format may @@ -164,7 +182,7 @@ show up as such in the scheduler. Lists of students to be scheduled now take availability conditions (groups and groupings) into account. -Feature changes: +#### Feature changes: The handling of "group mode" in Scheduler has changed. The feature of "booking entire groups into a slot" is now controlled by a setting "Booking in groups" @@ -184,32 +202,32 @@ For using the Overview screen outside the current scheduler, e.g., for displayin all slots of a user across the site, users will now need extra permissions; see CONTRIB-5750 for details. -Refactoring / API changes: +#### Refactoring / API changes: Config settings have been migrated to the config_plugins table. ---- Version 2.7 --- +### Version 2.7 Intended for Moodle 2.7 and later. -New features: +#### New features: Students can now be allowed to book several slots at a time. "Volatile slots" replaced with "guard time" - students cannot change their booking for slots closer than this time to the current time. -Feature changes: +#### Feature changes: "Notes" field will now be shown to students at booking time. -Refactoring / API changes: +#### Refactoring / API changes: Major refactoring of teacher view (slot list), student view (booking screen), teacher view of individual appointments, as well as of the backend. Security enhancements (sessionid parameter now used throughout). Adapted to changes in core API and to the new logging/event system (Event 2). ---- Version 2.5 --- +### Version 2.5 Intended for Moodle 2.5 and later. @@ -217,16 +235,17 @@ Module adapted to API changes Moodle core. "Add slot" and "Edit slot" forms refactored, now based on Moodle Forms. Language packs migrated to AMOS, removed from plugin codebase. ---- Version 2.3 --- +### Version 2.3 Intended for Moodle 2.3 and later; no major functional changes, but API adapted and minor enhancements. ---- Version 2.0 --- +### Version 2.0 No major functional changes over 1.9; bug fixes and API migration only. Requires 1.9 for database upgrades. -=== Technical notes === +Technical notes +--------------- The code of this module is rather old, some of it still predates even Moodle 1.9. It has now largely, but not completely, been adapted to the new APIs. diff --git a/amd/build/studentid.min.js b/amd/build/studentid.min.js new file mode 100644 index 00000000..2f95f499 --- /dev/null +++ b/amd/build/studentid.min.js @@ -0,0 +1,10 @@ +/** + * Potential user selector module. + * + * @module mod_scheduler/studentid + * @copyright 2022 University of Glasgow + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define("mod_scheduler/studentid",["jquery","core/ajax","core/templates"],(function($,Ajax,Templates){return{processResults:function(selector,results){var users=[];return $.each(results,(function(index,user){users.push({value:user.id,label:user._label})})),users},transport:function(selector,query,success,failure){let scheduler=$(selector).attr("scheduler")||null,groupids=$(selector).attr("groupids")||null;Ajax.call([{methodname:"mod_scheduler_studentid",args:{query:query,scheduler:scheduler,groupids:groupids}}])[0].then((function(results){var promises=[],i=0;return $.each(results,(function(index,user){promises.push(Templates.render("mod_scheduler/studentid",user))})),$.when.apply($.when,promises).then((function(){var args=arguments;$.each(results,(function(index,user){user._label=args[i],i++})),success(results)}))})).fail(failure)}}})); + +//# sourceMappingURL=studentid.min.js.map \ No newline at end of file diff --git a/amd/build/studentid.min.js.map b/amd/build/studentid.min.js.map new file mode 100644 index 00000000..515c5dde --- /dev/null +++ b/amd/build/studentid.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"studentid.min.js","sources":["../src/studentid.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Potential user selector module.\n *\n * @module mod_scheduler/studentid\n * @copyright 2022 University of Glasgow\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {\n\n return /** @alias module:mod_scheduler/studentid */ {\n\n processResults: function(selector, results) {\n var users = [];\n $.each(results, function(index, user) {\n users.push({\n value: user.id,\n label: user._label\n });\n });\n return users;\n },\n\n transport: function(selector, query, success, failure) {\n var promise;\n\n let scheduler = $(selector).attr('scheduler') || null;\n let groupids = $(selector).attr('groupids') || null;\n promise = Ajax.call([{\n methodname: 'mod_scheduler_studentid',\n args: {\n query: query,\n scheduler: scheduler,\n groupids: groupids\n }\n }]);\n\n promise[0].then(function(results) {\n var promises = [],\n i = 0;\n\n // Render the label.\n $.each(results, function(index, user) {\n promises.push(Templates.render('mod_scheduler/studentid', user));\n });\n\n // Apply the label to the results.\n return $.when.apply($.when, promises).then(function() {\n var args = arguments;\n $.each(results, function(index, user) {\n user._label = args[i];\n i++;\n });\n success(results);\n return;\n });\n\n }).fail(failure);\n }\n\n };\n\n});"],"names":["define","$","Ajax","Templates","processResults","selector","results","users","each","index","user","push","value","id","label","_label","transport","query","success","failure","scheduler","attr","groupids","call","methodname","args","then","promises","i","render","when","apply","arguments","fail"],"mappings":";;;;;;;AAuBAA,iCAAO,CAAC,SAAU,YAAa,mBAAmB,SAASC,EAAGC,KAAMC,iBAEZ,CAEhDC,eAAgB,SAASC,SAAUC,aAC3BC,MAAQ,UACZN,EAAEO,KAAKF,SAAS,SAASG,MAAOC,MAC5BH,MAAMI,KAAK,CACPC,MAAOF,KAAKG,GACZC,MAAOJ,KAAKK,YAGbR,OAGXS,UAAW,SAASX,SAAUY,MAAOC,QAASC,aAGtCC,UAAYnB,EAAEI,UAAUgB,KAAK,cAAgB,KAC7CC,SAAWrB,EAAEI,UAAUgB,KAAK,aAAe,KACrCnB,KAAKqB,KAAK,CAAC,CACjBC,WAAY,0BACZC,KAAM,CACFR,MAAOA,MACPG,UAAWA,UACXE,SAAUA,aAIV,GAAGI,MAAK,SAASpB,aACjBqB,SAAW,GACXC,EAAI,SAGR3B,EAAEO,KAAKF,SAAS,SAASG,MAAOC,MAC5BiB,SAAShB,KAAKR,UAAU0B,OAAO,0BAA2BnB,UAIvDT,EAAE6B,KAAKC,MAAM9B,EAAE6B,KAAMH,UAAUD,MAAK,eACnCD,KAAOO,UACX/B,EAAEO,KAAKF,SAAS,SAASG,MAAOC,MAC5BA,KAAKK,OAASU,KAAKG,GACnBA,OAEJV,QAAQZ,eAIb2B,KAAKd"} \ No newline at end of file diff --git a/amd/src/studentid.js b/amd/src/studentid.js new file mode 100644 index 00000000..76d2dc27 --- /dev/null +++ b/amd/src/studentid.js @@ -0,0 +1,78 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Potential user selector module. + * + * @module mod_scheduler/studentid + * @copyright 2022 University of Glasgow + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +define(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) { + + return /** @alias module:mod_scheduler/studentid */ { + + processResults: function(selector, results) { + var users = []; + $.each(results, function(index, user) { + users.push({ + value: user.id, + label: user._label + }); + }); + return users; + }, + + transport: function(selector, query, success, failure) { + var promise; + + let scheduler = $(selector).attr('scheduler') || null; + let groupids = $(selector).attr('groupids') || null; + promise = Ajax.call([{ + methodname: 'mod_scheduler_studentid', + args: { + query: query, + scheduler: scheduler, + groupids: groupids + } + }]); + + promise[0].then(function(results) { + var promises = [], + i = 0; + + // Render the label. + $.each(results, function(index, user) { + promises.push(Templates.render('mod_scheduler/studentid', user)); + }); + + // Apply the label to the results. + return $.when.apply($.when, promises).then(function() { + var args = arguments; + $.each(results, function(index, user) { + user._label = args[i]; + i++; + }); + success(results); + return; + }); + + }).fail(failure); + } + + }; + +}); \ No newline at end of file diff --git a/backup/moodle2/backup_scheduler_activity_task.class.php b/backup/moodle2/backup_scheduler_activity_task.class.php index d834a575..dc259b48 100644 --- a/backup/moodle2/backup_scheduler_activity_task.class.php +++ b/backup/moodle2/backup_scheduler_activity_task.class.php @@ -57,7 +57,7 @@ protected function define_my_steps() { * * @param string $content some HTML text that eventually contains URLs to the activity instance scripts */ - static public function encode_content_links($content) { + public static function encode_content_links($content) { global $CFG; $base = preg_quote($CFG->wwwroot, "/"); diff --git a/backup/moodle2/backup_scheduler_stepslib.php b/backup/moodle2/backup_scheduler_stepslib.php index a2e4540c..3aaf6f27 100644 --- a/backup/moodle2/backup_scheduler_stepslib.php +++ b/backup/moodle2/backup_scheduler_stepslib.php @@ -22,8 +22,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -defined('MOODLE_INTERNAL') || die(); - /** * Define the complete scheduler structure for backup, with file and id annotations * diff --git a/backup/moodle2/restore_scheduler_activity_task.class.php b/backup/moodle2/restore_scheduler_activity_task.class.php index 47bc47af..b8a8a7ce 100644 --- a/backup/moodle2/restore_scheduler_activity_task.class.php +++ b/backup/moodle2/restore_scheduler_activity_task.class.php @@ -55,7 +55,7 @@ protected function define_my_steps() { * Define the contents in the activity that must be * processed by the link decoder */ - static public function define_decode_contents() { + public static function define_decode_contents() { $contents = array(); $contents[] = new restore_decode_content('scheduler', array('intro'), 'scheduler'); @@ -67,7 +67,7 @@ static public function define_decode_contents() { * Define the decoding rules for links belonging * to the activity to be executed by the link decoder */ - static public function define_decode_rules() { + public static function define_decode_rules() { $rules = array(); $rules[] = new restore_decode_rule('SCHEDULERVIEWBYID', '/mod/scheduler/view.php?id=$1', 'course_module'); @@ -79,11 +79,11 @@ static public function define_decode_rules() { /** * Define the restore log rules that will be applied - * by the {@link restore_logs_processor} when restoring + * by the {@see restore_logs_processor} when restoring * scheduler logs. It must return one array - * of {@link restore_log_rule} objects + * of {@see restore_log_rule} objects */ - static public function define_restore_log_rules() { + public static function define_restore_log_rules() { $rules = array(); $rules[] = new restore_log_rule('scheduler', 'add', 'view.php?id={course_module}', '{scheduler}'); @@ -95,15 +95,15 @@ static public function define_restore_log_rules() { /** * Define the restore log rules that will be applied - * by the {@link restore_logs_processor} when restoring + * by the {@see restore_logs_processor} when restoring * course logs. It must return one array - * of {@link restore_log_rule} objects + * of {@see restore_log_rule} objects * * Note this rules are applied when restoring course logs * by the restore final task, but are defined here at * activity level. All them are rules not linked to any module instance (cmid = 0) */ - static public function define_restore_log_rules_for_course() { + public static function define_restore_log_rules_for_course() { $rules = array(); $rules[] = new restore_log_rule('scheduler', 'view all', 'index.php?id={course}', null); diff --git a/backup/moodle2/restore_scheduler_stepslib.php b/backup/moodle2/restore_scheduler_stepslib.php index 1cea1e4e..39332709 100644 --- a/backup/moodle2/restore_scheduler_stepslib.php +++ b/backup/moodle2/restore_scheduler_stepslib.php @@ -22,8 +22,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -defined('MOODLE_INTERNAL') || die(); - /** * Structure step to restore one scheduler activity * diff --git a/classes/event/appointment_base.php b/classes/event/appointment_base.php index 2c4fb888..a5f7e9c5 100644 --- a/classes/event/appointment_base.php +++ b/classes/event/appointment_base.php @@ -24,8 +24,6 @@ namespace mod_scheduler\event; -defined('MOODLE_INTERNAL') || die(); - /** * The mod_scheduler abstract base event class for appointment-based events. * diff --git a/classes/event/appointment_list_viewed.php b/classes/event/appointment_list_viewed.php index d7f88029..df1ff7e3 100644 --- a/classes/event/appointment_list_viewed.php +++ b/classes/event/appointment_list_viewed.php @@ -23,8 +23,6 @@ namespace mod_scheduler\event; -defined('MOODLE_INTERNAL') || die(); - /** * The mod_scheduler appointment list viewed event. * diff --git a/classes/event/booking_added.php b/classes/event/booking_added.php index e96ac684..3fcd17b0 100644 --- a/classes/event/booking_added.php +++ b/classes/event/booking_added.php @@ -23,7 +23,6 @@ */ namespace mod_scheduler\event; -defined('MOODLE_INTERNAL') || die(); /** * The mod_scheduler booking form added event. diff --git a/classes/event/booking_form_viewed.php b/classes/event/booking_form_viewed.php index 5a770e62..d63e4ab7 100644 --- a/classes/event/booking_form_viewed.php +++ b/classes/event/booking_form_viewed.php @@ -24,8 +24,6 @@ namespace mod_scheduler\event; -defined('MOODLE_INTERNAL') || die(); - /** * The mod_scheduler booking form viewed event. * diff --git a/classes/event/booking_removed.php b/classes/event/booking_removed.php index ddb51410..c740fc4b 100644 --- a/classes/event/booking_removed.php +++ b/classes/event/booking_removed.php @@ -24,8 +24,6 @@ namespace mod_scheduler\event; -defined('MOODLE_INTERNAL') || die(); - /** * The mod_scheduler booking form removed event. * diff --git a/classes/event/course_module_instance_list_viewed.php b/classes/event/course_module_instance_list_viewed.php index 275b39e4..ee2376ce 100644 --- a/classes/event/course_module_instance_list_viewed.php +++ b/classes/event/course_module_instance_list_viewed.php @@ -24,8 +24,6 @@ namespace mod_scheduler\event; -defined('MOODLE_INTERNAL') || die(); - /** * The mod_scheduler course module viewed event. * diff --git a/classes/event/scheduler_base.php b/classes/event/scheduler_base.php index 47b82e7e..379263f0 100644 --- a/classes/event/scheduler_base.php +++ b/classes/event/scheduler_base.php @@ -24,8 +24,6 @@ namespace mod_scheduler\event; -defined('MOODLE_INTERNAL') || die(); - /** * The mod_scheduler abstract base event class. * diff --git a/classes/event/slot_added.php b/classes/event/slot_added.php index 550fb2dd..6f47eb07 100644 --- a/classes/event/slot_added.php +++ b/classes/event/slot_added.php @@ -24,8 +24,6 @@ namespace mod_scheduler\event; -defined('MOODLE_INTERNAL') || die(); - /** * The mod_scheduler slot added event. * diff --git a/classes/event/slot_base.php b/classes/event/slot_base.php index b7f391d0..05df4c18 100644 --- a/classes/event/slot_base.php +++ b/classes/event/slot_base.php @@ -24,8 +24,6 @@ namespace mod_scheduler\event; -defined('MOODLE_INTERNAL') || die(); - /** * The mod_scheduler abstract base event class for slot-based events. * diff --git a/classes/event/slot_deleted.php b/classes/event/slot_deleted.php index 77f4deab..3b25555d 100644 --- a/classes/event/slot_deleted.php +++ b/classes/event/slot_deleted.php @@ -24,8 +24,6 @@ namespace mod_scheduler\event; -defined('MOODLE_INTERNAL') || die(); - /** * The mod_scheduler slot deleted event. * diff --git a/classes/external.php b/classes/external.php new file mode 100644 index 00000000..1c0de6c7 --- /dev/null +++ b/classes/external.php @@ -0,0 +1,125 @@ +. + +/** + * This is the external API for this component. + * + * @package mod_scheduler + * @copyright 2022 University of Glasgow + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_scheduler; + +defined('MOODLE_INTERNAL') || die(); + +require_once("$CFG->libdir/externallib.php"); + +use external_api; +use external_function_parameters; +use external_value; +use external_single_structure; +use external_multiple_structure; + +use \mod_scheduler\model\scheduler; + +/** + * This is the external API for this component. + * + * @copyright 2022 University of Glasgow + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class external extends external_api { + + /** + * studentid parameters + * + * @return external_function_parameters + */ + public static function studentid_parameters() { + return new external_function_parameters([ + 'query' => new external_value(PARAM_TEXT, 'The search query', VALUE_REQUIRED), + 'scheduler' => new external_value(PARAM_INT, 'The scheduler id', VALUE_REQUIRED), + 'groupids' => new external_value(PARAM_INT, 'The group ids', VALUE_DEFAULT) + ]); + } + + /** + * Fetch the details of a user's data request. + * + * @since Moodle 3.5 + * @param string $query The search query. + * @param string $scheduler The scheduler id. + * @param string $groupids The group ids. + * @return array + * @throws required_capability_exception + * @throws dml_exception + * @throws invalid_parameter_exception + * @throws restricted_context_exception + */ + + public static function studentid($query, $scheduler, $groupids) { + $params = external_api::validate_parameters(self::studentid_parameters(), [ + 'query' => $query, + 'scheduler' => $scheduler, + 'groupids' => $groupids + ]); + $query = $params['query']; + $scheduler = $params['scheduler']; + $groupids = $params['groupids']; + if (empty($groupids)) { + $groupids = 0; + } + + $scheduler = scheduler::load_by_id($scheduler); + $availablestudents = $scheduler->get_available_students($groupids); + + $students = []; + $i = 0; + $maxstudents = 100; + foreach ($availablestudents as $id => $student) { + $fullname = fullname($student); + + if (empty($query) || mb_stripos($fullname, $query) !== false) { + $students[] = ['id' => $id, 'fullname' => fullname($student)]; + $i++; + if ($i >= $maxstudents) { + return $students; + } + } + } + + return $students; + + } + + /** + * Parameter description for get_users(). + * + * @since Moodle 3.5 + * @return external_description + * @throws coding_exception + */ + public static function studentid_returns() { + return new external_multiple_structure( + new external_single_structure([ + 'id' => new external_value(PARAM_INT, 'User ID'), + 'fullname' => new external_value(PARAM_NOTAGS, 'User fullname') + ]) + ); + } + +} diff --git a/classes/model/appointment.php b/classes/model/appointment.php index 6542d0f0..977e23e9 100644 --- a/classes/model/appointment.php +++ b/classes/model/appointment.php @@ -26,7 +26,8 @@ defined('MOODLE_INTERNAL') || die(); - +// Elements from lib.php needed for grade functionality. +require_once($CFG->dirroot.'/mod/scheduler/lib.php'); /** * A class for representing a scheduler appointment. diff --git a/classes/model/appointment_factory.php b/classes/model/appointment_factory.php index 91f091de..0c76b2f0 100644 --- a/classes/model/appointment_factory.php +++ b/classes/model/appointment_factory.php @@ -25,8 +25,6 @@ namespace mod_scheduler\model; -defined('MOODLE_INTERNAL') || die(); - /** * A factory class for scheduler appointments. * diff --git a/classes/model/mvc_child_list.php b/classes/model/mvc_child_list.php index e30404c5..cb3dfe02 100644 --- a/classes/model/mvc_child_list.php +++ b/classes/model/mvc_child_list.php @@ -24,8 +24,6 @@ namespace mod_scheduler\model; -defined('MOODLE_INTERNAL') || die(); - /** * A list of child records. * @@ -214,5 +212,4 @@ public function delete_children() { $child->delete(); } } - -} \ No newline at end of file +} diff --git a/classes/model/mvc_child_model_factory.php b/classes/model/mvc_child_model_factory.php index 0a2eb8f8..47694629 100644 --- a/classes/model/mvc_child_model_factory.php +++ b/classes/model/mvc_child_model_factory.php @@ -25,8 +25,6 @@ namespace mod_scheduler\model; -defined('MOODLE_INTERNAL') || die(); - /** * An abstract factory class for loading child records from the database. * @@ -62,7 +60,7 @@ public function create() { * * @param mvc_record_model $parent */ - public abstract function create_child(mvc_record_model $parent); + abstract public function create_child(mvc_record_model $parent); /** * Create a child record from a database entry, already loaded diff --git a/classes/model/mvc_child_record_model.php b/classes/model/mvc_child_record_model.php index 72f884e9..9ffe6d7d 100644 --- a/classes/model/mvc_child_record_model.php +++ b/classes/model/mvc_child_record_model.php @@ -24,8 +24,6 @@ namespace mod_scheduler\model; -defined('MOODLE_INTERNAL') || die(); - /** * A model mirroring one datebase record which as a "parent-child" relationship to a record in another table. * diff --git a/classes/model/mvc_model.php b/classes/model/mvc_model.php index b6498411..eb916345 100644 --- a/classes/model/mvc_model.php +++ b/classes/model/mvc_model.php @@ -24,8 +24,6 @@ namespace mod_scheduler\model; -defined('MOODLE_INTERNAL') || die(); - /** * A generic MVC model (currently rather empty!). * diff --git a/classes/model/mvc_model_factory.php b/classes/model/mvc_model_factory.php index d008a21d..caa1f93e 100644 --- a/classes/model/mvc_model_factory.php +++ b/classes/model/mvc_model_factory.php @@ -24,8 +24,6 @@ namespace mod_scheduler\model; -defined('MOODLE_INTERNAL') || die(); - /** * An abstract factory class for loading records from the database. * @@ -39,7 +37,7 @@ abstract class mvc_model_factory { * * @return mvc_model */ - public abstract function create(); + abstract public function create(); /** * Create a new record by loading it from the database. diff --git a/classes/model/mvc_record_model.php b/classes/model/mvc_record_model.php index 6db2990b..d726332d 100644 --- a/classes/model/mvc_record_model.php +++ b/classes/model/mvc_record_model.php @@ -24,9 +24,6 @@ namespace mod_scheduler\model; -defined('MOODLE_INTERNAL') || die(); - - /** * A model mirroring one datebase record in a specific table of the Moodle DB. * diff --git a/classes/model/slot.php b/classes/model/slot.php index 11c05507..3404d113 100644 --- a/classes/model/slot.php +++ b/classes/model/slot.php @@ -24,8 +24,6 @@ namespace mod_scheduler\model; -defined('MOODLE_INTERNAL') || die(); - /** * A class for representing a scheduler slot. * @@ -334,12 +332,12 @@ public function delete_all_appointments() { } - /* The event code is SSstu (for a student event) or SSsup (for a teacher event). - * then, the id of the scheduler slot that it belongs to. - * finally, the courseID (legacy reasons -- not really used), - * all in a colon delimited string. This will run into problems when the IDs of slots and courses - * are bigger than 7 digits in length... - */ + /* + * The event code is SSstu (for a student event) or SSsup (for a teacher event). + * then the id of the scheduler slot that it belongs to, + * in a colon delimited string. + * This allows for slot ids of at most 14 digits. + */ /** * Get the id string for teacher events in this slot @@ -347,8 +345,7 @@ public function delete_all_appointments() { */ private function get_teacher_eventtype() { $slotid = $this->get_id(); - $courseid = $this->get_parent()->get_courseid(); - return "SSsup:{$slotid}:{$courseid}"; + return "SSsup:{$slotid}"; } /** @@ -357,8 +354,7 @@ private function get_teacher_eventtype() { */ private function get_student_eventtype() { $slotid = $this->get_id(); - $courseid = $this->get_parent()->get_courseid(); - return "SSstu:{$slotid}:{$courseid}"; + return "SSstu:{$slotid}"; } /** diff --git a/classes/model/slot_factory.php b/classes/model/slot_factory.php index da9dcafb..2c79fbf7 100644 --- a/classes/model/slot_factory.php +++ b/classes/model/slot_factory.php @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . - /** * A factory class for scheduler slots. * @@ -25,8 +24,6 @@ namespace mod_scheduler\model; -defined('MOODLE_INTERNAL') || die(); - /** * A factory class for scheduler slots. * diff --git a/classes/permission/permissions_manager.php b/classes/permission/permissions_manager.php index dff2ecaa..ce0be45c 100644 --- a/classes/permission/permissions_manager.php +++ b/classes/permission/permissions_manager.php @@ -24,8 +24,6 @@ namespace mod_scheduler\permission; -defined('MOODLE_INTERNAL') || die(); - /** * The base class for controllers. * diff --git a/classes/permission/scheduler_permissions.php b/classes/permission/scheduler_permissions.php index fdbf72c5..b80f1043 100644 --- a/classes/permission/scheduler_permissions.php +++ b/classes/permission/scheduler_permissions.php @@ -24,8 +24,6 @@ namespace mod_scheduler\permission; -defined('MOODLE_INTERNAL') || die(); - /** * The base class for controllers. * diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 78ba7f20..490de14f 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -34,8 +34,6 @@ use core_privacy\local\request\transform; use core_privacy\local\request\writer; -defined('MOODLE_INTERNAL') || die(); - /** * Implementation of the privacy subsystem plugin provider for the scheduler activity module. * diff --git a/classes/search/activity.php b/classes/search/activity.php index 1c10aff1..d7e41c82 100644 --- a/classes/search/activity.php +++ b/classes/search/activity.php @@ -24,8 +24,6 @@ namespace mod_scheduler\search; -defined('MOODLE_INTERNAL') || die(); - /** * Search area for mod_scheduler activities. * diff --git a/classes/task/purge_unused_slots.php b/classes/task/purge_unused_slots.php index f1fd35cc..fe83dee6 100644 --- a/classes/task/purge_unused_slots.php +++ b/classes/task/purge_unused_slots.php @@ -24,8 +24,6 @@ namespace mod_scheduler\task; -defined('MOODLE_INTERNAL') || die(); - /** * Scheduled background task for sending automated appointment reminders * @@ -49,4 +47,4 @@ public function get_name() { public function execute() { \mod_scheduler\model\scheduler::free_late_unused_slots(); } -} \ No newline at end of file +} diff --git a/classes/task/send_reminders.php b/classes/task/send_reminders.php index 6413eac9..fdc48ec5 100644 --- a/classes/task/send_reminders.php +++ b/classes/task/send_reminders.php @@ -68,7 +68,7 @@ public function execute() { $slotm = $scheduler->get_slot($slot->id); $course = $scheduler->get_courserec(); - // Mark as sent. (Do this first for safe fallback in case of an exception.) + // Mark as sent (Do this first for safe fallback in case of an exception). $slot->emaildate = -1; $DB->update_record('scheduler_slots', $slot); @@ -82,5 +82,4 @@ public function execute() { } cron_setup_user(); } - -} \ No newline at end of file +} diff --git a/customlib.php b/customlib.php index 200f2561..826cc8e2 100644 --- a/customlib.php +++ b/customlib.php @@ -22,8 +22,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -defined('MOODLE_INTERNAL') || die(); - /** * Get a list of fields to be displayed in lists of users, etc. * diff --git a/datelist.php b/datelist.php index 65e47ffa..eb2782fd 100644 --- a/datelist.php +++ b/datelist.php @@ -112,7 +112,7 @@ $teacherselect = $ufields->get_sql('u2', true, '', 'teacherid', false)->selects; $sql = "SELECT a.id AS id, - $studselect,". + $studselect," . $DB->sql_fullname('u1.firstname', 'u1.lastname')." AS studentfullname, a.appointmentnote, a.appointmentnoteformat, diff --git a/db/services.php b/db/services.php new file mode 100644 index 00000000..6faa22da --- /dev/null +++ b/db/services.php @@ -0,0 +1,37 @@ +. + +/** + * Mod Scheduler webservice definitions + * + * @package mod_scheduler + * @copyright 2022 University of Glasgow + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$functions = array( + + 'mod_scheduler_studentid' => array( + 'classname' => 'mod_scheduler\external', + 'methodname' => 'studentid', + 'description' => 'Retrieve the list of potential studentids.', + 'type' => 'read', + 'ajax' => true, + 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) + ), +); diff --git a/db/tasks.php b/db/tasks.php index 891c8cdf..6a7709df 100644 --- a/db/tasks.php +++ b/db/tasks.php @@ -41,4 +41,4 @@ 'dayofweek' => '*', 'month' => '*' ) -); \ No newline at end of file +); diff --git a/db/upgrade.php b/db/upgrade.php index bf9130e4..8dbee09d 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -22,8 +22,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -defined('MOODLE_INTERNAL') || die(); - /** * Migrate a configuration setting from global to plugin specific. * @@ -56,6 +54,26 @@ function scheduler_migrate_groupmode($sid) { } } +/** + * Migrate event type settings to new 4.1 conventions + * + * @param string $prefix The prefix + * @throws dml_exception + */ +function scheduler_migrate_eventtype($prefix) { + global $DB; + $rs = $DB->get_recordset_sql('SELECT * FROM {event} WHERE eventtype LIKE ?', ["$prefix:%"]); + foreach ($rs as $record) { + $parts = explode(':', $record->eventtype, 3); + if (count($parts) > 2) { + $record->eventtype = $parts[0].":".$parts[1]; + $DB->update_record('event', $record); + } + } + $rs->close(); +} + + /** * This function does anything necessary to upgrade older versions to match current functionality. * @@ -337,5 +355,17 @@ function xmldb_scheduler_upgrade($oldversion=0) { // Scheduler savepoint reached. upgrade_mod_savepoint(true, 2017040100, 'scheduler'); } + + /* ******************* 4.1 upgrade line ********************** */ + + if ($oldversion < 2022120200) { + // Migrate eventtype field in the events table to shorter format. + scheduler_migrate_eventtype('SSstu'); + scheduler_migrate_eventtype('SSsup'); + + // Scheduler savepoint reached. + upgrade_mod_savepoint(true, 2022120200, 'scheduler'); + } + return true; -} \ No newline at end of file +} diff --git a/exportlib.php b/exportlib.php index 81a96b63..3bc98af1 100644 --- a/exportlib.php +++ b/exportlib.php @@ -69,7 +69,7 @@ public function is_available(scheduler $scheduler) { /** * Retrieve the unique id (a string) for this field */ - public abstract function get_id(); + abstract public function get_id(); /** * Retrieve the group that this field belongs to - @@ -77,7 +77,7 @@ public abstract function get_id(); * * @return string the group id as above */ - public abstract function get_group(); + abstract public function get_group(); /** * Retrieve the header (in the sense of table header in the output) @@ -149,7 +149,7 @@ public function is_wrapping() { * @param mixed $appointment the appointment to evaluate (may be null for an empty slot) * @return string the value of this field for the given data */ - public abstract function get_value(slot $slot, $appointment); + abstract public function get_value(slot $slot, $appointment); /** * Retrieve the value of this field as an array. @@ -1328,7 +1328,7 @@ abstract class scheduler_canvas { * * @param mixed $title the title of the page */ - public abstract function start_page($title); + abstract public function start_page($title); /** * Write a string into a certain position of the canvas. @@ -1338,7 +1338,7 @@ public abstract function start_page($title); * @param mixed $str the string to write * @param mixed $format the format to use (one of the $format... fields of this object), can be null */ - public abstract function write_string($row, $col, $str, $format); + abstract public function write_string($row, $col, $str, $format); /** * Write a number into a certain position of the canvas. @@ -1348,7 +1348,7 @@ public abstract function write_string($row, $col, $str, $format); * @param mixed $num the number to write * @param mixed $format the format to use (one of the $format... fields of this object), can be null */ - public abstract function write_number($row, $col, $num, $format); + abstract public function write_number($row, $col, $num, $format); /** * Merge a range of cells in the same row. @@ -1357,7 +1357,7 @@ public abstract function write_number($row, $col, $num, $format); * @param mixed $fromcol the first column to merge * @param mixed $tocol the last column to merge */ - public abstract function merge_cells($row, $fromcol, $tocol); + abstract public function merge_cells($row, $fromcol, $tocol); /** * Set the width of a particular column. (This will make sense only for certain outout formats, @@ -1391,7 +1391,7 @@ public function set_title($title) { * * @param string $filename the file name to send */ - public abstract function send($filename); + abstract public function send($filename); } diff --git a/index.php b/index.php index 85280f36..4b910fb1 100644 --- a/index.php +++ b/index.php @@ -87,8 +87,8 @@ // Show dimmed if the mod is hidden. $attr = $scheduler->visible ? null : array('class' => 'dimmed'); $link = html_writer::link($url, $scheduler->name, $attr); - if ($scheduler->visible or has_capability('moodle/course:viewhiddenactivities', $coursecontext)) { - if ($course->format == 'weeks' or $course->format == 'topics') { + if ($scheduler->visible || has_capability('moodle/course:viewhiddenactivities', $coursecontext)) { + if ($course->format == 'weeks' || $course->format == 'topics') { $table->data[] = array ($scheduler->section, $link); } else { $table->data[] = array ($link); diff --git a/lang/en/scheduler.php b/lang/en/scheduler.php index f5a67e5d..72168585 100644 --- a/lang/en/scheduler.php +++ b/lang/en/scheduler.php @@ -124,6 +124,8 @@ $string['appointmentnote'] = 'Notes for appointment (visible to student)'; $string['appointments'] = 'Appointments'; $string['appointmentsgrouped'] = 'Appointments grouped by slot'; +$string['appointmentsperpage'] = 'Appointments per page'; +$string['appointmentsperpage_desc'] = 'The number of appointments to show per page in the teacher view when editing a slot. This can be helpful to reduce memory for slots with large numbers of appointments. Default of zero disables paging.'; $string['appointsolo'] = 'just me'; $string['appointsomeone'] = 'Add new appointment'; $string['appointmentsummary'] = 'Appointment on {$a->startdate} from {$a->starttime} to {$a->endtime} with {$a->teacher}'; @@ -338,6 +340,7 @@ $string['name'] = 'Scheduler name'; $string['needteachers'] = 'Slots cannot be added as this course has no teachers'; $string['negativerange'] = 'Range is negative. This can\'t be.'; +$string['negativetimerange'] = 'End time must be later than start time.'; $string['never'] = 'Never'; $string['nfiles'] = '{$a} files'; $string['noappointments'] = 'No appointments'; @@ -396,6 +399,7 @@ $string['revoke'] = 'Revoke the appointment'; $string['saturday'] = 'Saturday'; $string['save'] = 'Save'; +$string['savechangesandcontinueediting'] = 'Save changes and continue editing'; $string['savechoice'] = 'Save my choice'; $string['saveseen'] = 'Save seen'; $string['schedule'] = 'Schedule'; diff --git a/lib.php b/lib.php index 147bd44e..1dd62a64 100644 --- a/lib.php +++ b/lib.php @@ -78,6 +78,20 @@ function scheduler_add_instance($data, $mform = null) { return $data->id; } +function student_autocomplete_callback($value) { + global $OUTPUT; + + $userfieldsapi = \core_user\fields::for_name(); + $allusernames = $userfieldsapi->get_sql('', false, '', '', false)->selects; + $fields = 'id, ' . $allusernames; + $user = \core_user::get_user($value, $fields); + $useroptiondata = [ + 'fullname' => fullname($user) + ]; + + return $OUTPUT->render_from_template('mod_scheduler/studentid', $useroptiondata); +} + /** * Given an object containing all the necessary data, * (defined by the form in mod.html) this function @@ -267,7 +281,7 @@ function scheduler_scale_used($cmid, $scaleid) { function scheduler_scale_used_anywhere($scaleid) { global $DB; - if ($scaleid and $DB->record_exists('scheduler', array('scale' => -$scaleid))) { + if ($scaleid && $DB->record_exists('scheduler', array('scale' => -$scaleid))) { return true; } else { return false; @@ -375,7 +389,8 @@ function scheduler_supports($feature) { return false; case FEATURE_BACKUP_MOODLE2: return true; - + case FEATURE_MOD_PURPOSE: + return MOD_PURPOSE_ADMINISTRATION; default: return null; } @@ -718,7 +733,8 @@ function scheduler_pluginfile($course, $cm, $context, $filearea, $args, $forcedo $fullpath = "/$context->id/mod_scheduler/$filearea/$entryid/$relativepath"; $fs = get_file_storage(); - if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { + $file = $fs->get_file_by_hash(sha1($fullpath)); + if (!$file || $file->is_directory()) { return false; } diff --git a/locallib.php b/locallib.php index f45cf07c..60ce8232 100644 --- a/locallib.php +++ b/locallib.php @@ -40,14 +40,8 @@ function scheduler_delete_calendar_events($slot) { global $DB; - $scheduler = $DB->get_record('scheduler', array('id' => $slot->schedulerid)); - - if (!$scheduler) { - return false; - } - - $teachereventtype = "SSsup:{$slot->id}:{$scheduler->course}"; - $studenteventtype = "SSstu:{$slot->id}:{$scheduler->course}"; + $teachereventtype = "SSsup:{$slot->id}"; + $studenteventtype = "SSstu:{$slot->id}"; $teacherdeletionsuccess = $DB->delete_records('event', array('eventtype' => $teachereventtype)); $studentdeletionsuccess = $DB->delete_records('event', array('eventtype' => $studenteventtype)); @@ -64,8 +58,8 @@ function scheduler_delete_calendar_events($slot) { * * @uses $CFG * @uses $USER - * @param user $user A {@link $USER} object representing a user - * @param course $course A {@link $COURSE} object representing a course + * @param user $user A {@see $USER} object representing a user + * @param course $course A {@see $COURSE} object representing a course * @param bool $messageselect whether to include a checkbox to select the user * @param bool $return whether the HTML fragment is to be returned as a string (otherwise printed) * @return string HTML fragment, if so selected @@ -128,7 +122,7 @@ function scheduler_print_user($user, $course, $messageselect=false, $return=fals $output .= ''; $output .= '
'.fullname($user, has_capability('moodle/site:viewfullnames', $context)).'
'; $output .= '
'; - if (!empty($user->role) and ($user->role <> $course->teacher)) { + if (!empty($user->role) && ($user->role <> $course->teacher)) { $output .= $string->role .': '. $user->role .'
'; } @@ -151,13 +145,13 @@ function scheduler_print_user($user, $course, $messageselect=false, $return=fals $output .= ''.get_string('blogs', 'blog').'
'; } // Link to notes. - if (!empty($CFG->enablenotes) and (has_capability('moodle/notes:manage', $context) + if (!empty($CFG->enablenotes) && (has_capability('moodle/notes:manage', $context) || has_capability('moodle/notes:view', $context))) { $output .= ''. get_string('notes', 'notes').'
'; } - if (has_capability('moodle/site:viewreports', $context) or + if (has_capability('moodle/site:viewreports', $context) || has_capability('moodle/user:viewuseractivitiesreport', $usercontext)) { $output .= ''. $string->activity .'
'; diff --git a/mailtemplatelib.php b/mailtemplatelib.php index 65ab9324..85d6ad3b 100644 --- a/mailtemplatelib.php +++ b/mailtemplatelib.php @@ -22,8 +22,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -defined ( 'MOODLE_INTERNAL' ) || die (); - use \mod_scheduler\model\scheduler; use \mod_scheduler\model\slot; @@ -45,7 +43,7 @@ class scheduler_messenger { * @return string */ protected static function get_message_language($user, $course) { - if ($course && ! empty ($course->id) and $course->id != SITEID and !empty($course->lang)) { + if ($course && ! empty ($course->id) && $course->id != SITEID && !empty($course->lang)) { // Course language overrides user language. $return = $course->lang; } else if (!empty($user->lang)) { @@ -75,7 +73,7 @@ protected static function get_message_language($user, $course) { public static function compile_mail_template($template, $format, $parameters, $module = 'scheduler', $lang = null) { $params = array (); foreach ($parameters as $key => $value) { - $params [strtolower($key)] = $value; + $params[strtolower($key)] = $value; } $mailstr = get_string_manager()->get_string("email_{$template}_{$format}", $module, $params, $lang); return $mailstr; @@ -92,9 +90,9 @@ public static function compile_mail_template($template, $format, $parameters, $m * @param int $isnotification * 1 for notifications, 0 for personal messages * @param stdClass $sender - * A {@link $USER} object describing the sender + * A {@see $USER} object describing the sender * @param stdClass $recipient - * A {@link $USER} object describing the recipient + * A {@see $USER} object describing the recipient * @param object $course * The course that the activity is in. Can be null. * @param string $template @@ -151,10 +149,10 @@ public static function send_message_from_template($modulename, $messagename, $is * * @param scheduler $scheduler The scheduler instance * @param slot $slot The slot data as an MVC object, may be null - * @param user $teacher A {@link $USER} object describing the attendant (teacher) - * @param user $student A {@link $USER} object describing the attendee (student) + * @param user $teacher A {@see $USER} object describing the attendant (teacher) + * @param user $student A {@see $USER} object describing the attendee (student) * @param object $course A course object relating to the ontext of the message - * @param object $recipient A {@link $USER} object describing the recipient of the message + * @param object $recipient A {@see $USER} object describing the recipient of the message * (used for determining the message language) * @return array A hash with mail template substitutions */ @@ -177,10 +175,10 @@ public static function get_scheduler_variables(scheduler $scheduler, $slot, $vars['SCHEDULER_URL'] = $CFG->wwwroot.'/mod/scheduler/view.php?id='.$scheduler->cmid; } if ($slot) { - $vars ['DATE'] = userdate($slot->starttime, get_string('strftimedate'), $tz); - $vars ['TIME'] = userdate($slot->starttime, get_string('strftimetime'), $tz); - $vars ['ENDTIME'] = userdate($slot->endtime, get_string('strftimetime'), $tz); - $vars ['LOCATION'] = format_string($slot->appointmentlocation); + $vars['DATE'] = userdate($slot->starttime, get_string('strftimedate'), $tz); + $vars['TIME'] = userdate($slot->starttime, get_string('strftimetime'), $tz); + $vars['ENDTIME'] = userdate($slot->endtime, get_string('strftimetime'), $tz); + $vars['LOCATION'] = format_string($slot->appointmentlocation); } if ($teacher) { $vars['ATTENDANT'] = fullname($teacher); @@ -218,4 +216,4 @@ public static function send_slot_notification(slot $slot, $messagename, $templat self::send_message_from_template('mod_scheduler', $messagename, 1, $sender, $recipient, $course, $template, $vars); } -} \ No newline at end of file +} diff --git a/pix/monologo.svg b/pix/monologo.svg new file mode 100644 index 00000000..0d3d37e2 --- /dev/null +++ b/pix/monologo.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + diff --git a/renderable.php b/renderable.php index 9d9dd685..dfd6d318 100644 --- a/renderable.php +++ b/renderable.php @@ -696,5 +696,4 @@ public static function make_for_teacher(slot $slot, appointment $appointment) { return $info; } - -} \ No newline at end of file +} diff --git a/renderer.php b/renderer.php index dc9314b4..fc9bca14 100644 --- a/renderer.php +++ b/renderer.php @@ -319,23 +319,6 @@ public function render_attachments($contextid, $filearea, $itemid) { } - /** - * Render the module introduction of a scheduler. - * - * @param scheduler $scheduler the scheduler in question - * @return string rendered module info - */ - public function mod_intro($scheduler) { - $o = $this->heading(format_string($scheduler->name), 2); - - if (trim(strip_tags($scheduler->intro))) { - $o .= $this->box_start('mod_introbox'); - $o .= format_module_intro('scheduler', $scheduler->get_data(), $scheduler->cmid); - $o .= $this->box_end(); - } - return $o; - } - /** * Construct a tab header in the teacher view. * @@ -1060,7 +1043,7 @@ public function render_scheduler_appointment_info(scheduler_appointment_info $ai if ($ai->showresult) { if ($ai->scheduler->uses_appointmentnotes() && $ai->appointment->appointmentnote) { $row = new html_table_row(); - $cell1 = new html_table_cell(get_string('appointmentnotes', 'scheduler')); + $cell1 = new html_table_cell(get_string('appointmentnote', 'scheduler')); $note = $this->format_notes($ai->appointment->appointmentnote, $ai->appointment->appointmentnoteformat, $ai->scheduler->get_context(), 'appointmentnote', $ai->appointment->id); $cell2 = new html_table_cell($note); diff --git a/settings.php b/settings.php index b2fee38b..efe1d3b7 100644 --- a/settings.php +++ b/settings.php @@ -53,6 +53,11 @@ get_string('uploadmaxfilesglobal_desc', 'scheduler'), 5, PARAM_INT)); + $settings->add(new admin_setting_configtext('mod_scheduler/appointmentsperpage', + get_string('appointmentsperpage', 'scheduler'), + get_string('appointmentsperpage_desc', 'scheduler'), + 0, PARAM_INT)); + $settings->add(new admin_setting_configcheckbox('mod_scheduler/revealteachernotes', get_string('revealteachernotes', 'scheduler'), get_string('revealteachernotes_desc', 'scheduler'), diff --git a/slotforms.php b/slotforms.php index f8410ac7..123f373a 100644 --- a/slotforms.php +++ b/slotforms.php @@ -211,6 +211,18 @@ protected function definition() { if (isset($this->_customdata['timeoptions'])) { $timeoptions = $this->_customdata['timeoptions']; } + $offset = null; + if (isset($this->_customdata['offset'])) { + $offset = $this->_customdata['offset']; + } + $lastpage = null; + if (isset($this->_customdata['lastpage'])) { + $lastpage = $this->_customdata['lastpage']; + } + $pagingbar = null; + if (isset($this->_customdata['pagingbar'])) { + $pagingbar = $this->_customdata['pagingbar']; + } // Start date/time of the slot. $mform->addElement('date_time_selector', 'starttime', get_string('date', 'scheduler'), $timeoptions); @@ -244,19 +256,28 @@ protected function definition() { // Appointments. + if (!empty($pagingbar)) { + $mform->addElement('html', $pagingbar); + } + $repeatarray = array(); $grouparray = array(); - $repeatarray[] = $mform->createElement('header', 'appointhead', get_string('appointmentno', 'scheduler', '{no}')); - - // Choose student. - $students = $this->scheduler->get_available_students($this->usergroups); - $studentchoices = array(); - if ($students) { - foreach ($students as $astudent) { - $studentchoices[$astudent->id] = fullname($astudent); - } + $repeatarray[] = $mform->createElement('header', 'appointhead', get_string('appointmentno', 'scheduler', '{number}')); + + $options = [ + 'ajax' => 'mod_scheduler/studentid', + 'valuehtmlcallback' => 'student_autocomplete_callback' + ]; + + if (!empty($this->_customdata['scheduler'])) { + $options['scheduler'] = $this->_customdata['scheduler']; } - $grouparray[] = $mform->createElement('searchableselector', 'studentid', '', $studentchoices); + if (!empty($this->_customdata['groupids'])) { + $options['groupids'] = $this->usergroups; + } + + $grouparray[] = $mform->createElement('autocomplete', 'studentid', '', [], $options); + $grouparray[] = $mform->createElement('hidden', 'appointid', 0); // Seen tickbox. @@ -295,6 +316,10 @@ protected function definition() { $repeatno = 1; } + // Add hidden for number of appointments on this page. Then use disabledif on hidden value with calculation. + $mform->addElement('hidden', 'repeatno', $repeatno); + $mform->setType('repeatno', PARAM_INT); + $repeateloptions = array(); $repeateloptions['appointid']['type'] = PARAM_INT; $repeateloptions['studentid']['disabledif'] = array('appointid', 'neq', 0); @@ -309,7 +334,33 @@ protected function definition() { $this->repeat_elements($repeatarray, $repeatno, $repeateloptions, 'appointment_repeats', 'appointment_add', 1, get_string('addappointment', 'scheduler')); - $this->add_action_buttons(); + $appointmentsperpage = get_config('mod_scheduler', 'appointmentsperpage'); + + if (!empty($appointmentsperpage)) { + if (!$lastpage) { + $this->_form->disabledIf('appointment_add', 'repeatno', 'eq', $appointmentsperpage); + } + if (!empty($pagingbar)) { + $mform->addElement('html', $pagingbar); + } + } + + // Fix appointment numbers affected by paging here. repeat_elements doesn't support paging. + $number = $offset * $appointmentsperpage; + foreach ($this->_form->_elements as $element) { + if (is_a($element, 'HTML_QuickForm_header')) { + $element->setValue(str_replace('{number}', ($number + 1), $element->_text)); + $number++; + } + } + + $buttonarray = []; + $buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('savechanges')); + $buttonarray[] = &$mform->createElement('submit', 'savechangesandcontinueediting', + get_string('savechangesandcontinueediting', 'mod_scheduler')); + $buttonarray[] = &$mform->createElement('cancel'); + $mform->addGroup($buttonarray, 'buttonar', '', [' '], false); + $mform->closeHeaderBefore('buttonar'); } @@ -382,7 +433,7 @@ public function validation($data, $files) { * @param slot $slot * @return stdClass form data */ - public function prepare_formdata(slot $slot) { + public function prepare_formdata(slot $slot, $offset = 0) { $context = $slot->get_scheduler()->get_context(); @@ -400,7 +451,33 @@ public function prepare_formdata(slot $slot) { } $i = 0; - foreach ($slot->get_appointments() as $appointment) { + $slots = $slot->get_appointments(); + + $start = 0; + $end = count($slots); + $appointmentsperpage = get_config('mod_scheduler', 'appointmentsperpage'); + if (!empty($appointmentsperpage)) { + $start = $offset * $appointmentsperpage; + $end = $start + $appointmentsperpage; + } + + $counter = 0; + foreach ($slots as $appointment) { + + // Appointments on forms page need to be indexed from zero, even when paging. + // Use $i for indexing appointments that will be repeated on form. + // When $i > $appointmentsperpage, break from loop. + if ($i >= $appointmentsperpage) { + break; + } + + // Use counter to loop through appointments. + if ($counter < $start || $counter >= $end) { + $counter++; + continue; + } + $counter++; + $data->appointid[$i] = $appointment->id; $data->studentid[$i] = $appointment->studentid; $data->attended[$i] = $appointment->attended; @@ -558,12 +635,21 @@ protected function definition() { $minutes[$i] = sprintf("%02d", $i); } $timegroup = array(); - $timegroup[] = $mform->createElement('static', 'timefrom', '', get_string('timefrom', 'scheduler')); - $timegroup[] = $mform->createElement('select', 'starthour', get_string('hour', 'form'), $hours); - $timegroup[] = $mform->createElement('select', 'startminute', get_string('minute', 'form'), $minutes); - $timegroup[] = $mform->createElement('static', 'timeto', '', get_string('timeto', 'scheduler')); - $timegroup[] = $mform->createElement('select', 'endhour', get_string('hour', 'form'), $hours); - $timegroup[] = $mform->createElement('select', 'endminute', get_string('minute', 'form'), $minutes); + if (right_to_left()) { + $timegroup[] = $mform->createElement('static', 'timefrom', '', get_string('timefrom', 'scheduler')); + $timegroup[] = $mform->createElement('select', 'startminute', get_string('minute', 'form'), $minutes); + $timegroup[] = $mform->createElement('select', 'starthour', get_string('hour', 'form'), $hours); + $timegroup[] = $mform->createElement('static', 'timeto', '', get_string('timeto', 'scheduler')); + $timegroup[] = $mform->createElement('select', 'endminute', get_string('minute', 'form'), $minutes); + $timegroup[] = $mform->createElement('select', 'endhour', get_string('hour', 'form'), $hours); + } else { + $timegroup[] = $mform->createElement('static', 'timefrom', '', get_string('timefrom', 'scheduler')); + $timegroup[] = $mform->createElement('select', 'starthour', get_string('hour', 'form'), $hours); + $timegroup[] = $mform->createElement('select', 'startminute', get_string('minute', 'form'), $minutes); + $timegroup[] = $mform->createElement('static', 'timeto', '', get_string('timeto', 'scheduler')); + $timegroup[] = $mform->createElement('select', 'endhour', get_string('hour', 'form'), $hours); + $timegroup[] = $mform->createElement('select', 'endminute', get_string('minute', 'form'), $minutes); + } $mform->addGroup($timegroup, 'timerange', get_string('timerange', 'scheduler'), null, false); // Divide into slots? @@ -639,11 +725,11 @@ public function validation($data, $files) { } } - // Time range is negative. + // Time range is not positive. $starttime = $data['starthour'] * 60 + $data['startminute']; $endtime = $data['endhour'] * 60 + $data['endminute']; - if ($starttime > $endtime) { - $errors['timerange'] = get_string('negativerange', 'scheduler'); + if ($starttime >= $endtime) { + $errors['timerange'] = get_string('negativetimerange', 'scheduler'); } // First slot is in the past. diff --git a/studentview.controller.php b/studentview.controller.php index 4b2dd01f..1f0b5f06 100644 --- a/studentview.controller.php +++ b/studentview.controller.php @@ -164,7 +164,6 @@ function scheduler_book_slot($scheduler, $slotid, $userid, $groupid, $mform, $fo echo $output->header(); echo $output->heading(get_string('bookaslot', 'scheduler')); - echo $output->box(format_text($scheduler->intro, $scheduler->introformat)); $info = scheduler_appointment_info::make_from_slot($slot, true, true, $groupinfo); echo $output->render($info); @@ -210,7 +209,6 @@ function scheduler_book_slot($scheduler, $slotid, $userid, $groupid, $mform, $fo echo $output->header(); echo $output->heading(get_string('bookingdetails', 'scheduler')); - echo $output->mod_intro($scheduler); $info = scheduler_appointment_info::make_from_appointment($slot, $appointment); echo $output->render($info); diff --git a/studentview.php b/studentview.php index fb204e1f..3095caff 100644 --- a/studentview.php +++ b/studentview.php @@ -63,9 +63,6 @@ echo $output->header(); -// Print intro. -echo $output->mod_intro($scheduler); - $showowngrades = $scheduler->uses_grades(); // Print total grade (if any). @@ -253,4 +250,4 @@ } -echo $output->footer(); \ No newline at end of file +echo $output->footer(); diff --git a/teacherview.controller.php b/teacherview.controller.php index 3bf4a9bf..6cdb092e 100644 --- a/teacherview.controller.php +++ b/teacherview.controller.php @@ -54,8 +54,8 @@ function scheduler_action_doaddsession($scheduler, $formdata, moodle_url $return if ($data->divide) { $slot->duration = $data->duration; } else { - $slot->duration = $data->endhour * 60 + $data->endminute - $data->starthour * 60 - $data->startminute; - }; + $slot->duration = max(1, $data->endhour * 60 + $data->endminute - $data->starthour * 60 - $data->startminute); + } $slot->notes = ''; $slot->notesformat = FORMAT_HTML; $slot->timemodified = time(); diff --git a/teacherview.php b/teacherview.php index 76e560e2..43c11d74 100644 --- a/teacherview.php +++ b/teacherview.php @@ -45,8 +45,8 @@ function scheduler_print_schedulebox(scheduler $scheduler, $studentid, $groupid $startdatecnv = $output->userdate($slot->starttime); $starttimecnv = $output->usertime($slot->starttime); - $startdatestr = ($startdatemem != '' and $startdatemem == $startdatecnv) ? "-----------------" : $startdatecnv; - $starttimestr = ($starttimemem != '' and $starttimemem == $starttimecnv) ? '' : $starttimecnv; + $startdatestr = ($startdatemem != '' && $startdatemem == $startdatecnv) ? "-----------------" : $startdatecnv; + $starttimestr = ($starttimemem != '' && $starttimemem == $starttimecnv) ? '' : $starttimecnv; $startdatemem = $startdatecnv; $starttimemem = $starttimecnv; @@ -148,7 +148,7 @@ function scheduler_print_schedulebox(scheduler $scheduler, $studentid, $groupid $actionurl = new moodle_url($baseurl, array('what' => 'addslot')); if (!$scheduler->has_available_teachers()) { - print_error('needteachers', 'scheduler', viewurl); + throw new moodle_exception('needteachers', 'scheduler', viewurl); } $mform = new scheduler_editslot_form($actionurl, $scheduler, $cm, $groupsicansee); @@ -184,19 +184,62 @@ function scheduler_print_schedulebox(scheduler $scheduler, $studentid, $groupid $timeoptions = array('step' => 5, 'optional' => false); } - $actionurl = new moodle_url($baseurl, array('what' => 'updateslot', 'slotid' => $slotid)); + $actionurl = new moodle_url($baseurl, array('what' => 'updateslot', 'slotid' => $slotid, 'sesskey' => sesskey())); + + // Paging. + $appointmentsperpage = get_config('mod_scheduler', 'appointmentsperpage'); + + $pagingbar = null; + $lastpage = false; + if (!empty($appointmentsperpage)) { + + global $DB; + $appointments = $DB->count_records('scheduler_appointment', array('slotid' => $slotid)); + + $pagesize = $appointmentsperpage; + $repeats = $appointmentsperpage; + if ($offset == -1) { + if ($appointments > $pagesize) { + $offset = floor($appointments / $pagesize); + } else { + $offset = 0; + } + } + if ($offset * $pagesize >= $appointments && $appointments > 0) { + $offset = floor(($appointments - 1) / $pagesize); + } + + if ($appointments - $offset * $pagesize <= $pagesize) { + $repeats = $appointments - $offset * $pagesize; + $lastpage = true; + } + + global $output; + $pagingbar = $output->paging_bar($appointments, $offset, $appointmentsperpage, $actionurl, 'offset'); + } + + $mform = new scheduler_editslot_form($actionurl, $scheduler, $cm, $groupsicansee, [ + 'slotid' => $slotid, + 'scheduler' => $scheduler->id, + 'timeoptions' => $timeoptions, + 'repeats' => $repeats, + 'offset' => $offset, + 'lastpage' => $lastpage, + 'pagingbar' => $pagingbar + ]); + $data = $mform->prepare_formdata($slot, $offset); - $mform = new scheduler_editslot_form($actionurl, $scheduler, $cm, $groupsicansee, array( - 'slotid' => $slotid, - 'timeoptions' => $timeoptions) - ); - $data = $mform->prepare_formdata($slot); $mform->set_data($data); if ($mform->is_cancelled()) { redirect($viewurl); } else if ($formdata = $mform->get_data()) { $mform->save_slot($slotid, $formdata); + + if (isset($formdata->savechangesandcontinueediting)) { + $viewurl = $actionurl; + } + redirect($viewurl, get_string('slotupdated', 'scheduler'), 0, @@ -204,7 +247,9 @@ function scheduler_print_schedulebox(scheduler $scheduler, $studentid, $groupid } else { echo $output->header(); echo $output->heading(get_string('updatesingleslot', 'scheduler')); + $mform->display(); + echo $output->footer($course); die; } @@ -218,7 +263,7 @@ function scheduler_print_schedulebox(scheduler $scheduler, $studentid, $groupid $actionurl = new moodle_url($baseurl, array('what' => 'addsession')); if (!$scheduler->has_available_teachers()) { - print_error('needteachers', 'scheduler', $viewurl); + throw new moodle_exception('needteachers', 'scheduler', $viewurl); } $mform = new scheduler_addsession_form($actionurl, $scheduler, $cm, $groupsicansee); @@ -422,9 +467,6 @@ function scheduler_print_schedulebox(scheduler $scheduler, $studentid, $groupid } } -// Print intro. -echo $output->mod_intro($scheduler); - if ($subpage == 'allappointments') { $teacherid = 0; @@ -641,7 +683,8 @@ function scheduler_print_schedulebox(scheduler $scheduler, $studentid, $groupid $groupcnt = 0; foreach ($groupsicanschedule as $group) { - $members = groups_get_members($group->id, 'u.*', 'u.lastname, u.firstname'); + $members = groups_get_members($group->id, + implode(',', \core_user\fields::get_picture_fields()), 'lastname, firstname'); if (empty($members)) { continue; } diff --git a/templates/studentid.mustache b/templates/studentid.mustache new file mode 100644 index 00000000..42658cd0 --- /dev/null +++ b/templates/studentid.mustache @@ -0,0 +1,52 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template mod_scheduler/studentid + + Moodle template for the list of valid options in an autocomplate form element. + + Classes required for JS: + * none + + Data attributes required for JS: + * none + + Context variables required for this template: + * fullname string Users full name + * email string user email field + + Example context (json): + { + "fullname": "Admin User", + "extrafields": [ + { + "name": "email", + "value": "admin@example.com" + }, + { + "name": "phone1", + "value": "0123456789" + } + ] + } +}} + + {{fullname}} + {{#extrafields}} + {{{value}}} + {{/extrafields}} + diff --git a/tests/behat/add_slots.feature b/tests/behat/add_slots.feature index 016d2053..b4ab4952 100644 --- a/tests/behat/add_slots.feature +++ b/tests/behat/add_slots.feature @@ -26,7 +26,6 @@ Feature: Teacher can add slots to a scheduler activity | activity | name | intro | course | idnumber | | scheduler | Test scheduler | n | C1 | scheduler1 | - @javascript Scenario: Teacher adds a single, empty slot to the scheduler When I am on the "scheduler1" Activity page logged in as teacher1 And I click on "Add slots" "link" @@ -40,7 +39,6 @@ Feature: Teacher can add slots to a scheduler activity Then I should see "1 slot added" And I should see "Friday, 1 April 2050" - @javascript Scenario: Teacher enters invalid values when adding a slot When I am on the "scheduler1" Activity page logged in as teacher1 And I click on "Add slots" "link" @@ -91,7 +89,6 @@ Feature: Teacher can add slots to a scheduler activity And I should see "Student 2" And I should see "Student 3" - @javascript Scenario: Teacher creates 10 slots at once When I log in as "teacher1" And I add 10 slots 5 days ahead in "scheduler1" scheduler and I fill the form with: diff --git a/tests/behat/behat_mod_scheduler.php b/tests/behat/behat_mod_scheduler.php index 1fe17915..5c55add5 100644 --- a/tests/behat/behat_mod_scheduler.php +++ b/tests/behat/behat_mod_scheduler.php @@ -40,7 +40,8 @@ class behat_mod_scheduler extends behat_base { /** * Adds a series of slots to the scheduler * - * @Given /^I add a slot (\d+) days ahead at (\d+) in "(?P(?:[^"]|\\")*)" scheduler and I fill the form with:$/ + * @Given /^I add a slot (\d+) days ahead at (\d+) in "(?P(?:[^"]|\\")*)" scheduler + * and I fill the form with:$/ * * @param int $daysahead * @param int $time @@ -140,7 +141,7 @@ public function i_add_the_upcoming_events_block_globally() { array('user', 'role'), array('globalmanager1', 'manager') )) ) ); - + $this->execute('behat_auth::i_log_in_as', 'globalmanager1'); $this->execute('behat_general::i_am_on_site_homepage'); $this->execute('behat_navigation::i_turn_editing_mode_on'); @@ -169,7 +170,8 @@ public function i_click_on_item_in_the_nth_autocomplete_list($item, $listnumber) $downarrowtarget = "(//span[contains(@class,'form-autocomplete-downarrow')])[$listnumber]"; $this->execute('behat_general::i_click_on', [$downarrowtarget, 'xpath_element']); - $xpathtarget = "(//ul[@class='form-autocomplete-suggestions']//*[contains(concat('|', string(.), '|'),'|" . $item . "|')])[$listnumber]"; + $xpathtarget = "(//ul[@class='form-autocomplete-suggestions']//*[contains(concat('|', string(.), '|'),'|" . + $item . "|')])[$listnumber]"; $this->execute('behat_general::i_click_on', [$xpathtarget, 'xpath_element']); } } diff --git a/tests/behat/conflicts.feature b/tests/behat/conflicts.feature index cef689e3..a4b5051e 100644 --- a/tests/behat/conflicts.feature +++ b/tests/behat/conflicts.feature @@ -27,14 +27,17 @@ Feature: Teachers are warned about scheduling conflicts | scheduler | Test scheduler A | n | C1 | schedulerA | 0 | oneonly | 1 | | scheduler | Test scheduler B | n | C1 | schedulerB | 0 | oneonly | 1 | - @javascript Scenario: A teacher edits a single slot and is warned about conflicts - Given I log in as "teacher1" - And I add 5 slots 5 days ahead in "schedulerA" scheduler and I fill the form with: - | Location | My office | - And I add a slot 5 days ahead at 1000 in "schedulerB" scheduler and I fill the form with: - | Location | My office | + Given the following "mod_scheduler > slots" exist: + | scheduler | starttime | duration | teacher | location | + | schedulerA | ##tomorrow 1:00am## | 45 | teacher1 | My office | + | schedulerA | ##tomorrow 2:00am## | 45 | teacher1 | My office | + | schedulerA | ##tomorrow 3:00am## | 45 | teacher1 | My office | + | schedulerA | ##tomorrow 4:00am## | 45 | teacher1 | My office | + | schedulerA | ##tomorrow 5:00am## | 45 | teacher1 | My office | + | schedulerB | ##tomorrow 10:00am## | 15 | teacher1 | My office | + And I log in as "teacher1" When I am on the "schedulerA" Activity page And I click on "Edit" "link" in the "2:00 AM" "table_row" @@ -65,16 +68,22 @@ Feature: Teachers are warned about scheduling conflicts And "9:55 AM" "table_row" should exist And I log out - @javascript Scenario: A manager edits slots for several teachers, creating conflicts - Given I log in as "manager1" - And I add 6 slots 5 days ahead in "schedulerA" scheduler and I fill the form with: - | Location | Office T1 | - | Teacher | Teacher 1 | - And I add 5 slots 5 days ahead in "schedulerB" scheduler and I fill the form with: - | Location | Office T2 | - | Teacher | Teacher 2 | + Given the following "mod_scheduler > slots" exist: + | scheduler | starttime | duration | teacher | location | + | schedulerA | ##tomorrow 1:00am## | 45 | teacher1 | Office T1 | + | schedulerA | ##tomorrow 2:00am## | 45 | teacher1 | Office T1 | + | schedulerA | ##tomorrow 3:00am## | 45 | teacher1 | Office T1 | + | schedulerA | ##tomorrow 4:00am## | 45 | teacher1 | Office T1 | + | schedulerA | ##tomorrow 5:00am## | 45 | teacher1 | Office T1 | + | schedulerA | ##tomorrow 6:00am## | 45 | teacher1 | Office T1 | + | schedulerB | ##tomorrow 1:00am## | 45 | teacher2 | Office T2 | + | schedulerB | ##tomorrow 2:00am## | 45 | teacher2 | Office T2 | + | schedulerB | ##tomorrow 3:00am## | 45 | teacher2 | Office T2 | + | schedulerB | ##tomorrow 4:00am## | 45 | teacher2 | Office T2 | + | schedulerB | ##tomorrow 5:00am## | 45 | teacher2 | Office T2 | + And I log in as "manager1" When I am on the "schedulerA" Activity page And I click on "Edit" "link" in the "3:00 AM" "table_row" @@ -111,30 +120,18 @@ Feature: Teachers are warned about scheduling conflicts And I should see "slot updated" And "6:40 AM" "table_row" should exist And "Save changes" "button" should not exist - And I log out - @javascript Scenario: A teacher adds a series of slots, creating conflicts - Given I log in as "teacher1" - And I add a slot 5 days ahead at 0125 in "schedulerA" scheduler and I fill the form with: - | Location | My office | - | duration | 15 | - # Blocks 3 other slots on a 1-hour grid - And I add a slot 5 days ahead at 0225 in "schedulerA" scheduler and I fill the form with: - | Location | My office | - | duration | 100 | - # Booked slot - must not be deleted as conflict - And I add a slot 5 days ahead at 0855 in "schedulerA" scheduler and I fill the form with: - | Location | My office | - | duration | 10 | - | studentid[0] | Student 1 | - # Slot in other scheduler - must not be deleted as conflict - And I add a slot 5 days ahead at 0605 in "schedulerB" scheduler and I fill the form with: - | Location | My office | - | duration | 20 | + Given the following "mod_scheduler > slots" exist: + | scheduler | starttime | duration | teacher | location | student | + | schedulerA | ##+5 days 1:25am## | 15 | teacher1 | My office | | + | schedulerA | ##+5 days 2:25am## | 100 | teacher1 | My office | | + | schedulerA | ##+5 days 8:55am## | 10 | teacher1 | My office | student1 | + | schedulerB | ##+5 days 6:05am## | 20 | teacher1 | My office | | - When I add 10 slots 5 days ahead in "schedulerA" scheduler and I fill the form with: + When I log in as "teacher1" + And I add 10 slots 5 days ahead in "schedulerA" scheduler and I fill the form with: | Location | Lecture hall | Then I should see "conflicting slots" And I should not see "deleted" @@ -152,8 +149,8 @@ Feature: Teachers are warned about scheduling conflicts And "8:00 AM" "table_row" should exist And "9:00 AM" "table_row" should not exist And "10:00 AM" "table_row" should exist - When I am on the "schedulerB" Activity page - Then "6:05 AM" "table_row" should exist + And I am on the "schedulerB" Activity page + And "6:05 AM" "table_row" should exist When I add 10 slots 5 days ahead in "schedulerA" scheduler and I fill the form with: | Location | Lecture hall | @@ -174,8 +171,5 @@ Feature: Teachers are warned about scheduling conflicts And "8:00 AM" "table_row" should exist And "9:00 AM" "table_row" should not exist And "10:00 AM" "table_row" should exist - And I am on "Course 1" course homepage - When I am on the "schedulerB" Activity page - Then "6:05 AM" "table_row" should exist - - And I log out + And I am on the "schedulerB" Activity page + And "6:05 AM" "table_row" should exist diff --git a/tests/behat/grades.feature b/tests/behat/grades.feature new file mode 100644 index 00000000..4754bb43 --- /dev/null +++ b/tests/behat/grades.feature @@ -0,0 +1,201 @@ +@mod @mod_scheduler +Feature: Teachers can grade student appointments with totals automatically computed + In order to grade a student + As a teacher + I need to enter the grade into the appointment screen + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Editingteacher | 1 | teacher1@example.com | + | student1 | Student | 1 | student1@example.com | + | student2 | Student | 2 | student2@example.com | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + | student2 | C1 | student | + And the following "activities" exist: + | activity | name | intro | course | idnumber | grade | + | scheduler | Test scheduler | n | C1 | scheduler1 | 10 | + And the following "mod_scheduler > slots" exist: + | scheduler | starttime | duration | teacher | location | student | + | scheduler1 | ##tomorrow 3am## | 45 | teacher1 | Here | student2 | + | scheduler1 | ##tomorrow 4am## | 45 | teacher1 | Here | student2 | + | scheduler1 | ##tomorrow 5am## | 45 | teacher1 | Here | student1 | + | scheduler1 | ##tomorrow 6am## | 45 | teacher1 | Here | student2 | + + Scenario: Teachers can enter a grade for a student + When I am on the "scheduler1" Activity page logged in as "teacher1" + And I click on "Student 1" "text" in the "5:00 AM" "table_row" + And I set the following fields to these values: + | Grade | 7 | + And I click on "Save" "button" + Then I should see "7/10" in the "div.totalgrade" "css_element" + And I am on the "scheduler1" Activity page + And I should see "7/10" in the "5:00 AM" "table_row" + And I log out + + When I am on the "scheduler1" Activity page logged in as "student1" + Then I should see "7/10" + + When I am on the "C1" Course page + And I follow "Grades" in the user menu + And I follow "Course 1" + Then I should see "7.00" in the "Test scheduler" "table_row" + And I log out + + Scenario: Teachers can enter several grades for a student, and the best is taken + When I am on the "scheduler1" Activity page logged in as "teacher1" + And I navigate to "Settings" in current page administration + And I set the field "Grading strategy" to "Take the highest grade" + And I press "Save and display" + + And I click on "Student 2" "text" in the "3:00 AM" "table_row" + And I set the following fields to these values: + | Grade | 3 | + And I click on "Save" "button" + Then I should see "3/10" in the "div.totalgrade" "css_element" + And I log out + + When I am on the "C1" Course page logged in as "student2" + And I follow "Grades" in the user menu + And I follow "Course 1" + Then I should see "3.00" in the "Test scheduler" "table_row" + And I log out + + When I am on the "scheduler1" Activity page logged in as "teacher1" + And I click on "Student 2" "text" in the "6:00 AM" "table_row" + And I set the following fields to these values: + | Grade | 6 | + And I click on "Save" "button" + Then I should see "6/10" in the "div.totalgrade" "css_element" + + When I am on the "scheduler1" Activity page + And I click on "Student 2" "text" in the "4:00 AM" "table_row" + And I set the following fields to these values: + | Grade | 4 | + And I click on "Save" "button" + And I should see "6/10" in the "div.totalgrade" "css_element" + And I log out + + When I am on the "C1" Course page logged in as "student2" + And I follow "Grades" in the user menu + And I follow "Course 1" + Then I should see "6.00" in the "Test scheduler" "table_row" + And I log out + + When I am on the "scheduler1" Activity page logged in as "teacher1" + And I click on "Student 2" "text" in the "6:00 AM" "table_row" + And I set the following fields to these values: + | Grade | 2 | + And I click on "Save" "button" + And I should see "4/10" in the "div.totalgrade" "css_element" + And I log out + And I am on the "C1" Course page logged in as "student2" + And I follow "Grades" in the user menu + And I follow "Course 1" + Then I should see "4.00" in the "Test scheduler" "table_row" + And I log out + + When I am on the "scheduler1" Activity page logged in as "teacher1" + And I click on "Student 2" "text" in the "3:00 AM" "table_row" + And I set the following fields to these values: + | Grade | 8 | + And I click on "Save" "button" + And I should see "8/10" in the "div.totalgrade" "css_element" + And I log out + And I am on the "C1" Course page logged in as "student2" + And I follow "Grades" in the user menu + And I follow "Course 1" + Then I should see "8.00" in the "Test scheduler" "table_row" + And I log out + + Scenario: Teachers can switch from best grade to mean value of grades + + When I am on the "scheduler1" Activity page logged in as "teacher1" + And I navigate to "Settings" in current page administration + And I set the field "Grading strategy" to "Take the highest grade" + And I press "Save and display" + And I click on "Student 2" "text" in the "3:00 AM" "table_row" + And I set the following fields to these values: + | Grade | 3 | + And I click on "Save" "button" + And I should see "3/10" in the "div.totalgrade" "css_element" + And I am on the "scheduler1" Activity page + And I click on "Student 2" "text" in the "4:00 AM" "table_row" + And I set the following fields to these values: + | Grade | 9 | + And I click on "Save" "button" + And I should see "9/10" in the "div.totalgrade" "css_element" + And I am on the "scheduler1" Activity page + And I click on "Student 2" "text" in the "6:00 AM" "table_row" + And I set the following fields to these values: + | Grade | 4 | + And I should see "9/10" in the "div.totalgrade" "css_element" + And I click on "Save" "button" + And I log out + And I am on the "C1" Course page logged in as "student2" + And I follow "Grades" in the user menu + And I follow "Course 1" + Then I should see "9.00" in the "Test scheduler" "table_row" + And I log out + + When I am on the "scheduler1" Activity page logged in as "teacher1" + And I navigate to "Settings" in current page administration + And I set the field "Grading strategy" to "Take the mean grade" + And I press "Save and display" + And I am on the "C1" Course page logged in as "student2" + And I follow "Grades" in the user menu + And I follow "Course 1" + Then I should see "5.33" in the "Test scheduler" "table_row" + And I log out + + @javascript + Scenario: Teachers can edit grades via the edit slot form + + When I am on the "scheduler1" Activity page logged in as "teacher1" + And I navigate to "Settings" in current page administration + And I expand all fieldsets + And I set the field "Grading strategy" to "Take the highest grade" + And I press "Save and display" + And I click on "Edit" "icon" in the "3:00 AM" "table_row" + And I set the following fields to these values: + | grade[0] | 5 | + And I click on "Save" "button" + Then I should see "5/10" in the "3:00 AM" "table_row" + And I click on "Student 2" "text" in the "3:00 AM" "table_row" + And I should see "5/10" in the "div.totalgrade" "css_element" + + When I am on the "scheduler1" Activity page logged in as "teacher1" + And I click on "Edit" "icon" in the "4:00 AM" "table_row" + And I set the following fields to these values: + | grade[0] | 7 | + And I click on "Save" "button" + Then I should see "5/10" in the "3:00 AM" "table_row" + And I should see "7/10" in the "4:00 AM" "table_row" + And I click on "Student 2" "text" in the "3:00 AM" "table_row" + And I should see "7/10" in the "div.totalgrade" "css_element" + + When I am on the "scheduler1" Activity page logged in as "teacher1" + And I click on "Edit" "icon" in the "4:00 AM" "table_row" + And I set the following fields to these values: + | grade[0] | 2 | + And I click on "Save" "button" + Then I should see "5/10" in the "3:00 AM" "table_row" + And I should see "2/10" in the "4:00 AM" "table_row" + And I click on "Student 2" "text" in the "3:00 AM" "table_row" + And I should see "5/10" in the "div.totalgrade" "css_element" + + When I am on the "scheduler1" Activity page logged in as "teacher1" + And I click on "Edit" "icon" in the "3:00 AM" "table_row" + And I set the following fields to these values: + | grade[0] | No grade | + And I click on "Save" "button" + Then I should not see "/10" in the "3:00 AM" "table_row" + But I should see "2/10" in the "4:00 AM" "table_row" + And I click on "Student 2" "text" in the "3:00 AM" "table_row" + And I should see "2/10" in the "div.totalgrade" "css_element" diff --git a/tests/behat/groupmode.feature b/tests/behat/groupmode.feature index 375b6ee2..21049857 100644 --- a/tests/behat/groupmode.feature +++ b/tests/behat/groupmode.feature @@ -74,7 +74,6 @@ Feature: Users can only see their own groups if the scheduler is in group mode | Location | There | And I log out - @javascript Scenario: Editing teachers can see all slots and all groups When I am on the "schedulerNone" Activity page logged in as "edteacher1" And I follow "Statistics" @@ -90,21 +89,21 @@ Feature: Users can only see their own groups if the scheduler is in group mode And I follow "Statistics" And I follow "All appointments" Then I should see "Visible groups" - And the "group" select box should contain "All participants" - And the "group" select box should contain "Group A" - And the "group" select box should contain "Group B" - And the "group" select box should contain "Group C" - And the "group" select box should contain "Group D" - When I set the field "group" to "All participants" + And the "Visible groups" select box should contain "All participants" + And the "Visible groups" select box should contain "Group A" + And the "Visible groups" select box should contain "Group B" + And the "Visible groups" select box should contain "Group C" + And the "Visible groups" select box should contain "Group D" + When I select "All participants" from the "Visible groups" singleselect Then I should see "Editingteacher 1" in the "slotmanager" "table" And I should see "Nonedteacher 1" in the "slotmanager" "table" - When I set the field "group" to "Group A" + When I select "Group A" from the "Visible groups" singleselect Then I should see "Editingteacher 1" in the "slotmanager" "table" And I should not see "Nonedteacher 1" in the "slotmanager" "table" - When I set the field "group" to "Group B" + When I select "Group B" from the "Visible groups" singleselect Then I should see "Editingteacher 1" in the "slotmanager" "table" And I should see "Nonedteacher 1" in the "slotmanager" "table" - When I set the field "group" to "Group C" + When I select "Group C" from the "Visible groups" singleselect Then I should not see "Editingteacher 1" in the "slotmanager" "table" And I should see "Nonedteacher 1" in the "slotmanager" "table" @@ -112,33 +111,33 @@ Feature: Users can only see their own groups if the scheduler is in group mode And I follow "Statistics" And I follow "All appointments" Then I should see "Separate groups" - And the "group" select box should contain "All participants" - And the "group" select box should contain "Group A" - And the "group" select box should contain "Group B" - And the "group" select box should contain "Group C" - And the "group" select box should contain "Group D" - When I set the field "group" to "All participants" + And the "Separate groups" select box should contain "All participants" + And the "Separate groups" select box should contain "Group A" + And the "Separate groups" select box should contain "Group B" + And the "Separate groups" select box should contain "Group C" + And the "Separate groups" select box should contain "Group D" + When I select "All participants" from the "Separate groups" singleselect Then I should see "Editingteacher 1" in the "slotmanager" "table" And I should see "Nonedteacher 1" in the "slotmanager" "table" And I should see "Student 1" in the "studentstoschedule" "table" And I should see "Student 3" in the "studentstoschedule" "table" And I should see "Student 5" in the "studentstoschedule" "table" And I should see "Student 6" in the "studentstoschedule" "table" - When I set the field "group" to "Group A" + When I select "Group A" from the "Separate groups" singleselect Then I should see "Editingteacher 1" in the "slotmanager" "table" And I should not see "Nonedteacher 1" in the "slotmanager" "table" And I should see "Student 1" in the "studentstoschedule" "table" And I should not see "Student 3" in the "studentstoschedule" "table" And I should not see "Student 5" in the "studentstoschedule" "table" And I should not see "Student 6" in the "studentstoschedule" "table" - When I set the field "group" to "Group B" + When I select "Group B" from the "Separate groups" singleselect Then I should see "Editingteacher 1" in the "slotmanager" "table" And I should see "Nonedteacher 1" in the "slotmanager" "table" And I should not see "Student 1" in the "studentstoschedule" "table" And I should see "Student 3" in the "studentstoschedule" "table" And I should not see "Student 5" in the "studentstoschedule" "table" And I should not see "Student 6" in the "studentstoschedule" "table" - When I set the field "group" to "Group C" + When I select "Group C" from the "Separate groups" singleselect Then I should not see "Editingteacher 1" in the "slotmanager" "table" And I should see "Nonedteacher 1" in the "slotmanager" "table" @@ -156,7 +155,6 @@ Feature: Users can only see their own groups if the scheduler is in group mode And I should not see "Student 6" in the "studentstoschedule" "table" And I log out - @javascript Scenario: Nonediting teachers can see groups only if allowed by the group mode When I am on the "schedulerNone" Activity page logged in as neteacher1 @@ -173,21 +171,21 @@ Feature: Users can only see their own groups if the scheduler is in group mode Then I should see "2 students still need to make an appointment" When I follow "All appointments" Then I should see "Visible groups" - And the "group" select box should contain "All participants" - And the "group" select box should contain "Group A" - And the "group" select box should contain "Group B" - And the "group" select box should contain "Group C" - And the "group" select box should contain "Group D" - When I set the field "group" to "All participants" + And the "Visible groups" select box should contain "All participants" + And the "Visible groups" select box should contain "Group A" + And the "Visible groups" select box should contain "Group B" + And the "Visible groups" select box should contain "Group C" + And the "Visible groups" select box should contain "Group D" + When I select "All participants" from the "Visible groups" singleselect Then I should see "Editingteacher 1" in the "slotmanager" "table" And I should see "Nonedteacher 1" in the "slotmanager" "table" - When I set the field "group" to "Group A" + When I select "Group A" from the "Visible groups" singleselect Then I should see "Editingteacher 1" in the "slotmanager" "table" And I should not see "Nonedteacher 1" in the "slotmanager" "table" - When I set the field "group" to "Group B" + When I select "Group B" from the "Visible groups" singleselect Then I should see "Editingteacher 1" in the "slotmanager" "table" And I should see "Nonedteacher 1" in the "slotmanager" "table" - When I set the field "group" to "Group C" + When I select "Group C" from the "Visible groups" singleselect Then I should not see "Editingteacher 1" in the "slotmanager" "table" And I should see "Nonedteacher 1" in the "slotmanager" "table" @@ -197,24 +195,24 @@ Feature: Users can only see their own groups if the scheduler is in group mode Then I should see "2 students still need to make an appointment" When I follow "All appointments" Then I should see "Separate groups" - And the "group" select box should not contain "All participants" - And the "group" select box should not contain "Group A" - And the "group" select box should contain "Group B" - And the "group" select box should contain "Group C" - And the "group" select box should not contain "Group D" - When I set the field "group" to "Group B" + And the "Separate groups" select box should not contain "All participants" + And the "Separate groups" select box should not contain "Group A" + And the "Separate groups" select box should contain "Group B" + And the "Separate groups" select box should contain "Group C" + And the "Separate groups" select box should not contain "Group D" + When I select "Group B" from the "Separate groups" singleselect Then I should see "Editingteacher 1" in the "slotmanager" "table" And I should see "Nonedteacher 1" in the "slotmanager" "table" - When I set the field "group" to "Group C" + When I select "Group C" from the "Separate groups" singleselect Then I should not see "Editingteacher 1" in the "slotmanager" "table" And I should see "Nonedteacher 1" in the "slotmanager" "table" - When I set the field "group" to "Group B" - And I click on "Edit" "link_or_button" in the "Nonedteacher 1" "table_row" - Then I should see "Appointment 1" - And "Student 1" "option" should not exist in the "studentid[0]" "field" - And "Student 3" "option" should exist in the "studentid[0]" "field" - And I click on "Save changes" "button" +# When I select "Group B" from the "Separate groups" singleselect +# And I click on "Edit" "link_or_button" in the "Nonedteacher 1" "table_row" +# Then I should see "Appointment 1" +# And "Student 1" "option" should not exist in the "studentid[0]" "field" +# And "Student 3" "option" should exist in the "studentid[0]" "field" +# And I click on "Save changes" "button" # In the "My appointments" tab, the teacher should only see students to schedule from their groups, # i.e., group B (and C). @@ -253,14 +251,13 @@ Feature: Users can only see their own groups if the scheduler is in group mode And I should see "Group mode: Separate groups" And I should see "students cannot book appointments with you" - @javascript Scenario: Students can see slots available to their own groups, or a slots if group mode is off When I log in as "student1" - + When I am on the "schedulerNone" Activity page Then I should see "Editingteacher 1" And I should see "Nonedteacher 1" - + When I am on the "schedulerVis" Activity page Then I should see "Editingteacher 1" And I should not see "Nonedteacher 1" @@ -275,7 +272,7 @@ Feature: Users can only see their own groups if the scheduler is in group mode When I am on the "schedulerNone" Activity page Then I should see "Editingteacher 1" And I should see "Nonedteacher 1" - + When I am on the "schedulerVis" Activity page Then I should see "Editingteacher 1" And I should see "Nonedteacher 1" @@ -286,11 +283,11 @@ Feature: Users can only see their own groups if the scheduler is in group mode And I log out When I log in as "student5" - + When I am on the "schedulerNone" Activity page Then I should see "Editingteacher 1" And I should see "Nonedteacher 1" - + When I am on the "schedulerVis" Activity page Then I should see "No slots are available" @@ -299,7 +296,7 @@ Feature: Users can only see their own groups if the scheduler is in group mode And I log out When I log in as "student6" - + When I am on the "schedulerNone" Activity page Then I should see "Editingteacher 1" And I should see "Nonedteacher 1" @@ -311,7 +308,6 @@ Feature: Users can only see their own groups if the scheduler is in group mode Then I should see "No slots are available" And I log out - @javascript Scenario: Students can see slots available to their own groups in forced group mode When I log in as "edteacher1" And I am on "Course 1" course homepage diff --git a/tests/behat/groupscheduling.feature b/tests/behat/groupscheduling.feature index 8455dec3..c8737341 100644 --- a/tests/behat/groupscheduling.feature +++ b/tests/behat/groupscheduling.feature @@ -104,7 +104,6 @@ Feature: Entire groups can be booked into slots at once And I should not see "Group B1" in the "groupstoschedule" "table" And I should not see "Group B2" in the "groupstoschedule" "table" - @javascript Scenario: Students can book their entire group into a slot Given I am logged in as edteacher1 And I add 8 slots 5 days ahead in "schedulerNone" scheduler and I fill the form with: @@ -122,7 +121,7 @@ Feature: Entire groups can be booked into slots at once And the "appointgroup" select box should not contain "Group A2" And the "appointgroup" select box should not contain "Group B2" - When I set the field "appointgroup" to "Group A1" + When I select "Group A1" from the "appointgroup" singleselect And I click on "Book slot" "button" in the "8:00 AM" "table_row" Then I should see "8:00 AM" in the "Large office" "table_row" And I log out diff --git a/tests/behat/notes.feature b/tests/behat/notes.feature index c77de8ae..0c68ddec 100644 --- a/tests/behat/notes.feature +++ b/tests/behat/notes.feature @@ -21,12 +21,12 @@ Feature: Teachers can write notes on slots and appointments And the following "activities" exist: | activity | name | intro | course | idnumber | usenotes | | scheduler | Test scheduler | n | C1 | scheduler1 | 3 | - And I log in as "edteacher1" - And I add 5 slots 10 days ahead in "scheduler1" scheduler and I fill the form with: - | Location | Here | - And I log out + And the following "mod_scheduler > slots" exist: + | scheduler | starttime | duration | teacher | location | + | scheduler1 | ##tomorrow 3am## | 45 | edteacher1 | Here | + | scheduler1 | ##tomorrow 4am## | 45 | edteacher1 | Here | + | scheduler1 | ##tomorrow 5am## | 45 | edteacher1 | Here | - @javascript Scenario: Teachers can enter slot notes and appointment notes for others to see When I am on the "scheduler1" Activity page logged in as "edteacher1" And I follow "Statistics" @@ -49,7 +49,7 @@ Feature: Teachers can write notes on slots and appointments When I am on the "scheduler1" Activity page logged in as "edteacher1" And I follow "Statistics" And I follow "All appointments" - And I click on "//a[text()='Student 1']" "xpath_element" in the "4:00 AM" "table_row" + And I click on "Student 1" "text" in the "4:00 AM" "table_row" Then I should see ", 4:00 AM" in the "Date and time" "table_row" And I should see "4:45 AM" in the "Date and time" "table_row" And I should see "Editingteacher 1" in the "Teacher" "table_row" @@ -68,7 +68,6 @@ Feature: Teachers can write notes on slots and appointments And I should not see "note-confidential" And I log out - @javascript Scenario: Teachers see only the comments fields specified in the configuration When I am on the "scheduler1" Activity page logged in as "student1" @@ -79,7 +78,7 @@ Feature: Teachers can write notes on slots and appointments When I am on the "scheduler1" Activity page logged in as "edteacher1" And I follow "Statistics" And I follow "All appointments" - And I click on "//a[text()='Student 1']" "xpath_element" in the "4:00 AM" "table_row" + And I click on "Student 1" "text" in the "4:00 AM" "table_row" And I set the following fields to these values: | Notes for appointment (visible to student) | note-for-appointment | | Confidential notes (visible to teacher only) | note-confidential | @@ -108,7 +107,7 @@ Feature: Teachers can write notes on slots and appointments And I navigate to "Settings" in current page administration And I set the field "Use notes for appointments" to "1" And I click on "Save and display" "button" - And I click on "//a[text()='Student 1']" "xpath_element" in the "4:00 AM" "table_row" + And I click on "Student 1" "text" in the "4:00 AM" "table_row" Then I should see "Notes for appointment" And I should see "note-for-appointment" And I should not see "Confidential notes" @@ -125,7 +124,7 @@ Feature: Teachers can write notes on slots and appointments And I navigate to "Settings" in current page administration And I set the field "Use notes for appointments" to "2" And I click on "Save and display" "button" - And I click on "//a[text()='Student 1']" "xpath_element" in the "4:00 AM" "table_row" + And I click on "Student 1" "text" in the "4:00 AM" "table_row" Then I should not see "Notes for appointment" And I should not see "note-for-appointment" And I should see "Confidential notes" @@ -142,7 +141,7 @@ Feature: Teachers can write notes on slots and appointments And I navigate to "Settings" in current page administration And I set the field "Use notes for appointments" to "3" And I click on "Save and display" "button" - And I click on "//a[text()='Student 1']" "xpath_element" in the "4:00 AM" "table_row" + And I click on "Student 1" "text" in the "4:00 AM" "table_row" Then I should see "Notes for appointment" And I should see "note-for-appointment" And I should see "Confidential notes" diff --git a/tests/behat/teacherpermissions.feature b/tests/behat/teacherpermissions.feature index 3348774e..5fa44025 100644 --- a/tests/behat/teacherpermissions.feature +++ b/tests/behat/teacherpermissions.feature @@ -27,26 +27,15 @@ Feature: Teachers can edit other teacher's appointments only by permission And the following "activities" exist: | activity | name | intro | course | idnumber | groupmode | usenotes | grade | | scheduler | Test scheduler | n | C1 | scheduler1 | 0 | 3 | 100 | + And the following "mod_scheduler > slots" exist: + | scheduler | starttime | duration | teacher | location | student | + | scheduler1 | ##tomorrow 3am## | 15 | edteacher1 | Office ed1 | student1 | + | scheduler1 | ##tomorrow 4am## | 15 | neteacher1 | Office ne1 | student2 | + | scheduler1 | ##tomorrow 5am## | 15 | neteacher2 | Office ne2 | student3 | And the following "permission overrides" exist: | capability | permission | role | contextlevel | reference | | mod/scheduler:canseeotherteachersbooking | Allow | teacher | Course | C1 | - And I log in as "edteacher1" - And I add a slot 10 days ahead at 0300 in "scheduler1" scheduler and I fill the form with: - | Location | Office ed1 | - | studentid[0] | Student 1 | - And I log out - And I log in as "neteacher1" - And I add a slot 10 days ahead at 0400 in "scheduler1" scheduler and I fill the form with: - | Location | Office ne1 | - | studentid[0] | Student 2 | - And I log out - And I log in as "neteacher2" - And I add a slot 10 days ahead at 0500 in "scheduler1" scheduler and I fill the form with: - | Location | Office ne2 | - | studentid[0] | Student 3 | - And I log out - @javascript Scenario: Editing teachers edit all appointments, nonediting teachers only their own When I am on the "scheduler1" Activity page logged in as "edteacher1" And I follow "Statistics" @@ -57,13 +46,13 @@ Feature: Teachers can edit other teacher's appointments only by permission And "seen[]" "checkbox" should exist in the "4:00 AM" "table_row" And I should see "Student 3" in the "5:00 AM" "table_row" And "seen[]" "checkbox" should exist in the "5:00 AM" "table_row" - When I click on "//a[text()='Student 3']" "xpath_element" in the "5:00 AM" "table_row" + When I click on "Student 3" "text" in the "5:00 AM" "table_row" Then the "Attended" "checkbox" should be enabled And "Notes for appointment (visible to student)" "field" should exist And "Confidential notes (visible to teacher only)" "field" should exist And the "grade" "field" should be enabled When I set the following fields to these values: - | Attended | 1 | + | Attended | 1 | And I click on "Save changes" "button" Then the field "Attended" matches value "1" And I log out @@ -77,26 +66,25 @@ Feature: Teachers can edit other teacher's appointments only by permission And "seen[]" "checkbox" should exist in the "4:00 AM" "table_row" And I should see "Student 3" in the "5:00 AM" "table_row" And "seen[]" "checkbox" should not exist in the "5:00 AM" "table_row" - When I click on "//a[text()='Student 2']" "xpath_element" in the "4:00 AM" "table_row" + When I click on "Student 2" "text" in the "4:00 AM" "table_row" Then the "Attended" "checkbox" should be enabled And "Notes for appointment (visible to student)" "field" should exist And "Confidential notes (visible to teacher only)" "field" should exist When I set the following fields to these values: - | Attended | 1 | + | Attended | 1 | And I click on "Save changes" "button" Then the field "Attended" matches value "1" - + When I am on the "scheduler1" Activity page And I follow "Statistics" And I follow "All appointments" - And I click on "//a[text()='Student 3']" "xpath_element" in the "5:00 AM" "table_row" + And I click on "Student 3" "text" in the "5:00 AM" "table_row" Then the "Attended" "checkbox" should be disabled And "Notes for appointment (visible to student)" "field" should not exist And "Confidential notes (visible to teacher only)" "field" should not exist And "grade" "field" should not exist And I log out - @javascript Scenario: Attended boxes can be edited if the teacher has permission Given I log in as "admin" And I set the following system permissions of "Non-editing teacher" role: @@ -113,28 +101,27 @@ Feature: Teachers can edit other teacher's appointments only by permission And "seen[]" "checkbox" should exist in the "4:00 AM" "table_row" And I should see "Student 3" in the "5:00 AM" "table_row" And "seen[]" "checkbox" should exist in the "5:00 AM" "table_row" - When I click on "//a[text()='Student 2']" "xpath_element" in the "4:00 AM" "table_row" + When I click on "Student 2" "text" in the "4:00 AM" "table_row" Then the "Attended" "checkbox" should be enabled When I set the following fields to these values: - | Attended | 1 | + | Attended | 1 | And I click on "Save changes" "button" Then the field "Attended" matches value "1" When I am on the "scheduler1" Activity page And I follow "Statistics" And I follow "All appointments" - When I click on "//a[text()='Student 3']" "xpath_element" in the "5:00 AM" "table_row" + When I click on "Student 3" "text" in the "5:00 AM" "table_row" Then the "Attended" "checkbox" should be enabled And "Notes for appointment (visible to student)" "field" should not exist And "Confidential notes (visible to teacher only)" "field" should not exist And "grade" "field" should not exist When I set the following fields to these values: - | Attended | 1 | + | Attended | 1 | And I click on "Save changes" "button" Then the field "Attended" matches value "1" And I log out - @javascript Scenario: Grade boxes can be edited if the teacher has permission Given I log in as "admin" And I set the following system permissions of "Non-editing teacher" role: @@ -151,29 +138,28 @@ Feature: Teachers can edit other teacher's appointments only by permission And "seen[]" "checkbox" should exist in the "4:00 AM" "table_row" And I should see "Student 3" in the "5:00 AM" "table_row" And "seen[]" "checkbox" should not exist in the "5:00 AM" "table_row" - When I click on "//a[text()='Student 2']" "xpath_element" in the "4:00 AM" "table_row" + When I click on "Student 2" "text" in the "4:00 AM" "table_row" Then the "grade" "field" should be enabled When I set the following fields to these values: - | Grade | 42 | + | Grade | 42 | And I click on "Save changes" "button" Then the field "Grade" matches value "42" When I am on the "scheduler1" Activity page And I follow "Statistics" And I follow "All appointments" - And I click on "//a[text()='Student 3']" "xpath_element" in the "5:00 AM" "table_row" + And I click on "Student 3" "text" in the "5:00 AM" "table_row" Then the "grade" "field" should be enabled And the "Attended" "checkbox" should be disabled And "Notes for appointment (visible to student)" "field" should not exist And "Confidential notes (visible to teacher only)" "field" should not exist - + When I set the following fields to these values: - | Grade | 33 | + | Grade | 33 | And I click on "Save changes" "button" Then the field "grade" matches value "33" And I log out - @javascript Scenario: Comment boxes can be edited if the teacher has permission Given I log in as "admin" And I set the following system permissions of "Non-editing teacher" role: @@ -190,12 +176,12 @@ Feature: Teachers can edit other teacher's appointments only by permission And "seen[]" "checkbox" should exist in the "4:00 AM" "table_row" And I should see "Student 3" in the "5:00 AM" "table_row" And "seen[]" "checkbox" should not exist in the "5:00 AM" "table_row" - When I click on "//a[text()='Student 2']" "xpath_element" in the "4:00 AM" "table_row" + When I click on "Student 2" "text" in the "4:00 AM" "table_row" Then the "Notes for appointment (visible to student)" "field" should be enabled And the "Confidential notes (visible to teacher only)" "field" should be enabled When I set the following fields to these values: - | Notes for appointment (visible to student) | notes-vis | - | Confidential notes (visible to teacher only) | notes-confid | + | Notes for appointment (visible to student) | notes-vis | + | Confidential notes (visible to teacher only) | notes-confid | And I click on "Save changes" "button" Then I should see "notes-vis" And I should see "notes-confid" @@ -203,15 +189,14 @@ Feature: Teachers can edit other teacher's appointments only by permission When I am on the "scheduler1" Activity page And I follow "Statistics" And I follow "All appointments" - And I click on "//a[text()='Student 3']" "xpath_element" in the "5:00 AM" "table_row" + And I click on "Student 3" "text" in the "5:00 AM" "table_row" Then "grade" "field" should not exist And the "Attended" "checkbox" should be disabled And the "Notes for appointment (visible to student)" "field" should be enabled And the "Confidential notes (visible to teacher only)" "field" should be enabled When I set the following fields to these values: - | Notes for appointment (visible to student) | notes-vis-3 | - | Confidential notes (visible to teacher only) | notes-confid-3 | + | Notes for appointment (visible to student) | notes-vis-3 | + | Confidential notes (visible to teacher only) | notes-confid-3 | And I click on "Save changes" "button" Then I should see "notes-vis-3" And I should see "notes-confid-3" - And I log out diff --git a/tests/behat/tutorappointments.feature b/tests/behat/tutorappointments.feature index 1b39cc11..12d908b4 100644 --- a/tests/behat/tutorappointments.feature +++ b/tests/behat/tutorappointments.feature @@ -97,7 +97,6 @@ Feature: Booking of appointments with individual tutors per group And I should not see "students still need to make an appointment" And I log out - @javascript Scenario: Several tutors add slots, they can be seen only by relevant users When I log in as "coor1" And I add 10 slots 5 days ahead in "scheduler1" scheduler and I fill the form with: diff --git a/tests/behat/viewslots.feature b/tests/behat/viewslots.feature index 785f1daa..f5cfe1a0 100644 --- a/tests/behat/viewslots.feature +++ b/tests/behat/viewslots.feature @@ -27,37 +27,25 @@ Feature: Students viewing slots available for booking And the following "activities" exist: | activity | name | intro | course | idnumber | groupmode | schedulermode | maxbookings | guardtime | | scheduler | Test scheduler | n | C1 | scheduler1 | 0 | oneonly | 1 | 172800 | - And I log in as "teacher1" + And the following "mod_scheduler > slots" exist: + | scheduler | starttime | duration | teacher | exclusivity | student | hideuntil | # Slot 1 is available to only 1 student and is not yet booked - And I add a slot 5 days ahead at 0100 in "scheduler1" scheduler and I fill the form with: - | exclusivity | 1 | + | scheduler1 | ##+5 days 1:00am## | 45 | teacher1 | 1 | | | # Slot 2 is available to only 1 student and is already booked - And I add a slot 5 days ahead at 0200 in "scheduler1" scheduler and I fill the form with: - | exclusivity | 1 | - | studentid[0] | Student 3 | + | scheduler1 | ##+5 days 2:00am## | 45 | teacher1 | 1 | student3 | | # Slot 3 is a group slot that is empty - And I add a slot 5 days ahead at 0300 in "scheduler1" scheduler and I fill the form with: - | exclusivity | 3 | + | scheduler1 | ##+5 days 3:00am## | 45 | teacher1 | 3 | | | # Slot 4 is a group slot that is partially booked - And I add a slot 5 days ahead at 0400 in "scheduler1" scheduler and I fill the form with: - | exclusivity | 2 | - | studentid[0] | Student 3 | + | scheduler1 | ##+5 days 4:00am## | 45 | teacher1 | 2 | student3 | | # Slot 5 is an unlimited group slot that is empty - And I add a slot 5 days ahead at 0500 in "scheduler1" scheduler and I fill the form with: - | exclusivityenable | 0 | + | scheduler1 | ##+5 days 5:00am## | 45 | teacher1 | 0 | | | # Slot 6 is an unlimited group slot that is partially booked - And I add a slot 5 days ahead at 0600 in "scheduler1" scheduler and I fill the form with: - | exclusivityenable | 0 | - | studentid[0] | Student 3 | + | scheduler1 | ##+5 days 6:00am## | 45 | teacher1 | 0 | student3 | | # Slot 7 is not yet available to students - And I add a slot 5 days ahead at 0700 in "scheduler1" scheduler and I fill the form with: - | hideuntil[year] | 2040 | + | scheduler1 | ##+5 days 7:00am## | 45 | teacher1 | 0 | | ##now +2years## | # Slot 8 is no longer available since the it's too close in the future - And I add a slot 1 days ahead at 0800 in "scheduler1" scheduler and I fill the form with: - | appointmentlocation | My office | - And I log out + | scheduler1 | ##tomorrow 8:00am## | 45 | teacher1 | 0 | | | - @javascript Scenario: A student can see only available upcoming slots (default setting) When I am on the "scheduler1" Activity page logged in as "student1" @@ -85,7 +73,6 @@ Feature: Students viewing slots available for booking And I should not see "4:00 AM" in the "slotbookertable" "table" And I log out - @javascript Scenario: Students can view all slots, even full ones Given the following "permission overrides" exist: | capability | permission | role | contextlevel | reference | @@ -115,14 +102,13 @@ Feature: Students viewing slots available for booking When I click on "Book slot" "button" in the "4:00 AM" "table_row" And I log out - + When I am on the "scheduler1" Activity page logged in as "student2" Then "Book slot" "button" should exist in the "3:00 AM" "table_row" And I should see "4:00 AM" in the "slotbookertable" "table" And "Book slot" "button" should not exist in the "4:00 AM" "table_row" And I log out - @javascript Scenario: Students can view all slots, but they cannot book any Given the following "permission overrides" exist: | capability | permission | role | contextlevel | reference | @@ -143,7 +129,6 @@ Feature: Students viewing slots available for booking And I log out - @javascript Scenario: Students can view bookable slots, but they cannot book any Given the following "permission overrides" exist: | capability | permission | role | contextlevel | reference | diff --git a/tests/generator/behat_mod_scheduler_generator.php b/tests/generator/behat_mod_scheduler_generator.php new file mode 100644 index 00000000..19ec172e --- /dev/null +++ b/tests/generator/behat_mod_scheduler_generator.php @@ -0,0 +1,77 @@ +. + +/** + * Behat data generator for mod_scheduler. + * + * @package mod_scheduler + * @category test + * @copyright 2022 Henning Bostelmann + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_mod_scheduler_generator extends behat_generator_base { + + /** + * Get a list of the entities that Behat can create using the generator step. + * + * @return array + */ + protected function get_creatable_entities(): array { + return [ + 'slots' => [ + 'singular' => 'slot', + 'datagenerator' => 'slot', + 'required' => ['scheduler', 'starttime', 'duration', 'teacher'], + 'switchids' => ['scheduler' => 'schedulerid', 'teacher' => 'teacherid', 'student' => 'studentid'], + ], + ]; + } + + /** + * Get the scheduler CMID using an activity idnumber. + * + * @param string $idnumber + * @return int The cmid + */ + protected function get_scheduler_id(string $idnumber): int { + return $this->get_activity_id($idnumber); + } + + /** + * Get the teacher user ID using a user idnumber. + * + * @param string $idnumber + * @return int The user id + */ + protected function get_teacher_id(string $idnumber): int { + return $this->get_user_id($idnumber); + } + + /** + * Get the student user ID using a user idnumber. + * + * @param string $idnumber + * @return int The user id + */ + protected function get_student_id(string $idnumber): int { + if ($idnumber) { + return $this->get_user_id($idnumber); + } else { + return 0; + } + } + +} diff --git a/tests/generator/lib.php b/tests/generator/lib.php index b9749d74..e1276424 100644 --- a/tests/generator/lib.php +++ b/tests/generator/lib.php @@ -22,8 +22,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -defined('MOODLE_INTERNAL') || die(); - /** * Scheduler module PHPUnit data generator class * @@ -90,7 +88,8 @@ public function create_instance($record = null, array $options = null) { $slot->schedulerid = $id; $slot->starttime = $time; $slot->duration = 10; - $slot->teacherid = isset($options['slotteachers'][$slotkey]) ? $options['slotteachers'][$slotkey] : 2; // Admin user as default. + $slot->teacherid = isset($options['slotteachers'][$slotkey]) ? + $options['slotteachers'][$slotkey] : 2; // Admin user as default. $slot->appointmentlocation = 'Test Loc'; $slot->timemodified = time(); $slot->notes = ''; @@ -120,4 +119,32 @@ public function create_instance($record = null, array $options = null) { return $modinst; } + + /** + * Create a scheduler slot, optionally with appointment for one student`. + * + * @param array $data + */ + public function create_slot(array $data): void { + + $scheduler = \mod_scheduler\model\scheduler::load_by_coursemodule_id($data['schedulerid']); + + $slot = new \mod_scheduler\model\slot($scheduler); + $slot->teacherid = $data['teacherid']; + $slot->starttime = $data['starttime']; + $slot->duration = $data['duration']; + $slot->appointmentlocation = isset($data['location']) ? $data['location'] : ''; + $slot->exclusivity = isset($data['exclusivity']) ? $data['exclusivity'] : 1; + $slot->hideuntil = isset($data['hideuntil']) ? $data['hideuntil'] : 0; + + if (isset($data['studentid']) && $data['studentid'] > 0) { + $app = $slot->create_appointment(); + $app->studentid = $data['studentid']; + $app->seen = isset($data['seen']) ? $data['seen'] : 0; + $app->grade = isset($data['grade']) ? $data['grade'] : -1; + } + + $slot->save(); + } + } diff --git a/tests/model_test.php b/tests/model_test.php index 5b723465..095166d4 100644 --- a/tests/model_test.php +++ b/tests/model_test.php @@ -22,6 +22,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace mod_scheduler; + defined('MOODLE_INTERNAL') || die(); use \mod_scheduler\model\scheduler; @@ -37,7 +39,7 @@ * @copyright 2014 Henning Bostelmann and others (see README.txt) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_scheduler_model_testcase extends advanced_testcase { +class model_test extends \advanced_testcase { /** * @var int Course_modules id used for testing @@ -90,6 +92,8 @@ protected function setUp(): void { /** * Test loading a scheduler instance from the database + * + * @covers \mod_scheduler\model\scheduler::load_by_coursemodule_id */ public function test_scheduler() { global $DB; @@ -105,7 +109,9 @@ public function test_scheduler() { /** * Test the "appointment" data object * (basic functionality, with minimal reference to slots) - **/ + * + * @covers \mod_scheduler\model\scheduler::load_by_coursemodule_id + */ public function test_appointment() { global $DB; @@ -116,7 +122,7 @@ public function test_appointment() { $user = $this->getdataGenerator()->create_user(); - $app0 = new stdClass(); + $app0 = new \stdClass(); $app0->slotid = 1; $app0->studentid = $user->id; $app0->attended = 0; diff --git a/tests/permissions_test.php b/tests/permissions_test.php index 63cca10b..619c75a6 100644 --- a/tests/permissions_test.php +++ b/tests/permissions_test.php @@ -22,6 +22,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace mod_scheduler; + defined('MOODLE_INTERNAL') || die(); use \mod_scheduler\model\scheduler; @@ -38,7 +40,7 @@ * @copyright 2019 Henning Bostelmann and others (see README.txt) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_scheduler_permissions_testcase extends advanced_testcase { +class permissions_test extends \advanced_testcase { /** * @var int Course_modules id used for testing @@ -148,7 +150,11 @@ protected function setUp(): void { } } - + /** + * Tests whether slots can be seen. + * + * @coversNothing + */ public function test_teacher_can_see_slot() { // Editing teacher sees all slots. @@ -177,6 +183,11 @@ public function test_teacher_can_see_slot() { } + /** + * Tests whether slots can be edited. + * + * @coversNothing + */ public function test_can_edit_slot() { // Editing teacher can edit all slots. @@ -205,6 +216,11 @@ public function test_can_edit_slot() { } + /** + * Tests whether own slots can be edited. + * + * @coversNothing + */ public function test_can_edit_own_slots() { // Both teachers can edit their own slots. @@ -221,6 +237,11 @@ public function test_can_edit_own_slots() { } + /** + * Tests whether slots can be edited. + * + * @coversNothing + */ public function test_can_edit_all_slots() { // Editing teachers can edit all slots. @@ -237,7 +258,11 @@ public function test_can_edit_all_slots() { } - + /** + * Tests whether appointments can be seen. + * + * @coversNothing + */ public function test_can_see_all_slots() { // Editing teachers can see all slots. @@ -258,7 +283,11 @@ public function test_can_see_all_slots() { } - + /** + * Test whether appointments can be seen. + * + * @coversNothing + */ public function test_can_see_appointment() { // Editing teacher can all appointments. diff --git a/tests/privacy_test.php b/tests/privacy_test.php index a1bf9e46..dd4abf4e 100644 --- a/tests/privacy_test.php +++ b/tests/privacy_test.php @@ -23,7 +23,11 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ + +namespace mod_scheduler; + defined('MOODLE_INTERNAL') || die(); + global $CFG; use core_privacy\tests\provider_testcase; @@ -41,7 +45,7 @@ * @copyright 2018 Henning Bostelmann * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_scheduler_privacy_testcase extends provider_testcase { +class privacy_test extends provider_testcase { /** * @var int course_module id used for testing @@ -116,7 +120,7 @@ protected function setUp(): void { $this->schedulerid = $scheduler->id; $this->moduleid = $coursemodule->id; - $this->context = context_module::instance($scheduler->cmid); + $this->context = \context_module::instance($scheduler->cmid); $recs = $DB->get_records('scheduler_slots', array('schedulerid' => $scheduler->id), 'id DESC'); $this->slotid = array_keys($recs)[0]; @@ -145,6 +149,8 @@ private function assert_appointment_status($schedulerid, $studentid, $expected) /** * Test getting the contexts for a user. + * + * @covers \mod_scheduler\privacy\provider::get_contexts_for_userid */ public function test_get_contexts_for_userid() { @@ -155,6 +161,8 @@ public function test_get_contexts_for_userid() { /** * Test getting the users within a context. + * + * @covers \mod_scheduler\privacy\provider::get_users_in_context */ public function test_get_users_in_context() { global $DB; @@ -175,6 +183,8 @@ public function test_get_users_in_context() { /** * Export test for teacher data. + * + * @covers \mod_scheduler\privacy\provider::export_user_data */ public function test_export_teacher_data() { global $DB; @@ -190,6 +200,8 @@ public function test_export_teacher_data() { /** * Export test for student1's data. + * + * @covers \mod_scheduler\privacy\provider::export_user_data */ public function test_export_user_data1() { @@ -203,6 +215,8 @@ public function test_export_user_data1() { /** * Test for delete_data_for_all_users_in_context(). + * + * @covers \mod_scheduler\privacy\provider::delete_data_for_all_users_in_context */ public function test_delete_data_for_all_users_in_context() { provider::delete_data_for_all_users_in_context($this->context); @@ -214,6 +228,8 @@ public function test_delete_data_for_all_users_in_context() { /** * Test for delete_data_for_user(). + * + * @covers \mod_scheduler\privacy\provider::delete_data_for_user */ public function test_delete_data_for_user() { $appctx = new approved_contextlist($this->student1, 'mod_scheduler', [$this->context->id]); @@ -226,6 +242,8 @@ public function test_delete_data_for_user() { /** * Test for delete_data_for_users(). + * + * @covers \mod_scheduler\privacy\provider::delete_data_for_users */ public function test_delete_data_for_users() { $component = 'mod_scheduler'; diff --git a/tests/scheduler_test.php b/tests/scheduler_test.php index e7bc511d..41429ebb 100644 --- a/tests/scheduler_test.php +++ b/tests/scheduler_test.php @@ -22,6 +22,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace mod_scheduler; + defined('MOODLE_INTERNAL') || die(); use \mod_scheduler\model\scheduler; @@ -38,7 +40,7 @@ * @copyright 2011 Henning Bostelmann and others (see README.txt) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_scheduler_scheduler_testcase extends advanced_testcase { +class scheduler_test extends \advanced_testcase { /** * @var int Course_module id used for testing @@ -125,6 +127,8 @@ private function assert_record_count($table, $field, $value, $expect) { /** * Test a scheduler instance + * + * @covers \mod_scheduler\model\scheduler::load_by_coursemodule_id */ public function test_scheduler() { global $DB; @@ -139,6 +143,8 @@ public function test_scheduler() { /** * Test the loading of slots + * + * @covers \mod_scheduler\model\scheduler::load_by_coursemodule_id */ public function test_load_slots() { global $DB; @@ -185,6 +191,8 @@ public function test_load_slots() { /** * Test adding slots to a scheduler + * + * @covers \mod_scheduler\model\scheduler::load_by_coursemodule_id */ public function test_add_slot() { @@ -204,6 +212,8 @@ public function test_add_slot() { /** * Test deleting a scheduler + * + * @covers \mod_scheduler\model\scheduler::load_by_id */ public function test_delete_scheduler() { @@ -288,6 +298,8 @@ private function check_timed_slots($schedulerid, $studentid, $slotoptions, /** * Test slot timings when parameters of the scheduler are altered. + * + * @coversNothing */ public function test_load_slot_timing() { @@ -382,6 +394,8 @@ public function test_load_slot_timing() { * @param int $expectedwithoutchangeables expected number of bookable appointments, excluding changeable ones * @param int $schedid scheduler id * @param int $studentid student id + * + * @covers \mod_scheduler\model\scheduler::load_by_id */ private function assert_bookable_appointments($expectedwithchangeables, $expectedwithoutchangeables, $schedid, $studentid) { @@ -461,6 +475,8 @@ private function create_data_for_bookable_appointments($schedulermode, $maxbooki /** * Test the retrieveal routines for bookable appointments. + * + * @coversNothing */ public function test_bookable_appointments() { diff --git a/tests/slot_test.php b/tests/slot_test.php index 55fda641..9f6f784a 100644 --- a/tests/slot_test.php +++ b/tests/slot_test.php @@ -22,6 +22,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace mod_scheduler; + defined('MOODLE_INTERNAL') || die(); use \mod_scheduler\model\scheduler; @@ -37,7 +39,7 @@ * @copyright 2014 Henning Bostelmann and others (see README.txt) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class mod_scheduler_slot_testcase extends advanced_testcase { +class slot_test extends \advanced_testcase { /** * @var int Course_modules id used for testing @@ -133,6 +135,8 @@ private function assert_record_absent($table, $id, $msg = "") { /** * Test creating a slot with appointments + * + * @covers \mod_scheduler\model\scheduler::load_by_id */ public function test_create() { @@ -163,6 +167,8 @@ public function test_create() { /** * Test deleting a slot and associated data + * + * @covers \mod_scheduler\model\scheduler::load_by_id */ public function test_delete() { @@ -191,6 +197,8 @@ public function test_delete() { /** * Test adding an appointment to a slot. + * + * @covers \mod_scheduler\model\scheduler::load_by_id */ public function test_add_appointment() { @@ -214,6 +222,8 @@ public function test_add_appointment() { /** * Test removing an appointment from a slot. + * + * @covers \mod_scheduler\model\scheduler::load_by_id */ public function test_remove_appointment() { @@ -236,6 +246,8 @@ public function test_remove_appointment() { /** * Test presence or absence of event records when appointments are modified. + * + * @covers \mod_scheduler\model\scheduler::load_by_id */ public function test_calendar_events() { global $DB; diff --git a/version.php b/version.php index b318756b..d1b81a41 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ */ $plugin->component = 'mod_scheduler'; // Full name of the plugin (used for diagnostics). -$plugin->version = 2022071500; // The current module version (Date: YYYYMMDDXX). +$plugin->version = 2023052400; // The current module version (Date: YYYYMMDDXX). $plugin->release = '4.x dev'; // Human-friendly version name. -$plugin->requires = 2022041900; // requires Moodle 4.0. +$plugin->requires = 2022041900; // Requires Moodle 4.0. $plugin->maturity = MATURITY_ALPHA; // Development release - not for production use. diff --git a/yui/build/moodle-mod_scheduler-delselected/moodle-mod_scheduler-delselected-min.js b/yui/build/moodle-mod_scheduler-delselected/moodle-mod_scheduler-delselected-min.js index 98fee208..b6e08dfb 100644 --- a/yui/build/moodle-mod_scheduler-delselected/moodle-mod_scheduler-delselected-min.js +++ b/yui/build/moodle-mod_scheduler-delselected/moodle-mod_scheduler-delselected-min.js @@ -1 +1 @@ -YUI.add("moodle-mod_scheduler-delselected",function(e,t){var n={DELACTION:"div.commandbar a#delselected",SELECTBOX:"table#slotmanager input.slotselect"},r;M.mod_scheduler=M.mod_scheduler||{},r=M.mod_scheduler.delselected={},r.collect_selection=function(t,r){var i="";e.all(n.SELECTBOX).each(function(e){e.get("checked")&&(i.length>0&&(i+=","),i+=e.get("value"))}),t.setAttribute("href",r+"&items="+i)},r.init=function(t){var r=e.one(n.DELACTION);r!==null&&r.on("click",function(){M.mod_scheduler.delselected.collect_selection(r,t)})}},"@VERSION@",{requires:["base","node","event"]}); +YUI.add("moodle-mod_scheduler-delselected",function(c,e){var t="div.commandbar a#delselected",d="table#slotmanager input.slotselect";M.mod_scheduler=M.mod_scheduler||{},M.mod_scheduler.delselected={collect_selection:function(e,l){var t="";c.all(d).each(function(e){e.get("checked")&&(0