Compare commits

..

138 Commits

Author SHA1 Message Date
Eloy Lafuente (stronk7) 54b614ca7e Moodle release 3.11.10 2022-09-10 12:16:05 +02:00
Jun Pataleta 4125c3c470 weekly release 3.11.9+ 2022-09-09 08:36:51 +08:00
Jun Pataleta 258cdfaa6a Merge branch 'MDL-74866-311-behat' of https://github.com/HuongNV13/moodle into MOODLE_311_STABLE 2022-09-08 11:29:43 +08:00
Huong Nguyen 8c2792cb5d MDL-74866 core_calendar: Fix Behat failures 2022-09-08 09:49:40 +07:00
Michael Hawkins 25d4bc7ffc MDL-68066 output: Apply disable lambda rendering to Mustache renderer 2022-09-07 23:56:51 +08:00
Lars Bonczek d98b39a597 MDL-68066 output: Mustache - Add option to disable lambda rendering
This commit is based on an outstanding pull request to the Mustache repo
2022-09-07 23:56:51 +08:00
Paul Holden 191badf5ad MDL-72012 mod_h5pactivity: respect user access when getting report. 2022-09-07 22:10:56 +08:00
Jun Pataleta c4ba598a0b MDL-75283 core: Fetch proper column mapping for sort
Calling \core_user\fields::get_sql() incerements a static uniqueid
variable. This should be called only once and not make a separate call
for the sort mapping data as it doing so will result to an incorrect
table alias for the sort mapping data.
2022-09-07 17:16:03 +04:00
Sara Arjona f77ea5bffa Merge branch 'MDL-74866-311' of https://github.com/HuongNV13/moodle into MOODLE_311_STABLE 2022-09-07 13:10:24 +02:00
stevando 3d11b25cce MDL-75283 admin: Update admin user fetching to use sort helper 2022-09-07 13:18:43 +08:00
Jun Pataleta 8a55673463 Merge branch 'MDL-75553-311' of https://github.com/junpataleta/moodle into MOODLE_311_STABLE 2022-09-07 12:58:54 +08:00
Jun Pataleta 230f7a2d1b MDL-75553 mod_data: Fix wording of data fields in Behat tests 2022-09-07 12:55:50 +08:00
Huong Nguyen 87344d03e1 MDL-74866 bennu: Fix parameter parsing
Some parameter values are wrapped by DQUOTE character.
We need to go through and get the actual value inside the quoted string.
2022-09-07 11:16:01 +07:00
David Mudrák aa392ce73a MDL-75553 lang: Fix Behat regressions in scenario files 2022-09-06 21:49:32 +02:00
Paul Holden 6220f712ab MDL-75405 block_html: safer unserialize during old block conversion. 2022-09-06 17:54:17 +02:00
Jun Pataleta 714b88a1b8 Merge branch 'MDL-75231-311' of https://github.com/lameze/moodle into MOODLE_311_STABLE 2022-09-06 23:49:08 +08:00
Sara Arjona bbadf7a02c Merge branch 'MDL-75548_311' of https://github.com/timhunt/moodle into MOODLE_311_STABLE 2022-09-05 13:09:57 +02:00
Ilya Tregubov 57b97b29b7 Merge branch 'MDL-60666-311' of https://github.com/ilyatregubov/moodle into MOODLE_311_STABLE 2022-09-05 14:38:41 +04:00
Peter Sistrom 6fe4eb2a86 MDL-60666 auth_ldap: One transaction per user 2022-09-05 14:35:26 +04:00
Sara Arjona 8a0274423d Merge branch 'MDL-75550-311' of https://github.com/mickhawkins/moodle into MOODLE_311_STABLE 2022-09-05 08:57:37 +02:00
Jun Pataleta ddd80a5d2c Merge branch 'MDL-75641_311' of https://github.com/stronk7/moodle into MOODLE_311_STABLE 2022-09-05 14:37:09 +08:00
Helen Foster 50bf83fede MDL-75553 lang: Import fixed English strings (en_fix) 2022-09-04 21:07:38 +02:00
Eloy Lafuente (stronk7) cafbfc6076 MDL-75641 core_lang: Add string for new Europe/Kyiv timezone
With IANA TZDB (timezone database) release 2022b, Europe/Kiev
has been renamed to Europe/Kyiv. So we need to add this string
to our list to keep tests passing.

Note that the old Europe/Kiev continues working (is an alias) and,
also, there are places in core where we are using it. Just we cannot
remove/replace those uses in core until we ensure that everybody is
using PHP 8.0.23 (TZDB 2022b) or more recent versions. Hence,
not changing those uses now.
2022-09-04 10:11:20 +02:00
Ilya Tregubov 2b22716dc2 weekly release 3.11.9+ 2022-09-02 08:49:20 +04:00
Jun Pataleta c59d4f9612 Merge branch 'MDL-75111_311' of https://github.com/stronk7/moodle into MOODLE_311_STABLE 2022-08-31 23:04:15 +08:00
Jun Pataleta e3ce532831 Merge branch 'MDL-75019-311' of https://github.com/andelacruz/moodle into MOODLE_311_STABLE 2022-08-31 20:24:56 +08:00
Ilya Tregubov 87e0d39e19 Merge branch 'MDL-74861-311' of https://github.com/andelacruz/moodle into MOODLE_311_STABLE 2022-08-31 11:52:30 +04:00
Michael Hawkins 90aceb5e6c MDL-75550 lib: Update Mustache const to reflect correct version
Mustache version 2.14.1 is already included, but the VERSION const
incorrectly listed 2.14.0 in the tagged Mustache release. A later
upstream commit corrected this in the library, which this now matches.
2022-08-31 15:29:52 +08:00
Sara Arjona 7d1996fb3a Merge branch 'MDL-68717-311' of https://github.com/laurentdavid/moodle into MOODLE_311_STABLE 2022-08-30 16:05:11 +02:00
Víctor Déniz cb9583c21c Merge branch 'MDL-75360-311' of https://github.com/paulholden/moodle into MOODLE_311_STABLE 2022-08-30 11:43:33 +01:00
Jun Pataleta 633c6d7e80 MDL-70480 mod_assign: Apply proper upgrade savepoint version 2022-08-30 17:18:38 +08:00
Jun Pataleta 76ed3885a9 MDL-70480 mod_assign: Bump version.php properly 2022-08-30 17:04:17 +08:00
Víctor Déniz f4456f6afe Merge branch 'MDL-70480-311' of https://github.com/chanqihui/moodle into MOODLE_311_STABLE 2022-08-30 09:17:00 +01:00
Ilya Tregubov 21384101e1 Merge branch 'MDL-71662-311' of https://github.com/paulholden/moodle into MOODLE_311_STABLE 2022-08-30 08:00:37 +04:00
Eloy Lafuente (stronk7) 61cedbd56e MDL-75111 phpunit: Move tests to use correct names and ns (take#4)
Applied the following changes to various testcase classes:

- Namespaced with component[\level2-API]
- Moved to level2-API subdirectory when required.
- Fixed incorrect use statements with leading backslash.
- Remove file phpdoc block
- Remove MOODLE_INTERNAL if not needed.
- Changed code to point to global scope when needed.
- Fix some relative paths and comments here and there.
- All them passing individually.
- Complete runs passing too.

Special mention to:

- The following task tests have been moved within the level2 directory:
  - \core\adhoc_task_test => \core\task\adhoc_task_test
  - \core\scheduled_task_test => \core\task\scheduled_task_test
  - \core\calendar_cron_task_test => \core\task\calendar_cron_task_test
  - \core\h5p_get_content_types_task_test => \core\task\h5p_get_content_types_task_test
  - \core\task_database_logger_test => \core\task\database_logger_test
  - \core\task_logging_test => \core\task\logging_test

- The following event tests have been moved within level2 directory:
  - \core\event_context_locked_test => \core\event\context_locked_test
  - \core\event_deprecated_test => \core\event\deprecated_test
  - \core\event_grade_deleted_test => \core\event\grade_deleted_test
  - \core\event_profile_field_test => \core\event\profile_field_test
  - \core\event_unknown_logged_test => \core\event\unknown_logged_test
  - \core\event_user_graded_test => \core\event\user_graded_test
  - \core\event_user_password_updated_test => \core\event\user_password_updated_test

- The following output tests have been moved within level2 directory:
  - \core\mustache_template_finder_test => \core\output\mustache_template_finder_test
  - \core\mustache_template_source_loader_test => \core\output\mustache_template_source_loader_test
  - \core\output_mustache_helper_collection_test => \core\output\mustache_helper_collection_test

- The following tests have been moved to their correct tests directories:
  - lib/tests/time_splittings_test.php => analytics/tests/time_splittings_test.php

- All the classes and tests under lib/filebrowser and lib/filestorage
  belong to core, not to core_files. Some day we should move
  them to their correct subsystem.
- All the classes and tests under lib/grade belong to core, not
  to core_grades. Some day we should move them to their correct
  subsystem.
- The core_grades_external class and its \core\grades_external_test
  unit test should belong to the grades subsystem or, alternatively,
  to \core\external, they both should be moved together.
- The core_grading_external class and its \core\grading_external_test
  unit test should belong to the grading subsystem or, alternatively,
  to \core\external, they both should be moved together.
- The \core\message\message and \core\message\inbound (may be others)
  classes, and their associated tests should go to the core_message
  subsystem.
- The core_user class, and its associated tests should go to the
  core_user subsystem.
- The \core\update namespace is plain wrong (update is not valid API)
  and needs action 1) create it or 2) move elsewhere.
2022-08-26 16:34:28 +02:00
Shamim Rezaie 376e57d267 weekly release 3.11.9+ 2022-08-26 21:07:42 +10:00
Tim Hunt 9de11ea641 MDL-75548 misc: final Required parameter follows optional notices 2022-08-22 15:19:55 +01:00
Simey Lameze d793ac8e5e MDL-75231 files: make bucket calculations more precise
This commit changes changes the unit test calculations to
keep track of the time the bucket starts/finishes bursting
and use milliseconds instead of seconds to have a precise
time to sleep.
2022-08-22 14:11:39 +08:00
Sara Arjona 3828f4b484 Moodle release 3.11.9 2022-08-19 09:50:31 +02:00
Sara Arjona 4c5288b32c weekly release 3.11.8+ 2022-08-18 14:54:20 +02:00
Laurent David 284b201a28 MDL-75210 mod_quiz: fix sequential quiz access
* As a student I should only be able to access the next question or the current question
* The API should not show more than the current question
* Once the quiz is previewed we can see the question in any order (existing behaviour)
* Related to ticket MDL-71728

Co-authored-by: Rajneel Totaram <rjnlfj@yahoo.com>
Co-authored-by: Tim Hunt <t.j.hunt@open.ac.uk>
2022-08-18 08:08:25 +04:00
Andrew Nicols 31c078e183 Merge branch 'MDL-75029-311' of https://github.com/paulholden/moodle into MOODLE_311_STABLE 2022-08-18 11:19:17 +08:00
Shamim Rezaie 6b8961b7ae Merge branch 'MDL-75386-311' of https://github.com/andrewnicols/moodle into MOODLE_311_STABLE 2022-08-17 13:29:53 +10:00
Shamim Rezaie 7ef79076fe Merge branch 'MDL-73876-311' of https://github.com/ilyatregubov/moodle into MOODLE_311_STABLE 2022-08-17 02:32:46 +10:00
Sara Arjona 1bf4ac0c11 Merge branch 'MDL-72178-311' of https://github.com/NoelDeMartin/moodle into MOODLE_311_STABLE 2022-08-16 16:58:43 +02:00
Mark Johnson 2c4cc3caf6 MDL-75388 lib: Update mustache to 2.14.1
MDL-67114 and MDL-73586 local changes removed as they are now available
upstream.
2022-08-12 04:14:19 +02:00
Petr Skoda 3fd34dd999 MDL-75237 core: improve PARAM_SAFEPATH docs 2022-08-12 04:14:19 +02:00
Petr Skoda 4dcb3b244c MDL-75237 qformat_blackboard_six: sanitise blackboard import file paths 2022-08-12 04:14:19 +02:00
Petr Skoda 6272227934 MDL-75237 core: Revert MDL-72029
This reverts commit edc7eb06b7.
2022-08-12 04:14:19 +02:00
Petr Skoda 36e0a49c00 MDL-75237 qformat_blackboard_six: Revert MDL-72029
This reverts commit 46ee4f4062.
2022-08-12 04:14:19 +02:00
Paul Holden 1ee3d6323c MDL-75326 h5p: require sesskey to toggle library enabled state. 2022-08-12 04:14:19 +02:00
Shamim Rezaie 4ed613fbc2 weekly release 3.11.8+ 2022-08-12 11:52:57 +10:00
Noel De Martin 123fc91eea MDL-72178 feedback: Implement behat generators
Based on Andrew's WIP from https://github.com/andrewnicols/moodle/commit/b3d40cb20d1f0edf0bf21077c8f94bce5dec56db

Co-authored-by: Andrew Nicols <andrew@nicols.co.uk>
2022-08-11 10:54:40 +02:00
Jun Pataleta 83c9f41a47 Merge branch 'MDL-75375-311' of https://github.com/junpataleta/moodle into MOODLE_311_STABLE 2022-08-10 00:25:41 +08:00
Brendan Heywood 3bec78df9b MDL-75375 cache: Fix file store path cleaning 2022-08-09 23:54:02 +08:00
Jun Pataleta 6793732790 Merge branch 'MDL-74636_311' of https://github.com/timhunt/moodle into MOODLE_311_STABLE 2022-08-09 11:10:58 +08:00
Laurent David a9ce394b63 MDL-68717 core_calendar: Hide completed event
* "expectcompletionon" type of event should be hidden if the related
activity is completed
2022-08-08 07:18:26 +02:00
Angelia Dela Cruz a8c9e82653 MDL-74861 behat: H5P behat generators use UI
Replace steps that manually add H5Pactivity instances via the UI
and use Behat generators. This improves the speed of the Behat test
runs.

Co-authored-by: Simey Lameze <simey@moodle.com>
2022-08-08 12:18:28 +08:00
Jun Pataleta 9ceb538216 Merge branch 'MDL-74211-311' of https://github.com/lameze/moodle into MOODLE_311_STABLE 2022-08-08 11:15:03 +08:00
Ilya Tregubov 5073d3542b Merge branch 'MDL-74475-311' of https://github.com/aanabit/moodle into MOODLE_311_STABLE 2022-08-05 12:27:21 +04:00
Angelia Dela Cruz 34f352ded2 MDL-75019 behat: Folder behat generators use UI
Replace steps that manually add Folder instances via the UI and
use Behat generators. This improves the speed of the Behat test
runs.
2022-08-05 15:41:57 +08:00
Andrew Nicols 1125231af0 MDL-75386 theme: Include CSS from editor subplugins in editor style
Editor stylesheets are kept separately to standard plugin stylesheets so
that they can be provided to style individual iframes where loading the
standard Moodle stylesheets would be incorrect.

Unfortunately the editor stylesheet system does not consider that
subplugins may want to add small individual styling to the editor but
this is required in some situations.
2022-08-05 14:18:49 +08:00
Ilya Tregubov 8a147fee4b weekly release 3.11.8+ 2022-08-05 08:29:20 +04:00
Andrew Lyons 1d4a1bc258 MDL-73876 behat: Show exceptions stack trace 2022-08-04 16:51:37 +04:00
Paul Holden 4119bd99a9 MDL-75029 block_news_items: indicate posts made by deleted users. 2022-08-04 09:43:09 +01:00
Qihui Chan a287546036 MDL-70480 mod_assign: fix user and group submission and update database 2022-08-04 18:03:42 +10:00
Jun Pataleta 6102f8553a Merge branch 'MDL-74895-311' of https://github.com/paulholden/moodle into MOODLE_311_STABLE 2022-08-04 11:35:30 +08:00
Jun Pataleta 982dda9417 Merge branch 'MDL-69251-311' of https://github.com/snake/moodle into MOODLE_311_STABLE 2022-08-04 11:16:03 +08:00
Andrew Nicols e5744719c9 Merge branch 'MDL-56923-311' of https://github.com/rezaies/moodle into MOODLE_311_STABLE 2022-08-04 11:13:11 +08:00
Andrew Nicols 1b513e8c42 Merge branch 'MDL-74502-311' of https://github.com/marinaglancy/moodle into MOODLE_311_STABLE 2022-08-04 11:09:15 +08:00
Jake Dallimore 1aa9a5b11f Merge branch 'MDL-74766-311' of https://github.com/JBThong/moodle into MOODLE_311_STABLE 2022-08-04 11:03:28 +08:00
Andrew Nicols a313e53b54 Merge branch 'MDL-75080-311' of https://github.com/andelacruz/moodle into MOODLE_311_STABLE 2022-08-04 10:45:06 +08:00
Simey Lameze 983a79347b MDL-74211 behat: convert few more steps to use new data generators 2022-08-04 10:38:39 +08:00
Andrew Nicols f6a3cbe339 Merge branch 'MDL-74655-311' of https://github.com/NoelDeMartin/moodle into MOODLE_311_STABLE 2022-08-04 10:09:08 +08:00
Andrew Nicols cccef66f0c MDL-74492 editor_atto: Comment the regex change 2022-08-04 10:05:21 +08:00
Andrew Nicols ad58a4c3c3 Merge branch 'MDL-74492__MOODLE_311_STABLE' of https://github.com/PhilippImhof/moodle into MOODLE_311_STABLE 2022-08-04 10:05:17 +08:00
Paul Holden 2eb859a839 MDL-71662 mod_h5pactivity: add group filter to attempts report. 2022-08-03 15:30:36 +01:00
Jun Pataleta 3583300da5 Merge branch 'MDL-62959-311' of https://github.com/snake/moodle into MOODLE_311_STABLE 2022-08-03 16:50:35 +08:00
Noel De Martin 2d9ee56211 MDL-74655 competency: Implement behat generators 2022-08-03 10:49:14 +02:00
Amaia Anabitarte 5ba22c14a1 MDL-74475 core_badges: Behat test using new step 2022-08-03 10:29:39 +02:00
Amaia Anabitarte b3fab1d80e MDL-74475 behat: Option to jump to course edit page 2022-08-03 10:29:39 +02:00
Andrew Nicols d5cd3c95b0 Merge branch 'MDL-72430-311' of https://github.com/HuongNV13/moodle into MOODLE_311_STABLE 2022-08-03 12:50:03 +08:00
Paul Holden 0448a7b267 MDL-75360 user: relax initial parameter types of external method.
Defer cleaning of supplied text to inside the method, so we don't
throw confusing exceptions if a user enters "unsafe" characters in
the form input.
2022-08-02 21:04:40 +01:00
Jun Pataleta 7b841a0f39 Merge branch 'MDL-74947-311' of https://github.com/snake/moodle into MOODLE_311_STABLE 2022-08-02 09:59:19 +08:00
Tim Hunt 25519bc4d8 MDL-74636 behat: step to skip this scenario if a plugin isn't installed 2022-07-29 09:51:11 +01:00
Jun Pataleta 5f6923adf1 Merge branch 'MDL-75244-311' of https://github.com/andrewnicols/moodle into MOODLE_311_STABLE 2022-07-29 15:28:55 +08:00
Philipp Imhof a08fcb8d13 MDL-74492 editor_atto: <line> tags should not be confused with <li> tags 2022-07-29 08:36:38 +02:00
Simey Lameze 9fbbdc8a0a MDL-74211 behat: replace steps to use new categories page type 2022-07-29 13:07:55 +08:00
Simey Lameze 56781dec40 MDL-74211 behat: add course question categories page type 2022-07-29 13:07:55 +08:00
Simey Lameze 15b31bfb39 MDL-74211 quiz: make use of question data generators 2022-07-29 13:07:55 +08:00
Andrew Nicols fc65a2e4a2 Merge branch 'MDL-72349_M311_update_strip_double_headers_function' of https://github.com/christina-roperto/moodle into MOODLE_311_STABLE 2022-07-29 12:12:27 +08:00
Jun Pataleta 3436523f17 weekly release 3.11.8+ 2022-07-29 10:56:47 +08:00
Angelia Dela Cruz 8062e43941 MDL-75080 behat: Chat behat generators use UI
Replace steps that manually add Chat instances via the UI and use
Behat generators. This improves the speed of the Behat test runs.
2022-07-29 10:56:09 +08:00
Andrew Nicols a28e929a74 MDL-75244 form: Change checker should check existence of tinyMCE API
The window.tinyMCE.editors API was present in version 3 of TinyMCE, but
is not present in later versions. As a result, this check tries to loop
over a variable which does not exist and throws an error in the process.

We should check that window.tinyMCE *and* window.tinyMCE.editors both
exist before attempting to loop over them.
2022-07-28 16:09:21 +08:00
Simey Lameze 838f1bfa7b MDL-62959 calendar: add behat to test event type changing 2022-07-27 15:31:10 +08:00
Simey Lameze 88a18df37f MDL-62959 calendar: fix event data mapping for group id 2022-07-27 11:48:16 +08:00
Huong Nguyen cad3403e71 MDL-72430 Calendar: Fix admin cannot edit course event 2022-07-26 11:47:50 +07:00
Andrew Nicols 34b551b4e7 Merge branch 'MDL-73788-311' of https://github.com/andelacruz/moodle into MOODLE_311_STABLE 2022-07-26 11:38:44 +08:00
Jun Pataleta a62e36a9f6 Merge branch 'MDL-74057-311' of https://github.com/andelacruz/moodle into MOODLE_311_STABLE 2022-07-26 11:01:53 +08:00
Paul Holden 7dd9098338 MDL-74895 h5p: more resilient error handling during content fetching.
On new installs, the task for downloading new H5P content types would
throw exceptions and exit when encountering unexpected and/or missing
data.
2022-07-25 20:05:59 +01:00
Ilya Tregubov a69d81beaa Merge branch 'MDL-75083-MOODLE_311_STABLE' of https://github.com/rzhtwberlin/moodle into MOODLE_311_STABLE 2022-07-25 14:56:29 +04:00
Angelia Dela Cruz 4434756fd6 MDL-74057 behat: SCORM behat generators use UI
Replace steps that manually add SCORM instances via the UI and use
Behat generators. This improves the speed of Behat test runs.
2022-07-25 18:22:22 +08:00
Ilya Tregubov 08e64ba626 Merge branch 'MDL-74974__MOODLE_311_STABLE' of https://github.com/PhilippImhof/moodle into MOODLE_311_STABLE 2022-07-25 11:46:42 +04:00
Philipp Imhof caa944907a MDL-74974 quiz override mark: do not hide_all_feedback
(backport of MDL-74495)
2022-07-22 15:34:16 +02:00
Thong Bui 19815c2c2c MDL-74766 html_editor: Fix form is submitted as an empty string 2022-07-22 10:17:45 +07:00
Ilya Tregubov 8de4b14be7 weekly release 3.11.8+ 2022-07-21 21:29:58 +04:00
Ilya Tregubov 2016d64b83 NOBUG: Fixed SVG browser compatibility 2022-07-21 21:29:56 +04:00
Ilya Tregubov 3eedb8a9c9 Merge branch 'install_311_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_311_STABLE 2022-07-21 21:29:56 +04:00
Jun Pataleta 7f8d209ba1 Merge branch 'MDL-75208_311' of https://github.com/stronk7/moodle into MOODLE_311_STABLE 2022-07-21 11:27:14 +08:00
Víctor Déniz 3f2ffcce08 Merge branch 'MDL-73332-311' of https://github.com/martygilbert/moodle into MOODLE_311_STABLE 2022-07-20 19:56:07 +01:00
Christina Thee Roperto 18f46b32a2 MDL-72349 filelib: update strip_double_headers function 2022-07-19 19:14:52 +10:00
Eloy Lafuente (stronk7) ffb762aae0 MDL-75208 oracle: Amend a couple of queries to make Oracle happy
The changes introduced here are completely safe, just we stop
binding SITEID and, instead, embed it in the SQL.

Why? Because Oracle 21 has started to return non-sense results
when SITEID is bound.

After lots of tests, attempts, debugging... we have been unable
to find any logic to the need of this change and also, have been
unable to reproduce the problem with a standalone script that
pretty much runs the same queries that the ones changed here.

I'm sure that there is something, somewhere, but have failed
to find it, grrr.

Please read MDL-75208 and linked issues to find more information
about this problem, that is one of the biggest mysteries I've
seen recently. Maybe at the end there is a tiny detail that
explains it all, but it's really well hidden.
2022-07-19 09:52:59 +02:00
Angelia Dela Cruz 9b6a36114c MDL-73788 behat: URL behat generators use UI
Replace steps that manually add URL instances via the UI and use
Behat generators. This improves the speed of Behat test runs.
2022-07-19 08:53:26 +08:00
Ilya Tregubov 8ef1574c47 Merge branch 'MDL-68843-311' of https://github.com/snake/moodle into MOODLE_311_STABLE 2022-07-18 14:16:37 +04:00
Jake Dallimore 09a93cb6eb MDL-68843 enrol_lti: auto disable orphaned enrolment instances
- pre_course_module_deletion hook to disable on mod delete
- upgrade step dealing with legacy orphaned records
2022-07-18 16:25:08 +08:00
Jake Dallimore b1fa2a9976 MDL-68843 enrol_lti: add deleted activity warning to published tools
Adds a warning to the 'name' column of the tables, letting teachers know
that the activity exposed by the method has been deleted and what their
options are.
2022-07-18 16:25:08 +08:00
AMOS bot 310da5eee7 Automatically generated installer lang files 2022-07-16 00:08:03 +00:00
Denis Brat f9e7aa5820 MDL-69251 enrol_lti: fix sync_members task inconsistent internal state 2022-07-15 12:21:09 +08:00
Marty 91c1bba220 MDL-73332 mustache: Fix shortentext helper
In the docs, it claims you can provide length to
the shortentext template helper as a context variable.
In reality, that is not the case.
2022-07-14 09:36:07 -04:00
Víctor Déniz 8984ffef4f weekly release 3.11.8+ 2022-07-14 12:06:12 +01:00
Víctor Déniz 419e169fa5 Merge branch 'install_311_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_311_STABLE 2022-07-14 12:06:10 +01:00
Víctor Déniz 73003c7c96 Merge branch 'MDL-74681-written_time_in_query_end_311' of https://github.com/catalyst/moodle into MOODLE_311_STABLE 2022-07-12 11:54:39 +01:00
Srdjan 4619b5128d MDL-74681 lib/dml: moodle_read_slave_trait: written table timestamping
Moved written table timestamping from query_start() to query_end():
We are adjusting table last written times at the end of transaction.
That does not apply to immediate database writes that are not performed
within transaction. This change is to set last written time after the query
has finished for such writes, rather than before it started. That way
long write operations cannot spill over the latency parameter.
2022-07-12 14:39:04 +10:00
Jun Pataleta 9a5389830e Merge branch 'MDL-71747_311' of https://github.com/stronk7/moodle into MOODLE_311_STABLE 2022-07-12 10:50:51 +08:00
AMOS bot 44cd084562 Automatically generated installer lang files 2022-07-12 00:07:59 +00:00
Jun Pataleta ca6f1479ad Merge branch 'MDL-74282-311' of https://github.com/paulholden/moodle into MOODLE_311_STABLE 2022-07-11 20:35:40 +08:00
Jun Pataleta 2e592749aa Merge branch 'MDL-73215-undef-plagiarismlinks-311' of https://github.com/leonstr/moodle into MOODLE_311_STABLE 2022-07-11 17:01:50 +08:00
Jun Pataleta e261c7ed25 Merge branch 'MDL-74784-311' of https://github.com/sarjona/moodle into MOODLE_311_STABLE 2022-07-11 16:53:41 +08:00
Jake Dallimore 57c492a6a5 MDL-74947 mod_lti: remove text validation from password field
This is a password field so should not be cleaned.
2022-07-11 13:37:31 +08:00
Johannes Burk 442e1f33c9 MDL-75083 login: prevent double submission
Apply core_form/submit JavaScript code to prevent double submission of login form and guest login button.
Add ID to guest login button to make it addressable.
2022-07-08 16:28:13 +02:00
Paul Holden 9cde5a75bb MDL-74282 grade: omit outcomes report if outcomes aren't enabled. 2022-07-08 10:43:31 +01:00
Eloy Lafuente (stronk7) 191351d2a3 MDL-71747 environment: Ensure that check_xmlrpc_usage is in >=39
This is a partial backport of MDL-73270 to ensure that the
check_xmlrpc_usage custom check is present in all the supported
branches and it's applied to all branches able to run php80:

- MOODLE_311_STABLE
- MOODLE_400_STABLE
- master (aka, 4.1 and up)

Note that the whole patch has not been backported, only the
environmental check (xml file and check implementation).
2022-07-07 23:44:57 +02:00
Eloy Lafuente (stronk7) fec4afdbd3 MDL-71747 environment: Apply agreed changes
- PHP 7.4 required.
- Moodle 3.9 required.
- MariaDB 10.4 required.
- PostgreSQL 12 required.
- Oracle 19c required.
2022-07-07 19:19:53 +02:00
Eloy Lafuente (stronk7) 02daea22d5 MDL-71747 environment: 4.1 base information
This is a direct copy of the 4.0 information, to
have the changes under control in the next commit.
2022-07-07 19:19:53 +02:00
Shamim Rezaie df61870abc MDL-56923 gradingform_guide: Fix icon positioning in RTL 2022-07-07 16:03:34 +10:00
Marina Glancy cb42a0ad6a MDL-74502 behat: when setting a field value it may not be visible
for example in case of editor field, the textarea element is hidden by wysiwyg widget but we still need to be able to set it
2022-07-01 10:31:28 +02:00
Leon Stringer 9b3be75879 MDL-73215 assign: Undefined var: plagiarismlinks
Fix: $plagiarismlinks is concatenated in return but may not have been
initialised.
2022-05-27 08:53:57 +01:00
Sara Arjona 1b9df59e3c MDL-74784 core_badges: Fix exception with related badges
System badges with related badges were displaying an error before this patch,
because the is_guest($context) function expects a context_course.
2022-05-18 09:18:05 +02:00
323 changed files with 5418 additions and 3607 deletions
+196
View File
@@ -3539,6 +3539,8 @@
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_max_input_vars" level="optional">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_xmlrpc_usage" level="optional">
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
<MOODLE version="4.0" requires="3.6">
@@ -3729,6 +3731,200 @@
<ON_CHECK message="iscustomadminwarnings" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_xmlrpc_usage" level="optional">
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
<MOODLE version="4.1" requires="3.9">
<UNICODE level="required">
<FEEDBACK>
<ON_ERROR message="unicoderequired" />
</FEEDBACK>
</UNICODE>
<DATABASE level="required">
<VENDOR name="mariadb" version="10.4" />
<VENDOR name="mysql" version="5.7" />
<VENDOR name="postgres" version="12" />
<VENDOR name="mssql" version="14.0" />
<VENDOR name="oracle" version="19" />
</DATABASE>
<PHP version="7.4.0" level="required">
</PHP>
<PCREUNICODE level="optional">
<FEEDBACK>
<ON_CHECK message="pcreunicodewarning" />
</FEEDBACK>
</PCREUNICODE>
<PHP_EXTENSIONS>
<PHP_EXTENSION name="iconv" level="required">
<FEEDBACK>
<ON_ERROR message="iconvrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="mbstring" level="required">
<FEEDBACK>
<ON_ERROR message="mbstringrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="curl" level="required">
<FEEDBACK>
<ON_ERROR message="curlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="openssl" level="required">
<FEEDBACK>
<ON_ERROR message="opensslrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="tokenizer" level="optional">
<FEEDBACK>
<ON_CHECK message="tokenizerrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="xmlrpc" level="optional">
<FEEDBACK>
<ON_CHECK message="xmlrpcrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="soap" level="optional">
<FEEDBACK>
<ON_CHECK message="soaprecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="ctype" level="required">
<FEEDBACK>
<ON_ERROR message="ctyperequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="zip" level="required">
<FEEDBACK>
<ON_ERROR message="ziprequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="zlib" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="gd" level="required">
<FEEDBACK>
<ON_ERROR message="gdrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="simplexml" level="required">
<FEEDBACK>
<ON_ERROR message="simplexmlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="spl" level="required">
<FEEDBACK>
<ON_ERROR message="splrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="pcre" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="dom" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="xml" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="xmlreader" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="intl" level="required">
<FEEDBACK>
<ON_ERROR message="intlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="json" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="hash" level="required"/>
<PHP_EXTENSION name="fileinfo" level="required"/>
<PHP_EXTENSION name="sodium" level="optional"/>
<PHP_EXTENSION name="exif" level="optional"/>
</PHP_EXTENSIONS>
<PHP_SETTINGS>
<PHP_SETTING name="memory_limit" value="96M" level="required">
<FEEDBACK>
<ON_ERROR message="settingmemorylimit" />
</FEEDBACK>
</PHP_SETTING>
<PHP_SETTING name="file_uploads" value="1" level="optional">
<FEEDBACK>
<ON_CHECK message="settingfileuploads" />
</FEEDBACK>
</PHP_SETTING>
<PHP_SETTING name="opcache.enable" value="1" level="optional">
<FEEDBACK>
<ON_CHECK message="opcacherecommended" />
</FEEDBACK>
</PHP_SETTING>
</PHP_SETTINGS>
<CUSTOM_CHECKS>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddbstorageengine" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
<FEEDBACK>
<ON_ERROR message="quizattemptsupgradedmessage" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
<FEEDBACK>
<ON_CHECK message="slashargumentswarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
<FEEDBACK>
<ON_CHECK message="unsupporteddbtablerowformat" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
<FEEDBACK>
<ON_CHECK message="unoconvwarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
<FEEDBACK>
<ON_CHECK message="libcurlwarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddbfileformat" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddbfilepertable" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddblargeprefix" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
<FEEDBACK>
<ON_CHECK message="ishttpswarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
<FEEDBACK>
<ON_CHECK message="incompleteunicodesupport" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
<FEEDBACK>
<ON_CHECK message="sixtyfourbitswarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_max_input_vars" level="optional">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_admin_dir_usage" level="optional">
<FEEDBACK>
<ON_CHECK message="iscustomadminwarnings" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_xmlrpc_usage" level="optional">
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
</COMPATIBILITY_MATRIX>
@@ -7,8 +7,8 @@ Feature: Behat steps for interacting with form work
@javascript
Scenario: Test fields in containers
Given the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
| fullname | shortname | summary | summaryformat |
| Course 1 | C1 | Red | 1 |
When I log in as "admin"
And I am on "Course 1" course homepage
# Just get to any form.
@@ -17,11 +17,15 @@ Feature: Behat steps for interacting with form work
And I set the following fields in the "Appearance" "fieldset" to these values:
| Show activity reports | Yes |
| Number of announcements | 1 |
And I set the following fields in the "Description" "fieldset" to these values:
| Course summary | Green |
Then the field "Show activity reports" in the "Appearance" "fieldset" matches value "Yes"
And the field "Show activity reports" in the "Appearance" "fieldset" does not match value "No"
And the following fields in the "region-main" "region" match these values:
| Course full name | Frog |
| Number of announcements | 1 |
| Course summary | Green |
And the following fields in the "region-main" "region" do not match these values:
| Course full name | Course 1 |
| Number of announcements | 5 |
| Course summary | Red |
@@ -323,7 +323,7 @@ class accessibility {
/**
* This function runs one specified check on the html item
*
* @param string $html The html string to be analysed; might be NULL.
* @param string|null $html The html string to be analysed; might be NULL.
* @param int $contentid The content area ID
* @param int $errid The error ID
* @param string $check The check name to run
@@ -333,7 +333,7 @@ class accessibility {
* @throws \dml_exception
*/
public static function run_one_check(
string $html = null,
?string $html,
int $contentid,
int $errid,
string $check,
@@ -63,4 +63,67 @@ class behat_tool_lp extends behat_base {
$this->execute('behat_general::i_click_on', [$xpathtarget, 'xpath_element']);
}
/**
* Convert page names to URLs for steps like 'When I am on the "[identifier]" "[page type]" page'.
*
* Recognised page names are:
* | pagetype | name meaning | description |
* | Course competencies | Course name | The course competencies page |
*
* @param string $page identifies which type of page this is, e.g. 'Course competencies'.
* @param string $identifier identifies the particular page, e.g. 'C1'.
* @return moodle_url the corresponding URL.
* @throws Exception with a meaningful error message if the specified page cannot be found.
*/
protected function resolve_page_instance_url(string $page, string $identifier): moodle_url {
switch (strtolower($page)) {
case 'course competencies':
$courseid = $this->get_course_id($identifier);
return new moodle_url('/admin/tool/lp/coursecompetencies.php', [
'courseid' => $courseid,
]);
default:
throw new Exception("Unrecognised page type '{$page}'");
}
}
/**
* Return a list of the exact named selectors for the component.
*
* @return behat_component_named_selector[]
*/
public static function get_exact_named_selectors(): array {
return [
new behat_component_named_selector('competency', [
"//*[@data-region='coursecompetencies']//table[contains(@class,'managecompetencies')]".
"//tr[contains(., //a[@title='View details'][contains(., %locator%)])]",
]),
new behat_component_named_selector('learning plan', [
"//*[@data-region='plan-competencies']//table[contains(@class,'managecompetencies')]".
"//tr[@data-node='user-competency'][contains(., //a[@data-usercompetency='true'][contains(., %locator%)])]",
]),
new behat_component_named_selector('competency description', [
"//td/p[contains(., %locator%)]",
]),
new behat_component_named_selector('competency grade', [
"//span[contains(concat(' ', normalize-space(@class), ' '), ' badge ')][contains(., %locator%)]",
]),
new behat_component_named_selector('learning plan rating', [
"//td[position()=2][contains(., %locator%)]",
]),
new behat_component_named_selector('learning plan proficiency', [
"//td[position()=3][contains(., %locator%)]",
]),
new behat_component_named_selector('competency page proficiency', [
"//dt[contains(., 'Proficient')]/following-sibling::dd[1][contains(., %locator%)]",
]),
new behat_component_named_selector('competency page rating', [
"//dt[contains(., 'Rating')]/following-sibling::dd[1][contains(., %locator%)]",
]),
new behat_component_named_selector('competency page related competency', [
"//*[@data-region='relatedcompetencies']//a[contains(., %locator%)]",
]),
];
}
}
@@ -0,0 +1,187 @@
@tool @tool_lp @javascript
Feature: View competencies
In order to access competency information
As a user
I need to view user competencies
Background:
Given the following "users" exist:
| username | firstname | lastname |
| student1 | Student | first |
| teacher1 | Teacher | first |
And the following "system role assigns" exist:
| user | role | contextlevel |
| teacher1 | editingteacher | System |
And the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/competency:planview | Allow | editingteacher | System | |
And the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| teacher1 | C1 | editingteacher |
And the following "scales" exist:
| name | scale |
| Test Scale | Bad, Good, Great |
And the following "core_competency > frameworks" exist:
| shortname | idnumber | scale |
| Cookery | cookery | Test Scale |
| Literacy | literacy | Test Scale |
And the following "core_competency > competencies" exist:
| shortname | idnumber | description | competencyframework |
| Salads | salads | Salads are important | cookery |
| Desserts | desserts | Desserts are important | cookery |
| Cakes | cakes | Cakes are important | cookery |
| Reading | reading | Reading is important | literacy |
| Writing | writing | Writing is important | literacy |
And the following "core_competency > related_competencies" exist:
| competency | relatedcompetency |
| desserts | cakes |
And the following "core_competency > plans" exist:
| name | description | competencies | user |
| Cookery | Cookery is important | salads, desserts, cakes | student1 |
| Literacy | Literacy is important | reading, writing | student1 |
And the following "core_competency > course_competencies" exist:
| course | competency |
| C1 | salads |
| C1 | desserts |
| C1 | cakes |
| C1 | reading |
| C1 | writing |
And the following "core_competency > user_competency" exist:
| competency | user | grade |
| salads | student1 | Good |
| desserts | student1 | Great |
| cakes | student1 | Great |
And the following "core_competency > user_competency_courses" exist:
| course | competency | user | grade |
| C1 | salads | student1 | Good |
| C1 | desserts | student1 | Great |
| C1 | cakes | student1 | Great |
Scenario: Student view
# Course competencies
Given I am on the "C1" "tool_lp > course competencies" page logged in as "student1"
Then I should see "You are proficient in 3 out of 5 competencies in this course"
And "Salads are important" "tool_lp > competency description" should exist in the "Salads" "tool_lp > competency"
And "Good" "tool_lp > competency grade" should exist in the "Salads" "tool_lp > competency"
And "Desserts are important" "tool_lp > competency description" should exist in the "Desserts" "tool_lp > competency"
And "Great" "tool_lp > competency grade" should exist in the "Desserts" "tool_lp > competency"
And "Cakes are important" "tool_lp > competency description" should exist in the "Cakes" "tool_lp > competency"
And "Great" "tool_lp > competency grade" should exist in the "Cakes" "tool_lp > competency"
And "Reading is important" "tool_lp > competency description" should exist in the "Reading" "tool_lp > competency"
And "Good" "tool_lp > competency grade" should not exist in the "Reading" "tool_lp > competency"
And "Great" "tool_lp > competency grade" should not exist in the "Reading" "tool_lp > competency"
And "Bad" "tool_lp > competency grade" should not exist in the "Reading" "tool_lp > competency"
And "Writing is important" "tool_lp > competency description" should exist in the "Writing" "tool_lp > competency"
And "Good" "tool_lp > competency grade" should not exist in the "Writing" "tool_lp > competency"
And "Great" "tool_lp > competency grade" should not exist in the "Writing" "tool_lp > competency"
And "Bad" "tool_lp > competency grade" should not exist in the "Writing" "tool_lp > competency"
# Course competencies details
And I click on "Desserts" "link" in the "Desserts" "tool_lp > competency"
And I should see "Desserts are important"
And "Yes" "tool_lp > competency page proficiency" should exist
And "Great" "tool_lp > competency page rating" should exist
# Course competencies summary
And I click on "Cakes" "link"
And I should see "Cakes are important"
# Learning plans
And I click on "Close" "button" in the "Cakes" "dialogue"
And I follow "Profile" in the user menu
And I click on "Learning plans" "link"
And I should see "Cookery"
And I should see "Literacy"
# Learning plans details
And I click on "Cookery" "link"
And I should see "Cookery is important"
And I should see "3 out of 3 competencies are proficient"
And "Good" "tool_lp > learning plan rating" should exist in the "Salads" "tool_lp > learning plan"
And "Yes" "tool_lp > learning plan proficiency" should exist in the "Salads" "tool_lp > learning plan"
And "Great" "tool_lp > learning plan rating" should exist in the "Desserts" "tool_lp > learning plan"
And "Yes" "tool_lp > learning plan proficiency" should exist in the "Desserts" "tool_lp > learning plan"
And "Great" "tool_lp > learning plan rating" should exist in the "Cakes" "tool_lp > learning plan"
And "Yes" "tool_lp > learning plan proficiency" should exist in the "Cakes" "tool_lp > learning plan"
And I should not see "Literacy"
And I should not see "Reading"
And I should not see "Writing"
# Learning plans competency details
And I click on "Desserts" "link" in the "Desserts" "tool_lp > learning plan"
And I should see "Desserts are important"
And "Yes" "tool_lp > competency page proficiency" should exist
And "Great" "tool_lp > competency page rating" should exist
# Learning plans competency summary
And I click on "Cakes cakes" "link"
And I should see "Cakes are important"
Scenario: Teacher view
# Participant competencies
Given I am on the "C1" "report_competency > breakdown" page logged in as "teacher1"
Then I should see "Student first"
And "Good" "report_competency > breakdown rating" should exist in the "Salads" "report_competency > breakdown"
And "Great" "report_competency > breakdown rating" should exist in the "Desserts" "report_competency > breakdown"
And "Great" "report_competency > breakdown rating" should exist in the "Cakes" "report_competency > breakdown"
And "Not rated" "report_competency > breakdown rating" should exist in the "Reading" "report_competency > breakdown"
And "Not rated" "report_competency > breakdown rating" should exist in the "Writing" "report_competency > breakdown"
# Participant competencies details
And I click on "Great" "report_competency > breakdown rating" in the "Desserts" "report_competency > breakdown"
And "Yes" "tool_lp > competency page proficiency" should exist
And "Great" "tool_lp > competency page rating" should exist
# Participant competencies summary
And I click on "Cakes" "tool_lp > competency page related competency"
And I should see "Cakes are important"
# Participant learning plans
And I click on "Close" "button" in the "Cakes" "dialogue"
And I click on "Close" "button" in the "User competency summary" "dialogue"
And I navigate to course participants
And I click on "Student first" "link"
And I click on "Learning plans" "link"
And I should see "Cookery"
And I should see "Literacy"
# Participant learning plans details
And I click on "Cookery" "link"
And I should see "Cookery is important"
And I should see "3 out of 3 competencies are proficient"
And "Good" "tool_lp > learning plan rating" should exist in the "Salads" "tool_lp > learning plan"
And "Yes" "tool_lp > learning plan proficiency" should exist in the "Salads" "tool_lp > learning plan"
And "Great" "tool_lp > learning plan rating" should exist in the "Desserts" "tool_lp > learning plan"
And "Yes" "tool_lp > learning plan proficiency" should exist in the "Desserts" "tool_lp > learning plan"
And "Great" "tool_lp > learning plan rating" should exist in the "Cakes" "tool_lp > learning plan"
And "Yes" "tool_lp > learning plan proficiency" should exist in the "Cakes" "tool_lp > learning plan"
And I should not see "Literacy"
And I should not see "Reading"
And I should not see "Writing"
# Learning plans competency details
And I click on "Desserts" "link"
And I should see "Desserts are important"
And "Yes" "tool_lp > competency page proficiency" should exist
And "Great" "tool_lp > competency page rating" should exist
# Learning plans competency summary
And I click on "Cakes" "tool_lp > competency page related competency"
And I should see "Cakes are important"
+5 -14
View File
@@ -14,17 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for the analysis class.
*
* @package core_analytics
* @copyright 2019 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/fixtures/test_timesplitting_upcoming_seconds.php');
namespace core_analytics;
/**
* Unit tests for the analysis class.
@@ -33,18 +23,19 @@ require_once(__DIR__ . '/fixtures/test_timesplitting_upcoming_seconds.php');
* @copyright 2019 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class analytics_analysis_testcase extends advanced_testcase {
class analysis_test extends \advanced_testcase {
/**
* Test fill_firstanalyses_cache.
* @return null
*/
public function test_fill_firstanalyses_cache() {
require_once(__DIR__ . '/fixtures/test_timesplitting_upcoming_seconds.php');
$this->resetAfterTest();
$modelid = 1;
$params = ['startdate' => (new DateTimeImmutable('-5 seconds'))->getTimestamp()];
$params = ['startdate' => (new \DateTimeImmutable('-5 seconds'))->getTimestamp()];
$course1 = $this->getDataGenerator()->create_course($params);
$course2 = $this->getDataGenerator()->create_course($params);
$analysable1 = new \core_analytics\course($course1);
@@ -72,7 +63,7 @@ class analytics_analysis_testcase extends advanced_testcase {
$this->assertEquals($earliest, $firstanalyses[$modelid . '_' . $course1->id]);
// Upcoming periodic time-splitting methods can read and process the cached data.
$seconds = new test_timesplitting_upcoming_seconds();
$seconds = new \test_timesplitting_upcoming_seconds();
$seconds->set_modelid($modelid);
$seconds->set_analysable($analysable1);
+6 -15
View File
@@ -14,18 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for the calculation info cache.
*
* @package core_analytics
* @copyright 2019 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/fixtures/test_indicator_max.php');
require_once(__DIR__ . '/fixtures/test_indicator_min.php');
namespace core_analytics;
/**
* Unit tests for the calculation info cache.
@@ -34,7 +23,7 @@ require_once(__DIR__ . '/fixtures/test_indicator_min.php');
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class analytics_calculation_info_testcase extends advanced_testcase {
class calculation_info_test extends \advanced_testcase {
/**
* test_calculation_info description
@@ -47,12 +36,14 @@ class analytics_calculation_info_testcase extends advanced_testcase {
* @return null
*/
public function test_calculation_info_add_pull($info1, $info2, $info3, $info4) {
require_once(__DIR__ . '/fixtures/test_indicator_max.php');
require_once(__DIR__ . '/fixtures/test_indicator_min.php');
$this->resetAfterTest();
$atimesplitting = new \core\analytics\time_splitting\quarters();
$indicator1 = new test_indicator_min();
$indicator2 = new test_indicator_max();
$indicator1 = new \test_indicator_min();
$indicator2 = new \test_indicator_max();
$calculationinfo = new \core_analytics\calculation_info();
$calculationinfo->add_shared(111, [111 => $info1]);
+2 -10
View File
@@ -14,15 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for the dataset manager.
*
* @package core_analytics
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
namespace core_analytics;
/**
* Unit tests for the dataset manager.
@@ -31,7 +23,7 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class dataset_manager_testcase extends advanced_testcase {
class dataset_manager_test extends \advanced_testcase {
/**
* setUp
+3 -9
View File
@@ -14,13 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for the indicator API.
*
* @package core_analytics
* @copyright 2019 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_analytics;
defined('MOODLE_INTERNAL') || die();
@@ -35,7 +29,7 @@ require_once(__DIR__ . '/fixtures/test_indicator_min.php');
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class analytics_indicator_testcase extends advanced_testcase {
class indicator_test extends \advanced_testcase {
/**
* test_validate_calculated_value
@@ -79,7 +73,7 @@ class analytics_indicator_testcase extends advanced_testcase {
->onlyMethods(['calculate_sample'])
->getMock();
$indicatormock->method('calculate_sample')->willReturn($willreturn);
$this->expectException(coding_exception::class);
$this->expectException(\coding_exception::class);
list($values, $unused) = $indicatormock->calculate([1], 'notrelevanthere');
}
+10 -8
View File
@@ -22,6 +22,8 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_analytics;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/fixtures/test_indicator_max.php');
@@ -39,7 +41,7 @@ require_once(__DIR__ . '/fixtures/test_analysis.php');
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class analytics_model_testcase extends advanced_testcase {
class model_test extends \advanced_testcase {
public function setUp(): void {
@@ -183,7 +185,7 @@ class analytics_model_testcase extends advanced_testcase {
global $DB;
$this->resetAfterTest();
$statictarget = new test_static_target_shortname();
$statictarget = new \test_static_target_shortname();
$indicators['test_indicator_max'] = \core_analytics\manager::get_indicator('test_indicator_max');
$model = \core_analytics\model::create($statictarget, $indicators, '\core\analytics\time_splitting\quarters');
$modelobj = $model->get_model_obj();
@@ -301,11 +303,11 @@ class analytics_model_testcase extends advanced_testcase {
$courses[$analysable->get_id()] = $course;
}
$target = new test_target_course_level_shortname();
$target = new \test_target_course_level_shortname();
$analyser = new \core\analytics\analyser\courses(1, $target, [], [], []);
$result = new \core_analytics\local\analysis\result_array(1, false, []);
$analysis = new test_analysis($analyser, false, $result);
$analysis = new \test_analysis($analyser, false, $result);
// Each analysable element takes 0.5 secs minimum (test_analysis), so the max (and likely) number of analysable
// elements that will be processed is 2.
@@ -342,7 +344,7 @@ class analytics_model_testcase extends advanced_testcase {
// Training and prediction data do not get mixed.
$result = new \core_analytics\local\analysis\result_array(1, false, []);
$analysis = new test_analysis($analyser, false, $result);
$analysis = new \test_analysis($analyser, false, $result);
$analysis->run();
$params = array('modelid' => 1, 'action' => 'training');
$this->assertLessThanOrEqual(2, $DB->count_records('analytics_used_analysables', $params));
@@ -402,7 +404,7 @@ class analytics_model_testcase extends advanced_testcase {
$this->model->update(true, [], false);
$this->assertFalse($this->model->can_export_configuration());
$statictarget = new test_static_target_shortname();
$statictarget = new \test_static_target_shortname();
$indicators['test_indicator_max'] = \core_analytics\manager::get_indicator('test_indicator_max');
$model = \core_analytics\model::create($statictarget, $indicators, '\\core\\analytics\\time_splitting\\quarters');
$this->assertFalse($model->can_export_configuration());
@@ -418,7 +420,7 @@ class analytics_model_testcase extends advanced_testcase {
$modelconfig = new \core_analytics\model_config($this->model);
$method = new ReflectionMethod('\\core_analytics\\model_config', 'export_model_data');
$method = new \ReflectionMethod('\\core_analytics\\model_config', 'export_model_data');
$method->setAccessible(true);
$modeldata = $method->invoke($modelconfig);
@@ -566,7 +568,7 @@ class analytics_model_testcase extends advanced_testcase {
private function add_fake_log() {
global $DB, $USER;
$log = new stdClass();
$log = new \stdClass();
$log->modelid = $this->modelobj->id;
$log->version = $this->modelobj->version;
$log->target = $this->modelobj->target;
+2 -8
View File
@@ -14,13 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for prediction actions.
*
* @package core_analytics
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_analytics;
defined('MOODLE_INTERNAL') || die();
@@ -34,7 +28,7 @@ require_once(__DIR__ . '/fixtures/test_target_shortname.php');
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class analytics_prediction_actions_testcase extends advanced_testcase {
class prediction_actions_test extends \advanced_testcase {
/**
* Common startup tasks
+12 -18
View File
@@ -14,21 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for evaluation, training and prediction.
*
* NOTE: in order to execute this test using a separate server for the
* python ML backend you need to define these variables in your config.php file:
*
* define('TEST_MLBACKEND_PYTHON_HOST', '127.0.0.1');
* define('TEST_MLBACKEND_PYTHON_PORT', 5000);
* define('TEST_MLBACKEND_PYTHON_USERNAME', 'default');
* define('TEST_MLBACKEND_PYTHON_PASSWORD', 'sshhhh');
*
* @package core_analytics
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_analytics;
defined('MOODLE_INTERNAL') || die();
@@ -48,11 +34,19 @@ require_once(__DIR__ . '/../../course/lib.php');
/**
* Unit tests for evaluation, training and prediction.
*
* NOTE: in order to execute this test using a separate server for the
* python ML backend you need to define these variables in your config.php file:
*
* define('TEST_MLBACKEND_PYTHON_HOST', '127.0.0.1');
* define('TEST_MLBACKEND_PYTHON_PORT', 5000);
* define('TEST_MLBACKEND_PYTHON_USERNAME', 'default');
* define('TEST_MLBACKEND_PYTHON_PASSWORD', 'sshhhh');
*
* @package core_analytics
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_analytics_prediction_testcase extends advanced_testcase {
class prediction_test extends \advanced_testcase {
/**
* Purge all the mlbackend outputs.
@@ -746,7 +740,7 @@ class core_analytics_prediction_testcase extends advanced_testcase {
$samples,
$ranges
);
$dataset = phpunit_util::call_internal_method($analysis, 'calculate_indicators', $params,
$dataset = \phpunit_util::call_internal_method($analysis, 'calculate_indicators', $params,
'\core_analytics\analysis');
$this->assertArrayHasKey('123-0', $dataset);
$this->assertArrayHasKey('123-1', $dataset);
@@ -770,7 +764,7 @@ class core_analytics_prediction_testcase extends advanced_testcase {
$samples,
$ranges
);
$dataset = phpunit_util::call_internal_method($analysis, 'calculate_indicators', $params,
$dataset = \phpunit_util::call_internal_method($analysis, 'calculate_indicators', $params,
'\core_analytics\analysis');
$this->assertArrayNotHasKey('123-0', $dataset);
$this->assertArrayNotHasKey('123-1', $dataset);
+4 -9
View File
@@ -14,14 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides the {@link analytics_stats_testcase} class.
*
* @package core_analytics
* @category test
* @copyright 2019 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_analytics;
defined('MOODLE_INTERNAL') || die();
@@ -31,10 +24,12 @@ require_once(__DIR__ . '/fixtures/test_target_shortname.php');
/**
* Unit tests for the analytics stats.
*
* @package core_analytics
* @category test
* @copyright 2019 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class analytics_stats_testcase extends advanced_testcase {
class stats_test extends \advanced_testcase {
/**
* Set up the test environment.
@@ -14,30 +14,26 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for core time splitting methods.
*
* @package core
* @category analytics
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_analytics;
use test_timesplitting_seconds;
use test_timesplitting_upcoming_seconds;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/../../analytics/tests/fixtures/test_timesplitting_seconds.php');
require_once(__DIR__ . '/../../analytics/tests/fixtures/test_timesplitting_upcoming_seconds.php');
require_once(__DIR__ . '/fixtures/test_timesplitting_seconds.php');
require_once(__DIR__ . '/fixtures/test_timesplitting_upcoming_seconds.php');
require_once(__DIR__ . '/../../lib/enrollib.php');
/**
* Unit tests for core time splitting methods.
*
* @package core
* @category analytics
* @category test
* @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_analytics_time_splittings_testcase extends advanced_testcase {
class time_splittings_test extends \advanced_testcase {
/**
* setUp
@@ -214,7 +210,7 @@ class core_analytics_time_splittings_testcase extends advanced_testcase {
// We now use an ongoing course not yet ready to generate predictions.
$threedaysago = new DateTime('-3 days');
$threedaysago = new \DateTime('-3 days');
$params = array(
'startdate' => $threedaysago->getTimestamp(),
);
@@ -229,7 +225,7 @@ class core_analytics_time_splittings_testcase extends advanced_testcase {
// We now use a ready-to-predict ongoing course.
$onemonthago = new DateTime('-30 days');
$onemonthago = new \DateTime('-30 days');
$params = array(
'startdate' => $onemonthago->getTimestamp(),
);
@@ -284,10 +280,10 @@ class core_analytics_time_splittings_testcase extends advanced_testcase {
// We now check how new ranges get added as time passes.
$fewsecsago = new DateTime('-5 seconds');
$fewsecsago = new \DateTime('-5 seconds');
$params = array(
'startdate' => $fewsecsago->getTimestamp(),
'enddate' => (new DateTimeImmutable('+1 year'))->getTimestamp(),
'enddate' => (new \DateTimeImmutable('+1 year'))->getTimestamp(),
);
$course = $this->getDataGenerator()->create_course($params);
$analysable = new \core_analytics\course($course);
+4 -9
View File
@@ -866,11 +866,8 @@ class auth_plugin_ldap extends auth_plugin_base {
if (!empty($users)) {
print_string('userentriestoupdate', 'auth_ldap', count($users));
$transaction = $DB->start_delegated_transaction();
$xcount = 0;
$maxxcount = 100;
foreach ($users as $user) {
$transaction = $DB->start_delegated_transaction();
echo "\t"; print_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id));
$userinfo = $this->get_userinfo($user->username);
if (!$this->update_user_record($user->username, $updatekeys, true,
@@ -878,12 +875,11 @@ class auth_plugin_ldap extends auth_plugin_base {
echo ' - '.get_string('skipped');
}
echo "\n";
$xcount++;
// Update system roles, if needed.
$this->sync_roles($user);
$transaction->allow_commit();
}
$transaction->allow_commit();
unset($users); // free mem
}
} else { // end do updates
@@ -903,8 +899,8 @@ class auth_plugin_ldap extends auth_plugin_base {
if (!empty($add_users)) {
print_string('userentriestoadd', 'auth_ldap', count($add_users));
$transaction = $DB->start_delegated_transaction();
foreach ($add_users as $user) {
$transaction = $DB->start_delegated_transaction();
$user = $this->get_userinfo_asobj($user->username);
// Prep a few params
@@ -939,9 +935,8 @@ class auth_plugin_ldap extends auth_plugin_base {
// Add roles if needed.
$this->sync_roles($euser);
$transaction->allow_commit();
}
$transaction->allow_commit();
unset($add_users); // free mem
} else {
print_string('nouserstobeadded', 'auth_ldap');
+2 -2
View File
@@ -482,13 +482,13 @@ class tree extends tree_node {
*
* @param bool $not True if there is a NOT in effect
* @param info $info Information about location of condition tree
* @param result $result Result object if this is a student display, else null
* @param result|null $result Result object if this is a student display, else null
* @param bool $root True if this is the root item
* @param bool $hidden Staff display; true if this tree has show=false (from parent)
* @return string|renderable Information to render
*/
protected function get_full_information_recursive(
$not, info $info, result $result = null, $root, $hidden = false) {
$not, info $info, ?result $result, $root, $hidden = false) {
// Get list of children - either full list, or those which are shown.
$children = $this->children;
$staff = true;
@@ -12,9 +12,10 @@ Feature: Restore Moodle 2 course backups
| Course 3 | C3 | 0 | topics | 2 | 0 |
| Course 4 | C4 | 0 | topics | 20 | 0 |
And the following "activities" exist:
| activity | course | idnumber | name | intro | section |
| assign | C3 | assign1 | Test assign name | Assign description | 1 |
| data | C3 | data1 | Test database name | Database description | 2 |
| activity | course | idnumber | name | intro | section | externalurl |
| assign | C3 | assign1 | Test assign name | Assign description | 1 | |
| data | C3 | data1 | Test database name | Database description | 2 | |
| url | C1 | url1 | Test URL name | Test URL description | 3 | http://www.moodle.org |
And I log in as "admin"
And I am on "Course 1" course homepage with editing mode on
And I add a "Forum" to section "1" and I fill the form with:
@@ -108,11 +109,7 @@ Feature: Restore Moodle 2 course backups
@javascript
Scenario: Restore a backup in an existing course retaining the backup course settings
Given I add a "URL" to section "3" and I fill the form with:
| Name | Test URL name |
| Description | Test URL description |
| id_externalurl | http://www.moodle.org |
And I hide section "3"
Given I hide section "3"
And I hide section "7"
When I backup "Course 1" course using this options:
| Confirmation | Filename | test_backup.mbz |
@@ -134,11 +131,7 @@ Feature: Restore Moodle 2 course backups
@javascript
Scenario: Restore a backup in an existing course keeping the target course settings
Given I add a "URL" to section "3" and I fill the form with:
| Name | Test URL name |
| Description | Test URL description |
| id_externalurl | http://www.moodle.org |
And I hide section "3"
Given I hide section "3"
And I hide section "7"
When I backup "Course 1" course using this options:
| Confirmation | Filename | test_backup.mbz |
@@ -160,11 +153,7 @@ Feature: Restore Moodle 2 course backups
@javascript
Scenario: Restore a backup in an existing course deleting contents and retaining the backup course settings
Given I add a "URL" to section "3" and I fill the form with:
| Name | Test URL name |
| Description | Test URL description |
| id_externalurl | http://www.moodle.org |
And I hide section "3"
Given I hide section "3"
And I hide section "7"
When I backup "Course 1" course using this options:
| Initial | Include enrolled users | 0 |
@@ -189,11 +178,7 @@ Feature: Restore Moodle 2 course backups
@javascript
Scenario: Restore a backup in an existing course deleting contents and keeping the current course settings
Given I add a "URL" to section "3" and I fill the form with:
| Name | Test URL name |
| Description | Test URL description |
| id_externalurl | http://www.moodle.org |
And I hide section "3"
Given I hide section "3"
And I hide section "7"
When I backup "Course 1" course using this options:
| Initial | Include enrolled users | 0 |
@@ -218,11 +203,7 @@ Feature: Restore Moodle 2 course backups
@javascript
Scenario: Restore a backup in an existing course deleting contents decreasing the number of sections
Given I add a "URL" to section "3" and I fill the form with:
| Name | Test URL name |
| Description | Test URL description |
| id_externalurl | http://www.moodle.org |
And I hide section "3"
Given I hide section "3"
And I hide section "7"
When I backup "Course 1" course using this options:
| Initial | Include enrolled users | 0 |
@@ -21,7 +21,7 @@ Feature: Restore Moodle 2 course backups with different user data settings
| data | Test database name | n | C1 | data1 |
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I add a "Text input" field to "Test database name" database and I fill the form with:
And I add a "Short text" field to "Test database name" database and I fill the form with:
| Field name | Test field name |
| Field description | Test field description |
And I follow "Templates"
+1 -1
View File
@@ -207,7 +207,7 @@ class issued_badge implements renderable {
$data->hasrelatedbadges = true;
$data->relatedbadges = [];
foreach ($relatedbadges as $related) {
if (isloggedin() && !is_guest($context)) {
if (isloggedin() && ($context instanceof context_course && !is_guest($context))) {
$related->url = (new moodle_url('/badges/overview.php', ['id' => $related->id]))->out(false);
}
$data->relatedbadges[] = (array)$related;
@@ -30,9 +30,7 @@ Feature: Award badges with separate groups
| teacher1 | CB |
| student2 | CA |
| teacher2 | CA |
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I navigate to "Edit settings" in current page administration
And I am on the "Course 1" "course editing" page logged in as "teacher1"
And I expand all fieldsets
And I set the field "Group mode" to "Separate groups"
And I press "Save and display"
+2 -2
View File
@@ -36,7 +36,7 @@ class moodle1_block_html_handler extends moodle1_block_handler {
$contextid = $this->converter->get_contextid(CONTEXT_BLOCK, $olddata['id']);
$decodeddata = base64_decode($olddata['configdata']);
list($updated, $configdata) = upgrade_fix_serialized_objects($decodeddata);
$configdata = unserialize($configdata);
$configdata = unserialize_object($configdata);
// get a fresh new file manager for this instance
$this->fileman = $this->converter->get_file_manager($contextid, 'block_html');
@@ -44,7 +44,7 @@ class moodle1_block_html_handler extends moodle1_block_handler {
// convert course files embedded in the block content
$this->fileman->filearea = 'content';
$this->fileman->itemid = 0;
$configdata->text = moodle1_converter::migrate_referenced_files($configdata->text, $this->fileman);
$configdata->text = moodle1_converter::migrate_referenced_files($configdata->text ?? '', $this->fileman);
$configdata->format = FORMAT_HTML;
return base64_encode(serialize($configdata));
+10 -1
View File
@@ -119,10 +119,19 @@ class block_news_items extends block_base {
if (!empty($CFG->forum_enabletimedposts) && ($discussion->timestart > $posttime)) {
$posttime = $discussion->timestart;
}
// If the user who created the discussion post has been deleted, indicate so.
if ($discussion->userdeleted) {
$userfullname = get_string('deleteduser', 'mod_forum');
} else {
$userfullname = fullname($discussion, has_capability('moodle/site:viewfullnames', $context));
}
$text .= '<li class="post">'.
'<div class="head clearfix">'.
'<div class="date">'.userdate($posttime, $strftimerecent).'</div>'.
'<div class="name">'.fullname($discussion).'</div></div>'.
'<div class="name">'.$userfullname.'</div>'.
'</div>'.
'<div class="info"><a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->discussion.'">'.$discussion->subject.'</a></div>'.
"</li>\n";
}
@@ -10,11 +10,18 @@ Feature: Add URL to main menu block
And I am on site homepage
And I turn editing mode on
And I add the "Main menu" block
When I add a "URL" to section "0" and I fill the form with:
| Name | reference link |
| Description | mooooooooodle |
| External URL | http://www.moodle.com |
| id_display | In pop-up |
And the following "activity" exists:
| activity | url |
| course | Acceptance test site |
| name | reference link |
| intro | mooooooooodle |
| externalurl | http://www.moodle.com |
| section | 0 |
When I am on the "reference link" "url activity editing" page
And I expand all fieldsets
And I set the following fields to these values:
| id_display | In pop-up |
And I press "Save and return to course"
Then "reference link" "link" should exist in the "Main menu" "block"
And "Add an activity or resource" "button" should exist in the "Main menu" "block"
@@ -33,12 +40,18 @@ Feature: Add URL to main menu block
And I set the following fields to these values:
| Page contexts | Display throughout the entire site |
And I press "Save changes"
When I am on the "C1" Course page
And I add a "URL" to section "0" and I fill the form with:
| Name | reference link |
| Description | mooooooooodle |
| External URL | http://www.moodle.com |
| Display description on course page | 1 |
| id_display | In pop-up |
When the following "activity" exists:
| activity | url |
| course | C1 |
| name | reference link |
| intro | mooooooooodle |
| externalurl | http://www.moodle.com |
| section | 0 |
| showdescription | 1 |
And I am on the "reference link" "url activity editing" page
And I expand all fieldsets
And I set the following fields to these values:
| id_display | In pop-up |
And I press "Save and return to course"
Then "reference link" "link" should not exist in the "Main menu" "block"
And I should see "mooooooooodle" in the "region-main" "region"
+1 -1
View File
@@ -43,7 +43,7 @@ class cachestore_file_addinstance_form extends cachestore_addinstance_form {
$form = $this->_form;
$form->addElement('text', 'path', get_string('path', 'cachestore_file'));
$form->setType('path', PARAM_SAFEPATH);
$form->setType('path', PARAM_RAW);
$form->addHelpButton('path', 'path', 'cachestore_file');
$form->addElement('checkbox', 'autocreate', get_string('autocreate', 'cachestore_file'));
+1 -1
View File
@@ -5,6 +5,6 @@
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define("core_calendar/crud",["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/modal_event_form","core_calendar/repository","core_calendar/events","core_calendar/modal_delete","core_calendar/selectors","core/pending"],(function($,Str,Notification,CustomEvents,Modal,ModalRegistry,ModalFactory,ModalEvents,ModalEventForm,CalendarRepository,CalendarEvents,ModalDelete,CalendarSelectors,Pending){return{registerRemove:function(root){root.on("click",CalendarSelectors.actions.remove,(function(e){var eventSource=$(this).closest(CalendarSelectors.eventItem);!function(eventId,eventTitle,eventCount){var deletePromise,pendingPromise=new Pending("core_calendar/crud:confirmDeletion"),deleteStrings=[{key:"deleteevent",component:"calendar"}],isRepeatedEvent=(eventCount=parseInt(eventCount,10))>1;isRepeatedEvent?(deleteStrings.push({key:"confirmeventseriesdelete",component:"calendar",param:{name:eventTitle,count:eventCount}}),deletePromise=ModalFactory.create({type:ModalDelete.TYPE})):(deleteStrings.push({key:"confirmeventdelete",component:"calendar",param:eventTitle}),deletePromise=ModalFactory.create({type:ModalFactory.types.SAVE_CANCEL}));var stringsPromise=Str.get_strings(deleteStrings);$.when(stringsPromise,deletePromise).then((function(strings,deleteModal){return deleteModal.setRemoveOnClose(!0),deleteModal.setTitle(strings[0]),deleteModal.setBody(strings[1]),isRepeatedEvent||deleteModal.setSaveButtonText(strings[0]),deleteModal.show(),deleteModal.getRoot().on(ModalEvents.save,(function(){var pendingPromise=new Pending("calendar/crud:initModal:deletedevent");CalendarRepository.deleteEvent(eventId,!1).then((function(){$("body").trigger(CalendarEvents.deleted,[eventId,!1])})).then(pendingPromise.resolve).catch(Notification.exception)})),deleteModal.getRoot().on(CalendarEvents.deleteAll,(function(){var pendingPromise=new Pending("calendar/crud:initModal:deletedallevent");CalendarRepository.deleteEvent(eventId,!0).then((function(){$("body").trigger(CalendarEvents.deleted,[eventId,!0])})).then(pendingPromise.resolve).catch(Notification.exception)})),deleteModal})).then((function(modal){return pendingPromise.resolve(),modal})).catch(Notification.exception)}(eventSource.data("eventId"),eventSource.data("eventTitle"),eventSource.data("eventCount")),e.preventDefault()}))},registerEditListeners:function(root,eventFormModalPromise){var pendingPromise=new Pending("core_calendar/crud:registerEditListeners");return eventFormModalPromise.then((function(modal){return $("body").on(CalendarEvents.editEvent,(function(e,eventId){var calendarWrapper=root.find(CalendarSelectors.wrapper);modal.setEventId(eventId),modal.setContextId(calendarWrapper.data("contextId")),modal.show(),e.stopImmediatePropagation()})),modal})).then((function(modal){return pendingPromise.resolve(),modal})).catch(Notification.exception)},registerEventFormModal:function(root){var eventFormPromise=ModalFactory.create({type:ModalEventForm.TYPE,large:!0});return root.on("click",CalendarSelectors.actions.create,(function(e){eventFormPromise.then((function(modal){var wrapper=root.find(CalendarSelectors.wrapper),categoryId=wrapper.data("categoryid");void 0!==categoryId&&modal.setCategoryId(categoryId);var today=root.find(CalendarSelectors.today),firstDay=root.find(CalendarSelectors.day);!today.length&&firstDay.length&&modal.setStartTime(firstDay.data("newEventTimestamp")),modal.setContextId(wrapper.data("contextId")),modal.setCourseId(wrapper.data("courseid")),modal.show()})).fail(Notification.exception),e.preventDefault()})),root.on("click",CalendarSelectors.actions.edit,(function(e){e.preventDefault();var target=$(e.currentTarget),calendarWrapper=target.closest(CalendarSelectors.wrapper),eventWrapper=target.closest(CalendarSelectors.eventItem);eventFormPromise.then((function(modal){modal.setEventId(eventWrapper.data("eventId")),modal.setContextId(calendarWrapper.data("contextId")),modal.show(),e.stopImmediatePropagation()})).fail(Notification.exception)})),eventFormPromise}}}));
define("core_calendar/crud",["jquery","core/str","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","core/modal_factory","core/modal_events","core_calendar/modal_event_form","core_calendar/repository","core_calendar/events","core_calendar/modal_delete","core_calendar/selectors","core/pending"],(function($,Str,Notification,CustomEvents,Modal,ModalRegistry,ModalFactory,ModalEvents,ModalEventForm,CalendarRepository,CalendarEvents,ModalDelete,CalendarSelectors,Pending){return{registerRemove:function(root){root.on("click",CalendarSelectors.actions.remove,(function(e){var eventSource=$(this).closest(CalendarSelectors.eventItem);!function(eventId,eventTitle,eventCount){var deletePromise,pendingPromise=new Pending("core_calendar/crud:confirmDeletion"),deleteStrings=[{key:"deleteevent",component:"calendar"}],isRepeatedEvent=(eventCount=parseInt(eventCount,10))>1;isRepeatedEvent?(deleteStrings.push({key:"confirmeventseriesdelete",component:"calendar",param:{name:eventTitle,count:eventCount}}),deletePromise=ModalFactory.create({type:ModalDelete.TYPE})):(deleteStrings.push({key:"confirmeventdelete",component:"calendar",param:eventTitle}),deletePromise=ModalFactory.create({type:ModalFactory.types.SAVE_CANCEL}));var stringsPromise=Str.get_strings(deleteStrings);$.when(stringsPromise,deletePromise).then((function(strings,deleteModal){return deleteModal.setRemoveOnClose(!0),deleteModal.setTitle(strings[0]),deleteModal.setBody(strings[1]),isRepeatedEvent||deleteModal.setSaveButtonText(strings[0]),deleteModal.show(),deleteModal.getRoot().on(ModalEvents.save,(function(){var pendingPromise=new Pending("calendar/crud:initModal:deletedevent");CalendarRepository.deleteEvent(eventId,!1).then((function(){$("body").trigger(CalendarEvents.deleted,[eventId,!1])})).then(pendingPromise.resolve).catch(Notification.exception)})),deleteModal.getRoot().on(CalendarEvents.deleteAll,(function(){var pendingPromise=new Pending("calendar/crud:initModal:deletedallevent");CalendarRepository.deleteEvent(eventId,!0).then((function(){$("body").trigger(CalendarEvents.deleted,[eventId,!0])})).then(pendingPromise.resolve).catch(Notification.exception)})),deleteModal})).then((function(modal){return pendingPromise.resolve(),modal})).catch(Notification.exception)}(eventSource.data("eventId"),eventSource.data("eventTitle"),eventSource.data("eventCount")),e.preventDefault()}))},registerEditListeners:function(root,eventFormModalPromise){var pendingPromise=new Pending("core_calendar/crud:registerEditListeners");return eventFormModalPromise.then((function(modal){return $("body").on(CalendarEvents.editEvent,(function(e,eventId){var calendarWrapper=root.find(CalendarSelectors.wrapper);modal.setEventId(eventId),modal.setContextId(calendarWrapper.data("contextId")),modal.show(),e.stopImmediatePropagation()})),modal})).then((function(modal){return pendingPromise.resolve(),modal})).catch(Notification.exception)},registerEventFormModal:function(root){var eventFormPromise=ModalFactory.create({type:ModalEventForm.TYPE,large:!0});return root.on("click",CalendarSelectors.actions.create,(function(e){eventFormPromise.then((function(modal){var wrapper=root.find(CalendarSelectors.wrapper),categoryId=wrapper.data("categoryid");void 0!==categoryId&&modal.setCategoryId(categoryId);var today=root.find(CalendarSelectors.today),firstDay=root.find(CalendarSelectors.day);!today.length&&firstDay.length&&modal.setStartTime(firstDay.data("newEventTimestamp")),modal.setContextId(wrapper.data("contextId")),modal.setCourseId(wrapper.data("courseid")),modal.show()})).fail(Notification.exception),e.preventDefault()})),root.on("click",CalendarSelectors.actions.edit,(function(e){e.preventDefault();var target=$(e.currentTarget),calendarWrapper=target.closest(CalendarSelectors.wrapper),eventWrapper=target.closest(CalendarSelectors.eventItem);eventFormPromise.then((function(modal){modal.setEventId(eventWrapper.data("eventId")),modal.setContextId(calendarWrapper.data("contextId")),modal.setCourseId(eventWrapper.data("courseId")),modal.show(),e.stopImmediatePropagation()})).fail(Notification.exception)})),eventFormPromise}}}));
//# sourceMappingURL=crud.min.js.map
File diff suppressed because one or more lines are too long
+1
View File
@@ -202,6 +202,7 @@ function(
modal.setEventId(eventWrapper.data('eventId'));
modal.setContextId(calendarWrapper.data('contextId'));
modal.setCourseId(eventWrapper.data('courseId'));
modal.show();
e.stopImmediatePropagation();
+7 -2
View File
@@ -180,8 +180,13 @@ class container {
$course = new \stdClass();
$course->id = $dbrow->courseid;
$completion = new \completion_info($course);
return (bool) !$completion->is_enabled($cm);
if ($completion->is_enabled($cm)) {
// Check if the event is completed, then in this case we do not need to complete it.
// Make sure we're using a cm_info object.
$completiondata = $completion->get_data($cm);
return intval($completiondata->completionstate) === COMPLETION_COMPLETE;
}
return true;
}
return false;
+13 -13
View File
@@ -118,16 +118,16 @@ class event implements event_interface {
* @param int $id The event's ID in the database.
* @param string $name The event's name.
* @param description_interface $description The event's description.
* @param proxy_interface $category The category associated with the event.
* @param proxy_interface $course The course associated with the event.
* @param proxy_interface $group The group associated with the event.
* @param proxy_interface $user The user associated with the event.
* @param event_collection_interface $repeats Collection of repeat events.
* @param proxy_interface $coursemodule The course module that created the event.
* @param proxy_interface|null $category The category associated with the event.
* @param proxy_interface|null $course The course associated with the event.
* @param proxy_interface|null $group The group associated with the event.
* @param proxy_interface|null $user The user associated with the event.
* @param event_collection_interface|null $repeats Collection of repeat events.
* @param proxy_interface|null $coursemodule The course module that created the event.
* @param string $type The event's type.
* @param times_interface $times The times associated with the event.
* @param bool $visible The event's visibility. True for visible, false for invisible.
* @param proxy_interface $subscription The event's subscription.
* @param proxy_interface|null $subscription The event's subscription.
* @param string $location The event's location.
* @param string $component The event's component.
*/
@@ -135,12 +135,12 @@ class event implements event_interface {
$id,
$name,
description_interface $description,
proxy_interface $category = null,
proxy_interface $course = null,
proxy_interface $group = null,
proxy_interface $user = null,
event_collection_interface $repeats = null,
proxy_interface $coursemodule = null,
?proxy_interface $category,
?proxy_interface $course,
?proxy_interface $group,
?proxy_interface $user,
?event_collection_interface $repeats,
?proxy_interface $coursemodule,
$type,
times_interface $times,
$visible,
@@ -109,6 +109,9 @@ class create_update_form_mapper implements create_update_form_mapper_interface {
} else {
$properties->courseid = $data->courseid;
}
if (empty($properties->groupid)) {
$properties->groupid = 0;
}
}
// Decode the form fields back into valid event property.
+26 -3
View File
@@ -136,16 +136,39 @@ class behat_calendar extends behat_base {
}
/**
* Navigate to a specific date in the calendar.
* Navigate to a specific month in the calendar.
*
* @Given /^I view the calendar for "(?P<month>\d+)" "(?P<year>\d+)"$/
* @param int $month the month selected as a number
* @param int $year the four digit year
*/
public function i_view_the_calendar_for($month, $year) {
$time = make_timestamp($year, $month, 1);
$this->execute('behat_general::i_visit', ['/calendar/view.php?view=month&course=1&time='.$time]);
$this->view_the_calendar('month', 1, $month, $year);
}
/**
* Navigate to a specific date in the calendar.
*
* @Given /^I view the calendar for "(?P<day>\d+)" "(?P<month>\d+)" "(?P<year>\d+)"$/
* @param int $day the day selected as a number
* @param int $month the month selected as a number
* @param int $year the four digit year
*/
public function i_view_the_calendar_day_view(int $day, int $month, int $year) {
$this->view_the_calendar('day', $day, $month, $year);
}
/**
* View the correct calendar view with specific day
*
* @param string $type type of calendar view: month or day
* @param int $day the day selected as a number
* @param int $month the month selected as a number
* @param int $year the four digit year
*/
private function view_the_calendar(string $type, int $day, int $month, int $year) {
$time = make_timestamp($year, $month, $day);
$this->execute('behat_general::i_visit', ['/calendar/view.php?view=' . $type . '&course=1&time=' . $time]);
}
/**
+68 -1
View File
@@ -16,11 +16,13 @@ Feature: Perform basic calendar functionality
| Course 1 | C1 | topics |
| Course 2 | C2 | topics |
| Course 3 | C3 | topics |
| Course 4 | C4 | topics |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student3 | C1 | student |
| teacher1 | C1 | teacher |
| admin | C1 | editingteacher |
And the following "groups" exist:
| name | course | idnumber |
| Group 1 | C1 | G1 |
@@ -195,7 +197,6 @@ Feature: Perform basic calendar functionality
Scenario: Admin can only see all courses if calendar_adminseesall setting is enabled.
Given I log in as "admin"
And I am on "Course 1" course homepage
And I enrol "admin" user as "Teacher"
And I am viewing site calendar
And I click on "New event" "button"
And I set the field "Type of event" to "Course"
@@ -247,3 +248,69 @@ Feature: Perform basic calendar functionality
Then the page should meet accessibility standards
And the page should meet "wcag131, wcag143, wcag412" accessibility standards
And the page should meet accessibility standards with "wcag131, wcag143, wcag412" extra tests
@javascript
Scenario: Admin can create and edit course events if calendar_adminseesall setting is disabled
Given I log in as "admin"
And the following config values are set as admin:
| calendar_adminseesall | 0 |
And I am on "Course 4" course homepage with editing mode on
And I add the "Upcoming events" block
And I click on "Go to calendar..." "link" in the "Upcoming events" "block"
And I click on "New event" "button"
And I should see "Course" in the "Type of event" "select"
And "Course 4" "autocomplete_selection" should exist
And I set the field "Event title" to "Test course event"
And I click on "Save" "button"
And I should see "Test course event"
When I click on "Edit" "link" in the "region-main" "region"
Then the field "Event title" matches value "Test course event"
And the field "Type of event" matches value "Course"
And "Course 4" "autocomplete_selection" should exist
@javascript
Scenario: Changing the event type should clear previous data
Given I log in as "admin"
And I am on "Course 1" course homepage
And I follow "This month"
And I press "New event"
And I set the following fields to these values:
| Event title | Group 1 event |
| Type of event | Group |
And I press "Save"
And I am on "Course 1" course homepage
And I follow "This month"
And I click on "Group 1 event" "link"
And I should see "Group event"
And I should see "Group 1"
When I click on "Edit" "button"
And I set the following fields to these values:
| Event title | My own user event |
| Type of event | user |
And I press "Save"
And I click on "My own user event" "link"
Then I should see "User event"
And I should not see "Group 1"
And I click on "Edit" "button"
And I set the following fields to these values:
| Event title | Site event |
| Type of event | site |
And I press "Save"
And I click on "Site event" "link"
And I should see "Site event"
And I click on "Edit" "button"
And I set the following fields to these values:
| Event title | Course 1 event |
| Type of event | course |
And I expand the "Course" autocomplete
And I click on "Course 1" item in the autocomplete list
And I press "Save"
And I click on "Course 1 event" "link"
And I should see "Course event"
And I click on "Edit" "button"
And I set the following fields to these values:
| Event title | Category event |
| Type of event | category |
And I press "Save"
And I click on "Category event" "link"
And I should see "Category event"
@@ -74,3 +74,19 @@ Feature: Import and edit calendar events
And I upload "calendar/tests/fixtures/import.ics" file to "Calendar file (.ics)" filemanager
And I press "Add"
And I should see "Site events"
Scenario: Import iCalendar file with parameter.
Given I log in as "admin"
And I view the calendar for "7" "2022"
And I press "Manage subscriptions"
And I set the following fields to these values:
| Calendar name | Test Import |
| Import from | Calendar file (.ics) |
| Type of event | User |
And I upload "calendar/tests/fixtures/import_with_parameters.ics" file to "Calendar file (.ics)" filemanager
And I press "Add"
When I view the calendar for "1" "7" "2022"
Then I should see "First event"
And I should see "Description of the first event"
And I should see "Second event"
And I should see "Description of the second event"
+36
View File
@@ -23,6 +23,7 @@ use core_calendar\local\event\factories\event_factory;
use core_calendar\local\event\factories\event_factory_interface;
use core_calendar\local\event\mappers\event_mapper;
use core_calendar\local\event\mappers\event_mapper_interface;
use core_completion\api;
defined('MOODLE_INTERNAL') || die();
@@ -366,6 +367,41 @@ class container_test extends \advanced_testcase {
$this->assertNull($factory->create_instance($event));
}
/**
* Checks that completed activities events do not show.
* @covers \core_calendar\local\event::init
*/
public function test_event_factory_with_completed_module_related_event() {
global $CFG, $DB;
$this->setAdminUser();
// Create a course.
$course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
$user = $this->getDataGenerator()->create_and_enrol($course);
// Create an assign activity with a time set.
$time = time();
$assign = $this->getDataGenerator()->create_module(
'assign', ['course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL]);
// Create the event but set it to tomorrow.
$CFG->enablecompletion = true;
api::update_completion_date_event($assign->cmid, 'assign', $assign,
$time + DAYSECS);
$this->setUser($user);
// Check that we get should be completed event.
$this->assertCount(1, \core_calendar\local\event\container::get_event_vault()->get_events());
// Then Complete the activity.
$completion = new \completion_info($course);
$cmassign = get_coursemodule_from_id('assign', $assign->cmid);
// This should trigger another call to the update_completion_date_event.
$completion->update_state($cmassign, COMPLETION_COMPLETE, $user->id);
// Check that we do not see the event anymore.
$this->assertCount(0, \core_calendar\local\event\container::get_event_vault()->get_events());
}
/**
* Test that the event factory only returns an event if the logged in user
* is enrolled in the course.
+27
View File
@@ -0,0 +1,27 @@
BEGIN:VCALENDAR
METHOD:PUBLISH
PRODID:-//Huong Nguyen/NONSGML Moodle//EN
VERSION:2.0
BEGIN:VEVENT
UID:special_import_moodle_1
SUMMARY:First event
DESCRIPTION:Description of the first event\n
CLASS:PUBLIC
LAST-MODIFIED:20220728T034015Z
LOCATION:Vietnam
DTSTAMP:20220728T034031Z
DTSTART:20220701T070000Z
DTEND:20220701T070000Z
END:VEVENT
BEGIN:VEVENT
UID:special_import_moodle_2
SUMMARY:Second event
DESCRIPTION;ALTREP="http://moodle.com/":Description of the second event\n
CLASS:PUBLIC
LAST-MODIFIED:20220728T034021Z
LOCATION:Vietnam
DTSTAMP:20220728T034031Z
DTSTART:20220701T080000Z
DTEND:20220701T080000Z
END:VEVENT
END:VCALENDAR
@@ -0,0 +1,261 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
use core_competency\competency;
use core_competency\competency_framework;
use core_competency\plan;
/**
* Behat data generator for core_competency.
*
* @package core_competency
* @category test
* @copyright 2022 Noel De Martin
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_core_competency_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 [
'competencies' => [
'singular' => 'competency',
'datagenerator' => 'competency',
'required' => ['shortname', 'competencyframework'],
'switchids' => ['competencyframework' => 'competencyframeworkid'],
],
'course_competencies' => [
'singular' => 'course_competency',
'datagenerator' => 'course_competency',
'required' => ['course', 'competency'],
'switchids' => ['course' => 'courseid', 'competency' => 'competencyid'],
],
'frameworks' => [
'singular' => 'framework',
'datagenerator' => 'framework',
'required' => ['shortname'],
'switchids' => ['scale' => 'scaleid'],
],
'plans' => [
'singular' => 'plan',
'datagenerator' => 'plan',
'required' => ['name'],
'switchids' => ['user' => 'userid'],
],
'related_competencies' => [
'singular' => 'related_competency',
'datagenerator' => 'related_competency',
'required' => ['competency', 'relatedcompetency'],
'switchids' => ['competency' => 'competencyid', 'relatedcompetency' => 'relatedcompetencyid'],
],
'user_competency' => [
'singular' => 'user_competency',
'datagenerator' => 'user_competency',
'required' => ['competency', 'user'],
'switchids' => ['competency' => 'competencyid', 'user' => 'userid'],
],
'user_competency_courses' => [
'singular' => 'user_competency_course',
'datagenerator' => 'user_competency_course',
'required' => ['course', 'competency', 'user'],
'switchids' => ['course' => 'courseid', 'competency' => 'competencyid', 'user' => 'userid'],
],
'user_competency_plans' => [
'singular' => 'user_competency_plan',
'datagenerator' => 'user_competency_plan',
'required' => ['plan', 'competency', 'user'],
'switchids' => ['plan' => 'planid', 'competency' => 'competencyid', 'user' => 'userid'],
],
];
}
/**
* Get the competecy framework id using an idnumber.
*
* @param string $idnumber
* @return int The competecy framework id
*/
protected function get_competencyframework_id(string $idnumber): int {
global $DB;
if (!$id = $DB->get_field('competency_framework', 'id', ['idnumber' => $idnumber])) {
throw new Exception('The specified competency framework with idnumber "' . $idnumber . '" could not be found.');
}
return $id;
}
/**
* Get the competecy id using an idnumber.
*
* @param string $idnumber
* @return int The competecy id
*/
protected function get_competency_id(string $idnumber): int {
global $DB;
if (!$id = $DB->get_field('competency', 'id', ['idnumber' => $idnumber])) {
throw new Exception('The specified competency with idnumber "' . $idnumber . '" could not be found.');
}
return $id;
}
/**
* Get the learning plan id using a name.
*
* @param string $name
* @return int The learning plan id
*/
protected function get_plan_id(string $name): int {
global $DB;
if (!$id = $DB->get_field('competency_plan', 'id', ['name' => $name])) {
throw new Exception('The specified learning plan with name "' . $name . '" could not be found.');
}
return $id;
}
/**
* Get the related competecy id using an idnumber.
*
* @param string $idnumber
* @return int The related competecy id
*/
protected function get_relatedcompetency_id(string $idnumber): int {
return $this->get_competency_id($idnumber);
}
/**
* Add a plan.
*
* @param array $data Plan data.
*/
public function process_plan(array $data): void {
$generator = $this->get_data_generator();
$competencyids = $data['competencyids'] ?? [];
unset($data['competencyids']);
$plan = $generator->create_plan($data);
foreach ($competencyids as $competencyid) {
$generator->create_plan_competency([
'planid' => $plan->get('id'),
'competencyid' => $competencyid,
]);
}
}
/**
* Preprocess user competency data.
*
* @param array $data Raw data.
* @return array Processed data.
*/
protected function preprocess_user_competency(array $data): array {
$this->prepare_grading($data);
return $data;
}
/**
* Preprocess user course competency data.
*
* @param array $data Raw data.
* @return array Processed data.
*/
protected function preprocess_user_competency_course(array $data): array {
$this->prepare_grading($data);
return $data;
}
/**
* Preprocess user learning plan competency data.
*
* @param array $data Raw data.
* @return array Processed data.
*/
protected function preprocess_user_competency_plan(array $data): array {
$this->prepare_grading($data);
return $data;
}
/**
* Preprocess plan data.
*
* @param array $data Raw data.
* @return array Processed data.
*/
protected function preprocess_plan(array $data): array {
if (isset($data['competencies'])) {
$competencies = array_map('trim', str_getcsv($data['competencies']));
$data['competencyids'] = array_map([$this, 'get_competency_id'], $competencies);
unset($data['competencies']);
}
global $USER;
return $data + [
'userid' => $USER->id,
'status' => plan::STATUS_ACTIVE,
];
}
/**
* Prepare grading attributes for record data.
*
* @param array $data Record data.
*/
protected function prepare_grading(array &$data): void {
if (!isset($data['grade'])) {
return;
}
global $DB;
$competency = competency::get_record(['id' => $data['competencyid']]);
$competencyframework = competency_framework::get_record(['id' => $competency->get('competencyframeworkid')]);
$scale = $DB->get_field('scale', 'scale', ['id' => $competencyframework->get('scaleid')]);
$grades = array_map('trim', explode(',', $scale));
$grade = array_search($data['grade'], $grades);
if ($grade === false) {
throw new Exception('The grade "'.$data['grade'].'" was not found in the "'.
$competencyframework->get('shortname').'" competency framework.');
}
$data['proficiency'] = true;
$data['grade'] = $grade + 1;
}
/**
* Get the module data generator.
*
* @return core_competency_generator Competency data generator.
*/
protected function get_data_generator(): core_competency_generator {
return $this->componentdatagenerator;
}
}
+5 -2
View File
@@ -136,7 +136,11 @@ class core_competency_generator extends component_generator_base {
* @return competency_framework
*/
public function create_framework($record = null) {
$generator = phpunit_util::get_data_generator();
if (defined('BEHAT_TEST') && BEHAT_TEST) {
$generator = behat_util::get_data_generator();
} else {
$generator = phpunit_util::get_data_generator();
}
$this->frameworkcount++;
$i = $this->frameworkcount;
$record = (object) $record;
@@ -584,4 +588,3 @@ class core_competency_generator extends component_generator_base {
}
}
+2 -2
View File
@@ -58,9 +58,9 @@ class bankcontent implements renderable, templatable {
*
* @param \core_contentbank\content[] $contents Array of content bank contents.
* @param array $toolbar List of content bank toolbar options.
* @param \context $context Optional context to check (default null)
* @param \context|null $context Optional context to check (default null)
*/
public function __construct(array $contents, array $toolbar, \context $context = null) {
public function __construct(array $contents, array $toolbar, ?\context $context) {
$this->contents = $contents;
$this->toolbar = $toolbar;
$this->context = $context;
+2 -5
View File
@@ -2477,9 +2477,7 @@ function update_course($data, $editoroptions = NULL) {
function average_number_of_participants(bool $onlyactive = false, int $lastloginsince = null): float {
global $DB;
$params = [
'siteid' => SITEID,
];
$params = [];
$sql = "SELECT DISTINCT ue.userid, e.courseid
FROM {user_enrolments} ue
@@ -2490,8 +2488,7 @@ function average_number_of_participants(bool $onlyactive = false, int $lastlogin
$sql .= "JOIN {user} u ON u.id = ue.userid ";
}
$sql .= "WHERE e.courseid <> :siteid
AND c.visible = 1 ";
$sql .= "WHERE e.courseid <> " . SITEID . " AND c.visible = 1 ";
if ($onlyactive) {
$sql .= "AND ue.status = :active
+3 -3
View File
@@ -14,11 +14,11 @@ Feature: Force group mode in a course
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
And the following "activities" exist:
| activity | course | name |
| chat | C1 | Chat room |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I add a "Chat" to section "1" and I fill the form with:
| Name of this chat room | Chat room |
| Description | Chat description |
And I navigate to "Edit settings" in current page administration
@javascript
@@ -43,15 +43,15 @@ Feature: Course paged mode
Given the following "courses" exist:
| fullname | shortname | category | format | coursedisplay | numsections | startdate |
| Course 1 | C1 | 0 | <courseformat> | 1 | 3 | 0 |
And I log in as "admin"
And the following "activities" exist:
| activity | course | name |
| chat | C1 | Chat room |
When I log in as "admin"
And I am on "Course 1" course homepage with editing mode on
And I click on <section1> "link" in the <section1> "section"
And I should see <section1> in the "div.single-section" "css_element"
And I should see <section2> in the ".single-section span.mdl-right" "css_element"
And I should not see <prevunexistingsection> in the ".single-section" "css_element"
When I add a "Chat" to section "1" and I fill the form with:
| Name of this chat room | Chat room |
| Description | Chat description |
Then I should see <section1> in the "div.single-section" "css_element"
And I should see <section2> in the ".single-section span.mdl-right" "css_element"
And I should not see <prevunexistingsection> in the ".single-section" "css_element"
@@ -14,6 +14,9 @@ Feature: Restrict activities availability
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
And the following "activities" exist:
| activity | course | name |
| chat | C1 | Test chat name |
@javascript
Scenario: Activities can be added with the default permissions
@@ -22,9 +25,6 @@ Feature: Restrict activities availability
When I add a "Glossary" to section "1" and I fill the form with:
| Name | Test glossary name |
| Description | Test glossary description |
And I add a "Chat" to section "1" and I fill the form with:
| Name of this chat room | Test chat name |
| Description | Test chat description |
Then I should see "Test glossary name"
And I should see "Test chat name"
@@ -14,13 +14,10 @@ Feature: View subfolders in a course in-line
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I add a "Folder" to section "3" and I fill the form with:
| Name | Test folder |
| Display folder contents | On a separate page |
| Show subfolders expanded | |
And I am on the "Test folder" "folder activity" page
And the following "activities" exist:
| activity | course | name | display | showexpanded |
| folder | C1 | Test folder | 0 | 0 |
And I am on the "Test folder" "folder activity" page logged in as "teacher1"
And I press "Edit"
And I press "Create folder"
And I set the field "New folder name" to "Test subfolder 1"
+33 -2
View File
@@ -101,7 +101,8 @@ class manage_table extends \table_sql {
* @return string
*/
public function col_name($tool) {
$name = helper::get_name($tool);
$toolcontext = \context::instance_by_id($tool->contextid, IGNORE_MISSING);
$name = $toolcontext ? helper::get_name($tool) : $this->get_deleted_activity_name_html($tool);
return $this->get_display_text($tool, $name);
}
@@ -243,9 +244,39 @@ class manage_table extends \table_sql {
*/
protected function get_display_text($tool, $text) {
if ($tool->status != ENROL_INSTANCE_ENABLED) {
return \html_writer::tag('span', $text, array('class' => 'dimmed_text'));
return \html_writer::tag('div', $text, array('class' => 'dimmed_text'));
}
return $text;
}
/**
* Get a warning icon, with tooltip, describing enrolment instances sharing activities which have been deleted.
*
* @param \stdClass $tool the tool instance record.
* @return string the HTML for the name column.
*/
protected function get_deleted_activity_name_html(\stdClass $tool): string {
global $OUTPUT;
$icon = \html_writer::tag(
'a',
$OUTPUT->pix_icon('enrolinstancewarning', get_string('deletedactivityalt' , 'enrol_lti'), 'enrol_lti'), [
"class" => "btn btn-link p-0",
"role" => "button",
"data-container" => "body",
"data-toggle" => "popover",
"data-placement" => right_to_left() ? "left" : "right",
"data-content" => get_string('deletedactivitydescription', 'enrol_lti'),
"data-html" => "true",
"tabindex" => "0",
"data-trigger" => "focus"
]
);
$name = \html_writer::span($icon . get_string('deletedactivity', 'enrol_lti'));
if ($tool->name) {
$name .= \html_writer::empty_tag('br') . \html_writer::empty_tag('br') . $tool->name;
}
return $name;
}
}
+18 -21
View File
@@ -50,9 +50,6 @@ class sync_members extends scheduled_task {
/** @var array Array of user photos. */
protected $userphotos = [];
/** @var array Array of current LTI users. */
protected $currentusers = [];
/** @var data_connector $dataconnector A data_connector instance. */
protected $dataconnector;
@@ -111,15 +108,15 @@ class sync_members extends scheduled_task {
// Fetched members count.
$membercount = count($members);
$usercount += $membercount;
mtrace("$membercount members received.\n");
// Process member information.
list($usercount, $enrolcount) = $this->sync_member_information($tool, $consumer, $members);
}
list($users, $enrolledcount) = $this->sync_member_information($tool, $consumer, $members);
$enrolcount += $enrolledcount;
// Now we check if we have to unenrol users who were not listed.
if ($this->should_sync_unenrol($tool->membersyncmode)) {
$unenrolcount = $this->sync_unenrol($tool);
// Now sync unenrolments for the consumer.
$unenrolcount += $this->sync_unenrol($tool, $consumer->getKey(), $users);
}
mtrace("Completed - Synced members for tool '$tool->id' in the course '$tool->courseid'. " .
@@ -216,17 +213,15 @@ class sync_members extends scheduled_task {
* @param stdClass $tool
* @param ToolConsumer $consumer
* @param User[] $members
* @return array An array containing the number of members that were processed and the number of members that were enrolled.
* @return array An array of users from processed members and the number that were enrolled.
*/
protected function sync_member_information(stdClass $tool, ToolConsumer $consumer, $members) {
global $DB;
$usercount = 0;
$users = [];
$enrolcount = 0;
// Process member information.
foreach ($members as $member) {
$usercount++;
// Set the user data.
$user = new stdClass();
$user->username = helper::create_username($consumer->getKey(), $member->ltiUserId);
@@ -248,9 +243,7 @@ class sync_members extends scheduled_task {
user_update_user($user);
// Add the information to the necessary arrays.
if (!in_array($user->id, $this->currentusers)) {
$this->currentusers[] = $user->id;
}
$users[$user->id] = $user;
$this->userphotos[$user->id] = $member->image;
} else {
if ($this->should_sync_enrol($tool->membersyncmode)) {
@@ -264,7 +257,7 @@ class sync_members extends scheduled_task {
$user->id = user_create_user($user);
// Add the information to the necessary arrays.
$this->currentusers[] = $user->id;
$users[$user->id] = $user;
$this->userphotos[$user->id] = $member->image;
}
}
@@ -290,16 +283,18 @@ class sync_members extends scheduled_task {
}
}
return [$usercount, $enrolcount];
return [$users, $enrolcount];
}
/**
* Performs unenrolment of users that are no longer enrolled in the consumer side.
*
* @param stdClass $tool The tool record object.
* @param string $consumerkey ensure we only unenrol users from this tool consumer.
* @param array $currentusers The list of current users.
* @return int The number of users that have been unenrolled.
*/
protected function sync_unenrol(stdClass $tool) {
protected function sync_unenrol(stdClass $tool, string $consumerkey, array $currentusers) {
global $DB;
$ltiplugin = enrol_get_plugin('lti');
@@ -308,16 +303,18 @@ class sync_members extends scheduled_task {
return 0;
}
if (empty($this->currentusers)) {
if (empty($currentusers)) {
return 0;
}
$unenrolcount = 0;
$ltiusersrs = $DB->get_recordset('enrol_lti_users', array('toolid' => $tool->id), 'lastaccess DESC', 'userid');
$select = "toolid = :toolid AND " . $DB->sql_compare_text('consumerkey', 255) . " = :consumerkey";
$ltiusersrs = $DB->get_recordset_select('enrol_lti_users', $select, ['toolid' => $tool->id, 'consumerkey' => $consumerkey],
'lastaccess DESC', 'userid');
// Go through the users and check if any were never listed, if so, remove them.
foreach ($ltiusersrs as $ltiuser) {
if (!in_array($ltiuser->userid, $this->currentusers)) {
if (!array_key_exists($ltiuser->userid, $currentusers)) {
$instance = new stdClass();
$instance->id = $tool->enrolid;
$instance->courseid = $tool->courseid;
+13 -1
View File
@@ -37,7 +37,7 @@
* @return boolean
*/
function xmldb_enrol_lti_upgrade($oldversion) {
global $CFG;
global $CFG, $DB;
// Automatically generated Moodle v3.6.0 release upgrade line.
// Put any upgrade step following this.
@@ -57,5 +57,17 @@ function xmldb_enrol_lti_upgrade($oldversion) {
// Automatically generated Moodle v3.11.0 release upgrade line.
// Put any upgrade step following this.
if ($oldversion < 2021051701) {
// Disable all orphaned enrolment method instances.
$sql = "id IN (SELECT t.enrolid
FROM {enrol_lti_tools} t
LEFT JOIN {context} c ON (t.contextid = c.id)
WHERE c.id IS NULL)";
$DB->set_field_select('enrol', 'status', 1, $sql);
// Lti savepoint reached.
upgrade_plugin_savepoint(true, 2021051701, 'enrol', 'lti');
}
return true;
}
+3
View File
@@ -25,6 +25,9 @@
$string['allowframeembedding'] = 'Note: It is recommended that the site administration setting \'Allow frame embedding\' is enabled, so that tools are displayed within a frame rather than in a new window.';
$string['authltimustbeenabled'] = 'Note: This plugin requires the LTI authentication plugin to be enabled too.';
$string['couldnotestablishproxy'] = 'Could not establish proxy with consumer.';
$string['deletedactivity'] = 'Deleted activity';
$string['deletedactivityalt'] = 'The activity shared by this instance has been deleted';
$string['deletedactivitydescription'] = 'The activity shared by this instance has been deleted. You can select another activity to share by editing the instance or, if no longer required, you can simply delete the instance. Deleting the instance will remove any associated user enrolments.';
$string['enrolenddate'] = 'End date';
$string['enrolenddate_help'] = 'If enabled, users can access until this date only.';
$string['enrolenddateerror'] = 'Enrolment end date cannot be earlier than start date';
+24
View File
@@ -415,3 +415,27 @@ function enrol_lti_extend_navigation_course($navigation, $course, $context) {
}
}
}
/**
* Get icon mapping for font-awesome.
*/
function enrol_lti_get_fontawesome_icon_map() {
return [
'enrol_lti:enrolinstancewarning' => 'fa-exclamation-circle text-danger',
];
}
/**
* Pre-delete course module hook which disables any methods referring to the deleted module, preventing launches and allowing remap.
*
* @param stdClass $cm The deleted course module record.
*/
function enrol_lti_pre_course_module_delete(stdClass $cm) {
global $DB;
$sql = "id IN (SELECT t.enrolid
FROM {enrol_lti_tools} t
JOIN {context} c ON (t.contextid = c.id)
WHERE c.contextlevel = :contextlevel
AND c.instanceid = :cmid)";
$DB->set_field_select('enrol', 'status', ENROL_INSTANCE_DISABLED, $sql, ['contextlevel' => CONTEXT_MODULE, 'cmid' => $cm->id]);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMid meet"><path d="M896 128q209 0 385.5 103t279.5 279.5 103 385.5-103 385.5-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103zm128 1247v-190q0-14-9-23.5t-22-9.5h-192q-13 0-23 10t-10 23v190q0 13 10 23t23 10h192q13 0 22-9.5t9-23.5zm-2-344l18-621q0-12-10-18-10-8-24-8h-220q-14 0-24 8-10 6-10 18l17 621q0 10 10 17.5t24 7.5h185q14 0 23.5-7.5t10.5-17.5z" fill="#999"/></svg>

After

Width:  |  Height:  |  Size: 560 B

+32
View File
@@ -169,4 +169,36 @@ class lib_test extends \advanced_testcase {
// LTI enrolment has 1 enrol actions for active users -- unenrol.
$this->assertCount(1, $actions);
}
/**
* Test the behaviour of an enrolment method when the activity to which it provides access is deleted.
*
* @covers \enrol_lti_pre_course_module_delete
*/
public function test_course_module_deletion() {
// Create two modules and publish them.
$course = $this->getDataGenerator()->create_course();
$mod = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
$mod2 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
$tooldata = [
'cmid' => $mod->cmid,
'courseid' => $course->id,
];
$tool = $this->getDataGenerator()->create_lti_tool((object)$tooldata);
$tooldata['cmid'] = $mod2->cmid;
$tool2 = $this->getDataGenerator()->create_lti_tool((object)$tooldata);
// Verify the instances are both enabled.
$modinstance = helper::get_lti_tool($tool->id);
$mod2instance = helper::get_lti_tool($tool2->id);
$this->assertEquals(ENROL_INSTANCE_ENABLED, $modinstance->status);
$this->assertEquals(ENROL_INSTANCE_ENABLED, $mod2instance->status);
// Delete a module and verify the associated instance is disabled.
course_delete_module($mod->cmid);
$modinstance = helper::get_lti_tool($tool->id);
$mod2instance = helper::get_lti_tool($tool2->id);
$this->assertEquals(ENROL_INSTANCE_DISABLED, $modinstance->status);
$this->assertEquals(ENROL_INSTANCE_ENABLED, $mod2instance->status);
}
}
+11 -16
View File
@@ -213,10 +213,10 @@ class sync_members_test extends \advanced_testcase {
* Test for sync_members::sync_member_information().
*/
public function test_sync_member_information() {
list($totalcount, $enrolledcount) = $this->task->sync_member_information($this->tool, $this->consumer, $this->members);
list($users, $enrolledcount) = $this->task->sync_member_information($this->tool, $this->consumer, $this->members);
$membercount = count($this->members);
$this->assertCount(10, $this->members);
$this->assertEquals($membercount, $totalcount);
$this->assertCount($membercount, $users);
$this->assertEquals($membercount, $enrolledcount);
}
@@ -225,10 +225,10 @@ class sync_members_test extends \advanced_testcase {
*/
public function test_sync_profile_images() {
$task = $this->task;
list($totalcount, $enrolledcount) = $task->sync_member_information($this->tool, $this->consumer, $this->members);
list($users, $enrolledcount) = $task->sync_member_information($this->tool, $this->consumer, $this->members);
$membercount = count($this->members);
$this->assertCount(10, $this->members);
$this->assertEquals($membercount, $totalcount);
$this->assertCount($membercount, $users);
$this->assertEquals($membercount, $enrolledcount);
// Suppress output.
@@ -244,14 +244,14 @@ class sync_members_test extends \advanced_testcase {
$tool = $this->tool;
$task = $this->task;
$task->sync_member_information($tool, $this->consumer, $this->members);
list($users) = $task->sync_member_information($tool, $this->consumer, $this->members);
// Simulate that the fetched list of current users has been reduced by 3.
$unenrolcount = 3;
for ($i = 0; $i < $unenrolcount; $i++) {
$task->pop_current_users();
array_pop($users);
}
$this->assertEquals($unenrolcount, $task->sync_unenrol($tool));
$this->assertEquals($unenrolcount, $task->sync_unenrol($tool, 'Consumer1Key', $users));
}
/**
@@ -297,13 +297,6 @@ class dummy_sync_members_task extends sync_members {
return $this->dataconnector;
}
/**
* Helper method that removes an element in the array of current users.
*/
public function pop_current_users() {
array_pop($this->currentusers);
}
/**
* Exposes sync_members::do_context_membership_request()
*
@@ -390,10 +383,12 @@ class dummy_sync_members_task extends sync_members {
* Exposes sync_members::sync_unenrol()
*
* @param \stdClass $tool
* @param string $consumerkey
* @param array $currentusers
* @return int
*/
public function sync_unenrol(\stdClass $tool) {
$count = parent::sync_unenrol($tool);
public function sync_unenrol(\stdClass $tool, string $consumerkey, array $currentusers) {
$count = parent::sync_unenrol($tool, $consumerkey, $currentusers);
return $count;
}
}
+1 -1
View File
@@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2021051700; // The current plugin version (Date: YYYYMMDDXX).
$plugin->version = 2021051701; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2021051100; // Requires this Moodle version.
$plugin->component = 'enrol_lti'; // Full name of the plugin (used for diagnostics).
+1 -1
View File
@@ -135,7 +135,7 @@
.gradingform_guide.editor .addcriterion input,
.gradingform_guide.editor .addcomment input {
background-position: 5px 8px;
background-position: left 5px top 8px;
height: 30px;
line-height: 29px;
margin-bottom: 14px;
+6 -1
View File
@@ -3050,7 +3050,7 @@ abstract class grade_helper {
* @return array
*/
public static function get_plugins_reports($courseid) {
global $SITE;
global $SITE, $CFG;
if (self::$gradereports !== null) {
return self::$gradereports;
@@ -3064,6 +3064,11 @@ abstract class grade_helper {
continue;
}
// Remove outcomes report if outcomes not enabled.
if ($plugin === 'outcomes' && empty($CFG->enableoutcomes)) {
continue;
}
// Remove ones we can't see
if (!has_capability('gradereport/'.$plugin.':view', $context)) {
continue;
+4
View File
@@ -39,6 +39,10 @@ $context = context_course::instance($course->id);
require_capability('gradereport/outcomes:view', $context);
if (empty($CFG->enableoutcomes)) {
redirect(course_get_url($course), get_string('outcomesdisabled', 'core_grades'));
}
// First make sure we have proper final grades.
grade_regrade_final_grades_if_required($course);
-1
View File
@@ -71,7 +71,6 @@ Feature: We can enter in grades and view reports from the gradebook
Scenario: Grade a grade item and ensure the results display correctly in the gradebook
When I navigate to "View > User report" in the course gradebook
And "Grader report" "link" should exist
And "Outcomes report" "link" should exist
And the "Select all or one user" select box should contain "All users (1)"
And I log out
And I log in as "student1"
+7 -1
View File
@@ -277,7 +277,13 @@ class core extends H5PCore {
$file->delete();
$librarykey = static::libraryToString($library);
$libraryid = $factory->get_storage()->h5pC->librariesJsonData[$librarykey]["libraryId"];
$libraryjson = $factory->get_storage()->h5pC->librariesJsonData[$librarykey];
if (!array_key_exists('libraryId', $libraryjson)) {
return null;
}
$libraryid = $libraryjson['libraryId'];
// Update example and tutorial (if any of them are defined in $library).
$params = ['id' => $libraryid];
+2
View File
@@ -93,11 +93,13 @@ class libraries implements renderable, templatable {
$version->toggleenabledurl = new moodle_url('/h5p/libraries.php', [
'id' => $version->id,
'action' => 'disable',
'sesskey' => sesskey(),
]);
} else {
$version->toggleenabledurl = new moodle_url('/h5p/libraries.php', [
'id' => $version->id,
'action' => 'enable',
'sesskey' => sesskey(),
]);
}
$installed[] = $version;
+2
View File
@@ -66,6 +66,8 @@ if ($deletelibrary) {
}
if (!is_null($action)) {
require_sesskey();
if ($action == 'enable' || $action == 'disable') {
// If action is enable or disable, library id is required too.
$libraryid = required_param('id', PARAM_INT);
+1 -1
View File
@@ -31,4 +31,4 @@ defined('MOODLE_INTERNAL') || die();
$string['parentlanguage'] = 'de';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Deutsch (Du)';
$string['thislanguage'] = 'Deutsch (du)';
+32
View File
@@ -0,0 +1,32 @@
<?php
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
/**
* Automatically generated strings for Moodle installer
*
* Do not edit this file manually! It contains just a subset of strings
* needed during the very first steps of installation. This file was
* generated automatically by export-installer.php (which is part of AMOS
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
* list of strings defined in /install/stringnames.txt.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$string['thislanguage'] = 'isiXhosa';
+4 -1
View File
@@ -419,7 +419,7 @@ $string['courselistshortnames_desc'] = 'If enabled, course short names will be d
$string['coursemgmt'] = 'Manage courses and categories';
$string['courseoverview'] = 'Course overview';
$string['courserequestnotify'] = 'Course request notification';
$string['courserequestnotifyemail'] = 'User {$a->user} requested a new course at {$a->link}';
$string['courserequestnotifyemail'] = '{$a->user} requested a new course <a href="{$a->link}">{$a->link}</a>';
$string['courserequests'] = 'Course requests';
$string['courserequestspending'] = 'Pending course requests';
$string['courses'] = 'Courses';
@@ -1506,6 +1506,9 @@ $string['warningiconvbuggy'] = 'Your version of the iconv library does not suppo
$string['webproxy'] = 'Web proxy';
$string['webproxyinfo'] = 'Fill in the following options if your Moodle server cannot access the internet directly. Internet access is required for the download of environment data, language packs, RSS feeds, timezones, etc.<br /><em>The PHP cURL extension is highly recommended.</em>';
$string['xmlrpcrecommended'] = 'The XMLRPC extension is useful for web services and Moodle networking.';
$string['xmlrpcmaharaenabled'] = 'It has been detected that the Mahara ePortfolio is enabled on your site. This feature relies on the PHP XML-RPC extension which is no longer maintained by PHP.';
$string['xmlrpcmnetenabled'] = 'It has been detected that the Moodle Networking is enabled on your site. This feature relies on the PHP XML-RPC extension which is no longer maintained by PHP.';
$string['xmlrpcwebserviceenabled'] = 'It has been detected that the XML-RPC Web Service protocol is enabled on your site. This feature relies on the PHP XML-RPC extension which is no longer maintained by PHP.';
$string['yuicomboloading'] = 'YUI combo loading';
$string['ziprequired'] = 'The Zip PHP extension is now required by Moodle, info-ZIP binaries or PclZip library are not used anymore.';
+1 -1
View File
@@ -36,7 +36,7 @@ $string['asyncemailenable'] = 'Enable message notifications';
$string['asyncemailenabledetail'] = 'If enabled, users will receive a message when an asynchronous backup or restore completes.';
$string['asyncmessagebody'] = 'Message';
$string['asyncmessagebodydetail'] = 'Message to send when an asynchronous backup or restore completes.';
$string['asyncmessagebodydefault'] = 'Hi {user_firstname},<br/> Your {operation} (ID: {backupid}) has completed successfully. <br/><br/>You can access it here: {link}.';
$string['asyncmessagebodydefault'] = 'Your {operation} (ID: {backupid}) has completed successfully. You can access it here: <a href="{link}">{link}</a>.';
$string['asyncmessagesubject'] = 'Subject';
$string['asyncmessagesubjectdetail'] = 'Message subject';
$string['asyncmessagesubjectdefault'] = 'Moodle {operation} completed successfully';
+1 -1
View File
@@ -81,7 +81,7 @@ $string['selecteditems'] = 'Selected items:';
$string['showadvanced'] = 'Show advanced';
$string['showless'] = 'Show less...';
$string['showmore'] = 'Show more...';
$string['somefieldsrequired'] = 'There are required fields in this form marked {$a}.';
$string['somefieldsrequired'] = '{$a} Required';
$string['suggestions'] = 'Suggestions';
$string['time'] = 'Time';
$string['timeunit'] = 'Time unit';
+1
View File
@@ -599,6 +599,7 @@ $string['outcomescoursecustom'] = 'Custom used (no remove)';
$string['outcomescoursenotused'] = 'Standard not used';
$string['outcomescourseused'] = 'Standard used (no remove)';
$string['outcomescustom'] = 'Custom outcomes';
$string['outcomesdisabled'] = 'Outcomes are not enabled';
$string['outcomeshortname'] = 'Short name';
$string['outcomesstandard'] = 'Standard outcomes';
$string['outcomesstandardavailable'] = 'Available standard outcomes';
+1 -1
View File
@@ -65,7 +65,7 @@ $string['disableall'] = 'Disable notifications';
$string['disabled'] = 'Messaging is disabled on this site';
$string['disallowed'] = 'Disallowed';
$string['editmessages'] = 'Edit messages';
$string['emailtagline'] = 'This is a copy of a message sent to you at "{$a->sitename}". Go to {$a->url} to reply.';
$string['emailtagline'] = 'This is a copy of a message sent to you on the site {$a->sitename}. Go to <a href="{$a->url}">{$a->url}</a> to reply.';
$string['enabled'] = 'Enabled';
$string['errorcallingprocessor'] = 'Error calling defined output';
$string['errorconversationdoesnotexist'] = 'Conversation does not exist';
+1
View File
@@ -363,6 +363,7 @@ $string['europe/jersey'] = 'Europe/Jersey';
$string['europe/kaliningrad'] = 'Europe/Kaliningrad';
$string['europe/kiev'] = 'Europe/Kiev';
$string['europe/kirov'] = 'Europe/Kirov';
$string['europe/kyiv'] = 'Europe/Kyiv';
$string['europe/lisbon'] = 'Europe/Lisbon';
$string['europe/ljubljana'] = 'Europe/Ljubljana';
$string['europe/london'] = 'Europe/London';
+5 -2
View File
@@ -944,8 +944,11 @@ EOF;
}
} else {
$errorinfo = $this->get_debug_text($errorinfoboxes[0]->getHtml()) . "\n" .
$this->get_debug_text($errorinfoboxes[1]->getHtml());
$errorinfo = implode("\n", [
$this->get_debug_text($errorinfoboxes[0]->getHtml()),
$this->get_debug_text($errorinfoboxes[1]->getHtml()),
html_to_text($errorinfoboxes[2]->find('css', 'ul')->getHtml()),
]);
}
$msg = "Moodle exception: " . $errormsg->getText() . "\n" . $errorinfo;
+12
View File
@@ -303,6 +303,18 @@ class iCalendar_component {
$component = $this; // use the iCalendar
}
$cleanedparams = [];
// Some parameter values are wrapped by DQUOTE character.
// We need to go through and get the actual value inside the quoted string.
foreach ($params as $param => $value) {
if (preg_match('#"(?P<actualvalue>[^"]*?)"#', $value, $matches)) {
$cleanedparams[$param] = $matches['actualvalue'];
} else {
$cleanedparams[$param] = $value;
}
}
$params = $cleanedparams;
if ($component->add_property($label, $data, $params) === false) {
$this->parser_error("Failed to add property '$label' on line $key");
}
+1
View File
@@ -28,3 +28,4 @@ Changelog
9/ MDL-60391: replace create_function() with lambda function for PHP 7.2 compatibility (13 Oct 2017)
10/ MDL-62914: added handling for TZURL property (13 July 2018)
11/ MDL-67029: replace curly by square brackets for string offsets. PHP 7.4 compatibility (25 Oct 2019)
12/ MDL-74866: fixed parameter parsing if the value is wrapped by DQUOTE character (28 Jul 2022)
@@ -55,8 +55,9 @@ class mustache_shorten_text_helper {
$length = trim($length);
$text = trim($text);
// Allow mustache tags in the text.
// Allow mustache tags in the length and text.
$text = $helper->render($text);
$length = $helper->render($length);
return shorten_text($text, $length);
}
+11 -7
View File
@@ -522,22 +522,26 @@ function get_users_listing($sort='lastaccess', $dir='ASC', $page=0, $recordsperp
$params = $params + (array)$extraparams;
}
if ($sort) {
$sort = " ORDER BY $sort $dir";
}
// If a context is specified, get extra user fields that the current user
// is supposed to see, otherwise just get the name fields.
$userfields = \core_user\fields::for_name();
if ($extracontext) {
$userfields->with_identity($extracontext, true);
}
$userfields->excluding('id', 'username', 'email', 'city', 'country', 'lastaccess', 'confirmed', 'mnethostid');
['selects' => $selects, 'joins' => $joins, 'params' => $joinparams] =
$userfields->excluding('id');
$userfields->including('username', 'email', 'city', 'country', 'lastaccess', 'confirmed', 'mnethostid', 'suspended');
['selects' => $selects, 'joins' => $joins, 'params' => $joinparams, 'mappings' => $mappings] =
(array)$userfields->get_sql('u', true);
if ($sort) {
$orderbymap = $mappings;
$orderbymap['default'] = 'lastaccess';
$sort = get_safe_orderby($orderbymap, $sort, $dir);
}
// warning: will return UNCONFIRMED USERS
return $DB->get_records_sql("SELECT u.id, username, email, city, country, lastaccess, confirmed, mnethostid, suspended $selects
return $DB->get_records_sql("SELECT u.id $selects
FROM {user} u
$joins
WHERE $select
+80 -72
View File
@@ -14,18 +14,26 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
use database_column_info;
use moodle_database;
use sql_generator;
use xmldb_field;
use xmldb_index;
use xmldb_key;
use xmldb_structure;
use xmldb_table;
/**
* DDL layer tests.
*
* @package core_ddl
* @category phpunit
* @category test
* @copyright 2008 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
class core_ddl_testcase extends database_driver_testcase {
class ddl_test extends \database_driver_testcase {
/** @var xmldb_table[] keys are table name. Created in setUp. */
private $tables = array();
/** @var array table name => array of stdClass test records loaded into that table. Created in setUp. */
@@ -165,7 +173,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$result = $DB->get_records('test_table0');
} catch (dml_exception $e) {
} catch (\dml_exception $e) {
$result = false;
}
$this->resetDebugging();
@@ -187,7 +195,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$result = $DB->get_records('test_table0');
} catch (dml_exception $e) {
} catch (\dml_exception $e) {
$result = false;
}
$this->resetDebugging();
@@ -274,7 +282,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_exception', $e);
}
@@ -289,7 +297,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_exception', $e);
}
@@ -320,7 +328,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -336,7 +344,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -365,7 +373,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -381,7 +389,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -397,7 +405,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -413,7 +421,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -429,7 +437,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -445,7 +453,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -461,7 +469,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -477,7 +485,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -493,7 +501,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -509,7 +517,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -525,7 +533,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -541,7 +549,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
}
@@ -558,7 +566,7 @@ class core_ddl_testcase extends database_driver_testcase {
$text = str_repeat('š', 1333);
$data = new stdClass();
$data = new \stdClass();
$data->name = 'test';
$table = new xmldb_table('test_innodb');
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
@@ -575,7 +583,7 @@ class core_ddl_testcase extends database_driver_testcase {
$expected = (array)$data;
$expected['id'] = (string)$id;
$this->assertEqualsCanonicalizing($expected, (array)$DB->get_record('test_innodb', array('id' => $id)));
} catch (dml_exception $e) {
} catch (\dml_exception $e) {
// Give some nice error message when known problematic MySQL with InnoDB detected.
if ($DB->get_dbfamily() === 'mysql') {
$engine = strtolower($DB->get_dbengine());
@@ -590,7 +598,7 @@ class core_ddl_testcase extends database_driver_testcase {
$dbman->drop_table($table);
$data = new stdClass();
$data = new \stdClass();
$data->name = 'test';
$table = new xmldb_table('test_innodb');
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
@@ -613,7 +621,7 @@ class core_ddl_testcase extends database_driver_testcase {
$dbman->drop_table($table);
// MySQL VARCHAR fields may hit a different 65535 row size limit when creating tables.
$data = new stdClass();
$data = new \stdClass();
$data->name = 'test';
$table = new xmldb_table('test_innodb');
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
@@ -695,7 +703,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->rename_table($sourcetable, $targettable->getName());
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_exception', $e);
$this->assertEquals('Table "test_table1" already exists (can not rename table)', $e->getMessage());
}
@@ -714,7 +722,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->field_exists('nonexistenttable', 'id');
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('moodle_exception', $e);
}
@@ -732,7 +740,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->field_exists($nonexistenttable, $realfield);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('moodle_exception', $e);
}
@@ -751,13 +759,13 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$this->assertFalse($dbman->field_exists($nonexistenttable, 'id'));
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('moodle_exception', $e);
}
try {
$this->assertFalse($dbman->field_exists('nonexistenttable', $realfield));
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('moodle_exception', $e);
}
// Non existing fields (return false).
@@ -783,7 +791,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->add_field($table, $field);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_exception', $e);
}
@@ -793,7 +801,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->add_field($table, $field);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_exception', $e);
}
@@ -948,7 +956,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->drop_field($table, $field);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_dependency_exception', $e);
}
$this->assertTrue($dbman->field_exists($table, 'type')); // Continues existing, drop aborted.
@@ -959,7 +967,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->drop_field($table, $field);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_dependency_exception', $e);
}
$this->assertTrue($dbman->field_exists($table, $field)); // Continues existing, drop aborted.
@@ -970,7 +978,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->drop_field($table, $field);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_field_missing_exception', $e);
}
@@ -1004,7 +1012,7 @@ class core_ddl_testcase extends database_driver_testcase {
$table->add_index('onenumber', XMLDB_INDEX_NOTUNIQUE, array('onenumber'));
$dbman->create_table($table);
$record = new stdClass();
$record = new \stdClass();
$record->onenumber = 2;
$record->anothernumber = 4;
$recoriginal = $DB->insert_record('test_table_cust0', $record);
@@ -1015,7 +1023,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->change_field_type($table, $field);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_dependency_exception', $e);
}
// Column continues being integer 10 not null default 2.
@@ -1051,7 +1059,7 @@ class core_ddl_testcase extends database_driver_testcase {
// TODO: check the rest of attributes.
// Insert one string value and try to convert to integer. Must throw exception.
$record = new stdClass();
$record = new \stdClass();
$record->onenumber = 7;
$record->anothernumber = 'string value';
$rectodrop = $DB->insert_record('test_table_cust0', $record);
@@ -1060,7 +1068,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->change_field_type($table, $field);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_change_structure_exception', $e);
}
// Column continues being char 30 not null default "test'n drop" now.
@@ -1182,7 +1190,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->change_field_precision($table, $field);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_change_structure_exception', $e);
}
// No changes in field specs at all.
@@ -1216,7 +1224,7 @@ class core_ddl_testcase extends database_driver_testcase {
// TODO: check the rest of attributes.
// Insert one record with 6-digit field.
$record = new stdClass();
$record = new \stdClass();
$record->course = 10;
$record->secondname = 'third record';
$record->intro = 'third record';
@@ -1228,7 +1236,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->change_field_precision($table, $field);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_change_structure_exception', $e);
}
// No changes in field specs at all.
@@ -1242,7 +1250,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->change_field_precision($table, $field);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_dependency_exception', $e);
}
// No changes in field specs at all.
@@ -1261,12 +1269,12 @@ class core_ddl_testcase extends database_driver_testcase {
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$dbman->create_table($table);
$record = new stdClass();
$record = new \stdClass();
$record->name = null;
try {
$result = $DB->insert_record('test_table_cust0', $record, false);
} catch (dml_exception $e) {
} catch (\dml_exception $e) {
$result = false;
}
$this->resetDebugging();
@@ -1287,7 +1295,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$result = $DB->insert_record('test_table_cust0', $record, false);
} catch (dml_exception $e) {
} catch (\dml_exception $e) {
$result = false;
}
$this->resetDebugging();
@@ -1311,7 +1319,7 @@ class core_ddl_testcase extends database_driver_testcase {
$field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'Moodle2');
$dbman->change_field_default($table, $field);
$record = new stdClass();
$record = new \stdClass();
$record->onenumber = 666;
$id = $DB->insert_record('test_table_cust0', $record);
@@ -1322,7 +1330,7 @@ class core_ddl_testcase extends database_driver_testcase {
$field->set_attributes(XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 666);
$dbman->change_field_default($table, $field);
$record = new stdClass();
$record = new \stdClass();
$record->name = 'something';
$id = $DB->insert_record('test_table_cust0', $record);
@@ -1343,7 +1351,7 @@ class core_ddl_testcase extends database_driver_testcase {
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$dbman->create_table($table);
$record = new stdClass();
$record = new \stdClass();
$record->onenumber = 666;
$record->name = 'something';
$DB->insert_record('test_table_cust0', $record, false);
@@ -1354,7 +1362,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$result = $DB->insert_record('test_table_cust0', $record, false);
} catch (dml_exception $e) {
} catch (\dml_exception $e) {
$result = false;
}
$this->resetDebugging();
@@ -1375,7 +1383,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->add_index($table, $index);
$this->fail('Exception expected for duplicate indexes');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_exception', $e);
}
@@ -1384,7 +1392,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->add_index($table, $index);
$this->fail('Exception expected for duplicate indexes');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_exception', $e);
}
@@ -1398,7 +1406,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$table->add_index('onenumber', XMLDB_INDEX_NOTUNIQUE, array('onenumber'));
$this->fail('Coding exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -1412,7 +1420,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$table->add_key('onenumber', XMLDB_KEY_FOREIGN, array('onenumber'));
$this->fail('Coding exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -1614,7 +1622,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->delete_tables_from_xmldb_file('fpsoiudfposui');
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->resetDebugging();
$this->assertInstanceOf('moodle_exception', $e);
}
@@ -1622,7 +1630,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->delete_tables_from_xmldb_file(__DIR__ . '/fixtures/invalid.xml');
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->resetDebugging();
$this->assertInstanceOf('moodle_exception', $e);
}
@@ -1645,7 +1653,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->install_from_xmldb_file('fpsoiudfposui');
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->resetDebugging();
$this->assertInstanceOf('moodle_exception', $e);
}
@@ -1653,7 +1661,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->install_from_xmldb_file(__DIR__ . '/fixtures/invalid.xml');
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->resetDebugging();
$this->assertInstanceOf('moodle_exception', $e);
}
@@ -1680,7 +1688,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_temp_table($dupetable);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_exception', $e);
}
@@ -1689,7 +1697,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($dupetable);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_exception', $e);
}
@@ -1726,7 +1734,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->drop_table($noetable);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('ddl_table_missing_exception', $e);
}
@@ -1875,7 +1883,7 @@ class core_ddl_testcase extends database_driver_testcase {
$dbman->drop_table($table0);
$rc = new ReflectionClass('moodle_database');
$rc = new \ReflectionClass('moodle_database');
$rcm = $rc->getMethod('get_temp_tables_cache');
$rcm->setAccessible(true);
$metacachetemp = $rcm->invokeArgs($DB, []);
@@ -1886,7 +1894,7 @@ class core_ddl_testcase extends database_driver_testcase {
// Data of test_table1 should be intact.
$this->assertEquals(true, $metacachetemp->has('test_table1'));
$rc = new ReflectionClass('moodle_database');
$rc = new \ReflectionClass('moodle_database');
$rcm = $rc->getMethod('get_metacache');
$rcm->setAccessible(true);
$metacache = $rcm->invokeArgs($DB, []);
@@ -2009,7 +2017,7 @@ class core_ddl_testcase extends database_driver_testcase {
$tablename = $table->getName();
$this->tables[$tablename] = $table;
$rec = new stdClass();
$rec = new \stdClass();
$rec->name = $maxstr;
$id = $DB->insert_record($tablename, $rec);
@@ -2029,7 +2037,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
}
@@ -2062,7 +2070,7 @@ class core_ddl_testcase extends database_driver_testcase {
$tablename = $table->getName();
$this->tables[$tablename] = $table;
$rec = new stdClass();
$rec = new \stdClass();
$rec->name1 = $maxstr;
$rec->name2 = $reststr;
@@ -2088,7 +2096,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
}
@@ -2116,7 +2124,7 @@ class core_ddl_testcase extends database_driver_testcase {
$maxstr .= 'a'; // Ascii only.
}
$rec = new stdClass();
$rec = new \stdClass();
$rec->name = $maxstr;
$id = $DB->insert_record($tablename, $rec);
@@ -2131,7 +2139,7 @@ class core_ddl_testcase extends database_driver_testcase {
$maxstr .= '言'; // Random long string that should fix exactly the limit for one char column.
}
$rec = new stdClass();
$rec = new \stdClass();
$rec->name = $maxstr;
try {
@@ -2163,7 +2171,7 @@ class core_ddl_testcase extends database_driver_testcase {
try {
$dbman->create_table($table);
$this->fail('Exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
}
+2 -2
View File
@@ -415,12 +415,12 @@ abstract class moodle_database {
/**
* This should be called before each db query.
* @param string $sql The query string.
* @param array $params An array of parameters.
* @param array|null $params An array of parameters.
* @param int $type The type of query. ( SQL_QUERY_SELECT | SQL_QUERY_AUX | SQL_QUERY_INSERT | SQL_QUERY_UPDATE | SQL_QUERY_STRUCTURE )
* @param mixed $extrainfo This is here for any driver specific extra information.
* @return void
*/
protected function query_start($sql, array $params=null, $type, $extrainfo=null) {
protected function query_start($sql, ?array $params, $type, $extrainfo=null) {
if ($this->loggingquery) {
return;
}
+24 -13
View File
@@ -264,16 +264,35 @@ trait moodle_read_slave_trait {
/**
* Called before each db query.
* @param string $sql
* @param array $params array of parameters
* @param array|null $params An array of parameters.
* @param int $type type of query
* @param mixed $extrainfo driver specific extra information
* @return void
*/
protected function query_start($sql, array $params = null, $type, $extrainfo = null) {
protected function query_start($sql, ?array $params, $type, $extrainfo = null) {
parent::query_start($sql, $params, $type, $extrainfo);
$this->select_db_handle($type, $sql);
}
/**
* This should be called immediately after each db query. It does a clean up of resources.
*
* @param mixed $result The db specific result obtained from running a query.
* @return void
*/
protected function query_end($result) {
if ($this->written) {
// Adjust the written time.
array_walk($this->written, function (&$val) {
if ($val === true) {
$val = microtime(true);
}
});
}
parent::query_end($result);
}
/**
* Select appropriate db handle - readwrite or readonly
* @param int $type type of query
@@ -339,10 +358,8 @@ trait moodle_read_slave_trait {
return true;
case SQL_QUERY_INSERT:
case SQL_QUERY_UPDATE:
// If we are in transaction we cannot set the written time yet.
$now = $this->slavelatency && !$this->transactions ? microtime(true) : true;
foreach ($this->table_names($sql) as $tablename) {
$this->written[$tablename] = $now;
$this->written[$tablename] = true;
}
return false;
case SQL_QUERY_STRUCTURE:
@@ -370,16 +387,10 @@ trait moodle_read_slave_trait {
return;
}
if (!$this->slavelatency) {
return;
}
$now = null;
foreach ($this->written as $tablename => $when) {
if ($when === true) {
$now = $now ?: microtime(true);
$this->written[$tablename] = $now;
}
$now = $now ?: microtime(true);
$this->written[$tablename] = $now;
}
}
+2 -2
View File
@@ -245,12 +245,12 @@ class oci_native_moodle_database extends moodle_database {
/**
* Called before each db query.
* @param string $sql
* @param array array of parameters
* @param array|null $params An array of parameters.
* @param int $type type of query
* @param mixed $extrainfo driver specific extra information
* @return void
*/
protected function query_start($sql, array $params=null, $type, $extrainfo=null) {
protected function query_start($sql, ?array $params, $type, $extrainfo=null) {
parent::query_start($sql, $params, $type, $extrainfo);
// oci driver tents to send debug to output, we do not need that ;-)
$this->last_error_reporting = error_reporting(0);
+2 -2
View File
@@ -614,12 +614,12 @@ abstract class pdo_moodle_database extends moodle_database {
* Overridden to ensure $this->lastErorr is reset each query
*
* @param string $sql
* @param array array of parameters
* @param array|null $params An array of parameters.
* @param int $type type of query
* @param mixed $extrainfo driver specific extra information
* @return void
*/
protected function query_start($sql, array $params=null, $type, $extrainfo=null) {
protected function query_start($sql, ?array $params, $type, $extrainfo=null) {
$this->lastError = null;
parent::query_start($sql, $params, $type, $extrainfo);
}
+17 -8
View File
@@ -41,6 +41,7 @@ class pgsql_native_moodle_database extends moodle_database {
select_db_handle as read_slave_select_db_handle;
can_use_readonly as read_slave_can_use_readonly;
query_start as read_slave_query_start;
query_end as read_slave_query_end;
}
/** @var array $dbhcursor keep track of open cursors */
@@ -184,12 +185,20 @@ class pgsql_native_moodle_database extends moodle_database {
}
ob_start();
if (empty($this->dboptions['dbpersist'])) {
$this->pgsql = pg_connect($connection, PGSQL_CONNECT_FORCE_NEW);
} else {
$this->pgsql = pg_pconnect($connection, PGSQL_CONNECT_FORCE_NEW);
// It seems that pg_connect() handles some errors differently.
// For example, name resolution error will raise an exception, and non-existing
// database or wrong credentials will just return false.
// We need to cater for both.
try {
if (empty($this->dboptions['dbpersist'])) {
$this->pgsql = pg_connect($connection, PGSQL_CONNECT_FORCE_NEW);
} else {
$this->pgsql = pg_pconnect($connection, PGSQL_CONNECT_FORCE_NEW);
}
$dberr = ob_get_contents();
} catch (\Exception $e) {
$dberr = $e->getMessage();
}
$dberr = ob_get_contents();
ob_end_clean();
$status = $this->pgsql ? pg_connection_status($this->pgsql) : false;
@@ -306,12 +315,12 @@ class pgsql_native_moodle_database extends moodle_database {
/**
* Called before each db query.
* @param string $sql
* @param array array of parameters
* @param array|null $params An array of parameters.
* @param int $type type of query
* @param mixed $extrainfo driver specific extra information
* @return void
*/
protected function query_start($sql, array $params=null, $type, $extrainfo=null) {
protected function query_start($sql, ?array $params, $type, $extrainfo=null) {
$this->read_slave_query_start($sql, $params, $type, $extrainfo);
// pgsql driver tends to send debug to output, we do not need that.
$this->last_error_reporting = error_reporting(0);
@@ -326,7 +335,7 @@ class pgsql_native_moodle_database extends moodle_database {
// reset original debug level
error_reporting($this->last_error_reporting);
try {
parent::query_end($result);
$this->read_slave_query_end($result);
if ($this->savepointpresent and $this->last_type != SQL_QUERY_AUX and $this->last_type != SQL_QUERY_SELECT) {
$res = @pg_query($this->pgsql, "RELEASE SAVEPOINT moodle_pg_savepoint; SAVEPOINT moodle_pg_savepoint");
if ($res) {
+2 -2
View File
@@ -306,12 +306,12 @@ class sqlsrv_native_moodle_database extends moodle_database {
/**
* Called before each db query.
* @param string $sql
* @param array $params array of parameters
* @param array|null $params An array of parameters.
* @param int $type type of query
* @param mixed $extrainfo driver specific extra information
* @return void
*/
protected function query_start($sql, array $params = null, $type, $extrainfo = null) {
protected function query_start($sql, ?array $params, $type, $extrainfo = null) {
parent::query_start($sql, $params, $type, $extrainfo);
}
+2 -1
View File
@@ -34,8 +34,9 @@ require_once(__DIR__.'/fixtures/read_slave_moodle_database_mock_mysqli.php');
* @category dml
* @copyright 2018 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \mysqli_native_moodle_database
*/
class core_dml_mysqli_read_slave_testcase extends base_testcase {
class dml_mysqli_read_slave_test extends base_testcase {
/**
* Test readonly handle is not used for reading from special pg_*() call queries,
* pg_try_advisory_lock and pg_advisory_unlock.
+2 -1
View File
@@ -34,8 +34,9 @@ require_once(__DIR__.'/fixtures/read_slave_moodle_database_mock_pgsql.php');
* @category dml
* @copyright 2018 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \pgsql_native_moodle_database
*/
class core_dml_pgsql_read_slave_testcase extends base_testcase {
class dml_pgsql_read_slave_test extends base_testcase {
/**
* Test correct database handles are used for cursors
*
+46 -1
View File
@@ -35,8 +35,9 @@ require_once(__DIR__.'/fixtures/read_slave_moodle_database_special.php');
* @category dml
* @copyright 2018 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \moodle_read_slave_trait
*/
class core_dml_read_slave_testcase extends base_testcase {
class dml_read_slave_test extends base_testcase {
/** @var float */
static private $dbreadonlylatency = 0.8;
@@ -311,6 +312,50 @@ class core_dml_read_slave_testcase extends base_testcase {
$this->assert_readonly_handle($handle);
}
/**
* Test readonly handle is not used immediately after update
* Test last written time is adjusted post-write,
* so the latency parameter is applied properly.
*
* @return void
* @covers ::can_use_readonly
* @covers ::query_end
*/
public function test_long_update(): void {
$DB = $this->new_db(true);
$this->assertNull($DB->get_dbhwrite());
$skip = false;
list($sql, $params, $ptype) = $DB->fix_sql_params("UPDATE {table} SET a = 1 WHERE id = 1");
$DB->with_query_start_end($sql, $params, SQL_QUERY_UPDATE, function ($dbh) use (&$now) {
sleep(1);
$now = microtime(true);
});
// This condition should always evaluate true, however we need to
// safeguard from an unaccounted delay that can break this test.
if (microtime(true) - $now < self::$dbreadonlylatency) {
// Not enough time passed, use rw handle.
$handle = $DB->get_records_sql("SELECT * FROM {table}");
$this->assertEquals('test_rw::test:test', $handle);
// Make sure enough time passes.
sleep(1);
} else {
$skip = true;
}
// Exceeded latency time, use ro handle.
$handle = $DB->get_records_sql("SELECT * FROM {table}");
$this->assert_readonly_handle($handle);
if ($skip) {
$this->markTestSkipped("Delay too long to test write handle immediately after transaction");
}
}
/**
* Test failed readonly connection falls back to write connection.
*
+10 -16
View File
@@ -14,27 +14,21 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* DML Table tests.
*
* @package core_dml
* @category phpunit
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core;
defined('MOODLE_INTERNAL') || die();
use \core\dml\table;
use core\dml\table;
use xmldb_table;
/**
* DML Table tests.
*
* @package core
* @category test
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \core\dml\table
*/
class core_dml_table_testcase extends database_driver_testcase {
class dml_table_test extends \database_driver_testcase {
/**
* Data provider for various \core\dml\table method tests.
@@ -162,15 +156,15 @@ class core_dml_table_testcase extends database_driver_testcase {
* @param array $fieldlist The list of fields
* @param string $primarykey The name of the primary key
* @param string $fieldprefix The prefix to use for each field
* @param stdClass $result The result of the get_records_sql
* @param stdClass $expected The expected output
* @param \stdClass $result The result of the get_records_sql
* @param \stdClass $expected The expected output
*/
public function test_extract_fields_from_result(
array $fieldlist,
string $primarykey,
string $fieldprefix,
stdClass $result,
stdClass $expected
\stdClass $result,
\stdClass $expected
) {
$dbman = $this->tdb->get_manager();
+145 -127
View File
@@ -17,15 +17,33 @@
/**
* DML layer tests.
*
* @package core_dml
* @category phpunit
* @package core
* @category test
* @copyright 2008 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core;
use dml_exception;
use dml_missing_record_exception;
use dml_multiple_records_exception;
use moodle_database;
use moodle_transaction;
use xmldb_key;
use xmldb_table;
defined('MOODLE_INTERNAL') || die();
class core_dml_testcase extends database_driver_testcase {
/**
* DML layer tests.
*
* @package core
* @category test
* @copyright 2008 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class dml_test extends \database_driver_testcase {
protected function setUp(): void {
parent::setUp();
@@ -212,7 +230,7 @@ class core_dml_testcase extends database_driver_testcase {
try {
list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_DOLLAR, 'param', false);
$this->fail('An Exception is missing, expected due to not supported SQL_PARAMS_DOLLAR');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
$this->assertSame('typenotimplement', $e->errorcode);
}
@@ -222,7 +240,7 @@ class core_dml_testcase extends database_driver_testcase {
try {
list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
$this->fail('An Exception is missing, expected due to empty array of items');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -354,7 +372,7 @@ class core_dml_testcase extends database_driver_testcase {
try {
$DB->fix_sql_params($sql, $params);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
@@ -364,7 +382,7 @@ class core_dml_testcase extends database_driver_testcase {
try {
$DB->fix_sql_params($sql, $params);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
@@ -374,7 +392,7 @@ class core_dml_testcase extends database_driver_testcase {
try {
$DB->fix_sql_params($sql, $params);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
@@ -391,7 +409,7 @@ class core_dml_testcase extends database_driver_testcase {
try {
$DB->fix_sql_params($sql, $params);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
@@ -402,7 +420,7 @@ class core_dml_testcase extends database_driver_testcase {
try {
$DB->fix_sql_params($sql, $params);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
@@ -419,7 +437,7 @@ class core_dml_testcase extends database_driver_testcase {
try {
$DB->fix_sql_params($sql, $params);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -459,7 +477,7 @@ class core_dml_testcase extends database_driver_testcase {
$DB = $this->tdb;
require_once($CFG->dirroot . '/lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php');
$fixture = new test_dml_sql_debugging_fixture($this);
$fixture = new \test_dml_sql_debugging_fixture($this);
$sql = "SELECT * FROM {users}";
@@ -493,7 +511,7 @@ SELECT * FROM {users}
-- line 74 of /lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to test_dml_sql_debugging_fixture->one()
-- line 83 of /lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to test_dml_sql_debugging_fixture->two()
-- line 92 of /lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to test_dml_sql_debugging_fixture->three()
-- line 489 of /lib/dml/tests/dml_test.php: call to test_dml_sql_debugging_fixture->four()
-- line 507 of /lib/dml/tests/dml_test.php: call to test_dml_sql_debugging_fixture->four()
EOD;
$this->assertEquals($this->unix_to_os_dirsep($expected), $out);
@@ -549,7 +567,7 @@ EOD;
$this->assertTrue($dbman->table_exists($tablename));
// Test insert record.
$rec1 = new stdClass();
$rec1 = new \stdClass();
$rec1->long_int_columnname_with_28c = 28;
$rec1->long_dec_columnname_with_28c = 28.28;
$rec1->long_str_columnname_with_28c = '28';
@@ -1007,7 +1025,7 @@ EOD;
try {
$DB->execute($sql);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
@@ -1138,7 +1156,7 @@ EOD;
try {
$rs = $DB->get_recordset($tablename, $conditions);
$this->fail('An Exception is missing, expected due to equating of text fields');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
$this->assertSame('textconditionsnotallowed', $e->errorcode);
}
@@ -1546,7 +1564,7 @@ EOD;
// Only in debug mode - hopefully all devs test code in debug mode...
$this->fail('An Exception is missing, expected due to equating of text fields');
}
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
$this->assertSame('textconditionsnotallowed', $e->errorcode);
}
@@ -1556,7 +1574,7 @@ EOD;
try {
$records = $DB->get_records('xxxx', array('id' => 0));
$this->fail('An Exception is missing, expected due to query against non-existing table');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
if (debugging()) {
// Information for developers only, normal users get general error message.
@@ -1567,7 +1585,7 @@ EOD;
try {
$records = $DB->get_records('xxxx', array('id' => '1'));
$this->fail('An Exception is missing, expected due to query against non-existing table');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
if (debugging()) {
// Information for developers only, normal users get general error message.
@@ -1579,7 +1597,7 @@ EOD;
try {
$records = $DB->get_records($tablename, array('xxxx' => 0));
$this->fail('An Exception is missing, expected due to query against non-existing column');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
if (debugging()) {
// Information for developers only, normal users get general error message.
@@ -1847,7 +1865,7 @@ EOD;
$DB->insert_record($tablename, array('course' => 2));
$record = $DB->get_record($tablename, array('id' => 2));
$this->assertInstanceOf('stdClass', $record);
$this->assertInstanceOf(\stdClass::class, $record);
$this->assertEquals(2, $record->course);
$this->assertEquals(2, $record->id);
@@ -1870,7 +1888,7 @@ EOD;
$DB->insert_record($tablename, array('course' => 2));
$record = $DB->get_record_select($tablename, "id = ?", array(2));
$this->assertInstanceOf('stdClass', $record);
$this->assertInstanceOf(\stdClass::class, $record);
$this->assertEquals(2, $record->course);
@@ -1894,7 +1912,7 @@ EOD;
// Standard use.
$record = $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(2));
$this->assertInstanceOf('stdClass', $record);
$this->assertInstanceOf(\stdClass::class, $record);
$this->assertEquals(2, $record->course);
$this->assertEquals(2, $record->id);
@@ -1975,7 +1993,7 @@ EOD;
// Only in debug mode - hopefully all devs test code in debug mode...
$this->fail('An Exception is missing, expected due to equating of text fields');
}
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
$this->assertSame('textconditionsnotallowed', $e->errorcode);
}
@@ -2098,7 +2116,7 @@ EOD;
$this->assertEquals($record, $before);
$record = $DB->get_record($tablename, array('course' => 1));
$this->assertInstanceOf('stdClass', $record);
$this->assertInstanceOf(\stdClass::class, $record);
$this->assertSame('xx', $record->onechar);
$result = $DB->insert_record_raw($tablename, array('course' => 2, 'onechar' => 'yy'), false);
@@ -2107,21 +2125,21 @@ EOD;
// Note: bulk not implemented yet.
$DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'zz'), true, true);
$record = $DB->get_record($tablename, array('course' => 3));
$this->assertInstanceOf('stdClass', $record);
$this->assertInstanceOf(\stdClass::class, $record);
$this->assertSame('zz', $record->onechar);
// Custom sequence (id) - returnid is ignored.
$result = $DB->insert_record_raw($tablename, array('id' => 10, 'course' => 3, 'onechar' => 'bb'), true, false, true);
$this->assertTrue($result);
$record = $DB->get_record($tablename, array('id' => 10));
$this->assertInstanceOf('stdClass', $record);
$this->assertInstanceOf(\stdClass::class, $record);
$this->assertSame('bb', $record->onechar);
// Custom sequence - missing id error.
try {
$DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'bb'), true, false, true);
$this->fail('Exception expected due to missing record');
} catch (coding_exception $ex) {
} catch (\coding_exception $ex) {
$this->assertTrue(true);
}
@@ -2184,7 +2202,7 @@ EOD;
$this->assertEquals(99, $record->course);
// Check nulls are set properly for all types.
$record = new stdClass();
$record = new \stdClass();
$record->oneint = null;
$record->onenum = null;
$record->onechar = null;
@@ -2200,7 +2218,7 @@ EOD;
$this->assertNull($record->onebinary);
// Check zeros are set properly for all types.
$record = new stdClass();
$record = new \stdClass();
$record->oneint = 0;
$record->onenum = 0;
$recid = $DB->insert_record($tablename, $record);
@@ -2209,7 +2227,7 @@ EOD;
$this->assertEquals(0, $record->onenum);
// Check booleans are set properly for all types.
$record = new stdClass();
$record = new \stdClass();
$record->oneint = true; // Trues.
$record->onenum = true;
$record->onechar = true;
@@ -2221,7 +2239,7 @@ EOD;
$this->assertEquals(1, $record->onechar);
$this->assertEquals(1, $record->onetext);
$record = new stdClass();
$record = new \stdClass();
$record->oneint = false; // Falses.
$record->onenum = false;
$record->onechar = false;
@@ -2234,34 +2252,34 @@ EOD;
$this->assertEquals(0, $record->onetext);
// Check string data causes exception in numeric types.
$record = new stdClass();
$record = new \stdClass();
$record->oneint = 'onestring';
$record->onenum = 0;
try {
$DB->insert_record($tablename, $record);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
$record = new stdClass();
$record = new \stdClass();
$record->oneint = 0;
$record->onenum = 'onestring';
try {
$DB->insert_record($tablename, $record);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
// Check empty string data is stored as 0 in numeric datatypes.
$record = new stdClass();
$record = new \stdClass();
$record->oneint = ''; // Empty string.
$record->onenum = 0;
$recid = $DB->insert_record($tablename, $record);
$record = $DB->get_record($tablename, array('id' => $recid));
$this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
$record = new stdClass();
$record = new \stdClass();
$record->oneint = 0;
$record->onenum = ''; // Empty string.
$recid = $DB->insert_record($tablename, $record);
@@ -2269,7 +2287,7 @@ EOD;
$this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
// Check empty strings are set properly in string types.
$record = new stdClass();
$record = new \stdClass();
$record->oneint = 0;
$record->onenum = 0;
$record->onechar = '';
@@ -2280,7 +2298,7 @@ EOD;
$this->assertTrue($record->onetext === '');
// Check operation ((210.10 + 39.92) - 150.02) against numeric types.
$record = new stdClass();
$record = new \stdClass();
$record->oneint = ((210.10 + 39.92) - 150.02);
$record->onenum = ((210.10 + 39.92) - 150.02);
$recid = $DB->insert_record($tablename, $record);
@@ -2295,7 +2313,7 @@ EOD;
'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
foreach ($teststrings as $teststring) {
$record = new stdClass();
$record = new \stdClass();
$record->onechar = $teststring;
$record->onetext = $teststring;
$recid = $DB->insert_record($tablename, $record);
@@ -2307,7 +2325,7 @@ EOD;
// Check LOBs in text/binary columns.
$clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
$blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
$record = new stdClass();
$record = new \stdClass();
$record->onetext = $clob;
$record->onebinary = $blob;
$recid = $DB->insert_record($tablename, $record);
@@ -2320,7 +2338,7 @@ EOD;
// And "small" LOBs too, just in case.
$newclob = substr($clob, 0, 500);
$newblob = substr($blob, 0, 250);
$record = new stdClass();
$record = new \stdClass();
$record->onetext = $newclob;
$record->onebinary = $newblob;
$recid = $DB->insert_record($tablename, $record);
@@ -2334,7 +2352,7 @@ EOD;
// And "diagnostic" LOBs too, just in case.
$newclob = '\'"\\;/ěščřžýáíé';
$newblob = '\'"\\;/ěščřžýáíé';
$record = new stdClass();
$record = new \stdClass();
$record->onetext = $newclob;
$record->onebinary = $newblob;
$recid = $DB->insert_record($tablename, $record);
@@ -2346,7 +2364,7 @@ EOD;
$this->assertEquals(false, $rs->key()); // Ensure recordset key() method to be working ok after closing.
// Test data is not modified.
$record = new stdClass();
$record = new \stdClass();
$record->id = -1; // Has to be ignored.
$record->course = 3;
$record->lalala = 'lalal'; // Unused.
@@ -2409,7 +2427,7 @@ EOD;
try {
$DB->insert_record($tablename, $record, false);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
@@ -2417,7 +2435,7 @@ EOD;
try {
$DB->insert_record($tablename, $record, true);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
@@ -2425,7 +2443,7 @@ EOD;
try {
$DB->insert_record('nonexistenttable', $record, true);
$this->fail("Expecting an exception, none occurred");
} catch (exception $e) {
} catch (\Exception $e) {
$this->assertTrue($e instanceof dml_exception);
}
}
@@ -2448,7 +2466,7 @@ EOD;
$this->assertCount(0, $DB->get_records($tablename));
$record = new stdClass();
$record = new \stdClass();
$record->id = '1';
$record->course = '1';
$record->oneint = null;
@@ -2506,7 +2524,7 @@ EOD;
try {
$DB->insert_records($tablename, $records);
$this->fail('coding_exception expected when insert_records receives different object data structures');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -2517,7 +2535,7 @@ EOD;
try {
$DB->insert_records($tablename, $records);
$this->fail('coding_exception expected when insert_records receives different object data structures');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -2525,7 +2543,7 @@ EOD;
try {
$DB->insert_records($tablename, $records);
$this->fail('coding_exception expected when insert_records receives non-traversable data');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -2533,7 +2551,7 @@ EOD;
try {
$DB->insert_records($tablename, $records);
$this->fail('coding_exception expected when insert_records receives non-objet record');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
}
@@ -2564,7 +2582,7 @@ EOD;
try {
$DB->insert_record($tablename, (object) ['notnull1' => 1, 'nullable1' => 1, 'nullable2' => 1]);
$this->fail('dml_write_exception expected when a record violates a unique index');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_write_exception', $e);
}
@@ -2638,7 +2656,7 @@ EOD;
$this->assertEquals(2, $records[13]->course);
// Check nulls are set properly for all types.
$record = new stdClass();
$record = new \stdClass();
$record->id = 20;
$record->oneint = null;
$record->onenum = null;
@@ -2655,7 +2673,7 @@ EOD;
$this->assertNull($record->onebinary);
// Check zeros are set properly for all types.
$record = new stdClass();
$record = new \stdClass();
$record->id = 23;
$record->oneint = 0;
$record->onenum = 0;
@@ -2665,29 +2683,29 @@ EOD;
$this->assertEquals(0, $record->onenum);
// Check string data causes exception in numeric types.
$record = new stdClass();
$record = new \stdClass();
$record->id = 32;
$record->oneint = 'onestring';
$record->onenum = 0;
try {
$DB->import_record($tablename, $record);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
$record = new stdClass();
$record = new \stdClass();
$record->id = 35;
$record->oneint = 0;
$record->onenum = 'onestring';
try {
$DB->import_record($tablename, $record);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
// Check empty strings are set properly in string types.
$record = new stdClass();
$record = new \stdClass();
$record->id = 44;
$record->oneint = 0;
$record->onenum = 0;
@@ -2699,7 +2717,7 @@ EOD;
$this->assertTrue($record->onetext === '');
// Check operation ((210.10 + 39.92) - 150.02) against numeric types.
$record = new stdClass();
$record = new \stdClass();
$record->id = 47;
$record->oneint = ((210.10 + 39.92) - 150.02);
$record->onenum = ((210.10 + 39.92) - 150.02);
@@ -2716,7 +2734,7 @@ EOD;
'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
foreach ($teststrings as $teststring) {
$record = new stdClass();
$record = new \stdClass();
$record->id = $i;
$record->onechar = $teststring;
$record->onetext = $teststring;
@@ -2729,7 +2747,7 @@ EOD;
// Check LOBs in text/binary columns.
$clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
$record = new stdClass();
$record = new \stdClass();
$record->id = 70;
$record->onetext = $clob;
$record->onebinary = '';
@@ -2740,7 +2758,7 @@ EOD;
$this->assertEquals($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
$blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
$record = new stdClass();
$record = new \stdClass();
$record->id = 71;
$record->onetext = '';
$record->onebinary = $blob;
@@ -2753,7 +2771,7 @@ EOD;
// And "small" LOBs too, just in case.
$newclob = substr($clob, 0, 500);
$newblob = substr($blob, 0, 250);
$record = new stdClass();
$record = new \stdClass();
$record->id = 73;
$record->onetext = $newclob;
$record->onebinary = $newblob;
@@ -2793,7 +2811,7 @@ EOD;
try {
$DB->update_record_raw($tablename, $record);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('moodle_exception', $e);
}
@@ -2802,7 +2820,7 @@ EOD;
try {
$DB->update_record_raw($tablename, $record);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
}
@@ -2892,7 +2910,7 @@ EOD;
try {
$DB->update_record($tablename, $record);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
$record->oneint = 0;
@@ -2900,7 +2918,7 @@ EOD;
try {
$DB->update_record($tablename, $record);
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
@@ -3062,7 +3080,7 @@ EOD;
// Only in debug mode - hopefully all devs test code in debug mode...
$this->fail('An Exception is missing, expected due to equating of text fields');
}
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
$this->assertSame('textconditionsnotallowed', $e->errorcode);
}
@@ -3168,13 +3186,13 @@ EOD;
try {
$DB->set_field_select($tablename, 'oneint', 'onestring', 'id = ?', array(1));
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
try {
$DB->set_field_select($tablename, 'onenum', 'onestring', 'id = ?', array(1));
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
}
@@ -3277,7 +3295,7 @@ EOD;
// Only in debug mode - hopefully all devs test code in debug mode...
$this->fail('An Exception is missing, expected due to equating of text fields');
}
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
$this->assertSame('textconditionsnotallowed', $e->errorcode);
}
@@ -3330,14 +3348,14 @@ EOD;
try {
$DB->count_records_sql("SELECT onechar FROM {{$tablename}} WHERE course = ?", array(3));
$this->fail('Exception expected when non-number field used in count_records_sql');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
try {
$DB->count_records_sql("SELECT course FROM {{$tablename}} WHERE 1 = 2");
$this->fail('Exception expected when non-number field used in count_records_sql');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
}
@@ -3370,7 +3388,7 @@ EOD;
// Only in debug mode - hopefully all devs test code in debug mode...
$this->fail('An Exception is missing, expected due to equating of text fields');
}
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
$this->assertSame('textconditionsnotallowed', $e->errorcode);
}
@@ -3521,7 +3539,7 @@ EOD;
// Only in debug mode - hopefully all devs test code in debug mode...
$this->fail('An Exception is missing, expected due to equating of text fields');
}
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
$this->assertSame('textconditionsnotallowed', $e->errorcode);
}
@@ -3534,7 +3552,7 @@ EOD;
// Only in debug mode - hopefully all devs test code in debug mode...
$this->fail('An Exception is missing, expected due to equating of text fields');
}
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_exception', $e);
$this->assertSame('textconditionsnotallowed', $e->errorcode);
}
@@ -3617,11 +3635,11 @@ EOD;
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$dbman->create_table($table);
$o = new stdClass(); // Objects without __toString - never worked.
$o = new \stdClass(); // Objects without __toString - never worked.
try {
$DB->fix_sql_params("SELECT {{$tablename}} WHERE course = ? ", array($o));
$this->fail('coding_exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
@@ -3630,89 +3648,89 @@ EOD;
try {
$DB->fix_sql_params("SELECT {{$tablename}} WHERE course = ? ", array($o));
$this->fail('coding_exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
try {
$DB->execute("SELECT {{$tablename}} WHERE course = ? ", array($o));
$this->fail('coding_exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
try {
$DB->get_recordset_sql("SELECT {{$tablename}} WHERE course = ? ", array($o));
$this->fail('coding_exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
try {
$DB->get_records_sql("SELECT {{$tablename}} WHERE course = ? ", array($o));
$this->fail('coding_exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
try {
$record = new stdClass();
$record = new \stdClass();
$record->course = $o;
$DB->insert_record_raw($tablename, $record);
$this->fail('coding_exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
try {
$record = new stdClass();
$record = new \stdClass();
$record->course = $o;
$DB->insert_record($tablename, $record);
$this->fail('coding_exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
try {
$record = new stdClass();
$record = new \stdClass();
$record->course = $o;
$DB->import_record($tablename, $record);
$this->fail('coding_exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
try {
$record = new stdClass();
$record = new \stdClass();
$record->id = 1;
$record->course = $o;
$DB->update_record_raw($tablename, $record);
$this->fail('coding_exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
try {
$record = new stdClass();
$record = new \stdClass();
$record->id = 1;
$record->course = $o;
$DB->update_record($tablename, $record);
$this->fail('coding_exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
try {
$DB->set_field_select($tablename, 'course', 1, "course = ? ", array($o));
$this->fail('coding_exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
try {
$DB->delete_records_select($tablename, "course = ? ", array($o));
$this->fail('coding_exception expected');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
}
}
@@ -4024,7 +4042,7 @@ EOD;
try {
$DB->insert_record($tablename, array('name'=>'AAA'));
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
// TODO: ignore case insensitive uniqueness problems for now.
// $this->fail("Unique index is case sensitive - this may cause problems in some tables");
}
@@ -4033,7 +4051,7 @@ EOD;
$DB->insert_record($tablename, array('name'=>'aäa'));
$DB->insert_record($tablename, array('name'=>'aáa'));
$this->assertTrue(true);
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$family = $DB->get_dbfamily();
if ($family === 'mysql' or $family === 'mssql') {
$this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages. This is usually caused by accent insensitive default collation.");
@@ -4567,9 +4585,9 @@ EOD;
// Silence php warning.
@$DB->sql_substr("name");
$this->fail("Expecting an exception, none occurred");
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('coding_exception', $e);
} catch (Error $error) {
} catch (\Error $error) {
// PHP 7.1 throws Error even earlier.
$this->assertMatchesRegularExpression('/Too few arguments to function/', $error->getMessage());
}
@@ -5045,7 +5063,7 @@ EOD;
$long = '1234567890123456789';
$DB->replace_all_text($tablename, $columns['name'], 'kk', $long);
$result = $DB->get_records($tablename, array(), 'id ASC');
$expected[$id5]->name = core_text::substr($long.'llll', 0, 20);
$expected[$id5]->name = \core_text::substr($long.'llll', 0, 20);
$this->assertEquals($expected, $result);
$DB->replace_all_text($tablename, $columns['intro'], 'kk', $long);
@@ -5095,7 +5113,7 @@ EOD;
$this->assertEquals(1, $DB->count_records($tablename));
try {
$DB->insert_record($tablename, (object)array('course'=>1));
} catch (Exception $e) {
} catch (\Exception $e) {
// This must be ignored and it must not roll back the whole transaction.
}
$DB->insert_record($tablename, (object)array('course'=>2));
@@ -5112,7 +5130,7 @@ EOD;
$this->assertEquals(1, $DB->count_records($tablename));
try {
$DB->get_records_sql('s e l e c t');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
// This must be ignored and it must not roll back the whole transaction.
}
$DB->insert_record($tablename, (object)array('course'=>2));
@@ -5129,7 +5147,7 @@ EOD;
$this->assertEquals(1, $DB->count_records($tablename));
try {
$DB->execute('xxxx');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
// This must be ignored and it must not roll back the whole transaction.
}
$DB->insert_record($tablename, (object)array('course'=>2));
@@ -5146,7 +5164,7 @@ EOD;
$this->assertEquals(1, $DB->count_records($tablename));
try {
$DB->change_database_structure('xxxx');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
// This must be ignored and it must not roll back the whole transaction.
}
$DB->insert_record($tablename, (object)array('course'=>2));
@@ -5178,9 +5196,9 @@ EOD;
$DB->insert_record($tablename, $data);
$this->assertEquals(1, $DB->count_records($tablename));
try {
$transaction->rollback(new Exception('test'));
$transaction->rollback(new \Exception('test'));
$this->fail('transaction rollback must rethrow exception');
} catch (Exception $e) {
} catch (\Exception $e) {
// Ignored.
}
$this->assertEquals(0, $DB->count_records($tablename));
@@ -5224,9 +5242,9 @@ EOD;
$DB->insert_record($tablename, $data);
$transaction2->allow_commit();
try {
$transaction1->rollback(new Exception('test'));
$transaction1->rollback(new \Exception('test'));
$this->fail('transaction rollback must rethrow exception');
} catch (Exception $e) {
} catch (\Exception $e) {
$this->assertEquals(get_class($e), 'Exception');
}
$this->assertEquals(0, $DB->count_records($tablename));
@@ -5241,15 +5259,15 @@ EOD;
$data = (object)array('course'=>4);
$DB->insert_record($tablename, $data);
try {
$transaction2->rollback(new Exception('test'));
$transaction2->rollback(new \Exception('test'));
$this->fail('transaction rollback must rethrow exception');
} catch (Exception $e) {
} catch (\Exception $e) {
$this->assertEquals(get_class($e), 'Exception');
}
$this->assertEquals(2, $DB->count_records($tablename)); // Not rolled back yet.
try {
$transaction1->allow_commit();
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_transaction_exception', $e);
}
$this->assertEquals(2, $DB->count_records($tablename)); // Not rolled back yet.
@@ -5347,7 +5365,7 @@ EOD;
$DB->insert_record($tablename, $data);
try {
$DB->transactions_forbidden();
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_transaction_exception', $e);
}
// The previous test does not force rollback.
@@ -5378,13 +5396,13 @@ EOD;
try {
$transaction1->allow_commit();
$this->fail('wrong order of commits must throw exception');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_transaction_exception', $e);
}
try {
$transaction2->allow_commit();
$this->fail('first wrong commit forces rollback');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_transaction_exception', $e);
}
// This is done in default exception handler usually.
@@ -5403,18 +5421,18 @@ EOD;
$DB->insert_record($tablename, $data);
try {
// This first rollback should prevent all other rollbacks.
$transaction1->rollback(new Exception('test'));
} catch (Exception $e) {
$transaction1->rollback(new \Exception('test'));
} catch (\Exception $e) {
$this->assertEquals(get_class($e), 'Exception');
}
try {
$transaction2->rollback(new Exception('test'));
} catch (Exception $e) {
$transaction2->rollback(new \Exception('test'));
} catch (\Exception $e) {
$this->assertEquals(get_class($e), 'Exception');
}
try {
$transaction1->rollback(new Exception('test'));
} catch (moodle_exception $e) {
$transaction1->rollback(new \Exception('test'));
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_transaction_exception', $e);
}
// This is done in default exception handler usually.
@@ -5430,13 +5448,13 @@ EOD;
try {
$transaction2->allow_commit();
$this->fail('foreign transaction must fail');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_transaction_exception', $e);
}
try {
$transaction1->allow_commit();
$this->fail('first wrong commit forces rollback');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_transaction_exception', $e);
}
$DB->force_transaction_rollback();
@@ -5541,7 +5559,7 @@ EOD;
$DB2->get_session_lock($rowid, $timeout);
$DB2->release_session_lock($rowid); // Should not be executed, but here for safety.
$this->fail('An Exception is missing, expected due to session lock acquired.');
} catch (moodle_exception $e) {
} catch (\moodle_exception $e) {
$this->assertInstanceOf('dml_sessionwait_exception', $e);
$DB->release_session_lock($rowid); // Release lock on connection1.
}
@@ -5876,7 +5894,7 @@ EOD;
// Inserts counts as writes.
$rec1 = new stdClass();
$rec1 = new \stdClass();
$rec1->fieldvalue = 11;
$rec1->id = $DB->insert_record($tablename, $rec1);
$this->assertEquals($initwrites + 1, $DB->perf_get_writes());
@@ -5887,7 +5905,7 @@ EOD;
$this->assertGreaterThanOrEqual($previousqueriestime, $lastqueriestime);
$previousqueriestime = $lastqueriestime;
$rec2 = new stdClass();
$rec2 = new \stdClass();
$rec2->fieldvalue = 22;
$rec2->id = $DB->insert_record($tablename, $rec2);
$this->assertEquals($initwrites + 2, $DB->perf_get_writes());
+6 -2
View File
@@ -89,13 +89,17 @@ class read_slave_moodle_database extends test_moodle_database {
/**
* Query wrapper that calls query_start() and query_end()
* @param string $sql
* @param array $params
* @param array|null $params
* @param int $querytype
* @param ?callable $callback
* @return string $handle handle property
*/
private function with_query_start_end($sql, array $params = null, $querytype) {
public function with_query_start_end($sql, ?array $params, $querytype, $callback = null) {
$this->query_start($sql, $params, $querytype);
$ret = $this->handle;
if ($callback) {
call_user_func($callback, $ret);
}
$this->query_end(null);
return $ret;
}
+4 -2
View File
@@ -50,6 +50,7 @@ trait test_moodle_read_slave_trait {
$ro = fopen("php://memory", 'r+');
fputs($ro, 'ro');
$this->prefix = 'test_'; // Default, not to leave empty.
$this->wantreadslave = true;
$this->dbhwrite = $rw;
$this->dbhreadonly = $ro;
@@ -96,11 +97,11 @@ trait test_moodle_read_slave_trait {
/**
* Upgrade to public
* @param string $sql
* @param array $params
* @param array|null $params
* @param int $type
* @param array $extrainfo
*/
public function query_start($sql, array $params = null, $type, $extrainfo = null) {
public function query_start($sql, ?array $params, $type, $extrainfo = null) {
return parent::query_start($sql, $params, $type);
}
@@ -109,6 +110,7 @@ trait test_moodle_read_slave_trait {
* @param mixed $result
*/
public function query_end($result) {
parent::query_end($result);
$this->set_db_handle($this->dbhwrite);
}
+5 -14
View File
@@ -14,26 +14,17 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Test \core\dml\recordset_walk.
*
* @package core
* @category dml
* @copyright 2015 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
namespace core;
/**
* Test case for recordset_walk.
*
* @package core
* @category dml
* @category test
* @copyright 2015 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_recordset_walk_testcase extends advanced_testcase {
class recordset_walk_test extends \advanced_testcase {
public function setUp(): void {
parent::setUp();
@@ -113,7 +104,7 @@ class core_recordset_walk_testcase extends advanced_testcase {
/**
* Simple callback requiring 1 row fields.
*
* @param stdClass $data
* @param \stdClass $data
* @return \Traversable
*/
public function simple_callback($data, $nothing = 'notpassed') {
@@ -126,7 +117,7 @@ class core_recordset_walk_testcase extends advanced_testcase {
/**
* Callback requiring 1 row fields + other params.
*
* @param stdClass $data
* @param \stdClass $data
* @param mixed $extra
* @return \Traversable
*/
@@ -15,14 +15,18 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Test sqlsrv dml support.
* Test case for sqlsrv dml support.
*
* @package core
* @category dml
* @category test
* @copyright 2017 John Okely
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core;
use sqlsrv_native_moodle_database;
defined('MOODLE_INTERNAL') || die();
global $CFG;
@@ -32,11 +36,11 @@ require_once($CFG->dirroot.'/lib/dml/sqlsrv_native_moodle_database.php');
* Test case for sqlsrv dml support.
*
* @package core
* @category dml
* @category test
* @copyright 2017 John Okely
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sqlsrv_native_moodle_database_testcase extends advanced_testcase {
class sqlsrv_native_moodle_database_test extends \advanced_testcase {
public function setUp(): void {
parent::setUp();
@@ -118,7 +122,7 @@ class sqlsrv_native_moodle_database_testcase extends advanced_testcase {
public function test_add_no_lock_to_temp_tables($input, $expected) {
$sqlsrv = new sqlsrv_native_moodle_database();
$reflector = new ReflectionObject($sqlsrv);
$reflector = new \ReflectionObject($sqlsrv);
$method = $reflector->getMethod('add_no_lock_to_temp_tables');
$method->setAccessible(true);
@@ -247,7 +251,7 @@ EOT
$this->assertSame($expectedmainquery, $mainquery);
// The has_query_order_by static method is protected. Use Reflection to call the method.
$method = new ReflectionMethod('sqlsrv_native_moodle_database', 'has_query_order_by');
$method = new \ReflectionMethod('sqlsrv_native_moodle_database', 'has_query_order_by');
$method->setAccessible(true);
$result = $method->invoke(null, $sql);
$this->assertSame($expectedresult, $result);
@@ -10,3 +10,22 @@ Feature: Atto edit HTML
And I click on "Show more buttons" "button"
And I click on "HTML" "button"
Then the field "Description" matches value "<p style=\"color: blue;\">Smurf</p>"
@javascript
Scenario: Validation of empty string when the form is submitted with HTML source mode.
Given the following "courses" exist:
| fullname | shortname | format |
| Course 1 | C1 | weeks |
And the following "question categories" exist:
| contextlevel | reference | name |
| Course | C1 | Test questions |
And the following "questions" exist:
| questioncategory | qtype | name | questiontext |
| Test questions | essay | Essay 01 new | Write about whatever you want |
And I am on the "Essay 01 new" "core_question > edit" page logged in as admin
And I click on "Show more buttons" "button" in the "Question text" "form_row"
And I click on "HTML" "button" in the "Question text" "form_row"
And I press the shift + end key
And I press the delete key
When I press "id_submitbutton"
Then I should see "You must supply a value here." in the "Question text" "form_row"
@@ -132,6 +132,10 @@ Y.namespace('M.atto_html').Button = Y.Base.create('button', Y.M.editor_atto.Edit
_hideCodeMirror: function() {
var host = this.get('host');
// Disable placeholder for empty content.
// This will prevent the editor to copy and submit the empty placeholder.
host.disablePlaceholderForEmptyContent();
// Enable all plugins.
host.enablePlugins();
@@ -164,6 +168,9 @@ Y.namespace('M.atto_html').Button = Y.Base.create('button', Y.M.editor_atto.Edit
// And re-mark everything as updated.
this.markUpdated();
// Enable placeholder for empty content.
host.enablePlaceholderForEmptyContent();
}
}, {
ATTRS: {
@@ -1 +1 @@
YUI.add("moodle-atto_html-button",function(o,t){o.namespace("M.atto_html").Button=o.Base.create("button",o.M.editor_atto.EditorPlugin,[],{_codeMirror:null,initializer:function(){this.addButton({icon:"e/source_code",callback:this._toggleHTML});var t=this.get("host").textarea.ancestor("form");t&&t.on("submit",function(){this.get("isHTML")&&this._hideCodeMirror()},this)},_toggleHTML:function(){this.set("isHTML",!this.get("isHTML")),this.get("isHTML")?this._showCodeMirror():this._hideCodeMirror()},_showCodeMirror:function(){var t,e,i=this.get("host");i.disablePlugins(),i.enablePlugins(this.name),i.updateOriginal(),t={width:this.editor.getComputedStyle("width"),height:this.editor.getComputedStyle("height")},e=o.M.atto_html.beautify.html_beautify(i.textarea.get("value"),{indent_size:4,indent_inner_html:!0}),i.textarea.set("value",e),this._codeMirror=o.M.atto_html.CodeMirror.fromTextArea(i.textarea.getDOMNode(),{lineNumbers:!0,mode:"htmlmixed",tabSize:2,lineWrapping:!0}),this._codeMirror.on("change",function(t){t.save()}),this.editor.hide(),this._codeMirror.setSize(t.width,t.height),this._codeMirror.focus()},_hideCodeMirror:function(){var t=this.get("host");t.enablePlugins(),t.updateFromTextArea(),this._codeMirror&&(o.one(this._codeMirror.getWrapperElement()).hide(),this._codeMirror.toTextArea(),o.Object.each(this._codeMirror._handlers,function(t,e){o.Array.each(t,function(t){this.off(e,t)},this)},this._codeMirror),delete this._codeMirror,this._codeMirror=null),t.textarea.hide(),this.editor.show(),t.focus(),this.markUpdated()}},{ATTRS:{isHTML:{value:!1}}})},"@VERSION@",{requires:["promise","moodle-editor_atto-plugin","moodle-atto_html-beautify","moodle-atto_html-codemirror","event-valuechange"]});
YUI.add("moodle-atto_html-button",function(o,t){o.namespace("M.atto_html").Button=o.Base.create("button",o.M.editor_atto.EditorPlugin,[],{_codeMirror:null,initializer:function(){this.addButton({icon:"e/source_code",callback:this._toggleHTML});var t=this.get("host").textarea.ancestor("form");t&&t.on("submit",function(){this.get("isHTML")&&this._hideCodeMirror()},this)},_toggleHTML:function(){this.set("isHTML",!this.get("isHTML")),this.get("isHTML")?this._showCodeMirror():this._hideCodeMirror()},_showCodeMirror:function(){var t,e,i=this.get("host");i.disablePlugins(),i.enablePlugins(this.name),i.updateOriginal(),t={width:this.editor.getComputedStyle("width"),height:this.editor.getComputedStyle("height")},e=o.M.atto_html.beautify.html_beautify(i.textarea.get("value"),{indent_size:4,indent_inner_html:!0}),i.textarea.set("value",e),this._codeMirror=o.M.atto_html.CodeMirror.fromTextArea(i.textarea.getDOMNode(),{lineNumbers:!0,mode:"htmlmixed",tabSize:2,lineWrapping:!0}),this._codeMirror.on("change",function(t){t.save()}),this.editor.hide(),this._codeMirror.setSize(t.width,t.height),this._codeMirror.focus()},_hideCodeMirror:function(){var t=this.get("host");t.disablePlaceholderForEmptyContent(),t.enablePlugins(),t.updateFromTextArea(),this._codeMirror&&(o.one(this._codeMirror.getWrapperElement()).hide(),this._codeMirror.toTextArea(),o.Object.each(this._codeMirror._handlers,function(t,e){o.Array.each(t,function(t){this.off(e,t)},this)},this._codeMirror),delete this._codeMirror,this._codeMirror=null),t.textarea.hide(),this.editor.show(),t.focus(),this.markUpdated(),t.enablePlaceholderForEmptyContent()}},{ATTRS:{isHTML:{value:!1}}})},"@VERSION@",{requires:["promise","moodle-editor_atto-plugin","moodle-atto_html-beautify","moodle-atto_html-codemirror","event-valuechange"]});
@@ -132,6 +132,10 @@ Y.namespace('M.atto_html').Button = Y.Base.create('button', Y.M.editor_atto.Edit
_hideCodeMirror: function() {
var host = this.get('host');
// Disable placeholder for empty content.
// This will prevent the editor to copy and submit the empty placeholder.
host.disablePlaceholderForEmptyContent();
// Enable all plugins.
host.enablePlugins();
@@ -164,6 +168,9 @@ Y.namespace('M.atto_html').Button = Y.Base.create('button', Y.M.editor_atto.Edit
// And re-mark everything as updated.
this.markUpdated();
// Enable placeholder for empty content.
host.enablePlaceholderForEmptyContent();
}
}, {
ATTRS: {
@@ -130,6 +130,10 @@ Y.namespace('M.atto_html').Button = Y.Base.create('button', Y.M.editor_atto.Edit
_hideCodeMirror: function() {
var host = this.get('host');
// Disable placeholder for empty content.
// This will prevent the editor to copy and submit the empty placeholder.
host.disablePlaceholderForEmptyContent();
// Enable all plugins.
host.enablePlugins();
@@ -162,6 +166,9 @@ Y.namespace('M.atto_html').Button = Y.Base.create('button', Y.M.editor_atto.Edit
// And re-mark everything as updated.
this.markUpdated();
// Enable placeholder for empty content.
host.enablePlaceholderForEmptyContent();
}
}, {
ATTRS: {
+10
View File
@@ -1,5 +1,15 @@
This files describes API changes in the editor_atto code.
=== 3.11 ===
* Two new helper functions have been added to lib/editor/atto
to handle enable/disable the empty placeholder for empty content in the HTML source view:
- enablePlaceholderForEmptyContent() - Handle to enable the empty placeholder for empty content
- disablePlaceholderForEmptyContent() - Handle to disable the empty placeholder for empty content.
* One property have been added to lib/editor/atto to handle the state of the empty placeholder content.
- enableAppropriateEmptyContent - Enable/disable the empty placeholder content.
=== 3.4 ===
* The following functions, previously used (exclusively) by upgrade steps are not available
@@ -158,6 +158,14 @@ Y.extend(Editor, Y.Base, {
*/
coreDirection: null,
/**
* Enable/disable the empty placeholder content.
*
* @property enableAppropriateEmptyContent
* @type Boolean
*/
enableAppropriateEmptyContent: null,
/**
* Event Handles to clear on editor destruction.
*
@@ -207,6 +215,9 @@ Y.extend(Editor, Y.Base, {
// Set diretcion according to current page language.
this.coreDirection = Y.one('body').hasClass('dir-rtl') ? 'rtl' : 'ltr';
// Enable the placeholder for empty content.
this.enablePlaceholderForEmptyContent();
// Add everything to the wrapper.
this.setupToolbar();
@@ -420,6 +431,20 @@ Y.extend(Editor, Y.Base, {
}
},
/**
* Enable the empty placeholder for empty content.
*/
enablePlaceholderForEmptyContent: function() {
this.enableAppropriateEmptyContent = true;
},
/**
* Disable the empty placeholder for empty content.
*/
disablePlaceholderForEmptyContent: function() {
this.enableAppropriateEmptyContent = false;
},
/**
* Register an event handle for disposal in the destructor.
*
@@ -692,6 +717,10 @@ EditorTextArea.prototype = {
* @private
*/
_getEmptyContent: function() {
if (!this.enableAppropriateEmptyContent) {
// Return the empty string if we do not enable the empty placeholder. Ex: HTML mode.
return '';
}
var alignment;
if (this.coreDirection === 'rtl') {
alignment = 'style="text-align: right;"';
@@ -1793,7 +1822,8 @@ EditorClean.prototype = {
// Use a regular expression to find the next open or close li, ul, or ol tag.
// Keep going until there are no more matching tags left.
while ((match = toProcess.match(/<(\/?)(li|ul|ol)[^>]*>/i))) {
// This expression looks for whole words by employing the word boundary (\b) metacharacter.
while ((match = toProcess.match(/<(\/?)(li|ul|ol)\b[^>]*>/i))) {
currentTag = {
tag: match[2],
tagLowerCase: match[2].toLowerCase(),
File diff suppressed because one or more lines are too long
@@ -158,6 +158,14 @@ Y.extend(Editor, Y.Base, {
*/
coreDirection: null,
/**
* Enable/disable the empty placeholder content.
*
* @property enableAppropriateEmptyContent
* @type Boolean
*/
enableAppropriateEmptyContent: null,
/**
* Event Handles to clear on editor destruction.
*
@@ -205,6 +213,9 @@ Y.extend(Editor, Y.Base, {
// Set diretcion according to current page language.
this.coreDirection = Y.one('body').hasClass('dir-rtl') ? 'rtl' : 'ltr';
// Enable the placeholder for empty content.
this.enablePlaceholderForEmptyContent();
// Add everything to the wrapper.
this.setupToolbar();
@@ -417,6 +428,20 @@ Y.extend(Editor, Y.Base, {
}
},
/**
* Enable the empty placeholder for empty content.
*/
enablePlaceholderForEmptyContent: function() {
this.enableAppropriateEmptyContent = true;
},
/**
* Disable the empty placeholder for empty content.
*/
disablePlaceholderForEmptyContent: function() {
this.enableAppropriateEmptyContent = false;
},
/**
* Register an event handle for disposal in the destructor.
*
@@ -687,6 +712,10 @@ EditorTextArea.prototype = {
* @private
*/
_getEmptyContent: function() {
if (!this.enableAppropriateEmptyContent) {
// Return the empty string if we do not enable the empty placeholder. Ex: HTML mode.
return '';
}
var alignment;
if (this.coreDirection === 'rtl') {
alignment = 'style="text-align: right;"';
@@ -1780,7 +1809,8 @@ EditorClean.prototype = {
// Use a regular expression to find the next open or close li, ul, or ol tag.
// Keep going until there are no more matching tags left.
while ((match = toProcess.match(/<(\/?)(li|ul|ol)[^>]*>/i))) {
// This expression looks for whole words by employing the word boundary (\b) metacharacter.
while ((match = toProcess.match(/<(\/?)(li|ul|ol)\b[^>]*>/i))) {
currentTag = {
tag: match[2],
tagLowerCase: match[2].toLowerCase(),
+2 -1
View File
@@ -498,7 +498,8 @@ EditorClean.prototype = {
// Use a regular expression to find the next open or close li, ul, or ol tag.
// Keep going until there are no more matching tags left.
while ((match = toProcess.match(/<(\/?)(li|ul|ol)[^>]*>/i))) {
// This expression looks for whole words by employing the word boundary (\b) metacharacter.
while ((match = toProcess.match(/<(\/?)(li|ul|ol)\b[^>]*>/i))) {
currentTag = {
tag: match[2],
tagLowerCase: match[2].toLowerCase(),
+25
View File
@@ -156,6 +156,14 @@ Y.extend(Editor, Y.Base, {
*/
coreDirection: null,
/**
* Enable/disable the empty placeholder content.
*
* @property enableAppropriateEmptyContent
* @type Boolean
*/
enableAppropriateEmptyContent: null,
/**
* Event Handles to clear on editor destruction.
*
@@ -205,6 +213,9 @@ Y.extend(Editor, Y.Base, {
// Set diretcion according to current page language.
this.coreDirection = Y.one('body').hasClass('dir-rtl') ? 'rtl' : 'ltr';
// Enable the placeholder for empty content.
this.enablePlaceholderForEmptyContent();
// Add everything to the wrapper.
this.setupToolbar();
@@ -418,6 +429,20 @@ Y.extend(Editor, Y.Base, {
}
},
/**
* Enable the empty placeholder for empty content.
*/
enablePlaceholderForEmptyContent: function() {
this.enableAppropriateEmptyContent = true;
},
/**
* Disable the empty placeholder for empty content.
*/
disablePlaceholderForEmptyContent: function() {
this.enableAppropriateEmptyContent = false;
},
/**
* Register an event handle for disposal in the destructor.
*

Some files were not shown because too many files have changed in this diff Show More