Compare commits

..

271 Commits

Author SHA1 Message Date
Mihail Geshoski fb02f4fa9f Moodle release 4.5.6 2025-08-08 10:48:30 +08:00
Mihail Geshoski 85a6851a8e NOBUG: Add upgrade notes 2025-08-08 10:48:29 +08:00
Sara Arjona 2db7c80f80 weekly release 4.5.5+ 2025-08-07 16:45:16 +02:00
Sara Arjona ec3ab5528a NOBUG: Add upgrade notes 2025-08-07 16:45:16 +02:00
Sara Arjona 256080fb2c NOBUG: Fixed file access permissions 2025-08-07 16:45:10 +02:00
Sara Arjona 8c2110c80c Merge branch 'install_405_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_405_STABLE 2025-08-07 16:45:07 +02:00
Huong Nguyen 9a45e5c064 Merge branch 'MDL-86004-405' of https://github.com/davewoloszyn/moodle into MOODLE_405_STABLE 2025-08-07 14:10:00 +07:00
David Woloszyn db896863cb MDL-86004 core: Modify upgrade step for Microsoft endpoints
The upgrade step for Microsoft OAuth2 issuers has been modified
to address an oversight in MDL-84432. The upgrade now only updates
service base urls for multi-tenant issuers.

Co-authored-by: Raju Tummoji <raju.tummoji@moodle.com>
2025-08-07 17:01:14 +10:00
Huong Nguyen 5f0d1b806c Merge branch 'MDL-85718-405' of https://github.com/yusufwib01/moodle into MOODLE_405_STABLE 2025-08-07 12:23:54 +07:00
Huong Nguyen 789f112c86 Merge branch 'MDL-86205-405' of https://github.com/junpataleta/moodle into MOODLE_405_STABLE 2025-08-07 12:15:07 +07:00
Huong Nguyen f35a5c1047 Merge branch 'MDL-85794-405' of https://github.com/junpataleta/moodle into MOODLE_405_STABLE 2025-08-07 11:37:33 +07:00
Jun Pataleta 43aa89c255 MDL-86205 message: Close drawer when activating elements outside of it 2025-08-07 12:31:05 +08:00
Huong Nguyen 99dc7feb31 Merge branch 'MDL-85795-405-2' of https://github.com/junpataleta/moodle into MOODLE_405_STABLE 2025-08-07 11:26:26 +07:00
Huong Nguyen eb0dd85d88 MDL-84217 editor_tiny: Remove incorrect map file 2025-08-07 11:13:28 +07:00
Huong Nguyen 13d7e5dc6b Merge branch 'MDL-84217-405' of https://github.com/stevandoMoodle/moodle into MOODLE_405_STABLE 2025-08-07 10:56:34 +07:00
Ilya Tregubov 1646ce747e MDL-63395 user: Fix profile visibility check.
It needs to be done in user_get_user_details as well.
2025-08-07 10:30:04 +07:00
Jun Pataleta 0082009561 Merge branch 'MDL-85674-405' of https://github.com/lameze/moodle into MOODLE_405_STABLE 2025-08-07 10:44:07 +08:00
Jun Pataleta ec3f9ed5e4 Merge branch 'MDL-78233-405' of https://github.com/Tasiobg/moodle into MOODLE_405_STABLE 2025-08-07 10:35:06 +08:00
Jun Pataleta ca7501babb Merge branch 'MDL-85802-405' of https://github.com/mihailges/moodle into MOODLE_405_STABLE 2025-08-07 10:33:37 +08:00
Mihail Geshoski 6035d07677 Merge branch 'MDL-79540_405_STABLE' of https://github.com/marxjohnson/moodle into MOODLE_405_STABLE 2025-08-07 10:19:19 +08:00
Huong Nguyen f71bd594d1 Merge branch 'MDL-84010-m405' of https://github.com/sammarshallou/moodle into MOODLE_405_STABLE 2025-08-07 09:17:44 +07:00
AMOS bot 19e31c4169 Automatically generated installer lang files 2025-08-07 00:07:56 +00:00
ferran 17c8757314 Merge branch 'MDL-86176-405' of https://github.com/sarjona/moodle into MOODLE_405_STABLE 2025-08-06 16:22:00 +02:00
Sara Arjona 0427e1bf7b MDL-86176 badges: Adjust badges due to Canvas Credentials changes
This change addresses necessary adjustments to how OBV2.0 badges are
sent to backpacks, specifically to ensure compatibility and correct
functionality following recent updates to Canvas Credentials' conditions.

It ensures our badge integration continues to operate smoothly despite
these external changes.
2025-08-06 15:05:55 +02:00
Jun Pataleta 8c84d14195 MDL-85795 behat: Update button text used in search steps 2025-08-06 19:58:09 +08:00
Simey Lameze e375efca79 MDL-85674 behat: replace multiple steps by a single table step 2025-08-06 18:20:54 +08:00
andelacruz d870292683 MDL-85674 mod_grade: Behat test for multiple questions manual grading 2025-08-06 18:20:54 +08:00
Jun Pataleta 2e9c76095c Merge branch 'MDL-86095-405' of https://github.com/rjnl/moodle into MOODLE_405_STABLE 2025-08-06 15:41:11 +08:00
yusufwib01 0a3e64de5d MDL-85718 tiny_link: Improve image link behavior in TinyMCE 2025-08-06 14:31:16 +07:00
Rajneel Totaram 5008146b77 MDL-86095 mod_book: Let screenreader know print book opens in new window 2025-08-06 18:33:24 +12:00
Huong Nguyen 03430a4060 Merge branch 'MDL-85132_405_STABLE' of https://github.com/vladaskidanovas/moodle into MOODLE_405_STABLE 2025-08-06 10:54:18 +07:00
Laurent David 7616aa1eef MDL-84464 Report: Hide report for teachers without groups 2025-08-06 10:22:13 +07:00
Laurent David 6978147a03 MDL-84463 mod_feedback: Fix typo in behat test description 2025-08-06 03:05:21 +00:00
Laurent David a0f12b787b MDL-84463 mod_feedback: Fix users in no group and email
* Non editing teachers in no group should not receive feedback emails
from students in no group
2025-08-06 03:05:21 +00:00
Laurent David 890d43e1c3 MDL-84463 mod_feedback: Fix show entries and nonrespondents
* Non editing teachers not in a groups should not see entries
from other users
2025-08-06 03:05:21 +00:00
raortegar 4068e8ee2b MDL-85704 mod_bigbluebuttonbn: Update sesskey handling for compatibility 2025-08-06 03:05:21 +00:00
Huong Nguyen 4d12c8fac3 Merge branch 'MDL-85510-405' of https://github.com/NashTechOpenUniversity/moodle into MOODLE_405_STABLE 2025-08-06 08:30:16 +07:00
Khoa Nguyen Dang f56e272ff7 MDL-85510 Questions: clearfix from MDL-84708 affects spacing
Co-authored-by: Mikel Martín Corrales <mikel@moodle.com>
2025-08-05 15:53:22 +07:00
Stevani Andolo 98f8fdee6a MDL-84217 editor_tiny: Added 'img-fluid' class to dropped image files 2025-08-05 15:00:45 +08:00
sam marshall a08193f094 MDL-84010 core_files: sync_external_file can cause infinite recursion 2025-08-05 07:49:02 +01:00
Mihail Geshoski 1159f0763b Merge branch 'MDL-86001-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-08-05 11:42:33 +08:00
Mihail Geshoski 33b7ccdd88 Merge branch 'MDL-85565_405' of https://github.com/PhilippImhof/moodle into MOODLE_405_STABLE 2025-08-05 11:35:54 +08:00
Mihail Geshoski eb97c10d89 Merge branch 'MDL-85492_405' of https://github.com/krisbax/moodle into MOODLE_405_STABLE 2025-08-05 11:20:41 +08:00
Huong Nguyen 49e3a29b00 Merge branch 'MDL-85338-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-08-05 09:11:07 +07:00
Huong Nguyen b784f20565 Merge branch 'MDL-85231-405' of https://github.com/junpataleta/moodle into MOODLE_405_STABLE 2025-08-05 08:50:32 +07:00
Huong Nguyen 375e56bb9b Merge branch 'MDL-86043-405' of https://github.com/tasiobg/moodle into MOODLE_405_STABLE 2025-08-05 08:32:14 +07:00
Jun Pataleta 76f0a88d89 MDL-85795 message: Enclose the search widget in a group 2025-08-04 18:46:50 +08:00
Jun Pataleta c4b864606f MDL-85795 search: Enclose the search widget in a group 2025-08-04 18:46:49 +08:00
Shamim Rezaie 86d14f5932 Merge branch 'MDL-85787-405-2' of https://github.com/junpataleta/moodle into MOODLE_405_STABLE 2025-08-04 09:53:52 +10:00
Jun Pataleta d1575b2739 MDL-85794 message: Set notification preferences controls within a group 2025-08-01 18:51:22 +08:00
Mihail Geshoski 9a419d4378 MDL-85802 mod_quiz: Fix double-escaping in notification content
Avoids double-escaping when filtering notification content. Adds
a unit test to validate the content filtering logic.
2025-08-01 18:50:12 +08:00
Iñigo Zendegi dc869f0c05 MDL-85802 mod_quiz: format course/quiz notification content. 2025-08-01 18:50:12 +08:00
Jun Pataleta 9e7c8cd9c0 MDL-85231 message: Style the notification buttons consistently 2025-08-01 13:09:02 +08:00
Jun Pataleta 43b5b809e6 MDL-85231 message_popup: Add a close button on the notification popover
This follows the recommendation on the ARIA Authoring Practices Guide
(APG) for modal dialogues:
> It is strongly recommended that the tab sequence of all dialogs
> include a visible element with role button that closes the dialog,
such as a close icon or cancel button.

https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
2025-08-01 13:09:02 +08:00
Laurent David b56614fce6 MDL-85231 message_popup: Add focustrap in message popup 2025-08-01 13:09:02 +08:00
David Mudrák 8143ad5cb5 MDL-85945 lang: Use fixed strings in the tests, too 2025-07-31 17:06:06 +02:00
Helen Foster 580a46bf12 MDL-85945 lang: Import fixed English strings (en_fix) 2025-07-31 17:05:30 +02:00
Sara Arjona b479c7a056 weekly release 4.5.5+ 2025-07-31 16:08:40 +02:00
Sara Arjona 353f4ed7cc NOBUG: Add upgrade notes 2025-07-31 16:08:39 +02:00
Sara Arjona 599b74c1a2 Merge branch 'install_405_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_405_STABLE 2025-07-31 16:08:33 +02:00
Michael Hawkins 6498193e6d Merge branch 'MDL-85362-405' of https://github.com/HuongNV13/moodle into MOODLE_405_STABLE 2025-07-31 14:37:06 +08:00
Huong Nguyen eea759e64c Merge branch 'MDL-86062-405' of https://github.com/andrewnicols/moodle into MOODLE_405_STABLE 2025-07-31 10:21:03 +07:00
Huong Nguyen b9bea60fa8 Merge branch 'MDL-84485-Select-webservice-in-wstoken-behat-breadcrumb-405' of https://github.com/farhan6318/moodle into MOODLE_405_STABLE 2025-07-31 10:17:52 +07:00
Huong Nguyen afd0e41b69 Merge branch 'MDL-85897-405' of https://github.com/andrewnicols/moodle into MOODLE_405_STABLE 2025-07-31 08:58:32 +07:00
AMOS bot 5bf57f83c9 Automatically generated installer lang files 2025-07-31 00:08:02 +00:00
Shamim Rezaie b00188cd82 Merge branch 'MDL-85295-405' of https://github.com/rjnl/moodle into MOODLE_405_STABLE 2025-07-31 02:44:01 +10:00
Shamim Rezaie 5ea0f0f35e Merge branch 'MDL-72133-filter-deleted-logs-MOODLE_405_STABLE' of https://github.com/bwalkerl/moodle into MOODLE_405_STABLE 2025-07-31 02:27:18 +10:00
Mihail Geshoski 3e43057840 Merge branch 'MDL-86113-405' of https://github.com/snake/moodle into MOODLE_405_STABLE 2025-07-30 23:29:17 +08:00
Mihail Geshoski 8730b522e6 Merge branch 'MDL-85470-405' of https://github.com/junpataleta/moodle into MOODLE_405_STABLE 2025-07-30 23:10:44 +08:00
Tasio 3bcbaf2ec8 MDL-86043 core: test for the task sending new users an email 2025-07-30 16:23:42 +02:00
Tasio d205f1bcb4 MDL-86043 core: fix sql to work in oracle
I’ve updated the query to use sql_isnotempty, which generates the correct SQL for the selected database.
I have aso added an "order by" because after some testing I noticed that Oracle and MySQL retun the results
in different order which feels inconsistent and makes it more difficult to test.
2025-07-30 16:23:34 +02:00
Paul Holden 116dc12ed1 MDL-85338 message: make messaging drawer responsive to sticky footer.
Co-authored-by: David Woloszyn <david.woloszyn@moodle.com>
2025-07-30 13:59:01 +01:00
Jun Pataleta de57e4a1cf Merge branch 'MDL-85995-405' of https://github.com/rjnl/moodle into MOODLE_405_STABLE 2025-07-30 17:10:09 +08:00
Andrew Nicols 86479d5871 MDL-86062 mod_choice: Stop calling get_string from lib.php 2025-07-30 13:58:40 +08:00
Andrew Nicols 63a21837db MDL-85897 core: Move all deprecations to emit_deprecation 2025-07-30 13:53:27 +08:00
Andrew Nicols 2181327bf2 MDL-85897 core: Add emit_deprecation() method
This changeset adds a new `\core\deprecation::emit_deprecation` method
with the same signature as the `emit_deprecation_if_found()` method.

It is intended to be used in places where something is guaranteed to
have been deprecated, and will throw appropriate debugging if the
deprecated attribute is not found.

The `emit_deprecation_if_found()` method should still be used where the
item being checked is not known to be deprecated or not, for example in
calling code.
2025-07-30 13:53:21 +08:00
Huong Nguyen f12170bb56 Merge branch 'MDL-85590_fix_modal_backdrop_attachment_point_405' of https://github.com/MariaMozgunova/moodle into MOODLE_405_STABLE 2025-07-30 08:37:46 +07:00
Farhan Karmali 4985662a5b MDL-84485 webservice: Add Behat step for mobile web service selection
This commit introduces a mandatory service selection step in our Behat tests
to ensure consistent behavior in environments with multiple third-party services.
2025-07-29 16:00:51 +05:30
Huong Nguyen 763bff0289 Merge branch 'MDL-85855-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-07-29 16:49:51 +07:00
Jun Pataleta af455ccfe2 MDL-85470 badges: Add a11y Behat test for the manage badges page 2025-07-29 15:17:24 +08:00
Jun Pataleta f83d35c631 MDL-85470 badges: Use badge:namewithimagelink column in badges table 2025-07-29 15:17:24 +08:00
Jun Pataleta bc100f6efa MDL-85470 reportbuilder: New column containing badge image, name, link 2025-07-29 15:17:24 +08:00
Jun Pataleta fde36bb49c MDL-85787 course: Limit course index drag and drop to certain pages only
Only allow the drag and drop functionality in the course index on pages
that provide alternatives to move course items such as the course
homepage and the course section view pages.

This is to meet the WCAG 2.2 Level AA Success Criterion 2.5.7 for
dragging movements.
See: https://www.w3.org/WAI/WCAG22/Understanding/dragging-movements
2025-07-29 11:46:02 +08:00
Jayce Birrell 38fe5cc384 MDL-85698 core: combo box informs screen reader users 2025-07-29 10:52:55 +09:30
AMOS bot 67249bea47 Automatically generated installer lang files 2025-07-28 13:16:59 +00:00
Maria Mozgunova 20dfe6475b MDL-85590 core: Fix modal backdrop attachment point for full screen 2025-07-28 10:46:30 +01:00
Benjamin Walker b08eaaa028 MDL-72133 report_log: Add fallback context display 2025-07-28 09:06:40 +10:00
Benjamin Walker b3e2e81b0d MDL-72133 report_loglive: Display logs filtered by deleted course ids 2025-07-28 09:06:40 +10:00
Benjamin Walker eb0634d489 MDL-72133 report_log: Display logs filtered by deleted course ids 2025-07-28 09:06:40 +10:00
Philipp Imhof 59808c6870 MDL-85565 phpunit: check for <select> before <option>
Make sure that assert_select_options() will fail when the output does
not contain a <select> and therefore does not have the given options.
2025-07-25 13:13:30 +02:00
Jake Dallimore 4a02a9975d MDL-86113 mod_lti: skip slow network calls during tests
This check exists to determine whether a given tool URL is a cartridge,
specifically when the URL does not end in .xml. It hits the URL to
determine this. In tests, we have many mock tool URLs, which won't
resolve, and running this code causes large hangs and random failures.
Any tests covering cartridge support still work fine, provided they
continue to use URLs ending in .xml; this change only skips the check
during tests for other non-xml URLs.
2025-07-25 16:49:07 +08:00
Michael Hawkins 51662cb8ef weekly release 4.5.5+ 2025-07-25 12:14:36 +08:00
Michael Hawkins c3e13a4ca6 Merge branch 'MDL-83111-405' of https://github.com/sh-csg/moodle into MOODLE_405_STABLE 2025-07-24 12:01:17 +08:00
Huong Nguyen 35eec5b9aa Merge branch 'MDL-86049-405' of https://github.com/andrewnicols/moodle into MOODLE_405_STABLE 2025-07-24 09:47:51 +07:00
Huong Nguyen 3b68cecb15 Merge branch 'MDL-85504-405' of https://github.com/junpataleta/moodle into MOODLE_405_STABLE 2025-07-24 09:44:11 +07:00
Rajneel Totaram 4c3be75ef2 MDL-85995 mod_book: Make previous/next buttons accessible 2025-07-24 10:08:39 +12:00
Jun Pataleta dc91c2144c MDL-85504 mod_feedback: Enhance the question edit form via JS
The drag handle and actions menu should not be within the accessible
labels of the question items. In order to enhance the question edit
form with these, they need to be rendered via JS outside of container
of the questions' accessible name.
2025-07-23 16:15:52 +08:00
Amaia Anabitarte 319113f872 Merge branch 'MDL-85989-405' of https://github.com/sarjona/moodle into MOODLE_405_STABLE 2025-07-23 10:14:00 +02:00
Jun Pataleta e894f95f6c MDL-85504 form: Legend's label template data should not be escaped
The label context data in the fieldset's legend tag should not be
escaped and allow raw HTML given that legend tags can contain
phrasing content and headings.

See developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/legend.
2025-07-23 16:10:53 +08:00
Huong Nguyen 977eacdfc0 Merge branch 'MDL-60799_405' of https://github.com/jonof/moodle into MOODLE_405_STABLE 2025-07-23 09:03:42 +07:00
Huong Nguyen d43a89d86f Merge branch 'MDL-83996-readonly-false-positive-MOODLE_405_STABLE' of https://github.com/bwalkerl/moodle into MOODLE_405_STABLE 2025-07-23 09:00:13 +07:00
Huong Nguyen 39c57e2f02 Merge branch 'MDL-84787-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-07-23 08:51:37 +07:00
Huong Nguyen c68104c12f Merge branch 'MDL-85573-tool-brickfield-uninstall-405' of https://github.com/szymonk1101/moodle into MOODLE_405_STABLE 2025-07-23 08:33:49 +07:00
Mihail Geshoski b177a0cee6 Merge branch 'MDL-85997-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-07-22 23:35:25 +08:00
Mihail Geshoski 3d41378956 Merge branch 'MDL-85038-M405_dml_missing_record_exception-when-running-mod_assign-task-send_assignment_due_soon_notification_to_user' of https://github.com/ziegenberg/moodle into MOODLE_405_STABLE 2025-07-22 23:28:41 +08:00
Mihail Geshoski 09f9052355 Merge branch 'MDL-83558-405' of https://github.com/andrewgos/moodle into MOODLE_405_STABLE 2025-07-22 13:54:02 +08:00
Huong Nguyen 1ef0196b0d MDL-85362 core: Define environment requirements for Moodle 5.1 2025-07-22 10:54:17 +07:00
Rajneel Totaram a5e3d3f018 MDL-85295 qtype_ordering: render feedback properly
'answertext' is already passed through format_text(), so no need to escape it in the template.
2025-07-22 11:40:38 +12:00
Andrew Nicols 7c4210b6ea MDL-86049 core: Do not use the component cache before purge
When trying to resolve a corrupt cache where it is not possible to
complete the Moodle setup/bootstrap, it is necessary to ignore the
current cache to load the cache purging infrastructure.
2025-07-21 12:25:26 +08:00
Benjamin Walker 5aec7f1992 MDL-83996 sessions: Fix false positive read only sessions errors 2025-07-21 10:26:27 +10:00
Mihail Geshoski c97987e9ed weekly release 4.5.5+ 2025-07-18 14:52:50 +08:00
Mihail Geshoski 266d5f8857 Merge branch 'install_405_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_405_STABLE 2025-07-18 14:52:37 +08:00
Paul Holden 6a1d35dfa0 MDL-85944 block_myoverview: fix previously badly nested SCSS. 2025-07-17 15:25:05 +07:00
Mihail Geshoski d7d824e872 Merge branch 'MDL-85055-405' of https://github.com/lameze/moodle into MOODLE_405_STABLE 2025-07-17 12:14:56 +08:00
Paul Holden 396f77e552 MDL-85055 task: correct use of DI provided frozen Clock API.
Fixes some of the usage introduced in tests as part of a35335a9.
2025-07-17 11:56:55 +08:00
Huong Nguyen a736250f33 Merge branch 'MDL-85050-405' of https://github.com/NashTechOpenUniversity/moodle into MOODLE_405_STABLE 2025-07-17 10:24:34 +07:00
Huong Nguyen b0c912d6f0 Merge branch 'MDL-85944-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-07-17 08:53:01 +07:00
Huong Nguyen 72640fbc5a Merge branch 'MDL-81384-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-07-17 08:44:04 +07:00
Shamim Rezaie 29fab26069 Merge branch 'MDL-73003-405' of https://github.com/Jayce0808/moodle into MOODLE_405_STABLE 2025-07-17 07:29:47 +10:00
Mihail Geshoski 114917dc65 Merge branch 'MDL-77237-405' of https://github.com/meirzamoodle/moodle into MOODLE_405_STABLE 2025-07-16 23:53:15 +08:00
Huong Nguyen c9c1d71c32 Merge branch 'MDL-85609-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-07-16 09:52:48 +07:00
Andrew Gosali a61e0d92ed MDL-83558 mod_forum: fix incorrect message on edge case scenario
(when student tries to see all participants on a forum with visible groups mode)
2025-07-15 22:46:02 +07:00
Daniel Ziegenberg 4e822cccfd MDL-85038 assignment: fix exception on sending assignment notification
When the assignment gets deleted between the creation of the ad-hoc task
mod_assign\task\send_assignment_due_soon_notification_to_user or
mod_assign\task\send_assignment_overdue_notification_to_user and its
execution, the ad-hoc task fails with a dml_missing_record_exception and
the task gets stuck.

In this case we do not want the task to fail and just return with an
appropriate mtrace.

Signed-off-by: Daniel Ziegenberg <daniel@ziegenberg.at>
2025-07-15 10:46:35 +02:00
meirzamoodle 07af6ccc73 MDL-77237 core: Suppress debugging during capabilities cleanup
To prevent the debugging messages from appearing during the upgrade,
I set a flag to hide them.

For the test file, I adjusted the parameters in
the unassign_capability() function, which seemed to be using
the parameter intended for assign_capability().
2025-07-15 14:36:26 +07:00
Paul Holden cada977938 MDL-85944 block_myoverview: specific styling for "My courses" page. 2025-07-15 08:02:17 +01:00
Mihail Geshoski cd409009e4 Merge branch 'MDL-85246-405' of https://github.com/rjnl/moodle into MOODLE_405_STABLE 2025-07-15 11:11:26 +08:00
Huong Nguyen ef0ba8a988 Merge branch 'MDL-72582-MOODLE_405_STABLE' of https://github.com/tsmilan/moodle into MOODLE_405_STABLE 2025-07-15 10:07:55 +07:00
Huong Nguyen 4de8318ca9 Merge branch 'MDL-86016-405' of https://github.com/andrewnicols/moodle into MOODLE_405_STABLE 2025-07-15 09:56:41 +07:00
Huong Nguyen 9bd33ef7e6 Merge branch 'MDL-85040-405-new' of https://github.com/meirzamoodle/moodle into MOODLE_405_STABLE 2025-07-15 09:42:29 +07:00
Mihail Geshoski 4baac8c6ce Merge branch 'MDL-84385-requireonce-405' of https://github.com/leonstr/moodle into MOODLE_405_STABLE 2025-07-15 09:41:18 +08:00
Andrew Nicols b4092640b8 MDL-86016 core: Remove xmldb test in favour of unit test 2025-07-15 09:33:05 +08:00
Andrew Nicols 75ac9fc3ae MDL-86016 tests: Make install.xml test less restrictive 2025-07-15 09:33:04 +08:00
Amaia Anabitarte b7d3d4f191 MDL-81384 report_participation: Remove innecessary JS parameters
The parameter are usable in course participants page because user
could add bulk notes linked to users. But that script is not used
in report/participants, so we don't need those arguments on the call.

Backported from MDL-75669.
2025-07-14 21:32:30 +01:00
Paul Holden ccb3a73f98 MDL-81384 theme_boost: centre help icon margin/padding. 2025-07-14 21:32:30 +01:00
meirzamoodle d78eccdb13 MDL-85040 forms: Make required form icons decorative
The required icon in mforms is not necessary. The div containing it
already has a title attribute that enables mouse users to hover on the
icon and see its meaning.

Screen reader users will know whether a field is required or not via
the `aria-required` attribute.

Labelling the required icon via `aria-label` does not pass the
a11y audit.

Given these, it only makes sense to make the required icon decorative
for mforms.

Co-authored-by: Jun Pataleta <jun@moodle.com>
2025-07-14 14:25:23 +07:00
AMOS bot 12e3626a6d Automatically generated installer lang files 2025-07-12 00:07:54 +00:00
Paul Holden 86fe6f4ae7 MDL-86001 qbank_columnsortorder: populate preview data question text. 2025-07-11 15:23:46 +01:00
Paul Holden 1708db93e6 MDL-85997 output: require CLI library from corresponding renderer. 2025-07-11 11:59:41 +01:00
Mark Johnson 9f953080d1 MDL-79540 qbank_managecategories: Don't allow own child as parent
When we build the options for a question category selector, the
"nochildrenof" option isn't correctly compared to the category ID. This
means we can select a category's own children as its new parent.
2025-07-11 10:07:49 +01:00
Sara Arjona 0a01a0d119 MDL-85989 output: Fix choicelist when selected 0 is not first
Previously, when the selected value in a choicelist was 0 but not in
the first position, the behavior was incorrect.
This commit fixes that issue, ensuring the choicelist behaves as expected
regardless of the position of the 0 value.
2025-07-11 09:15:02 +02:00
Michael Hawkins ce34e8ff08 weekly release 4.5.5+ 2025-07-11 12:49:25 +08:00
Leon Stringer 34a0d939dd MDL-84385 mod_assign: Remove unneeded require_once 2025-07-10 09:26:28 +01:00
Huong Nguyen 14d442c52c Merge branch 'MDL-85863-405' of https://github.com/yusufwib01/moodle into MOODLE_405_STABLE 2025-07-10 10:46:48 +07:00
Mihail Geshoski c4c11a559f Merge branch 'MDL-85689-405' of https://github.com/andelacruz/moodle into MOODLE_405_STABLE 2025-07-10 11:38:36 +08:00
Mark Johnson 3e8f439d17 MDL-85721 qtype_multianswer: Update find_duplicated_subquestions for Oracle compatibility 2025-07-10 10:29:08 +07:00
Huong Nguyen ab271c4c24 Merge branch 'MDL-85262-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-07-10 08:33:44 +07:00
Huong Nguyen ed44561319 Merge branch 'MDL-84901-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-07-10 08:25:44 +07:00
Mihail Geshoski b85edb5f5a Merge branch 'MDL-85939-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-07-09 23:25:10 +08:00
Mihail Geshoski 0a7c65c43e Merge branch 'MDL-84255-405' of https://github.com/lucaboesch/moodle into MOODLE_405_STABLE 2025-07-09 23:17:22 +08:00
Rajneel Totaram ba71a95d34 MDL-85246 mod_quiz: Fix quiz grade feedback
Using $CFG->recovergradesdefault and/or $gradeitem->refresh_grades()
are problematic when viewing a quiz. When reenrolling a user with
previous attempts, a teacher can choose not to recover the user's grades,
even when recovergradesdefault=true. This must be respected when
the user views the quiz. For site with recovergradesdefault=false,
the users always see a grade of 0, which is wrong.

This change shows the correct grade score and feedback to users and
avoids unintentionally recovering the grades.
2025-07-09 20:33:14 +12:00
Jun Pataleta 09bbcdaf32 Merge branch 'MDL-85596-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-07-09 14:14:48 +08:00
andelacruz 0de18a98cf MDL-85689 mod_quiz: Behat for adding and deleting page from quiz 2025-07-09 13:26:39 +08:00
Mihail Geshoski 0f5f7c8dbe Merge branch 'MDL-85721_405_STABLE' of https://github.com/marxjohnson/moodle into MOODLE_405_STABLE 2025-07-09 10:20:09 +08:00
Huong Nguyen 4c406b2d81 Merge branch 'MDL-85697-405' of https://github.com/andelacruz/moodle into MOODLE_405_STABLE 2025-07-09 09:10:53 +07:00
Paul Holden 19edba7020 MDL-85939 qbank_columnsortorder: improve preview page table alignment. 2025-07-08 17:58:38 +01:00
Kristiina.Bax aee5428591 MDL-85492 report_questioninstances: sort qtypes in alphabetical order 2025-07-08 17:14:15 +01:00
Paul Holden 2d4b15f317 MDL-85262 tool_task: switch next run time comparison in renderer.
If a task is "due", then next run time should be considered "ASAP".
2025-07-08 13:49:47 +01:00
Paul Holden 6462abc6df MDL-84901 auth: specific session start error language string. 2025-07-08 12:33:07 +01:00
Huong Nguyen 422a6f94a9 Merge branch 'MDL-84976-405' of https://github.com/rjnl/moodle into MOODLE_405_STABLE 2025-07-08 18:13:44 +07:00
Huong Nguyen 6f961cacc5 Merge branch 'MDL-85037-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-07-08 18:09:00 +07:00
Huong Nguyen eed6310a08 Merge branch 'MDL-85947-M405_bump-GHA-ubuntu-24-04' of https://github.com/ziegenberg/moodle into MOODLE_405_STABLE 2025-07-08 17:35:12 +07:00
andelacruz ddcaa5f8a6 MDL-85697 mod_lesson: Behat for lesson group and user overrides 2025-07-08 17:03:21 +08:00
andelacruz 6b9cbd5a19 MDL-85697 mod_assign: Behat for assignment group and user overrides 2025-07-08 17:02:06 +08:00
andelacruz 0b86e7f574 MDL-85697 mod_quiz: Behat for quiz group and user overrides 2025-07-08 16:59:49 +08:00
Luca Bösch 547cd0de94 MDL-84255 quiz: Show quiz title in heading in accessrule_seb layout. 2025-07-08 07:51:51 +02:00
Daniel Ziegenberg 323d7ba996 MDL-85947 ci: Bump GHA to Ubuntu 24.04
Signed-off-by: Daniel Ziegenberg <daniel@ziegenberg.at>
2025-07-07 16:12:45 +02:00
yusufwib01 22c2f2455e MDL-85863 user: Defer new-password email until after transaction commit 2025-07-07 21:03:32 +07:00
Trisha Milan 4e9c6b3f37 MDL-72582 core_files: Add unit test for readfile_accel debugging output
Adds a unit test for the debugging message triggered when readfile_accel() detects
a non-empty output buffer. The readfile_accel() function directly manipulates the
output buffers, so calling it from within PHPUnit triggers an error like "Test code
or tested code closed output buffers other than its own".

As a workaround, the test runs a CLI script in a separate process, allowing buffer
behaviour to be isolated and debugging output to be captured for assertions.
2025-07-07 18:59:25 +10:00
Matt Rice a442938279 MDL-72582 core_files: Update readfile_accel error handling
Correct the error handling in readfile_accel to properly log errors when
working on a stored_file, rather than hiding the true error behind the
incorrect "Object of class stored_file could not be converted to
string."

Co-authored-by: Simey Lameze <simey@moodle.com>
Co-authored-by: Trisha Milan <trishamilan@catalyst-au.net>
2025-07-07 18:59:19 +10:00
Laurent David 067138aab4 MDL-81702 core_message: Add focustrap and backdrop
* Add focustrap and backdrop to message drawer.
2025-07-07 07:09:16 +02:00
Laurent David e41473d9ff MDL-81702 theme_boost: Add focus lock to drawers
* Add focus lock to drawers for small screens
2025-07-07 07:09:16 +02:00
Laurent David 97aa3dd1f9 MDL-81702 theme_boost: Remove site home link from drawers
* The site-home-link was added to all menu drawers and should
only be added to the main menu drawer (mobile version)
2025-07-07 07:09:16 +02:00
Mihail Geshoski 2fce831627 weekly release 4.5.5+ 2025-07-06 09:26:27 +08:00
Mihail Geshoski 6fe2fb1f0f NOBUG: Add upgrade notes 2025-07-06 09:26:27 +08:00
Mihail Geshoski 396a02396e Merge branch 'install_405_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_405_STABLE 2025-07-06 09:26:13 +08:00
Mihail Geshoski cb9bebdc8e Merge branch 'MDL-85350_405' of https://github.com/PhilippImhof/moodle into MOODLE_405_STABLE 2025-07-05 19:57:51 +08:00
Mihail Geshoski 9a28c68655 Merge branch 'MDL-85563-m405' of https://github.com/NeillM/moodle into MOODLE_405_STABLE 2025-07-05 19:57:51 +08:00
Paul Holden 98329e1d71 MDL-85037 block_html: correct access checks for plugin file serving. 2025-07-04 08:55:58 +08:00
Huong Nguyen 046a9c4905 Merge branch 'MDL-76342-405' of https://github.com/yusufwib01/moodle into MOODLE_405_STABLE 2025-07-03 09:07:59 +07:00
Huong Nguyen b37969c4f9 Merge branch 'MDL-85769-405' of https://github.com/meirzamoodle/moodle into MOODLE_405_STABLE 2025-07-03 09:05:24 +07:00
Mihail Geshoski cc2d6b276d Merge branch 'patch/MDL-85666-405' of https://github.com/skodak/moodle into MOODLE_405_STABLE 2025-07-03 09:54:33 +08:00
Petr Skoda 6a4b730dfd MDL-85666 phpunit: add general plugin tests
This defines new group which can be used
to limit execution of these tests to one plugin only.

For example to execute all tests for Label module use:

phpunit --testsuite=mod_label_testsuite
phpunit --group=plugin_checks --filter=mod_label

or

phpunit --filter=mod_label
2025-07-02 07:12:13 +02:00
AMOS bot 9fa2846df4 Automatically generated installer lang files 2025-07-02 00:09:53 +00:00
Mark Johnson e721309453 MDL-85721 qtype_numerical: Prevent duplication on restore
If question_numerical record for a question has been deleted, when its
options are loaded its answers will not have a tolerance property. This
can lead to duplication.

Also, older questions may not have a question_numerical_options record.

To avoid this, we use 0 as a default tolerance for all answers if there
is no matching record, and do the same when reading the question
structure from backups. We also generate default values for the rest of
the options.
2025-07-01 11:58:59 +01:00
Mark Johnson 3101c07a97 MDL-85721 qtype: Cope with missing options records
If we restore a question (or any other) which has had its
qtype_xxx_options record deleted, we get a notification output when we
try to build the options.

This may be called from an AJAX request (such as when we duplicate a
quiz), and outputting the notification breaks the AJAX response.
Returning false also means we don't get the answers attached to the
questiondata options, so the structure doesn't match the restored data,
and we get duplication.

This emits the errors via debugging instead, which allows it to be
supressed or logged, and allows get_question_options() to continue
running.
2025-07-01 11:58:58 +01:00
Mark Johnson 8c9d16e605 MDL-85721 qtype_multianswer: Clean up duplicate subquestions
This adds an ad-hoc task to clean up duplicate subquestions created by
the bug.

When the upgrade is run, it will run a query to check if any
subquestions have been duplicated (if there are multiple questions with
the same parent, text and stamp), and queue the cleanup task if there
are.

For each stamp identified, the task will find instances of the question
that are not referred to in their parent's sequence field, confirm that
they have 0 usages, and delete them.
2025-07-01 11:58:58 +01:00
Mark Johnson d3d2d67c8e MDL-85721 qtype_multichoice: Generate default options on restore
qtype_multichoice::get_question_options() will create a default options
object if no qtype_multichoice_options record exists. This means if we
restore a backup containing a multichoice question without any options
(see previous commit), it will always create a duplicate.

This change generates a default set of options for the backupdata if
none exists, so the identity hash will match if the same question exists
in the database without options, and we dont get duplicates.
2025-07-01 11:54:50 +01:00
Mark Johnson 7c0600ae16 MDL-85721 qtype_multianswer: Don't delete qtype data for subquestions
Historically, multianswer would re-use question IDs from subquestions,
so it was necessary to delete qtype-specific data if the qtype changed.
Since versioning was introduced, editing creates new versions of the
qtype and subquestions. Deleting the data means the original versions of
the subquestions cannot function correctly, so we shouldn't do that
anymore.
2025-07-01 11:54:49 +01:00
Andrew Nicols b6f3972fad Merge branch 'MOODLE_405_STABLE' of git.in.moodle.com:moodle/integration; branch 'MDL-84531-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-07-01 17:36:47 +08:00
Neill Magill 76275dadf5 MDL-85563 phpunit: Clock API should always use Moodle system time
When using `clock::now()` the frozen cloxk and incrementing clock will now
always return a `DateTimeImmutable` that uses the configured Moodle system
time. In unit tests this will be Australia/Perth.

Before this change, it would sometimes be UCT.
2025-06-30 11:43:46 +01:00
Huong Nguyen c36274a15a weekly release 4.5.5+ 2025-06-29 16:18:49 +07:00
meirzamoodle c080b4bf58 MDL-85769 AI: Trap focus within AI drawer on smaller screens
Co-authored-by: Laurent David <laurent.david@moodle.com>
2025-06-28 10:59:41 +07:00
Paul Holden be23488e54 MDL-85855 calendar: format course shortname attribute correctly. 2025-06-26 21:47:12 +08:00
Mihail Geshoski c8116a0dbe Merge branch 'MDL-85665-405' of https://github.com/andelacruz/moodle into MOODLE_405_STABLE 2025-06-26 10:44:07 +08:00
Huong Nguyen 2ed1dd557c Merge branch 'MDL-83752-M405' of https://github.com/ziegenberg/moodle into MOODLE_405_STABLE 2025-06-26 09:19:43 +07:00
Huong Nguyen fc34abe738 Merge branch 'MDL-76414-405' of https://github.com/yusufwib01/moodle into MOODLE_405_STABLE 2025-06-26 08:29:47 +07:00
Huong Nguyen c477dcd640 Merge branch 'MDL-85734-405' of https://github.com/lucaboesch/moodle into MOODLE_405_STABLE 2025-06-26 08:25:57 +07:00
Huong Nguyen c4e40860e4 Merge branch 'MDL-85466-405' of https://github.com/lucaboesch/moodle into MOODLE_405_STABLE 2025-06-26 08:22:29 +07:00
Huong Nguyen 91530e92ee Merge branch 'MDL-82109-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-06-26 08:19:58 +07:00
Jonathon Fowler f7b694d15f MDL-60799 enrol: update_instance() needs separate objects passed
Passing the same object means enrol_cohort does not recognise a change
in role when an edit or batch course upload operation is done.

 * enrol/editinstance.php was modifying $instance before passing it to
   enrol_plugin::update_instance() so the latter never sees a difference
   versus $data. This is redundant since update_instance() is doing the
   same thing a second time.
 * admin/tool/uploadcourse/classes/course.php is passing the same object
   as both arguments to enrol_plugin::update_instance().

There is an expectation that update_enrol_plugin_data() is to mutate its
$instance argument and then return it based on how derived classes in
enrol_guest and enrol_self behave, so cloning $instance within
update_enrol_plugin_data() before mutating it isn't an option.
2025-06-25 09:43:10 +10:00
Daniel Ziegenberg 471a73e98c MDL-83752 workshop: Handle empty feedbackreviewer
Causes PHP warnings when null on PHP 8.1.

Signed-off-by: Daniel Ziegenberg <daniel@ziegenberg.at>
2025-06-24 12:41:17 +02:00
andelacruz 651af33600 MDL-85665 mod_quiz: Behat to build quiz in single activity format course 2025-06-24 17:58:32 +08:00
Huong Nguyen 002398450f Merge branch 'MDL-85683-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-06-24 09:21:22 +07:00
Huong Nguyen 7f708151eb Merge branch 'MDL-85407-45' of https://github.com/durenadev/moodle into MOODLE_405_STABLE 2025-06-24 09:17:03 +07:00
Paul Holden 1263027e0f MDL-85596 output: update "More" sub-menus ARIA expanded attribute. 2025-06-20 21:10:29 +01:00
Luca Bösch 90ce0974bd MDL-85734 calendar: support multilang group names. 2025-06-20 16:47:45 +02:00
Luca Bösch 23dc4c7ca4 MDL-85466 course: space between number and course icon. 2025-06-20 16:47:16 +02:00
Huong Nguyen 97ed6b7083 weekly release 4.5.5+ 2025-06-20 15:14:38 +07:00
Huong Nguyen 499f6f3688 Merge branch 'install_405_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_405_STABLE 2025-06-20 15:14:26 +07:00
Sara Arjona 5a22bfc175 Merge branch 'MDL-75947-integration-405-fix' of https://github.com/mihailges/moodle into MOODLE_405_STABLE 2025-06-19 17:22:08 +02:00
Mihail Geshoski 66bf9156b3 MDL-75947 ltiservice_gradebookservices: Fix unit test
Fixes the test_sequential_score_posts() unit test in lineitem_test
by removing dynamic class property assignments, which were triggering
deprecation notices.
2025-06-19 16:15:36 +08:00
yusufwib01 a14b77ec1c MDL-76414 navigation: Show 'Calendar' link in primary nav to guests 2025-06-19 13:13:58 +07:00
Huong Nguyen 8d6e21135c Merge branch 'MDL-85223-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-06-19 11:26:20 +07:00
Mihail Geshoski 9d661d516e Merge branch 'MDL-75947-405' of https://github.com/Jayce0808/moodle into MOODLE_405_STABLE 2025-06-19 12:18:15 +08:00
Huong Nguyen 8369f19a64 Merge branch 'MDL-85678-405' of https://github.com/junpataleta/moodle into MOODLE_405_STABLE 2025-06-19 11:07:48 +07:00
Mihail Geshoski 1338ddaa3f Merge branch 'MDL-83487-405' of https://github.com/muhammadarnaldo/moodle into MOODLE_405_STABLE 2025-06-19 11:31:24 +08:00
Huong Nguyen 67827bf0fa Merge branch 'MDL-85159-405' of https://github.com/stevandoMoodle/moodle into MOODLE_405_STABLE 2025-06-19 10:03:51 +07:00
yusufwib01 7479f88d22 MDL-76342 core_output: Wrap buttons below heading on small screens 2025-06-19 10:01:33 +07:00
Mihail Geshoski 0e70433873 Merge branch 'MDL-85514-405' of https://github.com/snake/moodle into MOODLE_405_STABLE 2025-06-19 10:58:38 +08:00
Jake Dallimore d54a04197a MDL-85514 core_form: support 'in' rule for multiselects via subset of
The 'in' rule currently behaves the same way that 'eq' does when used
with multi-selects, which is not correct. It should be modelled by
checking whether the selected values are a subset of the rule values
(i.e. selected values are 'in' the rule values). This patch implements
that for core form rules as well as for the admin settings show/hide.
Several behat features are updated as well, because these were verifying
the incorrect behaviour for multi-select in rules.
2025-06-19 09:58:30 +08:00
Mihail Geshoski f6497cbe1f Merge branch 'MDL-42263-405' of https://github.com/Jayce0808/moodle into MOODLE_405_STABLE 2025-06-18 16:17:06 +08:00
Jayce Birrell 693d89cbdb MDL-73003 core_grades: Don't use 'scale' to determine grading method.
When a forum activity used a rubrics with a scale for grading, it was not treated as an advanced grading method, causing
the code to mishandle the grading object and not return any grades.
2025-06-18 16:51:20 +09:30
Huong Nguyen ccc6b2ee89 Merge branch 'MDL-84832-405' of https://github.com/junpataleta/moodle into MOODLE_405_STABLE 2025-06-18 09:50:58 +07:00
Paul Holden 5d7c70c21e Merge branch 'MDL-85773-405' of https://github.com/sebastsg/moodle into MOODLE_405_STABLE 2025-06-17 11:08:46 +01:00
Sebastian Gundersen d2e1d0e76e MDL-85773 forms: Read-only selects must have static labels
This is to prevent labels with an invalid for attribute, referencing non-existing elements.
2025-06-17 11:44:35 +02:00
Tasio fc21f64863 MDL-78233 accesslib: remove unreliable test
Co-authored-by: Marina Glancy <marina.glancy@gmail.com>
2025-06-17 10:37:57 +02:00
Mihail Geshoski b5fd491b73 Merge branch 'MDL-84978-405' of https://github.com/olivabigyo/moodle into MOODLE_405_STABLE 2025-06-17 15:15:30 +08:00
Mihail Geshoski e8ac6e2fec Merge branch 'MDL-82797_405' of https://github.com/cwarwicker/moodle into MOODLE_405_STABLE 2025-06-17 14:59:52 +08:00
Paul Holden 10aece9d65 MDL-85609 tool_mfa: fix user counting for factors in use table. 2025-06-16 13:03:43 +01:00
Jun Pataleta b419fd08fc MDL-85678 mod_feedback: a11y Behat coverage for Manage templates page 2025-06-16 13:50:25 +08:00
Jun Pataleta 5c3e474160 MDL-85678 mod_feedback: Fix a11y issues in the Manage templates page
- Fix the heading hierarchy.
- Add a visually-hidden column header for the actions column.
2025-06-16 13:50:25 +08:00
Jake Dallimore 1bf62c2e46 MDL-75947 ltiservice_gradebookservices: score->timestamp as dategraded
If this is not passed in to grade_update(), then it's not passed on to
update_raw_grade(), resulting in time() being used instead, which isn't
correct. The grade timemodified value should be set to the timestamp
provided by the tool in the score post JSON, not the time of the score
post itself.

Co-authored-by: Jayce Birrell <jayce.birrell@moodle.com>
2025-06-16 14:43:46 +09:30
Jake Dallimore 036f61da49 MDL-75947 ltiservice_gradebookservices: test sequential score posting
This tests highlights a problem with the score handling logic and
will currently fail. On score post, the current time is used for
grade->timemodified instead of the score->timestamp provided by the
tool in the POST JSON.
2025-06-16 14:43:46 +09:30
aya-saad1 e6894fcf11 MDL-42263 assign: Force language of email sent to graders
Co-authored-by: Jayce Birrell <jayce.birrell@moodle.com>
2025-06-16 11:01:18 +09:30
AMOS bot 4fceaed909 Automatically generated installer lang files 2025-06-15 00:09:52 +00:00
Mihail Geshoski 95518023c6 weekly release 4.5.5+ 2025-06-13 14:43:42 +08:00
Mihail Geshoski fe0d7c9b0a NOBUG: Add upgrade notes 2025-06-13 14:43:42 +08:00
Paul Holden 56801cdf3b MDL-84531 core: default deprecated attribute $replacement to null. 2025-06-12 14:50:56 +01:00
Huong Nguyen 9691e4d4c6 MDL-85316 dml: Revert DB vendor changes 2025-06-12 17:11:26 +07:00
Jun Pataleta d2711e792f Merge branch 'MDL-84752-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-06-12 17:15:43 +08:00
Paul Holden 102621f53d MDL-84752 qtype_essay: create accessible min/max words form elements.
Because of historical requirements involving fieldsets in question
type forms (see 958e7671), we should use ARIA label attribute on
grouped elements rather than normal labels in order to make them
accessible.

Co-authored-by: Jun Pataleta <jun@moodle.com>
2025-06-12 10:11:48 +01:00
Jun Pataleta 44fc7bd927 Merge branch 'MDL-85700-405' of https://github.com/roland04/moodle into MOODLE_405_STABLE 2025-06-12 17:06:40 +08:00
Huong Nguyen 18b20273fd MDL-72123 message: Add upgrade note 2025-06-12 17:06:40 +08:00
Huong Nguyen 49729e14a4 Merge branch 'MDL-72123-405' of https://github.com/yusufwib01/moodle into MOODLE_405_STABLE 2025-06-12 17:06:39 +08:00
Huong Nguyen 97ccd815f9 Merge branch 'MDL-84611-405' of https://github.com/sh-csg/moodle into MOODLE_405_STABLE 2025-06-12 08:37:34 +07:00
Mikel Martín abdf765a28 MDL-85700 theme_boost: Fix singleselect spacing
- Fix single_select template form layout.
2025-06-11 13:53:03 +02:00
Justin Hunt bd54d58d8a MDL-82797 question engine: gift import fix 2025-06-11 11:58:23 +01:00
Stefan Hanauska 172277eae4 MDL-84611 blocks: Respect instance_allow_multiple() 2025-06-11 11:22:05 +02:00
Stefan Hanauska 110e4f7aed MDL-84611 block_html: Change tests to respect capabilities 2025-06-11 11:22:05 +02:00
Huong Nguyen 8061a3cdc8 Merge branch 'MDL-77964-405' of https://github.com/paulholden/moodle into MOODLE_405_STABLE 2025-06-11 14:08:50 +07:00
Huong Nguyen 60c73f7ff7 Merge branch 'MDL-85316-405-mysql-version' of https://github.com/meirzamoodle/moodle into MOODLE_405_STABLE 2025-06-11 13:33:49 +07:00
Huong Nguyen d83b08f92c Merge branch 'MDL-85274-405' of https://github.com/davewoloszyn/moodle into MOODLE_405_STABLE 2025-06-11 13:30:18 +07:00
David Woloszyn 5dd77e0022 MDL-85274 calendar: Duration limits added to forms 2025-06-11 14:30:11 +10:00
meirzamoodle cc7efbf977 MDL-85316 admin: Add environment check for AWS Aurora database 2025-06-11 10:44:12 +07:00
Philipp Imhof cd11b9533f MDL-85350 questions: skip restore test for certain 3rd party plugins
The test_restore_course_with_same_stamp_questions() test assumes
that all question types using answers will also use the table
"question_answers" to store said answers. However, some 3rd party
qtypes might use custom tables and for those, the test would fail.
2025-06-06 15:22:19 +02:00
Rajneel Totaram 436c8070e2 MDL-84976 tool_policy: noclean param was not set 2025-06-06 15:14:40 +12:00
Paul Holden 4e1feb2c4e MDL-85683 enrol: use formatted course name for notification context. 2025-06-06 00:22:13 +01:00
yusufwib01 8c96b52f08 MDL-72123 message: Hide add to contacts button if user not allowed 2025-06-06 01:09:29 +07:00
Szymon Karolczuk dd639a9516 MDL-85573 tool_brickfield: fix uninstall code 2025-06-05 19:16:20 +02:00
vlad.kidanov a9b8b52a16 MDL-85132 mod_glossary: Add missing end div for entrybox element. 2025-05-30 10:30:06 +01:00
Jun Pataleta 3f16708b55 MDL-84832 tiny_media: Announce when alt text maxlength is reached
Co-authored-by: Meirza <meirza.arson@moodle.com>
2025-05-30 17:08:18 +08:00
Paul Holden 586ead512c MDL-85223 reportbuilder: initialize user entity filters just once. 2025-05-29 15:32:30 +01:00
Paul Holden 1e5ad34897 MDL-77964 availability: avoid JS console errors when closing modal. 2025-05-29 10:15:31 +01:00
Katalin Lukacs Toth 9a23fc37fb MDL-84978 mod_forum: Fix latest/earliest post id lookup in a discussion
This properly fixes the problems that commit 0f5a96f in MDL-80848 tried
to address.
2025-05-21 11:04:19 +02:00
Paul Holden bf3c210d82 MDL-82109 notes: include link to add site note if user has capability. 2025-05-20 13:18:37 +01:00
Muhammad Arnaldo 14c33bf2a8 MDL-83487 mod_forum: retain upcoming posts in user digests 2025-05-16 15:35:14 +07:00
toanlamt cbda104d14 MDL-85050 Javascript: core/dropzone Fix link & box click event conflict 2025-05-15 15:11:54 +07:00
Stevani Andolo 0b719c3951 MDL-85159 editor_tiny: Fix language list and track srclang a11y issue 2025-05-14 09:22:05 +08:00
Daniel Ureña 5d640c4b5c MDL-85407 mod_data: Fix unclosed HTML in list view 2025-05-08 11:15:49 +02:00
Stefan Hanauska b9144ae56a MDL-83111 tool_recyclebin: Avoid null value 2025-04-18 07:15:10 +02:00
Paul Holden 1c4b98c2d2 MDL-84787 calendar: restore course-specific management page parameter. 2025-03-11 13:32:52 +00:00
978 changed files with 8013 additions and 3203 deletions
+3 -3
View File
@@ -19,7 +19,7 @@ env:
jobs:
Grunt:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Checking out code
@@ -61,12 +61,12 @@ jobs:
matrix:
include:
# MySQL builds always run with the lowest PHP supported version.
- os: ubuntu-22.04
- os: ubuntu-24.04
php: 8.1
extensions:
db: mysqli
# PostgreSQL builds always run with the highest PHP supported version.
- os: ubuntu-22.04
- os: ubuntu-24.04
php: 8.3
db: pgsql
+32
View File
@@ -6,6 +6,38 @@ More detailed information on key changes can be found in the [Developer update n
The format of this change log follows the advice given at [Keep a CHANGELOG](https://keepachangelog.com).
## 4.5.6
### core
#### Added
- Add a new method has_valid_group in \core\report_helper that will return true or false depending if the user has a valid group. This is mainly false in case the user is not in any group in SEPARATEGROUPS. Used in report_log and report_loglive
For more information see [MDL-84464](https://tracker.moodle.org/browse/MDL-84464)
#### Changed
- The `\core\attribute\deprecated` attribute constructor `$replacement` parameter now defaults to null, and can be omitted
For more information see [MDL-84531](https://tracker.moodle.org/browse/MDL-84531)
- Added a new `\core\deprecation::emit_deprecation()` method which should be used in places where a deprecation is known to occur. This method will throw debugging if no deprecation notice was found, for example:
```php
public function deprecated_method(): void {
\core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
}
```
For more information see [MDL-85897](https://tracker.moodle.org/browse/MDL-85897)
### core_message
#### Added
- The web service `core_message_get_member_info` additionally returns `cancreatecontact` which is a boolean value for a user's permission to add a contact.
For more information see [MDL-72123](https://tracker.moodle.org/browse/MDL-72123)
## 4.5.5
### core
+1
View File
@@ -24,6 +24,7 @@
*/
define('CLI_SCRIPT', true);
define('IGNORE_COMPONENT_CACHE', true);
require(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/clilib.php');
+7 -1
View File
@@ -4716,6 +4716,7 @@
<VENDOR name="mysql" version="8.4" />
<VENDOR name="postgres" version="14" />
<VENDOR name="mssql" version="14.0" />
<VENDOR name="auroramysql" version="8.0" />
</DATABASE>
<PHP version="8.2.0" level="required">
</PHP>
@@ -4895,6 +4896,8 @@
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_async_backup" level="recommended">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_aurora_version" level="optional">
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
<MOODLE version="5.1" requires="4.2.3">
@@ -4906,8 +4909,9 @@
<DATABASE level="required">
<VENDOR name="mariadb" version="10.11.0" />
<VENDOR name="mysql" version="8.4" />
<VENDOR name="postgres" version="14" />
<VENDOR name="postgres" version="15" />
<VENDOR name="mssql" version="14.0" />
<VENDOR name="auroramysql" version="8.0" />
</DATABASE>
<PHP version="8.2.0" level="required">
</PHP>
@@ -5092,6 +5096,8 @@
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_async_backup" level="recommended">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_aurora_version" level="optional">
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
</COMPATIBILITY_MATRIX>
+2
View File
@@ -23,6 +23,8 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('IGNORE_COMPONENT_CACHE', true);
require_once('../config.php');
require_once($CFG->libdir.'/adminlib.php');
+11 -2
View File
@@ -24,5 +24,14 @@
defined('MOODLE_INTERNAL') || die;
// Remove the 'enableaccessibilitytools' configuration setting in case of reinstall.
unset_config('enableaccessibilitytools');
/**
* Plugin uninstall code.
*
* @return true
*/
function xmldb_tool_brickfield_uninstall(): bool {
// Remove the 'enableaccessibilitytools' configuration setting in case of reinstall.
unset_config('enableaccessibilitytools');
return true;
}
@@ -0,0 +1,70 @@
<?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/>.
namespace tool_capability;
// phpcs:disable moodle.PHPUnit.TestCaseProvider.dataProviderSyntaxMethodNotFound
/**
* Detect common problems in capability definitions of plugins.
*
* @group plugin_checks
* @package tool_capability
* @copyright 2025 Petr Skoda
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class plugin_checks_test extends \core\tests\plugin_checks_testcase {
/**
* Verify contents of plugin db/access.php file.
*
* @dataProvider all_plugins_provider
* @coversNothing
*
* @param string $component
* @param string $plugintype
* @param string $pluginname
* @param string $dir
*/
public function test_db_access_file(string $component, string $plugintype, string $pluginname, string $dir): void {
global $CFG;
$stringmanager = get_string_manager();
$corerolefile = "$CFG->dirroot/lang/en/role.php";
$langfile = "$dir/lang/en/$component.php";
$file = "$dir/db/access.php";
$capabilities = $this->fetch_array_from_file($file, 'capabilities');
if (!$capabilities) {
$this->expectNotToPerformAssertions();
return;
}
foreach ($capabilities as $capname => $capability) {
if ($plugintype === 'qbank' && str_starts_with($capname, 'moodle/question:')) {
// Question bank capabilities are irregular.
$strname = explode('/', $capname, 2)[1];
$this->assertTrue($stringmanager->string_exists($strname, 'core_role'),
"Missing capability name string '$strname' in $corerolefile");
continue;
}
$this->assertMatchesRegularExpression("|^$plugintype/$pluginname:[a-z0-9_]+$|", $capname);
$strname = substr($capname, strlen($plugintype) + 1);
$this->assertTrue($stringmanager->string_exists($strname, $component),
"Missing capability name string '$strname' in $langfile");
$this->assertSame($capname, clean_param($capname, PARAM_CAPABILITY));
}
}
}
+10 -10
View File
@@ -379,7 +379,7 @@ class renderer extends \plugin_renderer_base {
WHERE deleted = 0
AND suspended = 0
GROUP BY auth";
$allusersinfo = $DB->get_records_sql($alluserssql, []);
$allusersinfo = $DB->get_records_sql_menu($alluserssql);
$noncompletesql = "SELECT u.auth, COUNT(u.id)
FROM {user} u
@@ -388,7 +388,7 @@ class renderer extends \plugin_renderer_base {
AND (mfaa.lastverified < ?
OR mfaa.lastverified IS NULL)
GROUP BY u.auth";
$noncompleteinfo = $DB->get_records_sql($noncompletesql, [$lookback, $lookback]);
$noncompleteinfo = $DB->get_records_sql_menu($noncompletesql, [$lookback, $lookback]);
$nologinsql = "SELECT auth, COUNT(id)
FROM {user}
@@ -396,7 +396,7 @@ class renderer extends \plugin_renderer_base {
AND suspended = 0
AND lastlogin < ?
GROUP BY auth";
$nologininfo = $DB->get_records_sql($nologinsql, [$lookback]);
$nologininfo = $DB->get_records_sql_menu($nologinsql, [$lookback]);
$mfauserssql = "SELECT auth,
COUNT(DISTINCT tm.userid)
@@ -406,7 +406,7 @@ class renderer extends \plugin_renderer_base {
AND u.deleted = 0
AND u.suspended = 0
GROUP BY u.auth";
$mfausersinfo = $DB->get_records_sql($mfauserssql, [$lookback]);
$mfausersinfo = $DB->get_records_sql_menu($mfauserssql, [$lookback]);
$factorsusedsql = "SELECT CONCAT(u.auth, '_', tm.factor) as id,
COUNT(*)
@@ -417,7 +417,7 @@ class renderer extends \plugin_renderer_base {
AND u.suspended = 0
AND (tm.revoked = 0 OR (tm.revoked = 1 AND tm.timemodified > ?))
GROUP BY CONCAT(u.auth, '_', tm.factor)";
$factorsusedinfo = $DB->get_records_sql($factorsusedsql, [$lookback, $lookback]);
$factorsusedinfo = $DB->get_records_sql_menu($factorsusedsql, [$lookback, $lookback]);
// Auth rows.
$authtypes = get_enabled_auth_plugins(true);
@@ -426,10 +426,10 @@ class renderer extends \plugin_renderer_base {
$row[] = \html_writer::tag('b', $authtype);
// Setup the overall totals columns.
$row[] = $allusersinfo[$authtype]->count ?? '-';
$row[] = $mfausersinfo[$authtype]->count ?? '-';
$row[] = $noncompleteinfo[$authtype]->count ?? '-';
$row[] = $nologininfo[$authtype]->count ?? '-';
$row[] = $allusersinfo[$authtype] ?? '-';
$row[] = $mfausersinfo[$authtype] ?? '-';
$row[] = $noncompleteinfo[$authtype] ?? '-';
$row[] = $nologininfo[$authtype] ?? '-';
// Create a running counter for the total.
$authtotal = 0;
@@ -439,7 +439,7 @@ class renderer extends \plugin_renderer_base {
if (!empty($column)) {
// Get the information from the data key.
$key = $authtype . '_' . $column;
$count = $factorsusedinfo[$key]->count ?? 0;
$count = $factorsusedinfo[$key] ?? 0;
$authtotal += $count;
$row[] = $count ? format_float($count, 0) : '-';
@@ -71,7 +71,7 @@ $string['setupfactor:instructionsverification'] = '3. Enter the verification cod
$string['setupfactor:intro'] = 'To set up this method, you need to have a device with an authenticator app. If you don\'t have an app, you can download one. For example, <a href="https://2fas.com/" target="_blank">2FAS Auth</a>, <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a>, Google Authenticator, Microsoft Authenticator or Twilio Authy.';
$string['setupfactor:key'] = 'Secret key: ';
$string['setupfactor:link'] = 'Or enter details manually.';
$string['setupfactor:link_help'] = 'If you are on a mobile device and already have an authenticator app installed this link may work. Note that using TOTP on the same device as you login on can weaken the benefits of MFA.';
$string['setupfactor:link_help'] = 'If you are on a mobile device and already have an authenticator app installed this link may work. Note that using TOTP on the same device as you log in on can weaken the benefits of MFA.';
$string['setupfactor:linklabel'] = 'Open app already installed on this device';
$string['setupfactor:mode'] = 'Mode:';
$string['setupfactor:mode:timebased'] = 'Time-based';
+2
View File
@@ -590,6 +590,7 @@ class api {
'subdirs' => false,
'maxfiles' => -1,
'context' => context_system::instance(),
'noclean' => true,
];
}
@@ -606,6 +607,7 @@ class api {
'subdirs' => false,
'maxfiles' => -1,
'context' => context_system::instance(),
'noclean' => true,
];
}
+1 -1
View File
@@ -254,7 +254,7 @@ class course_bin extends base_bin {
// This hack will be removed once recycle bin switches to use its own backup mode, with
// own preferences and 100% separate from MOODLE_AUTOMATED.
// TODO: Remove this as part of MDL-65228.
$forcedrestoresettings = $CFG->forced_plugin_settings['restore'] ?? null;
$forcedrestoresettings = $CFG->forced_plugin_settings['restore'] ?? [];
$CFG->forced_plugin_settings['restore']['restore_general_users'] = 1;
$CFG->forced_plugin_settings['restore']['restore_general_groups'] = 1;
+1 -1
View File
@@ -274,7 +274,7 @@ class tool_task_renderer extends plugin_renderer_base {
$nextruntime = $task->get_next_run_time();
$due = $nextruntime < $now;
if ($task->get_attempts_available() > 0) {
$nextrun = $due ? userdate($nextruntime) : get_string('asap', 'tool_task');
$nextrun = $due ? get_string('asap', 'tool_task') : userdate($nextruntime);
} else {
$nextrun = get_string('never', 'admin');
}
@@ -0,0 +1,23 @@
@tool @tool_task @javascript
Feature: Manage adhoc task
In order to manage adhoc tasks
As an admin
I need to be able to view adhoc tasks
Scenario Outline: View adhoc tasks next run time
Given the following "tool_task > adhoc tasks" exist:
| classname | seconds | hostname | pid | nextruntime |
| \core\task\asynchronous_backup_task | 0 | c69335460f7f | 1915 | <nextruntime> |
When I log in as "admin"
And I navigate to "Server > Tasks > Ad hoc tasks" in site administration
Then the following should exist in the "Ad hoc tasks" table:
| Component / Class name | Next run |
| asynchronous_backup_task | <nextruntimestr> |
And I click on "asynchronous_backup_task" "link" in the "Ad hoc tasks" "table"
And the following should exist in the "\core\task\asynchronous_backup_task Ad hoc tasks" table:
| Next run |
| <nextruntimestr> |
Examples:
| nextruntime | nextruntimestr |
| ##yesterday## | ASAP |
| ##tomorrow noon## | ##tomorrow noon##%A, %d %B %Y, %I:%M## |
+3 -3
View File
@@ -43,7 +43,7 @@ class tool_task_generator extends testing_module_generator {
global $DB;
$conditions = ['classname' => $data['classname']];
$record = $DB->get_record('task_scheduled', $conditions, '*', MUST_EXIST);
$record->timestarted = time() - $data['seconds'];
$record->timestarted = $data['seconds'] > 0 ? time() - $data['seconds'] : 0;
$record->hostname = $data['hostname'];
$record->pid = $data['pid'];
$DB->update_record('task_scheduled', $record);
@@ -59,8 +59,8 @@ class tool_task_generator extends testing_module_generator {
global $DB;
$adhoctask = (object)[
'classname' => $data['classname'],
'nextruntime' => 0,
'timestarted' => time() - $data['seconds'],
'nextruntime' => $data['nextruntime'] ?? 0,
'timestarted' => $data['seconds'] > 0 ? time() - $data['seconds'] : 0,
'hostname' => $data['hostname'],
'pid' => $data['pid'],
];
+1 -1
View File
@@ -1146,7 +1146,7 @@ class tool_uploadcourse_course {
// Now update values.
// Sort out plugin specific fields.
$modifiedinstance = $plugin->update_enrol_plugin_data($course->id, $method, $instance);
$modifiedinstance = $plugin->update_enrol_plugin_data($course->id, $method, clone $instance);
$plugin->update_instance($instance, $modifiedinstance);
} else {
foreach ($errors as $key => $message) {
@@ -126,3 +126,19 @@ Feature: An admin can create courses with cohort enrolments using a CSV file
And I press "Cancel"
And I click on "Edit" "link" in the "Cohort 4" "table_row"
And the field "Assign role" matches value "Non-editing teacher"
@javascript
Scenario: Uploading a cohort enrolment method with a non-default role applies only the specified role
Given the following "users" exist:
| username | firstname | lastname | email |
| t1 | Teacher | 1 | s1@example.com |
And the following "cohort members" exist:
| user | cohort |
| t1 | CV4 |
And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_cohort_multiple.csv" file to "File" filemanager
And I click on "Preview" "button"
And I click on "Upload courses" "button"
When I am on the "Course 1" "course" page
And I navigate to course participants
Then I should see "Non-editing teacher" in the "Teacher 1" "table_row"
And I should not see "Student" in the "Teacher 1" "table_row"
@@ -1,12 +0,0 @@
@tool @tool_xmldb
Feature: The Reconcile XMLDB files report works and all the files are ok
In order to ensure that all the XMLDB Editor xml files are generated properly
As a developer
I need to be able to run the Reconcile XMLDB files report and verify that everything is ok
Scenario: The Reconcile XMLDB files reports that all files are ok
Given I log in as "admin"
And I navigate to "Development > XMLDB editor" in site administration
When I follow "[Reconcile XMLDB files]"
Then I should see "Look for XMLDB files needing reconciling"
And I should see "All files are OK. No reconciling is needed."
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -31,6 +31,8 @@ import AIHelper from 'core_ai/helper';
import DrawerEvents from 'core/drawer_events';
import {subscribe} from 'core/pubsub';
import * as MessageDrawerHelper from 'core_message/message_drawer_helper';
import * as FocusLock from 'core/local/aria/focuslock';
import {isSmall} from "core/pagehelpers";
const AICourseAssist = class {
@@ -60,6 +62,7 @@ const AICourseAssist = class {
this.jumpToElement = document.querySelector(Selectors.ELEMENTS.JUMPTO);
this.summaryActionElement = document.querySelector(Selectors.ACTIONS.SUMMARY);
this.aiDrawerCloseElement = this.aiDrawerElement.querySelector(Selectors.ELEMENTS.AIDRAWER_CLOSE);
this.isDrawerFocusLocked = false;
this.registerEventListeners();
}
@@ -85,6 +88,12 @@ const AICourseAssist = class {
}
});
document.addEventListener('keydown', e => {
if (this.isAIDrawerOpen() && e.key === 'Escape') {
this.closeAIDrawer();
}
});
// Close AI drawer if message drawer is shown.
subscribe(DrawerEvents.DRAWER_SHOWN, () => {
if (this.isAIDrawerOpen()) {
@@ -198,12 +207,27 @@ const AICourseAssist = class {
}
this.jumpToElement.setAttribute('tabindex', 0);
this.jumpToElement.focus();
// If the AI drawer is opened on a small screen, we need to trap the focus tab within the AI drawer.
if (isSmall()) {
FocusLock.trapFocus(this.aiDrawerElement);
this.aiDrawerElement.setAttribute('aria-modal', 'true');
this.aiDrawerElement.setAttribute('role', 'dialog');
this.isDrawerFocusLocked = true;
}
}
/**
* Close the AI drawer.
*/
closeAIDrawer() {
// Untrap focus if it was locked.
if (this.isDrawerFocusLocked) {
FocusLock.untrapFocus();
this.aiDrawerElement.removeAttribute('aria-modal');
this.aiDrawerElement.setAttribute('role', 'region');
}
this.aiDrawerElement.classList.remove('show');
this.aiDrawerElement.setAttribute('tabindex', '-1');
this.aiDrawerBodyElement.removeAttribute('aria-live');
@@ -22,6 +22,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['aidrawerlabel'] = 'AI-generated content drawer';
$string['aisummary'] = 'AI summary';
$string['courseassist:summarise_text'] = 'Summarise text';
$string['copy'] = 'Copy';
@@ -31,7 +31,7 @@
"content": "<p>Content to display</p>"
}
}}
<div class="ai-drawer" id="ai-drawer" tabindex="-1">
<div class="ai-drawer" id="ai-drawer" aria-label={{#quote}}{{#str}} aidrawerlabel, aiplacement_courseassist {{/str}}{{/quote}} tabindex="-1" role="region">
<div class="ai-drawer-header">
<button id="ai-drawer-close" class="btn ai-drawer-button" type="button" data-action="course-summarise">
{{#pix}} e/cancel, core {{/pix}}
+1 -1
View File
@@ -87,7 +87,7 @@ Please log in as before, using the link on the login page.
$string['emailpasswordchangeinfosubject'] = '{$a}: Change password information';
$string['info'] = 'External account';
$string['issuer'] = 'OAuth 2 service';
$string['issuernologin'] = 'This issuer can not be used to login';
$string['issuernologin'] = 'This issuer can not be used to log in.';
$string['key'] = 'Key';
$string['linkedlogins'] = 'Linked logins';
$string['linkedloginshelp'] = 'Help with linked logins';
+1 -1
View File
@@ -31,7 +31,7 @@ $string['auth_shib_contact_administrator'] = 'In case you are not associated wit
$string['auth_shibbolethdescription'] = 'Using this method users are created and authenticated using Shibboleth. For set-up details, see the <a href="{$a}">Shibboleth README</a>.';
$string['auth_shibboleth_errormsg'] = 'Please select the organization you are member of!';
$string['auth_shibboleth_login'] = 'Shibboleth login';
$string['auth_shibboleth_login_long'] = 'Login to Moodle via Shibboleth';
$string['auth_shibboleth_login_long'] = 'Log in via Shibboleth';
$string['auth_shibboleth_manual_login'] = 'Manual login';
$string['auth_shibboleth_select_member'] = 'I\'m a member of ...';
$string['auth_shibboleth_select_organization'] = 'For authentication via Shibboleth, please select your organisation from the drop-down menu:';
+1 -1
View File
@@ -31,6 +31,6 @@ class core_availability_renderer extends plugin_renderer_base {
*/
#[\core\attribute\deprecated(availability_info::class, since: '4.0', mdl: 'MDL-71691', final: true)]
public function render_core_availability_multiple_messages(): void {
\core\deprecation::emit_deprecation_if_present([self::class, __FUNCTION__]);
\core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
}
}
@@ -764,7 +764,7 @@ M.core_availability.List.prototype.clickAdd = function() {
dialogRef.dialog = new M.core.dialogue(config);
dialogRef.dialog.show();
cancel.on('click', function() {
dialogRef.dialog.destroy();
dialogRef.dialog.hide();
// Focus the button they clicked originally.
this.inner.one('> .availability-button').one('button').focus();
}, this);
@@ -801,7 +801,7 @@ M.core_availability.List.prototype.getAddHandler = function(type, dialogRef) {
M.core_availability.form.rootList.renumber();
this.updateHtml();
// Hide dialog.
dialogRef.dialog.destroy();
dialogRef.dialog.hide();
newItem.focusAfterAdd();
};
};
File diff suppressed because one or more lines are too long
@@ -764,7 +764,7 @@ M.core_availability.List.prototype.clickAdd = function() {
dialogRef.dialog = new M.core.dialogue(config);
dialogRef.dialog.show();
cancel.on('click', function() {
dialogRef.dialog.destroy();
dialogRef.dialog.hide();
// Focus the button they clicked originally.
this.inner.one('> .availability-button').one('button').focus();
}, this);
@@ -801,7 +801,7 @@ M.core_availability.List.prototype.getAddHandler = function(type, dialogRef) {
M.core_availability.form.rootList.renumber();
this.updateHtml();
// Hide dialog.
dialogRef.dialog.destroy();
dialogRef.dialog.hide();
newItem.focusAfterAdd();
};
};
+2 -2
View File
@@ -762,7 +762,7 @@ M.core_availability.List.prototype.clickAdd = function() {
dialogRef.dialog = new M.core.dialogue(config);
dialogRef.dialog.show();
cancel.on('click', function() {
dialogRef.dialog.destroy();
dialogRef.dialog.hide();
// Focus the button they clicked originally.
this.inner.one('> .availability-button').one('button').focus();
}, this);
@@ -799,7 +799,7 @@ M.core_availability.List.prototype.getAddHandler = function(type, dialogRef) {
M.core_availability.form.rootList.renumber();
this.updateHtml();
// Hide dialog.
dialogRef.dialog.destroy();
dialogRef.dialog.hide();
newItem.focusAfterAdd();
};
};
+2 -2
View File
@@ -111,7 +111,7 @@ abstract class base_controller extends backup implements loggable {
*/
#[\core\attribute\deprecated(since: '4.1', mdl: 'MDL-74548', final: true)]
public function set_copy(): void {
\core\deprecation::emit_deprecation_if_present([self::class, __FUNCTION__]);
\core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
}
/**
@@ -119,6 +119,6 @@ abstract class base_controller extends backup implements loggable {
*/
#[\core\attribute\deprecated('restore_controller::get_copy()', since: '4.1', mdl: 'MDL-74548', final: true)]
public function get_copy() {
\core\deprecation::emit_deprecation_if_present([self::class, __FUNCTION__]);
\core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
}
}
+113 -59
View File
@@ -60,6 +60,7 @@ if (!empty($issuedbadge->recipient->id)) {
$assertionid = $assertion->get_assertion_hash();
$assertionentityid = $assertiondata['id'];
$badgeadded = false;
$issuerexists = false;
if (badges_open_badges_backpack_api() == OPEN_BADGES_V2) {
$sitebackpack = badges_get_site_primary_backpack();
$api = new \core_badges\backpack_api($sitebackpack);
@@ -71,62 +72,75 @@ if (!empty($issuedbadge->recipient->id)) {
$issuer = $assertion->get_issuer();
if (!($issuerentityid = badges_external_get_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ISSUER, $issuer['email']))) {
$response = $api->put_issuer($issuer);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
if ($response) {
$issuerexists = true;
$issuerentityid = $response->id;
badges_external_create_mapping(
$sitebackpack->id,
OPEN_BADGES_V2_TYPE_ISSUER,
$issuer['email'],
$issuerentityid,
);
}
$issuerentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ISSUER, $issuer['email'],
$issuerentityid);
}
// Create badge.
$badge = $assertion->get_badge_class(false);
$badgeid = $assertion->get_badge_id();
if (!($badgeentityid = badges_external_get_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_BADGE, $badgeid))) {
$response = $api->put_badgeclass($issuerentityid, $badge);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
if ($issuerexists) {
// Create badge.
$badge = $assertion->get_badge_class(false);
$badgeid = $assertion->get_badge_id();
if (!($badgeentityid = badges_external_get_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_BADGE, $badgeid))) {
$response = $api->put_badgeclass($issuerentityid, $badge);
if ($response) {
$badgeentityid = $response->id;
badges_external_create_mapping(
$sitebackpack->id,
OPEN_BADGES_V2_TYPE_BADGE,
$badgeid,
$badgeentityid,
);
}
}
$badgeentityid = $response->id;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_BADGE, $badgeid,
$badgeentityid);
}
// Create assertion (Award the badge!).
$assertionentityid = badges_external_get_mapping(
$sitebackpack->id,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionid
);
if ($assertionentityid && strpos($sitebackpack->backpackapiurl, 'badgr')) {
$assertionentityid = badges_generate_badgr_open_url(
$sitebackpack,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionentityid
);
}
// Create an assertion for the recipient in the issuer's account.
if (!$assertionentityid) {
$response = $api->put_badgeclass_assertion($badgeentityid, $assertiondata);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
}
$assertionentityid = badges_generate_badgr_open_url($sitebackpack, OPEN_BADGES_V2_TYPE_ASSERTION, $response->id);
$badgeadded = true;
badges_external_create_mapping($sitebackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid,
$response->id);
} else {
// An assertion already exists. Make sure it's up to date.
$internalid = badges_external_get_mapping(
// Create assertion (Award the badge!).
$assertionentityid = badges_external_get_mapping(
$sitebackpack->id,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionid,
'externalid'
$assertionid
);
$response = $api->update_assertion($internalid, $assertiondata);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
if ($assertionentityid && strpos($sitebackpack->backpackapiurl, 'badgr')) {
$assertionentityid = badges_generate_badgr_open_url(
$sitebackpack,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionentityid
);
}
// Create an assertion for the recipient in the issuer's account.
if (!$assertionentityid) {
$response = $api->put_badgeclass_assertion($badgeentityid, $assertiondata);
if ($response) {
$assertionentityid = badges_generate_badgr_open_url(
$sitebackpack,
OPEN_BADGES_V2_TYPE_ASSERTION,
$response->id,
);
$badgeadded = true;
badges_external_create_mapping(
$sitebackpack->id,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionid,
$response->id,
);
}
} else {
// An assertion already exists. Make sure it's up to date.
$internalid = badges_external_get_mapping(
$sitebackpack->id,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionid,
'externalid'
);
$response = $api->update_assertion($internalid, $assertiondata);
}
}
}
@@ -136,21 +150,61 @@ if (!empty($issuedbadge->recipient->id)) {
// - If a user and site backpack have the same provider we can skip this as Badgr automatically maps recipients
// based on email address.
// - This is only needed when the backpacks are from different regions.
if ($assertionentityid && !badges_external_get_mapping($userbackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid)) {
if (
$assertionentityid
&& (!$issuerexists || !badges_external_get_mapping($userbackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid))
) {
$userapi = new \core_badges\backpack_api($userbackpack, $backpack);
$userapi->authenticate();
$response = $userapi->import_badge_assertion($assertionentityid);
if (!$response) {
throw new moodle_exception('invalidrequest', 'error');
if ($response) {
$assertionentityid = $response->id;
$badgeadded = true;
badges_external_create_mapping(
$userbackpack->id,
OPEN_BADGES_V2_TYPE_ASSERTION,
$assertionid,
$assertionentityid,
);
}
$assertionentityid = $response->id;
$badgeadded = true;
badges_external_create_mapping($userbackpack->id, OPEN_BADGES_V2_TYPE_ASSERTION, $assertionid,
$assertionentityid);
}
$response = $badgeadded ? ['success' => 'addedtobackpack'] : ['warning' => 'existsinbackpack'];
redirect(new moodle_url('/badges/mybadges.php', $response));
if ($badgeadded) {
$message = get_string('addedtobackpack', 'badges');
$messagetype = \core\output\notification::NOTIFY_SUCCESS;
} else {
if (isset($userapi) && !empty($userapi->get_errors())) {
// If the api used to import the badge to the backpack has errors, show them to inform the user.
if (array_filter($userapi->get_errors(), fn($element) => str_contains($element, "DUPLICATE_BADGE"))) {
// Duplicated badges are displayed as a warning.
$message = get_string('existsinbackpack', 'badges');
$messagetype = \core\output\notification::NOTIFY_WARNING;
} else {
// If the userapi has any other errors, we will use those to inform the user.
$message = get_string(
'error:cannotsendtobackpack',
'badges',
implode($userapi->get_errors()),
);
$messagetype = \core\output\notification::NOTIFY_ERROR;
}
} else if (isset($api) && !empty($api->get_errors())) {
// If the api used to create/update the issuer has errors, show them to inform the user.
$errors = $api->get_errors() ?? [get_string('invalidrequest', 'error')];
$message = get_string(
'error:cannotsendtobackpack',
'badges',
implode($errors),
);
$messagetype = \core\output\notification::NOTIFY_ERROR;
}
}
redirect(
url: new \core\url('/badges/mybadges.php'),
message: $message,
messagetype: $messagetype,
);
} else {
redirect(new moodle_url('/badges/mybadges.php'));
redirect(new \core\url('/badges/mybadges.php'));
}
+14
View File
@@ -512,6 +512,20 @@ class backpack_api {
return backpack_api_mapping::get_authentication_error();
}
/**
* List all errors occurred during the requests to the backpack.
*
* @return array The list of errors.
*/
public function get_errors(): array {
$errors = [];
foreach ($this->mappings as $mapping) {
$errors = array_merge($errors, $mapping->get_errors());
}
return $errors;
}
/**
* Get the list of badges in a collection.
*
+40
View File
@@ -81,6 +81,9 @@ class backpack_api_mapping {
/** @var int OpenBadges version. */
protected $backpackapiversion;
/** @var array Errors encountered during the request. */
protected $errors = [];
/**
* Create a mapping.
*
@@ -146,6 +149,26 @@ class backpack_api_mapping {
return self::$authenticationerror;
}
/**
* Get the errors encountered during the request.
*
* @return array The list of errors.
*/
public function get_errors() {
return $this->errors;
}
/**
* Add an error to the list of errors.
*
* @param string $error The error message.
* @return self This instance for method chaining.
*/
public function add_error(string $error): self {
$this->errors[] = $error;
return $this;
}
/**
* Does the action match this mapping?
*
@@ -342,6 +365,23 @@ class backpack_api_mapping {
$response = $curl->put($url, $post, $options);
}
$response = json_decode($response);
if ($response === null) {
$this->add_error(get_string('invalidrequest', 'error'));
return null;
}
if (isset($response->status) && isset($response->status->success) && $response->status->success != true) {
// If the response wasn't successful, store the errors and return null.
if (isset($response->validationErrors)) {
$error = implode(', ', $response->validationErrors);
} else if (isset($response->status->description)) {
$error = $response->status->description;
} else {
$error = get_string('invalidrequest', 'error');
}
$this->add_error($error);
return null;
}
if (isset($response->result)) {
$response = $response->result;
}
@@ -130,6 +130,38 @@ class badge extends base {
return html_writer::link($url, $row->name);
});
// Name with image and link.
$columns[] = (new column(
'namewithimagelink',
new lang_string('namewithimagelink', 'core_badges'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->add_join("LEFT JOIN {context} {$contextalias}
ON {$contextalias}.contextlevel = " . CONTEXT_COURSE . "
AND {$contextalias}.instanceid = {$badgealias}.courseid")
->add_fields(
"{$badgealias}.name, {$badgealias}.id, {$badgealias}.type, {$badgealias}.courseid"
)
->add_field($DB->sql_cast_to_char("{$badgealias}.imagecaption"), 'imagecaption')
->add_fields(context_helper::get_preload_record_columns_sql($contextalias))
->set_is_sortable(true)
->add_callback(static function ($value, stdClass $badge): string {
if ($badge->id === null) {
return '';
}
if ($badge->type == BADGE_TYPE_SITE) {
$context = context_system::instance();
} else {
context_helper::preload_from_record($badge);
$context = context_course::instance($badge->courseid);
}
$badgeimage = moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $badge->id, '/', 'f2');
$url = new moodle_url('/badges/overview.php', ['id' => $badge->id]);
return html_writer::img($badgeimage, $badge->imagecaption) . ' ' . html_writer::link($url, $badge->name);
});
// Description (note, this column contains plaintext so requires no post-processing).
$descriptionfieldsql = "{$badgealias}.description";
if ($DB->get_dbfamily() === 'oracle') {
@@ -94,8 +94,6 @@ class badges extends system_report {
$this->add_filters();
$this->add_actions();
// Set initial sorting by name.
$this->set_initial_sort_column('badge:namewithlink', SORT_ASC);
$this->set_default_no_results_notice(new lang_string('nomatchingbadges', 'core_badges'));
// Set if report can be downloaded.
@@ -129,8 +127,7 @@ class badges extends system_report {
*/
public function add_columns(string $badgeissuedalias): void {
$columns = [
'badge:image',
'badge:namewithlink',
'badge:namewithimagelink',
'badge:status',
'badge:criteria',
];
@@ -138,15 +135,12 @@ class badges extends system_report {
$canviewdraftbadges = $this->can_view_draft_badges();
if (!$canviewdraftbadges) {
// Remove status and recipients column.
unset($columns[2]);
unset($columns[1]);
}
$this->add_columns_from_entities($columns);
// Remove title from image column.
$this->get_column('badge:image')->set_title(null);
// Change title from namewithlink column.
$this->get_column('badge:namewithlink')->set_title(new lang_string('name'));
// Change title of the `namewithimagelink` column to 'Name'.
$this->get_column('badge:namewithimagelink')->set_title(new lang_string('name'));
// Recipients column.
if ($canviewdraftbadges) {
@@ -193,7 +187,7 @@ class badges extends system_report {
return $OUTPUT->action_icon($badgeurl, $icon, null, null, true);
});
$this->set_initial_sort_column('badge:namewithlink', SORT_ASC);
$this->set_initial_sort_column('badge:namewithimagelink', SORT_ASC);
}
/**
@@ -51,7 +51,7 @@ class course_badges extends system_report {
*/
#[\core\attribute\deprecated('course_badges::initialise', since: '4.5', mdl: 'MDL-82503')]
protected function initialise(): void {
\core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]);
\core\deprecation::emit_deprecation([$this, __FUNCTION__]);
global $USER;
// Our main entity, it contains all of the column definitions that we need.
$badgeentity = new badge();
@@ -103,7 +103,7 @@ class course_badges extends system_report {
*/
#[\core\attribute\deprecated('course_badges::can_view', since: '4.5', mdl: 'MDL-82503')]
protected function can_view(): bool {
\core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]);
\core\deprecation::emit_deprecation([$this, __FUNCTION__]);
return has_capability('moodle/badges:viewbadges', $this->get_context());
}
@@ -118,7 +118,7 @@ class course_badges extends system_report {
*/
#[\core\attribute\deprecated('course_badges::add_columns', since: '4.5', mdl: 'MDL-82503')]
protected function add_columns(): void {
\core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]);
\core\deprecation::emit_deprecation([$this, __FUNCTION__]);
$badgeissuedalias = $this->get_entity('badge_issued')->get_table_alias('badge_issued');
$this->add_columns_from_entities([
@@ -156,7 +156,7 @@ class course_badges extends system_report {
*/
#[\core\attribute\deprecated('course_badges::add_filters', since: '4.5', mdl: 'MDL-82503')]
protected function add_filters(): void {
\core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]);
\core\deprecation::emit_deprecation([$this, __FUNCTION__]);
$this->add_filters_from_entities([
'badge:name',
'badge_issued:issued',
+2
View File
@@ -118,6 +118,7 @@ Feature: Manage badges
| Admin User |
| User One |
@accessibility
Scenario: View list of badges with recipients
Given the following "users" exist:
| username | firstname | lastname |
@@ -139,6 +140,7 @@ Feature: Manage badges
| Badge #1 | Not available | 2 |
| Badge #2 | Available | 1 |
| Badge #3 | Available | 0 |
And the "Badges" "table" should meet accessibility standards with "best-practice" extra tests
@_file_upload
Scenario: Badge names are not unique anymore
@@ -125,6 +125,7 @@ final class badges_test extends core_reportbuilder_testcase {
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullname', 'sortenabled' => 1]);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:namewithlink']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:namewithimagelink']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:criteria']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:image']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:language']);
@@ -146,11 +147,12 @@ final class badges_test extends core_reportbuilder_testcase {
['id' => $badgeone->id]), ($badgeone->name));
// First badge, issued to user one.
[$badgename, $fullname, $namewithlink, $criteria, $image, $language, $version, $status, $expiry, $tag, $expires,
$visible, $coursename] = array_values($content[0]);
[$badgename, $fullname, $namewithlink, $namewithimagelink, $criteria, $image, $language, $version, $status, $expiry, $tag,
$expires, $visible, $coursename] = array_values($content[0]);
$this->assertEquals($badgeone->name, $badgename);
$this->assertEquals(fullname($user1), $fullname);
$this->assertEquals($expectedbadgeonelink, $namewithlink);
$this->assertEquals($image . ' ' . $expectedbadgeonelink, $namewithimagelink);
$this->assertStringContainsString('Awarded by: Manager', $criteria);
$this->assertStringContainsString('Image caption', $image);
$this->assertEquals('German', $language);
@@ -163,11 +165,12 @@ final class badges_test extends core_reportbuilder_testcase {
$this->assertEquals('PHPUnit test site', $coursename);
// First badge, issued to user two.
[$badgename, $fullname, $namewithlink, $criteria, $image, $language, $version, $status, $expiry, $tag, $expires,
$visible, $coursename] = array_values($content[1]);
[$badgename, $fullname, $namewithlink, $namewithimagelink, $criteria, $image, $language, $version, $status, $expiry, $tag,
$expires, $visible, $coursename] = array_values($content[1]);
$this->assertEquals($badgeone->name, $badgename);
$this->assertEquals(fullname($user2), $fullname);
$this->assertEquals($expectedbadgeonelink, $namewithlink);
$this->assertEquals($image . ' ' . $expectedbadgeonelink, $namewithimagelink);
$this->assertStringContainsString('Awarded by: Manager', $criteria);
$this->assertStringContainsString('Image caption', $image);
$this->assertEquals('German', $language);
@@ -183,11 +186,12 @@ final class badges_test extends core_reportbuilder_testcase {
['id' => $badgetwo->id]), ($badgetwo->name));
// Course badge, not issues to any users.
[$badgename, $fullname, $namewithlink, $criteria, $image, $language, $version, $status, $expiry, $tag, $expires,
$visible, $coursename] = array_values($content[2]);
[$badgename, $fullname, $namewithlink, $namewithimagelink, $criteria, $image, $language, $version, $status, $expiry, $tag,
$expires, $visible, $coursename] = array_values($content[2]);
$this->assertEquals($badgetwo->name, $badgename);
$this->assertEmpty($fullname);
$this->assertEquals($expectedbadgetwolink, $namewithlink);
$this->assertEquals($image . ' ' . $expectedbadgetwolink, $namewithimagelink);
$this->assertEquals('Criteria for this badge have not been set up yet.', $criteria);
$this->assertStringContainsString('Image caption', $image);
$this->assertEquals('English', $language);
+9 -4
View File
@@ -32,7 +32,9 @@
* @todo MDL-36050 improve capability check on stick blocks, so we can check user capability before sending images.
*/
function block_html_pluginfile($course, $birecord_or_cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
global $DB, $CFG, $USER;
global $CFG;
require_once("{$CFG->dirroot}/user/lib.php");
if ($context->contextlevel != CONTEXT_BLOCK) {
send_file_not_found();
@@ -51,9 +53,12 @@ function block_html_pluginfile($course, $birecord_or_cm, $context, $filearea, $a
if (!core_course_category::get($parentcontext->instanceid, IGNORE_MISSING)) {
send_file_not_found();
}
} else if ($parentcontext->contextlevel === CONTEXT_USER && $parentcontext->instanceid != $USER->id) {
// The block is in the context of a user, it is only visible to the user who it belongs to.
send_file_not_found();
} else if ($parentcontext->contextlevel === CONTEXT_USER) {
$user = core_user::get_user($parentcontext->instanceid, '*', MUST_EXIST);
$extracaps = block_method_result('html', 'get_extra_capabilities');
if (!user_can_view_profile($user, null, $parentcontext) || !has_any_capability($extracaps, $context)) {
send_file_not_found();
}
}
// At this point there is no way to check SYSTEM context, so ignoring it.
}
@@ -297,6 +297,7 @@ final class provider_test extends \core_privacy\tests\provider_testcase {
$user = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
$this->setUser($user);
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'editingteacher');
$block = $this->create_course_block($course, $title, $content, $format);
$context = \context_block::instance($block->instance->id);
@@ -324,6 +325,7 @@ final class provider_test extends \core_privacy\tests\provider_testcase {
$user = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
$this->setUser($user);
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'editingteacher');
$block = $this->create_course_block($course, $title, $content, $format);
$context = \context_block::instance($block->instance->id);
@@ -369,6 +371,7 @@ final class provider_test extends \core_privacy\tests\provider_testcase {
$this->setUser($user2);
$course = $this->getDataGenerator()->create_course();
$this->getDataGenerator()->enrol_user($user2->id, $course->id, 'editingteacher');
$courseblock = $this->create_course_block($course, $title, $content, $blockformat);
$coursecontext = \context_block::instance($courseblock->instance->id);
@@ -24,8 +24,6 @@
}}
<div id="block-myoverview-{{uniqid}}" class="block-myoverview block-cards" data-region="myoverview" role="navigation">
<hr class="mt-0"/>
<div role="search" data-region="filter" class="d-flex align-items-center my-2" aria-label="{{#str}} aria:controls, block_myoverview {{/str}}">
<div class="row g-0">
{{> block_myoverview/nav-grouping-selector }}
+6
View File
@@ -44,6 +44,7 @@ final class externallib_test extends externallib_advanced_testcase {
global $DB, $FULLME;
$this->resetAfterTest(true);
$this->setAdminUser();
$user = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
@@ -77,6 +78,7 @@ final class externallib_test extends externallib_advanced_testcase {
global $DB, $FULLME;
$this->resetAfterTest(true);
$this->setAdminUser();
$user = $this->getDataGenerator()->create_user();
@@ -141,6 +143,7 @@ final class externallib_test extends externallib_advanced_testcase {
global $DB, $FULLME;
$this->resetAfterTest(true);
$this->setAdminUser();
$user = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
@@ -237,6 +240,7 @@ final class externallib_test extends externallib_advanced_testcase {
global $DB, $CFG;
$this->resetAfterTest(true);
$this->setAdminUser();
// Enable MathJax filter in content and headings.
$this->configure_filters([
@@ -362,6 +366,7 @@ final class externallib_test extends externallib_advanced_testcase {
public function test_get_dashboard_blocks_default_dashboard_including_sticky_block(): void {
global $PAGE, $DB;
$this->resetAfterTest(true);
$this->setAdminUser();
$user = $this->getDataGenerator()->create_user();
$PAGE->set_url('/my/index.php'); // Need this because some internal API calls require the $PAGE url to be set.
@@ -410,6 +415,7 @@ final class externallib_test extends externallib_advanced_testcase {
public function test_get_dashboard_blocks_custom_user_dashboard(): void {
global $PAGE, $DB;
$this->resetAfterTest(true);
$this->setAdminUser();
$user = $this->getDataGenerator()->create_user();
$PAGE->set_url('/my/index.php'); // Need this because some internal API calls require the $PAGE url to be set.
+1 -1
View File
@@ -74,7 +74,7 @@ class footer_options_exporter extends exporter {
*/
protected function get_manage_subscriptions_link(): ?string {
if (calendar_user_can_add_event($this->calendar->course)) {
$managesubscriptionurl = new moodle_url('/calendar/managesubscriptions.php');
$managesubscriptionurl = new moodle_url('/calendar/managesubscriptions.php', ['course' => $this->calendar->courseid]);
return $managesubscriptionurl->out(true);
}
return null;
@@ -213,7 +213,8 @@ class create extends \moodleform {
$group[] = $mform->createElement('radio', 'duration', null, get_string('durationuntil', 'calendar'), 1);
$group[] = $mform->createElement('date_time_selector', 'timedurationuntil', '');
$group[] = $mform->createElement('radio', 'duration', null, get_string('durationminutes', 'calendar'), 2);
$group[] = $mform->createElement('text', 'timedurationminutes', get_string('durationminutes', 'calendar'));
$group[] = $mform->createElement('text', 'timedurationminutes',
get_string('durationminutes', 'calendar'), 'maxlength="7" size="7"');
$mform->addGroup($group, 'durationgroup', get_string('eventduration', 'calendar'), '<br />', false);
$mform->setAdvanced('durationgroup');
@@ -227,6 +228,9 @@ class create extends \moodleform {
$mform->setType('timedurationminutes', PARAM_INT);
$mform->disabledIf('timedurationminutes', 'duration', 'noteq', 2);
$mform->addGroupRule('durationgroup', [
'timedurationminutes' => [[get_string('maximumchars', '', 7), 'maxlength', 7, 'server']],
]);
$mform->setDefault('duration', 0);
}
@@ -238,11 +242,12 @@ class create extends \moodleform {
*/
protected function add_event_repeat_elements($mform) {
$mform->addElement('checkbox', 'repeat', get_string('repeatevent', 'calendar'), null);
$mform->addElement('text', 'repeats', get_string('repeatweeksl', 'calendar'), 'maxlength="10" size="10"');
$mform->addElement('text', 'repeats', get_string('repeatweeksl', 'calendar'), 'maxlength="3" size="3"');
$mform->setType('repeats', PARAM_INT);
$mform->setDefault('repeats', 1);
$mform->disabledIf('repeats', 'repeat', 'notchecked');
$mform->setAdvanced('repeat');
$mform->setAdvanced('repeats');
$mform->addRule('repeats', get_string('maximumchars', '', 3), 'maxlength', 3, 'server');
}
}
+7 -2
View File
@@ -121,7 +121,8 @@ class event_form extends moodleform {
$group[] =& $mform->createElement('radio', 'duration', null, get_string('durationuntil', 'calendar'), 1);
$group[] =& $mform->createElement('date_time_selector', 'timedurationuntil', '');
$group[] =& $mform->createElement('radio', 'duration', null, get_string('durationminutes', 'calendar'), 2);
$group[] =& $mform->createElement('text', 'timedurationminutes', get_string('durationminutes', 'calendar'));
$group[] =& $mform->createElement('text', 'timedurationminutes',
get_string('durationminutes', 'calendar'), 'maxlength="7" size="7"');
$mform->addGroup($group, 'durationgroup', '', '<br />', false);
@@ -134,6 +135,9 @@ class event_form extends moodleform {
$mform->setType('timedurationminutes', PARAM_INT);
$mform->disabledIf('timedurationminutes','duration','noteq', 2);
$mform->addGroupRule('durationgroup', [
'timedurationminutes' => [[get_string('maximumchars', '', 7), 'maxlength', 7, 'server']],
]);
$mform->setDefault('duration', ($hasduration)?1:0);
@@ -141,10 +145,11 @@ class event_form extends moodleform {
$mform->addElement('header', 'repeatevents', get_string('repeatedevents', 'calendar'));
$mform->addElement('checkbox', 'repeat', get_string('repeatevent', 'calendar'), null);
$mform->addElement('text', 'repeats', get_string('repeatweeksl', 'calendar'), 'maxlength="10" size="10"');
$mform->addElement('text', 'repeats', get_string('repeatweeksl', 'calendar'), 'maxlength="3" size="3"');
$mform->setType('repeats', PARAM_INT);
$mform->setDefault('repeats', 1);
$mform->disabledIf('repeats','repeat','notchecked');
$mform->addRule('repeats', get_string('maximumchars', '', 3), 'maxlength', 3, 'server');
} else if ($repeatedevents) {
+2 -2
View File
@@ -3642,7 +3642,7 @@ function calendar_output_fragment_event_form($args) {
$groupcoursedata = groups_get_course_data($courseid);
$formoptions['groups'] = [];
foreach ($groupcoursedata->groups as $groupid => $groupdata) {
$formoptions['groups'][$groupid] = $groupdata->name;
$formoptions['groups'][$groupid] = format_string($groupdata->name, false, ['context' => $context]);
}
}
@@ -3686,7 +3686,7 @@ function calendar_output_fragment_event_form($args) {
$groupcoursedata = groups_get_course_data($event->courseid);
$formoptions['groups'] = [];
foreach ($groupcoursedata->groups as $groupid => $groupdata) {
$formoptions['groups'][$groupid] = $groupdata->name;
$formoptions['groups'][$groupid] = format_string($groupdata->name, false, ['context' => $context]);
}
}
+14 -5
View File
@@ -27,8 +27,8 @@ Feature: Perform basic calendar functionality
| teacher1 | C1 | teacher |
| admin | C1 | editingteacher |
And the following "groups" exist:
| name | course | idnumber |
| Group 1 | C1 | G1 |
| name | course | idnumber |
| <span lang="en" class="multilang">Group</span><span lang="es" class="multilang">Grupo</span> 1 | C1 | G1 |
And the following "group members" exist:
| user | group |
| student1 | G1 |
@@ -75,7 +75,9 @@ Feature: Perform basic calendar functionality
@javascript
Scenario: Create a group event
Given I log in as "teacher1"
Given the "multilang" filter is "on"
And the "multilang" filter applies to "content and headings"
And I log in as "teacher1"
And I follow "Full calendar"
And I set the field "course" to "Course 1"
And I create a calendar event:
@@ -322,18 +324,23 @@ Feature: Perform basic calendar functionality
@javascript
Scenario: Changing the event type should clear previous data
Given I am on the "Course 1" course page logged in as admin
Given the "multilang" filter is "on"
And the "multilang" filter applies to "content and headings"
And I am on the "Course 1" course page logged in as admin
And I follow "Course calendar"
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 should see "Group 1"
And I should not see "GroupGrupo 1"
And I press "Save"
And I am on "Course 1" course homepage
And I follow "Course calendar"
And I click on "Group 1 event" "link"
And I should see "Group event"
And I should see "Group 1"
And I should not see "GroupGrupo 1"
When I click on "Edit" "button" in the "Group 1 event" "dialogue"
And I set the following fields to these values:
| Event title | My own user event |
@@ -369,7 +376,9 @@ Feature: Perform basic calendar functionality
@javascript
Scenario: Changing the event type from user to anything else should work
Given I log in as "teacher1"
Given the "multilang" filter is "on"
And the "multilang" filter applies to "content and headings"
And I log in as "teacher1"
# We need this so we can see the groups.
And the following "course enrolments" exist:
| user | course | role |
+4 -2
View File
@@ -144,8 +144,10 @@ $headingstr = get_string('calendar', 'core_calendar');
// If the user is on the course page,
// then make the course name linkable to ease the user's navigation to the course page.
if ($iscoursecalendar) {
$url = new \moodle_url('/course/view.php', ['id' => $courseid]);
$linkcourse = html_writer::link($url, $course->shortname);
$linkcourse = html_writer::link(
course_get_url($course),
format_string($course->shortname, options: ['context' => $PAGE->context]),
);
$headingstr = "{$headingstr}: {$linkcourse}";
}
$PAGE->set_heading($headingstr, false);
+3 -3
View File
@@ -71,7 +71,7 @@ class core_course_management_renderer extends plugin_renderer_base {
final: true,
)]
public function management_heading() {
\core\deprecation::emit_deprecation_if_present([self::class, __FUNCTION__]);
\core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
}
/**
@@ -187,7 +187,7 @@ class core_course_management_renderer extends plugin_renderer_base {
$a->parentcategory = $parent->get_formatted_name();
$textlabel = get_string('categorysubcategoryof', 'moodle', $a);
}
$courseicon = $this->output->pix_icon('i/course', get_string('courses'));
$courseicon = $this->output->pix_icon('i/course', get_string('courses'), 'core', ['class' => 'ps-1']);
$bcatinput = array(
'id' => 'categorylistitem' . $category->id,
'type' => 'checkbox',
@@ -1286,7 +1286,7 @@ class core_course_management_renderer extends plugin_renderer_base {
final: true,
)]
public function course_search_form() {
\core\deprecation::emit_deprecation_if_present([self::class, __FUNCTION__]);
\core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
}
/**
@@ -71,7 +71,7 @@ class completion {
*/
#[\core\attribute\deprecated(null, mdl: 'MDL-82467', since: '4.5')]
public static function get_days(?int $value, stdClass $row): ?int {
\core\deprecation::emit_deprecation_if_present([self::class, __FUNCTION__]);
\core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
// Do not show anything if there is no userid.
if (!$row->userid) {
@@ -10,6 +10,6 @@ define("core_courseformat/local/courseeditor/dndsectionitem",["exports","core/re
* @copyright 2021 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class _default extends _reactive.BaseComponent{configDragDrop(sectionid,state,fullregion){this.id=sectionid,void 0===this.section&&(this.section=state.section.get(this.id)),void 0===this.course&&(this.course=state.course),this.section.number>0&&null===this.section.component&&(this.getDraggableData=this._getDraggableData),this.fullregion=fullregion,this.reactive.isEditing&&this.reactive.supportComponents&&(this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.dragdrop&&this.dragdrop.unregister()}setDraggable(value){var _this$dragdrop;this.getDraggableData&&(null===(_this$dragdrop=this.dragdrop)||void 0===_this$dragdrop||_this$dragdrop.setDraggable(value))}dragStart(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!1)}_getDraggableData(){return this.reactive.getExporter().sectionDraggableData(this.reactive.state,this.id)}validateDropData(dropdata){if("cm"===(null==dropdata?void 0:dropdata.type)){var _this$section,_this$section2;if(null!==(_this$section=this.section)&&void 0!==_this$section&&_this$section.component&&!0===(null==dropdata?void 0:dropdata.hasdelegatedsection))return!1;const firstcmid=null===(_this$section2=this.section)||void 0===_this$section2?void 0:_this$section2.cmlist[0];return dropdata.id!==firstcmid}return!1}showDropZone(){this.element.classList.add(this.classes.DROPZONE)}hideDropZone(){this.element.classList.remove(this.classes.DROPZONE)}drop(dropdata,event){if("cm"==dropdata.type){var _this$section3;const mutation=event.altKey?"cmDuplicate":"cmMove";this.reactive.dispatch(mutation,[dropdata.id],this.id,null===(_this$section3=this.section)||void 0===_this$section3?void 0:_this$section3.cmlist[0])}}}return _exports.default=_default,_exports.default}));
class _default extends _reactive.BaseComponent{configDragDrop(sectionid,state,fullregion){let isDndAllowed=!(arguments.length>3&&void 0!==arguments[3])||arguments[3];this.id=sectionid,void 0===this.section&&(this.section=state.section.get(this.id)),void 0===this.course&&(this.course=state.course),this.section.number>0&&null===this.section.component&&(this.getDraggableData=this._getDraggableData),this.fullregion=fullregion,this.reactive.isEditing&&this.reactive.supportComponents&&isDndAllowed&&(this.dragdrop=new _reactive.DragDrop(this),this.classes=this.dragdrop.getClasses())}destroy(){void 0!==this.dragdrop&&this.dragdrop.unregister()}setDraggable(value){var _this$dragdrop;this.getDraggableData&&(null===(_this$dragdrop=this.dragdrop)||void 0===_this$dragdrop||_this$dragdrop.setDraggable(value))}dragStart(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!0)}dragEnd(dropdata){this.reactive.dispatch("sectionDrag",[dropdata.id],!1)}_getDraggableData(){return this.reactive.getExporter().sectionDraggableData(this.reactive.state,this.id)}validateDropData(dropdata){if("cm"===(null==dropdata?void 0:dropdata.type)){var _this$section,_this$section2;if(null!==(_this$section=this.section)&&void 0!==_this$section&&_this$section.component&&!0===(null==dropdata?void 0:dropdata.hasdelegatedsection))return!1;const firstcmid=null===(_this$section2=this.section)||void 0===_this$section2?void 0:_this$section2.cmlist[0];return dropdata.id!==firstcmid}return!1}showDropZone(){this.element.classList.add(this.classes.DROPZONE)}hideDropZone(){this.element.classList.remove(this.classes.DROPZONE)}drop(dropdata,event){if("cm"==dropdata.type){var _this$section3;const mutation=event.altKey?"cmDuplicate":"cmMove";this.reactive.dispatch(mutation,[dropdata.id],this.id,null===(_this$section3=this.section)||void 0===_this$section3?void 0:_this$section3.cmlist[0])}}}return _exports.default=_default,_exports.default}));
//# sourceMappingURL=dndsectionitem.min.js.map
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -8,6 +8,6 @@ define("core_courseformat/local/courseindex/cm",["exports","core_courseformat/lo
* @class core_courseformat/local/courseindex/cm
* @copyright 2021 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_dndcmitem=_interopRequireDefault(_dndcmitem),_templates=_interopRequireDefault(_templates),_prefetch=_interopRequireDefault(_prefetch),_config=_interopRequireDefault(_config),_pending=_interopRequireDefault(_pending);_prefetch.default.prefetchTemplate("core_courseformat/local/courseindex/cmcompletion");class Component extends _dndcmitem.default{create(){this.name="courseindex_cm",this.selectors={CM_NAME:"[data-for='cm_name']",CM_COMPLETION:"[data-for='cm_completion']"},this.classes={CMHIDDEN:"dimmed",LOCKED:"editinprogress",RESTRICTIONS:"restrictions",PAGEITEM:"pageitem",INDENTED:"indented"},this.id=this.element.dataset.id}static init(target,selectors){return new this({element:document.getElementById(target),selectors:selectors})}stateReady(state){this.configDragDrop(this.id);const cm=state.cm.get(this.id),course=state.course;this._refreshCompletion({state:state,element:cm});const anchor=new URL(window.location.href).hash.replace("#","");if((window.location.href==cm.url||window.location.href.includes(course.baseurl)&&anchor==cm.anchor)&&this.element.scrollIntoView({block:"center"}),_config.default.contextid!=_config.default.courseContextId&&_config.default.contextInstanceId==this.id&&(this.reactive.dispatch("setPageItem","cm",this.id,!0),this.element.scrollIntoView({block:"center"})),!cm.uservisible||!cm.url){const element=this.getElement(this.selectors.CM_NAME);this.addEventListener(element,"click",this._activityAnchor),document.getElementById(cm.anchor)||element.setAttribute("href",this._getActivitySectionURL(cm))}}getWatchers(){return[{watch:"cm[".concat(this.id,"]:deleted"),handler:this.remove},{watch:"cm[".concat(this.id,"]:updated"),handler:this._refreshCm},{watch:"cm[".concat(this.id,"].completionstate:updated"),handler:this._refreshCompletion},{watch:"course.pageItem:updated",handler:this._refreshPageItem}]}_refreshCm(_ref){var _element$dragging,_element$locked,_element$hascmrestric;let{element:element}=_ref;this.element.classList.toggle(this.classes.CMHIDDEN,!element.visible),this.getElement(this.selectors.CM_NAME).innerHTML=element.name,this.element.classList.toggle(this.classes.DRAGGING,null!==(_element$dragging=element.dragging)&&void 0!==_element$dragging&&_element$dragging),this.element.classList.toggle(this.classes.LOCKED,null!==(_element$locked=element.locked)&&void 0!==_element$locked&&_element$locked),this.element.classList.toggle(this.classes.RESTRICTIONS,null!==(_element$hascmrestric=element.hascmrestrictions)&&void 0!==_element$hascmrestric&&_element$hascmrestric),this.element.classList.toggle(this.classes.INDENTED,element.indent),this.locked=element.locked}_refreshPageItem(_ref2){let{element:element}=_ref2;if(!element.pageItem)return;const isPageId="cm"==element.pageItem.type&&element.pageItem.id==this.id;this.element.classList.toggle(this.classes.PAGEITEM,isPageId),isPageId&&!this.reactive.isEditing&&this.element.scrollIntoView({block:"nearest"})}async _refreshCompletion(_ref3){let{state:state,element:element}=_ref3;if(this.reactive.isEditing||!element.istrackeduser)return;const completionElement=this.getElement(this.selectors.CM_COMPLETION);if(!completionElement||completionElement.dataset.value==element.completionstate)return;const data=this.reactive.getExporter().cmCompletion(state,element),{html:html,js:js}=await _templates.default.renderForPromise("core_courseformat/local/courseindex/cmcompletion",data);_templates.default.replaceNode(completionElement,html,js)}_activityAnchor(event){const cm=this.reactive.get("cm",this.id);if(document.getElementById(cm.anchor)){this.reactive.dispatch("sectionContentCollapsed",[cm.sectionid],!1);const pendingAnchor=new _pending.default("courseformat/activity:openAnchor");setTimeout((()=>{this.reactive.dispatch("setPageItem","cm",cm.id),pendingAnchor.resolve()}),50)}else event.preventDefault(),window.location=this._getActivitySectionURL(cm)}_getActivitySectionURL(cm){let section=this.reactive.get("section",cm.sectionid);if(section.component&&section.parentsectionid&&(section=this.reactive.get("section",section.parentsectionid)),!section)return"#";const sectionurl=section.sectionurl.split("#")[0];return"".concat(sectionurl,"#").concat(cm.anchor)}}return _exports.default=Component,_exports.default}));
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_dndcmitem=_interopRequireDefault(_dndcmitem),_templates=_interopRequireDefault(_templates),_prefetch=_interopRequireDefault(_prefetch),_config=_interopRequireDefault(_config),_pending=_interopRequireDefault(_pending);_prefetch.default.prefetchTemplate("core_courseformat/local/courseindex/cmcompletion");class Component extends _dndcmitem.default{create(){this.name="courseindex_cm",this.selectors={CM_NAME:"[data-for='cm_name']",CM_COMPLETION:"[data-for='cm_completion']",DND_ALLOWED:"[data-courseindexdndallowed='true']"},this.classes={CMHIDDEN:"dimmed",LOCKED:"editinprogress",RESTRICTIONS:"restrictions",PAGEITEM:"pageitem",INDENTED:"indented"},this.id=this.element.dataset.id}static init(target,selectors){return new this({element:document.getElementById(target),selectors:selectors})}stateReady(state){document.querySelector(this.selectors.DND_ALLOWED)&&this.configDragDrop(this.id);const cm=state.cm.get(this.id),course=state.course;this._refreshCompletion({state:state,element:cm});const anchor=new URL(window.location.href).hash.replace("#","");if((window.location.href==cm.url||window.location.href.includes(course.baseurl)&&anchor==cm.anchor)&&this.element.scrollIntoView({block:"center"}),_config.default.contextid!=_config.default.courseContextId&&_config.default.contextInstanceId==this.id&&(this.reactive.dispatch("setPageItem","cm",this.id,!0),this.element.scrollIntoView({block:"center"})),!cm.uservisible||!cm.url){const element=this.getElement(this.selectors.CM_NAME);this.addEventListener(element,"click",this._activityAnchor),document.getElementById(cm.anchor)||element.setAttribute("href",this._getActivitySectionURL(cm))}}getWatchers(){return[{watch:"cm[".concat(this.id,"]:deleted"),handler:this.remove},{watch:"cm[".concat(this.id,"]:updated"),handler:this._refreshCm},{watch:"cm[".concat(this.id,"].completionstate:updated"),handler:this._refreshCompletion},{watch:"course.pageItem:updated",handler:this._refreshPageItem}]}_refreshCm(_ref){var _element$dragging,_element$locked,_element$hascmrestric;let{element:element}=_ref;this.element.classList.toggle(this.classes.CMHIDDEN,!element.visible),this.getElement(this.selectors.CM_NAME).innerHTML=element.name,this.element.classList.toggle(this.classes.DRAGGING,null!==(_element$dragging=element.dragging)&&void 0!==_element$dragging&&_element$dragging),this.element.classList.toggle(this.classes.LOCKED,null!==(_element$locked=element.locked)&&void 0!==_element$locked&&_element$locked),this.element.classList.toggle(this.classes.RESTRICTIONS,null!==(_element$hascmrestric=element.hascmrestrictions)&&void 0!==_element$hascmrestric&&_element$hascmrestric),this.element.classList.toggle(this.classes.INDENTED,element.indent),this.locked=element.locked}_refreshPageItem(_ref2){let{element:element}=_ref2;if(!element.pageItem)return;const isPageId="cm"==element.pageItem.type&&element.pageItem.id==this.id;this.element.classList.toggle(this.classes.PAGEITEM,isPageId),isPageId&&!this.reactive.isEditing&&this.element.scrollIntoView({block:"nearest"})}async _refreshCompletion(_ref3){let{state:state,element:element}=_ref3;if(this.reactive.isEditing||!element.istrackeduser)return;const completionElement=this.getElement(this.selectors.CM_COMPLETION);if(!completionElement||completionElement.dataset.value==element.completionstate)return;const data=this.reactive.getExporter().cmCompletion(state,element),{html:html,js:js}=await _templates.default.renderForPromise("core_courseformat/local/courseindex/cmcompletion",data);_templates.default.replaceNode(completionElement,html,js)}_activityAnchor(event){const cm=this.reactive.get("cm",this.id);if(document.getElementById(cm.anchor)){this.reactive.dispatch("sectionContentCollapsed",[cm.sectionid],!1);const pendingAnchor=new _pending.default("courseformat/activity:openAnchor");setTimeout((()=>{this.reactive.dispatch("setPageItem","cm",cm.id),pendingAnchor.resolve()}),50)}else event.preventDefault(),window.location=this._getActivitySectionURL(cm)}_getActivitySectionURL(cm){let section=this.reactive.get("section",cm.sectionid);if(section.component&&section.parentsectionid&&(section=this.reactive.get("section",section.parentsectionid)),!section)return"#";const sectionurl=section.sectionurl.split("#")[0];return"".concat(sectionurl,"#").concat(cm.anchor)}}return _exports.default=Component,_exports.default}));
//# sourceMappingURL=cm.min.js.map
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -8,6 +8,6 @@ define("core_courseformat/local/courseindex/section",["exports","core_courseform
* @class core_courseformat/local/courseindex/section
* @copyright 2021 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_sectiontitle=_interopRequireDefault(_sectiontitle),_dndsection=_interopRequireDefault(_dndsection);class Component extends _dndsection.default{create(){this.name="courseindex_section",this.selectors={SECTION:"[data-for='section']",SECTION_ITEM:"[data-for='section_item']",SECTION_TITLE:"[data-for='section_title']",CM_LAST:'[data-for="cm"]:last-child'},this.classes={SECTIONHIDDEN:"dimmed",SECTIONCURRENT:"current",LOCKED:"editinprogress",RESTRICTIONS:"restrictions",PAGEITEM:"pageitem",OVERLAYBORDERS:"overlay-preview-borders"},this.id=this.element.dataset.id,this.isPageItem=!1}static init(target,selectors){return new this({element:document.getElementById(target),selectors:selectors})}stateReady(state){this.configState(state);const sectionItem=this.getElement(this.selectors.SECTION_ITEM);if(this.reactive.isEditing&&this.reactive.supportComponents){const titleitem=new _sectiontitle.default({...this,element:sectionItem,fullregion:this.element});this.configDragDrop(titleitem)}const section=state.section.get(this.id);window.location.href==section.sectionurl.replace(/&amp;/g,"&")&&(this.reactive.dispatch("setPageItem","section",this.id),sectionItem.scrollIntoView())}getWatchers(){return[{watch:"section[".concat(this.id,"]:deleted"),handler:this.remove},{watch:"section[".concat(this.id,"]:updated"),handler:this._refreshSection},{watch:"course.pageItem:updated",handler:this._refreshPageItem}]}getLastCm(){return this.getElement(this.selectors.CM_LAST)}_refreshSection(_ref){var _element$hasrestricti,_element$dragging,_element$locked;let{element:element}=_ref;const sectionItem=this.getElement(this.selectors.SECTION_ITEM);sectionItem.classList.toggle(this.classes.SECTIONHIDDEN,!element.visible),sectionItem.classList.toggle(this.classes.RESTRICTIONS,null!==(_element$hasrestricti=element.hasrestrictions)&&void 0!==_element$hasrestricti&&_element$hasrestricti),this.element.classList.toggle(this.classes.SECTIONCURRENT,element.current),this.element.classList.toggle(this.classes.DRAGGING,null!==(_element$dragging=element.dragging)&&void 0!==_element$dragging&&_element$dragging),this.element.classList.toggle(this.classes.LOCKED,null!==(_element$locked=element.locked)&&void 0!==_element$locked&&_element$locked),this.locked=element.locked,this.getElement(this.selectors.SECTION_TITLE).innerHTML=element.title}_refreshPageItem(_ref2){var _element$pageItem,_this$pageItem;let{element:element,state:state}=_ref2;if(!element.pageItem)return;const containsPageItem=this._isPageItemInThisSection(element.pageItem);if(!containsPageItem||this._isParentSectionIndexCollapsed(state))return this.pageItem=!1,void this.getElement(this.selectors.SECTION_ITEM).classList.remove(this.classes.PAGEITEM);!state.section.get(this.id).indexcollapsed||null!==(_element$pageItem=element.pageItem)&&void 0!==_element$pageItem&&_element$pageItem.isStatic?this.pageItem="section"==element.pageItem.type&&element.pageItem.id==this.id:this.pageItem=containsPageItem;this.getElement(this.selectors.SECTION_ITEM).classList.toggle(this.classes.PAGEITEM,null!==(_this$pageItem=this.pageItem)&&void 0!==_this$pageItem&&_this$pageItem),this.pageItem&&!this.reactive.isEditing&&this.element.scrollIntoView({block:"nearest"})}_isPageItemInThisSection(pageItem){if(pageItem.sectionId==this.id)return!0;return!!this.element.querySelector("".concat(this.selectors.SECTION,'[data-id="').concat(pageItem.sectionId,'"]'))}_isParentSectionIndexCollapsed(state){const parentElement=this.element.parentElement.closest(this.selectors.SECTION);if(!parentElement||!parentElement.dataset.id)return null;return!!state.section.get(parentElement.dataset.id).indexcollapsed}async addOverlay(){this.element.classList.add(this.classes.OVERLAYBORDERS)}removeOverlay(){this.element.classList.remove(this.classes.OVERLAYBORDERS)}}return _exports.default=Component,_exports.default}));
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_sectiontitle=_interopRequireDefault(_sectiontitle),_dndsection=_interopRequireDefault(_dndsection);class Component extends _dndsection.default{create(){this.name="courseindex_section",this.selectors={SECTION:"[data-for='section']",SECTION_ITEM:"[data-for='section_item']",SECTION_TITLE:"[data-for='section_title']",CM_LAST:'[data-for="cm"]:last-child',DND_ALLOWED:"[data-courseindexdndallowed='true']"},this.classes={SECTIONHIDDEN:"dimmed",SECTIONCURRENT:"current",LOCKED:"editinprogress",RESTRICTIONS:"restrictions",PAGEITEM:"pageitem",OVERLAYBORDERS:"overlay-preview-borders"},this.id=this.element.dataset.id,this.isPageItem=!1}static init(target,selectors){return new this({element:document.getElementById(target),selectors:selectors})}stateReady(state){this.configState(state);const sectionItem=this.getElement(this.selectors.SECTION_ITEM);if(this.reactive.isEditing&&this.reactive.supportComponents&&document.querySelector(this.selectors.DND_ALLOWED)){const titleitem=new _sectiontitle.default({...this,element:sectionItem,fullregion:this.element});this.configDragDrop(titleitem)}const section=state.section.get(this.id);window.location.href==section.sectionurl.replace(/&amp;/g,"&")&&(this.reactive.dispatch("setPageItem","section",this.id),sectionItem.scrollIntoView())}getWatchers(){return[{watch:"section[".concat(this.id,"]:deleted"),handler:this.remove},{watch:"section[".concat(this.id,"]:updated"),handler:this._refreshSection},{watch:"course.pageItem:updated",handler:this._refreshPageItem}]}getLastCm(){return this.getElement(this.selectors.CM_LAST)}_refreshSection(_ref){var _element$hasrestricti,_element$dragging,_element$locked;let{element:element}=_ref;const sectionItem=this.getElement(this.selectors.SECTION_ITEM);sectionItem.classList.toggle(this.classes.SECTIONHIDDEN,!element.visible),sectionItem.classList.toggle(this.classes.RESTRICTIONS,null!==(_element$hasrestricti=element.hasrestrictions)&&void 0!==_element$hasrestricti&&_element$hasrestricti),this.element.classList.toggle(this.classes.SECTIONCURRENT,element.current),this.element.classList.toggle(this.classes.DRAGGING,null!==(_element$dragging=element.dragging)&&void 0!==_element$dragging&&_element$dragging),this.element.classList.toggle(this.classes.LOCKED,null!==(_element$locked=element.locked)&&void 0!==_element$locked&&_element$locked),this.locked=element.locked,this.getElement(this.selectors.SECTION_TITLE).innerHTML=element.title}_refreshPageItem(_ref2){var _element$pageItem,_this$pageItem;let{element:element,state:state}=_ref2;if(!element.pageItem)return;const containsPageItem=this._isPageItemInThisSection(element.pageItem);if(!containsPageItem||this._isParentSectionIndexCollapsed(state))return this.pageItem=!1,void this.getElement(this.selectors.SECTION_ITEM).classList.remove(this.classes.PAGEITEM);!state.section.get(this.id).indexcollapsed||null!==(_element$pageItem=element.pageItem)&&void 0!==_element$pageItem&&_element$pageItem.isStatic?this.pageItem="section"==element.pageItem.type&&element.pageItem.id==this.id:this.pageItem=containsPageItem;this.getElement(this.selectors.SECTION_ITEM).classList.toggle(this.classes.PAGEITEM,null!==(_this$pageItem=this.pageItem)&&void 0!==_this$pageItem&&_this$pageItem),this.pageItem&&!this.reactive.isEditing&&this.element.scrollIntoView({block:"nearest"})}_isPageItemInThisSection(pageItem){if(pageItem.sectionId==this.id)return!0;return!!this.element.querySelector("".concat(this.selectors.SECTION,'[data-id="').concat(pageItem.sectionId,'"]'))}_isParentSectionIndexCollapsed(state){const parentElement=this.element.parentElement.closest(this.selectors.SECTION);if(!parentElement||!parentElement.dataset.id)return null;return!!state.section.get(parentElement.dataset.id).indexcollapsed}async addOverlay(){this.element.classList.add(this.classes.OVERLAYBORDERS)}removeOverlay(){this.element.classList.remove(this.classes.OVERLAYBORDERS)}}return _exports.default=Component,_exports.default}));
//# sourceMappingURL=section.min.js.map
File diff suppressed because one or more lines are too long
@@ -8,6 +8,6 @@ define("core_courseformat/local/courseindex/sectiontitle",["exports","core_cours
* @class core_courseformat/local/courseindex/sectiontitle
* @copyright 2021 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_dndsectionitem=(obj=_dndsectionitem)&&obj.__esModule?obj:{default:obj};class Component extends _dndsectionitem.default{create(descriptor){this.name="courseindex_sectiontitle",this.id=descriptor.id,this.section=descriptor.section,this.course=descriptor.course,this.fullregion=descriptor.fullregion,this.section.number>0&&null===this.section.component&&(this.getDraggableData=this._getDraggableData)}static init(target,selectors){return new this({element:document.getElementById(target),selectors:selectors})}stateReady(state){this.configDragDrop(this.id,state,this.fullregion)}}return _exports.default=Component,_exports.default}));
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_dndsectionitem=(obj=_dndsectionitem)&&obj.__esModule?obj:{default:obj};class Component extends _dndsectionitem.default{create(descriptor){this.name="courseindex_sectiontitle",this.id=descriptor.id,this.section=descriptor.section,this.course=descriptor.course,this.fullregion=descriptor.fullregion,this.section.number>0&&null===this.section.component&&(this.getDraggableData=this._getDraggableData)}static init(target,selectors){return new this({element:document.getElementById(target),selectors:selectors})}stateReady(state){this.configDragDrop(this.id,state,this.fullregion,!!document.querySelector('[data-courseindexdndallowed="true"]'))}}return _exports.default=Component,_exports.default}));
//# sourceMappingURL=sectiontitle.min.js.map
@@ -1 +1 @@
{"version":3,"file":"sectiontitle.min.js","sources":["../../../src/local/courseindex/sectiontitle.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Course index section title component.\n *\n * This component is used to control specific course section interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/sectiontitle\n * @class core_courseformat/local/courseindex/sectiontitle\n * @copyright 2021 Ferran Recio <ferran@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DndSectionItem from 'core_courseformat/local/courseeditor/dndsectionitem';\n\nexport default class Component extends DndSectionItem {\n\n /**\n * Constructor hook.\n *\n * @param {Object} descriptor\n */\n create(descriptor) {\n // Optional component name for debugging.\n this.name = 'courseindex_sectiontitle';\n\n this.id = descriptor.id;\n this.section = descriptor.section;\n this.course = descriptor.course;\n this.fullregion = descriptor.fullregion;\n\n // Prevent topic zero and delegated sections from being draggable.\n if (this.section.number > 0 && this.section.component === null) {\n this.getDraggableData = this._getDraggableData;\n }\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new this({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the initial state\n */\n stateReady(state) {\n this.configDragDrop(this.id, state, this.fullregion);\n }\n}\n"],"names":["Component","DndSectionItem","create","descriptor","name","id","section","course","fullregion","this","number","component","getDraggableData","_getDraggableData","target","selectors","element","document","getElementById","stateReady","state","configDragDrop"],"mappings":";;;;;;;;;;mKA4BqBA,kBAAkBC,wBAOnCC,OAAOC,iBAEEC,KAAO,gCAEPC,GAAKF,WAAWE,QAChBC,QAAUH,WAAWG,aACrBC,OAASJ,WAAWI,YACpBC,WAAaL,WAAWK,WAGzBC,KAAKH,QAAQI,OAAS,GAAgC,OAA3BD,KAAKH,QAAQK,iBACnCC,iBAAmBH,KAAKI,+BAWzBC,OAAQC,kBACT,IAAIN,KAAK,CACZO,QAASC,SAASC,eAAeJ,QACjCC,UAAAA,YASRI,WAAWC,YACFC,eAAeZ,KAAKJ,GAAIe,MAAOX,KAAKD"}
{"version":3,"file":"sectiontitle.min.js","sources":["../../../src/local/courseindex/sectiontitle.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Course index section title component.\n *\n * This component is used to control specific course section interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/sectiontitle\n * @class core_courseformat/local/courseindex/sectiontitle\n * @copyright 2021 Ferran Recio <ferran@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DndSectionItem from 'core_courseformat/local/courseeditor/dndsectionitem';\n\nexport default class Component extends DndSectionItem {\n\n /**\n * Constructor hook.\n *\n * @param {Object} descriptor\n */\n create(descriptor) {\n // Optional component name for debugging.\n this.name = 'courseindex_sectiontitle';\n\n this.id = descriptor.id;\n this.section = descriptor.section;\n this.course = descriptor.course;\n this.fullregion = descriptor.fullregion;\n\n // Prevent topic zero and delegated sections from being draggable.\n if (this.section.number > 0 && this.section.component === null) {\n this.getDraggableData = this._getDraggableData;\n }\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new this({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the initial state\n */\n stateReady(state) {\n this.configDragDrop(this.id, state, this.fullregion, !!document.querySelector(`[data-courseindexdndallowed=\"true\"]`));\n }\n}\n"],"names":["Component","DndSectionItem","create","descriptor","name","id","section","course","fullregion","this","number","component","getDraggableData","_getDraggableData","target","selectors","element","document","getElementById","stateReady","state","configDragDrop","querySelector"],"mappings":";;;;;;;;;;mKA4BqBA,kBAAkBC,wBAOnCC,OAAOC,iBAEEC,KAAO,gCAEPC,GAAKF,WAAWE,QAChBC,QAAUH,WAAWG,aACrBC,OAASJ,WAAWI,YACpBC,WAAaL,WAAWK,WAGzBC,KAAKH,QAAQI,OAAS,GAAgC,OAA3BD,KAAKH,QAAQK,iBACnCC,iBAAmBH,KAAKI,+BAWzBC,OAAQC,kBACT,IAAIN,KAAK,CACZO,QAASC,SAASC,eAAeJ,QACjCC,UAAAA,YASRI,WAAWC,YACFC,eAAeZ,KAAKJ,GAAIe,MAAOX,KAAKD,aAAcS,SAASK"}
@@ -35,8 +35,9 @@ export default class extends BaseComponent {
* @param {number} sectionid the section id
* @param {Object} state the initial state
* @param {Element} fullregion the complete section region to mark as dragged
* @param {Boolean} isDndAllowed Whether drag and drop is allowed in this section.
*/
configDragDrop(sectionid, state, fullregion) {
configDragDrop(sectionid, state, fullregion, isDndAllowed = true) {
this.id = sectionid;
if (this.section === undefined) {
@@ -54,7 +55,7 @@ export default class extends BaseComponent {
this.fullregion = fullregion;
// Drag and drop is only available for components compatible course formats.
if (this.reactive.isEditing && this.reactive.supportComponents) {
if (this.reactive.isEditing && this.reactive.supportComponents && isDndAllowed) {
// Init the dropzone.
this.dragdrop = new DragDrop(this);
// Save dropzone classes.
@@ -46,6 +46,7 @@ export default class Component extends DndCmItem {
this.selectors = {
CM_NAME: `[data-for='cm_name']`,
CM_COMPLETION: `[data-for='cm_completion']`,
DND_ALLOWED: `[data-courseindexdndallowed='true']`,
};
// Default classes to toggle on refresh.
this.classes = {
@@ -79,7 +80,9 @@ export default class Component extends DndCmItem {
* @param {Object} state the course state.
*/
stateReady(state) {
this.configDragDrop(this.id);
if (document.querySelector(this.selectors.DND_ALLOWED)) {
this.configDragDrop(this.id);
}
const cm = state.cm.get(this.id);
const course = state.course;
// Refresh completion icon.
@@ -41,6 +41,7 @@ export default class Component extends DndSection {
SECTION_ITEM: `[data-for='section_item']`,
SECTION_TITLE: `[data-for='section_title']`,
CM_LAST: `[data-for="cm"]:last-child`,
DND_ALLOWED: `[data-courseindexdndallowed='true']`,
};
// Default classes to toggle on refresh.
this.classes = {
@@ -80,7 +81,7 @@ export default class Component extends DndSection {
this.configState(state);
const sectionItem = this.getElement(this.selectors.SECTION_ITEM);
// Drag and drop is only available for components compatible course formats.
if (this.reactive.isEditing && this.reactive.supportComponents) {
if (this.reactive.isEditing && this.reactive.supportComponents && document.querySelector(this.selectors.DND_ALLOWED)) {
// Init the inner dragable element passing the full section as affected region.
const titleitem = new SectionTitle({
...this,
@@ -68,6 +68,6 @@ export default class Component extends DndSectionItem {
* @param {Object} state the initial state
*/
stateReady(state) {
this.configDragDrop(this.id, state, this.fullregion);
this.configDragDrop(this.id, state, this.fullregion, !!document.querySelector(`[data-courseindexdndallowed="true"]`));
}
}
+8 -2
View File
@@ -198,15 +198,21 @@ if (core_communication\api::is_available() && has_capability('moodle/course:upda
$communication->show_communication_room_status_notification();
}
// Display a warning if asynchronous backups are pending for this course.
$containerattributes = [];
if ($PAGE->user_is_editing()) {
require_once($CFG->dirroot . '/backup/util/helper/async_helper.class.php');
// Display a warning if asynchronous backups are pending for this course.
if (async_helper::is_async_pending($course->id, 'course', 'backup')) {
echo $OUTPUT->notification(get_string('pendingasyncedit', 'backup'), 'warning');
}
// Allow drag and drop in the course index.
$containerattributes = [
'data-courseindexdndallowed' => 'true',
];
}
echo $renderer->container_start('course-content');
echo $renderer->container_start('course-content', attributes: $containerattributes);
// Include course AJAX.
include_course_ajax($course, $modinfo->get_used_module_names());
+6 -3
View File
@@ -311,18 +311,21 @@ if (has_capability('moodle/course:update', $context)) {
core_communication\helper::get_course_communication_status_notification($course);
}
if ($USER->editing == 1) {
$containerattributes = ['class' => 'course-content'];
if ($PAGE->user_is_editing()) {
// MDL-65321 The backup libraries are quite heavy, only require the bare minimum.
require_once($CFG->dirroot . '/backup/util/helper/async_helper.class.php');
if (async_helper::is_async_pending($id, 'course', 'backup')) {
echo $OUTPUT->notification(get_string('pendingasyncedit', 'backup'), 'warning');
}
// Allow drag and drop in the course index.
$containerattributes['data-courseindexdndallowed'] = 'true';
}
// Course wrapper start.
echo html_writer::start_tag('div', ['class' => 'course-content']);
echo html_writer::start_tag('div', $containerattributes);
// Make sure that section 0 exists (this function will create one if it is missing).
course_create_sections_if_missing($course, 0);
@@ -110,3 +110,26 @@ Feature: Cohort enrolment management
And I set the field "groups[]" to "Beta2 cohort (2)"
Then the "members" select box should contain "Bianca McAfee (s3@example.com)"
And the "members" select box should contain "Abigail Wyatt (s5@example.com)"
@javascript
Scenario: Editing a cohort enrolment method's role should change participants' role
Given the following "users" exist:
| username | firstname | lastname | email |
| s1 | Sandra | Cole | s1@example.com |
And the following "cohort members" exist:
| user | cohort |
| s1 | A1 |
When I log in as "teacher001"
And I add "Cohort sync" enrolment method in "Course 001" with:
| Cohort | Alpha1 |
And I should see "Cohort sync (Alpha1 - Student)"
And I navigate to course participants
And I should see "Student" in the "Sandra Cole" "table_row"
And I am on the "Course 001" "enrolment methods" page
And I click on "Edit" "link" in the "Alpha1" "table_row"
And I set the field "Assign role" to "Non-editing teacher"
And I click on "Save" "button"
Then I should see "Cohort sync (Alpha1 - Non-editing teacher)"
And I navigate to course participants
And I should see "Non-editing teacher" in the "Sandra Cole" "table_row"
And I should not see "Student" in the "Sandra Cole" "table_row"
-6
View File
@@ -89,12 +89,6 @@ if ($mform->is_cancelled()) {
$reset = ($instance->status != $data->status);
}
foreach ($data as $key => $value) {
$instance->$key = $value;
}
$instance->timemodified = time();
$plugin->update_instance($instance, $data);
if ($reset) {
+2 -2
View File
@@ -420,7 +420,7 @@ class enrol_self_plugin extends enrol_plugin {
*/
#[\core\attribute\deprecated('enrol_plugin::send_course_welcome_message_to_user', since: '4.4', mdl: 'MDL-4188')]
protected function email_welcome_message($instance, $user) {
\core\deprecation::emit_deprecation_if_present(__FUNCTION__);
\core\deprecation::emit_deprecation(__FUNCTION__);
$this->send_course_welcome_message_to_user(
instance: $instance,
userid: $user->id,
@@ -1193,7 +1193,7 @@ class enrol_self_plugin extends enrol_plugin {
*/
#[\core\attribute\deprecated('enrol_plugin::get_welcome_message_contact', since: '4.4', mdl: 'MDL-4188')]
public function get_welcome_email_contact($sendoption, $context) {
\core\deprecation::emit_deprecation_if_present(__FUNCTION__);
\core\deprecation::emit_deprecation(__FUNCTION__);
return $this->get_welcome_message_contact(
sendoption: $sendoption,
context: $context,
+1 -9
View File
@@ -204,15 +204,7 @@ abstract class component_gradeitem {
* @return bool
*/
public function is_using_advanced_grading(): bool {
if ($this->is_using_scale()) {
return false;
}
if ($this->get_advanced_grading_controller()) {
return true;
}
return false;
return (bool) $this->get_advanced_grading_controller();
}
/**
@@ -4,7 +4,7 @@ Feature: Converting rubric score to grades
As a teacher
I need to be able to use different grade settings
Scenario Outline: Convert rubric scores to grades.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
@@ -19,9 +19,11 @@ Feature: Converting rubric score to grades
And the following "scales" exist:
| name | scale |
| Test scale 1 | Disappointing, Good, Very good, Excellent |
And the following "activities" exist:
| activity | name | intro | course | idnumber | grade | advancedgradingmethod_submissions |
| assign | Test assignment 1 | Test | C1 | assign1 | <grade> | rubric |
Scenario Outline: Convert assignment rubric scores to grades.
Given the following "activities" exist:
| activity | name | intro | course | idnumber | grade | advancedgradingmethod_submissions |
| assign | Test assignment 1 | Test | C1 | assign1 | <grade> | rubric |
When I log in as "teacher1"
And I change window size to "large"
And I am on "Course 1" course homepage with editing mode on
@@ -55,3 +57,39 @@ Feature: Converting rubric score to grades
| 100 | | 10.00 |
| 70 | | 7.00 |
| Test scale 1 | | Disappointing |
Scenario Outline: Convert forum rubric scores with a scale to grades.
Given the following "activities" exist:
| activity | name | intro | course | idnumber | grade | advancedgradingmethod_submissions |
| forum | Test forum 1 | Test | C1 | forum1 | 100 | rubric |
And I change window size to "large"
And I am on the "Test forum 1" "forum activity editing" page logged in as teacher1
And I expand all fieldsets
And I set the following fields to these values:
| Whole forum grading > Type | Scale |
| Whole forum grading > Scale | Default competence scale |
| Grading method | Rubric |
And I press "Save and display"
And I go to "Test forum 1" advanced grading definition page
# Defining a rubric.
And I set the following fields to these values:
| Name | Forum 1 rubric |
| Description | Rubric test description |
And I define the following rubric:
| Criterion 1 | Level 11 | 1 | Level 12 | 20 | Level 13 | 40 | Level 14 | 50 |
| Criterion 2 | Level 21 | 10 | Level 22 | 20 | Level 23 | 30 | | |
| Criterion 3 | Level 31 | 5 | Level 32 | 20 | | | | |
And I press "Save rubric and make it ready"
When I am on the "Test forum 1" "forum activity" page logged in as teacher1
And I press "Grade users"
And I click on "<criterion1> points" "radio"
And I click on "<criterion2> points" "radio"
And I click on "<criterion3> points" "radio"
And I press "Save"
And I am on the "Course 1" "grades > Grader report > View" page
Then I should see "<studentgrade>" in the "student1@example.com" "table_row"
Examples:
| studentgrade | criterion1 | criterion2 | criterion3 |
| Competent | Level 14 50 | Level 23 30 | Level 32 20 |
| Not yet competent | Level 11 1 | Level 21 10 | Level 31 5 |
+2 -2
View File
@@ -58,7 +58,7 @@ class core_grades_renderer extends plugin_renderer_base {
public function group_selector(object $course, ?string $groupactionbaseurl = null): ?string {
global $USER;
\core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]);
\core\deprecation::emit_deprecation([$this, __FUNCTION__]);
if ($groupactionbaseurl !== null) {
debugging(
@@ -139,7 +139,7 @@ class core_grades_renderer extends plugin_renderer_base {
): stdClass {
global $SESSION, $COURSE;
\core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]);
\core\deprecation::emit_deprecation([$this, __FUNCTION__]);
// User search.
$searchvalue = optional_param('gpr_search', null, PARAM_NOTAGS);
$userid = optional_param('grp_userid', null, PARAM_INT);
+2 -2
View File
@@ -244,7 +244,7 @@ class behat_grade extends behat_base {
*/
#[\core\attribute\deprecated('behat_forms::i_set_the_field_to', since: '4.0', final: true)]
protected function select_in_gradebook_tabs() {
\core\deprecation::emit_deprecation_if_present([self::class, __FUNCTION__]);
\core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
}
/**
@@ -307,6 +307,6 @@ class behat_grade extends behat_base {
*/
#[\core\attribute\deprecated('behat_forms::i_set_the_field_to', since: '4.1', final: true)]
protected function select_in_gradebook_navigation_selector() {
\core\deprecation::emit_deprecation_if_present([self::class, __FUNCTION__]);
\core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
}
}
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+3 -3
View File
@@ -20,11 +20,11 @@
* 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.
* {@link https://moodledev.io/general/projects/api/amos}) using the
* list of strings defined in install/stringnames.txt file.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();

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