Compare commits

..

424 Commits

Author SHA1 Message Date
Jun Pataleta 143c2cd5be Moodle release 4.3.4 2024-04-20 15:57:33 +08:00
Huong Nguyen 06b6467f4f weekly release 4.3.3+ 2024-04-19 17:05:32 +07:00
Jun Pataleta 452279abad Merge branch 'MDL-81584-403' of https://github.com/mihailges/moodle into MOODLE_403_STABLE 2024-04-19 11:03:30 +08:00
Mihail Geshoski a51e542e5b MDL-81584 gradereport_grader: Pin the last row only when necessary
Fixes the current behavior where the last row in the grader report is
always 'pinned', ensuring it is pinned only when necessary.
2024-04-18 16:15:11 +08:00
Jun Pataleta 76c0c9ed59 Merge branch 'MDL-81525-403' of https://github.com/andrewnicols/moodle into MOODLE_403_STABLE 2024-04-17 23:42:14 +08:00
Laurent David 4443ecf6b4 MDL-81267 mod_data: Additional tests 2024-04-17 10:33:13 +02:00
Laurent David faadca21a9 MDL-81267 mod_data: Clean field type before inclusion
* Filter out wrong field types when restoring or creating a field.
2024-04-17 10:33:13 +02:00
Paul Holden bcb96be529 MDL-80878 tool_mfa: safely retrieve the referrer URL. 2024-04-17 03:24:20 +00:00
Stevani Andolo 8fdda09338 MDL-81354 core: Escape string of fullname 2024-04-17 03:24:19 +00:00
Paul Holden 593204b45d MDL-81463 auth: ensure login recaptcha always checked if configured. 2024-04-17 03:24:19 +00:00
Paul Holden c988992f7e MDL-81062 mod_lesson: escape identity fields in overview report. 2024-04-17 03:24:19 +00:00
Paul Holden 4408c3fcaa MDL-80712 mod_workshop: stricter cleaning of sub-plugin values. 2024-04-17 03:24:19 +00:00
Paul Holden 6c08d1c101 MDL-81058 admin: fix sesskey checks for admin preset actions. 2024-04-17 03:24:19 +00:00
Paul Holden dd433ba094 MDL-81059 tool_analytics: fix sesskey checks for model actions. 2024-04-17 03:24:19 +00:00
Laurent David fcd361badb MDL-81284 mod_wiki: Add stricter cleaning of contentformat
* When restoring backup we clean the contentformat and defaultformat
to avoid issue with invalid value.
* Add tests for format and defaultformat so to check that we
 filter out invalid formats
* Remove private $basepath variable as it was used one and not always
initialised correctly.
2024-04-17 03:24:19 +00:00
meirzamoodle bef5f4a121 MDL-81352 filter: Ensure equation safety prior to filtering 2024-04-17 03:24:19 +00:00
Paul Holden f9e6040e28 MDL-81135 mod_feedback: stricter cleaning of item type parameter. 2024-04-17 03:24:19 +00:00
Huong Nguyen d77f2d55ef MDL-81247 calendar: Extra validation for webservices 2024-04-17 03:24:19 +00:00
Paul Holden de7ec71ca4 MDL-80877 tool_mfa: protect logout process with sesskey check. 2024-04-17 03:24:19 +00:00
Paul Holden de366cea22 MDL-81247 calendar: freeze event type if "user" only allowed. 2024-04-17 03:24:19 +00:00
Leon Stringer 6bc016ad41 MDL-80585 report_log: sanitize descriptions
Strip any HTML tags from values in the "Description" column.
2024-04-17 03:24:19 +00:00
Huong Nguyen df23edbbab Merge branch 'MDL-81552-403' of https://github.com/junpataleta/moodle into MOODLE_403_STABLE 2024-04-17 10:13:41 +07:00
Jun Pataleta 33b34bc86a MDL-81552 core: Assign proper webdav_client class variables 2024-04-16 13:06:46 +08:00
Andrew Nicols ffc51ab3fd MDL-81525 core_user: Move after_complete_login to correct NS 2024-04-15 08:53:35 +08:00
Sara Arjona d706265b8e weekly release 4.3.3+ 2024-04-12 13:23:26 +02:00
Huong Nguyen 15912783c4 Merge branch 'MDL-77015-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-04-12 11:46:05 +07:00
Huong Nguyen b9dd2d29c5 Merge branch 'MDL-65381-403' of https://github.com/jleyva/moodle into MOODLE_403_STABLE 2024-04-12 11:11:28 +07:00
Andrew Nicols 99748cdee6 Merge branch 'MDL-70829-403' of https://github.com/junpataleta/moodle into MOODLE_403_STABLE 2024-04-12 11:59:04 +08:00
Huong Nguyen 692cda1525 MDL-79174 availability: Behat cleanups 2024-04-12 10:57:04 +07:00
Huong Nguyen 951a3d5e5c Merge branch 'MDL-79174_403_STABLE' of https://github.com/marxjohnson/moodle into MOODLE_403_STABLE 2024-04-12 10:56:50 +07:00
Andrew Nicols 07dab571dd Merge branch 'MDL-81192_403_STABLE' of https://github.com/marxjohnson/moodle into MOODLE_403_STABLE 2024-04-12 11:48:24 +08:00
Jun Pataleta 4544ea122b MDL-70829 output: Remove the presentation role for html_writer::img()
* A presentation role is not necessary for the img tag.
  - If a non-empty alt text is provided, the presentation role will
  conflict with the alt text.
  - An empty alt text denotes a decorative image. The presence of a
  presentation role is redundant.
* Make sure that the alt text is set to an empty string. Otherwise,
  an img tag without an alt attribute will be produced which is an
  invalid markup.
2024-04-12 11:45:35 +08:00
Andrew Nicols d16fc3c7e3 Merge branch 'MDL-78902_403' of https://github.com/timhunt/moodle into MOODLE_403_STABLE 2024-04-12 11:18:49 +08:00
Jun Pataleta 007e527c3c MDL-70829 core: Set the icon of action icons as a decorative image
* When text is rendered for the action icon, set the icon as a
decorative image by setting empty alt and title attributes and adding
the aria-hidden attribute as well. Otherwise, assistive technologies
will read the action name twice.
2024-04-12 11:14:06 +08:00
Jun Pataleta fea05fb8a3 MDL-70829 block_recentlyaccesseditems: Move listitem role out of <a>
* Axe DevTools flags an error on the recently accessed items block that
the listitem role is not allowed for <a> tags. This is because the
"listitem" role will override the native semantic meaning of the course
links. So the solution is to move the course links within a <div> tag
with the listitem role instead.
2024-04-12 11:14:06 +08:00
Jun Pataleta 84ef1906e0 MDL-70829 tool_lp: Add empty alt text for decorative image
* The decorative image for the course modules in the user competency
summary modal is missing an empty alt text.
2024-04-12 11:14:05 +08:00
Jun Pataleta 744065174c MDL-70829 theme_boost: Make context header icons decorative
The non-empty alt text for the icons of the context header buttons like
the "Message" and the "Add contact" buttons cause redundant information
to be read twice by screen readers when the buttons are rendered with
the icon and the button name. The icons need to be set as decorative by
setting an empty alt text for them.
* The presentation role is also unnecessary.
* The alt text is needed when the button gets rendered without the
button name text.
2024-04-12 11:14:05 +08:00
Jun Pataleta 1e2ef693f7 MDL-70829 core_search: Mark up decorative image properly
* An empty alt text is enough for decorative images. There's no need
  to set a presentation role.
2024-04-12 11:13:14 +08:00
Jun Pataleta 182e09ab5c MDL-70829 enrol_manual: Mark up decorative image properly
* An empty alt text is enough for decorative images. There's no need
  to set a presentation role.
2024-04-12 11:13:14 +08:00
Jun Pataleta ca0e61a79d MDL-70829 mod_workshop: Mark up decorative images properly
* Decorative images should have an empty alt text and there's no need
to set a presentation role.
2024-04-12 11:13:13 +08:00
Jun Pataleta f0aa5e9668 MDL-70829 mod_quiz: Mark up decorative images properly
* Decorative images should have an empty alt text and there's no need
to set a presentation role.
* Accessibility Behat tests added to cover the changes
2024-04-12 11:13:13 +08:00
Jun Pataleta 814fea407a MDL-70829 mod_data: Mark up decorative images properly
* Decorative images should have an empty alt text and there's no need
to set a presentation role.
* Accessibility Behat tests added to cover the changes
2024-04-12 11:13:13 +08:00
Jun Pataleta 1ae95e3b63 MDL-70829 course: Mark up decorative images properly
* Decorative images should have an empty alt text and there's no need
to set a presentation role.
* Accessibility Behat tests added to cover the changes:
  - block_starredcourses uses the core_course/no-courses template
  - core_completion tests changes in core_course/activity instance and
    core_course/editdefaultcompletion
2024-04-12 11:13:13 +08:00
Jun Pataleta f99f693238 MDL-70829 block_recentlyaccesseditems: Mark up decorative image properly
* The zero state image is purely decorative so there's no need to
set a non-empty alt text for it.
* The presentation role is also unnecessary for decorative image with
empty alt.
* Accessibility Behat tests added to cover changes
2024-04-12 11:13:12 +08:00
Jun Pataleta ae48b9e8a6 MDL-70829 block_myoverview: Mark up decorative image properly
* The zero state image is purely decorative so there's no need to
set a non-empty alt text for it.
* The presentation role is also unnecessary for decorative image with
empty alt.
* Accessibility Behat tests added to cover changes
2024-04-12 11:13:12 +08:00
Jun Pataleta f28b4d1ae1 MDL-70829 tool_lp: Remove presentation role for decorative images
* Behat tests in report_competency added to test the user summary
display in the user competency summary modal.
2024-04-12 11:13:12 +08:00
Jun Pataleta ca3218b23f MDL-70829 core: Remove presentation role for decorative images
* An image with non-empty alt text should not have a presentation role.
* An empty alt attribute is sufficient for decorative images. No need to
add a presentation role for them.
* Additionally, if there is already text that is present for an image,
there's no need to add an alt text with the same text. Such images are
decorative and should just have an empty alt text.
2024-04-12 11:13:12 +08:00
Huong Nguyen 8a1547bb17 Merge branch 'MDL-81320-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-04-12 10:11:42 +07:00
Huong Nguyen 1872094df6 Merge branch 'MDL-81256-403' of https://github.com/marinaglancy/moodle into MOODLE_403_STABLE 2024-04-12 08:58:28 +07:00
Shamim Rezaie 34d5c59153 Merge branch 'MDL-81306-403' of https://github.com/andrewnicols/moodle into MOODLE_403_STABLE 2024-04-12 11:46:06 +10:00
Huong Nguyen 8aed364c22 Merge branch 'MDL-81316-403' of https://github.com/andrewnicols/moodle into MOODLE_403_STABLE 2024-04-12 08:37:01 +07:00
Huong Nguyen bdd0d80a09 Merge branch 'MDL-81420-403' of https://github.com/sarjona/moodle into MOODLE_403_STABLE 2024-04-12 08:34:08 +07:00
Ilya Tregubov cbd81332e3 Merge branch 'MDL-80160-403-siteadminsconfigphp' of https://github.com/mudrd8mz/moodle into MOODLE_403_STABLE 2024-04-12 08:47:17 +08:00
Shamim Rezaie 33fd0471e8 Merge branch 'MDL-81482_403' of https://github.com/AnupamaSarjoshi/moodle into MOODLE_403_STABLE 2024-04-12 09:31:10 +10:00
Tomo Tsuyuki b81756cc04 MDL-78902 quiz restore: fix restore of 3.x backups with random questions
... where the random questions come from the quiz module context.

Co-authored-by: Tim Hunt <T.J.Hunt@open.ac.uk>
2024-04-11 22:09:59 +01:00
Sara Arjona 98a9e324d8 Merge branch 'MDL-81472-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-04-11 17:35:49 +02:00
Andrew Nicols a341231108 Merge branch 'MDL-81114_403' of https://github.com/timhunt/moodle into MOODLE_403_STABLE 2024-04-11 23:25:43 +08:00
Sara Arjona c5c52e66f3 Merge branch 'MDL-80469-403' of https://github.com/marinaglancy/moodle into MOODLE_403_STABLE 2024-04-11 14:52:00 +02:00
Marina Glancy 6e5d277fc2 MDL-80469 forms: add a hidden label to the button action group
This is a temporary solution to the accessibility problem
"Fieldsets without legend". We will no longer use a fieldset tag
if there is no legend after MDL-80431
2024-04-11 12:56:24 +01:00
Sara Arjona b6394a0b60 Merge branch 'MDL-81383_403' of https://github.com/timhunt/moodle into MOODLE_403_STABLE 2024-04-11 12:49:36 +02:00
Tim Hunt 6edfe1b415 MDL-81383 questions: get_filtered_questions order should be predictable
This method supports $limit and $offset paramters. The query must have
a predictable ORDER BY for that to make sense.
2024-04-11 10:27:41 +01:00
Pedro Jordao 95c957791e MDL-80195 forms: Keep dropdown menu open when tabbing into datepicker
When accessing a datepicker that is within a dropdown menu using the tab
key, the navigation sequence was changed since the datepicker code is
not rendered within the dropdown menu element, thus the dropdown was
closed as it was understood that we were not more inside it.
Furthermore, when exiting the datepicker by pressing tab, as the
dropdown was closed, the navigation jumped to the first element on the
page, which is the link to Skip to main content.

To resolve this, a validation was added that keeps the dropdown menu
open if the datepicker is open and accessed via the tab key.
2024-04-11 11:03:38 +02:00
Mark Johnson 217154895c MDL-81192 sessions: Prevent false positive session change errors
If $CFG->enable_read_only_sessions_debug was not enabled, debugging could be
enabled part-way through a request when restart_with_write_lock was
called. This meant that a diff between the initial and final session
would be made during write_close(), although the intial session state
was never captured. This generated false positives in the logs, and it
thought any variable set in the session was a change from the original
value.

This ensures that debugging is enabled before the debug flag is allowed
to change, preventing false positives.
2024-04-11 08:54:39 +01:00
Andrew Nicols 076f6e9464 MDL-77967 core_user: Coding style fixes in tests 2024-04-11 14:06:11 +08:00
Andrew Nicols 24c4875f6a Merge branch 'MDL-77967-403' of https://github.com/rmady/moodle into MOODLE_403_STABLE 2024-04-11 14:06:06 +08:00
Huong Nguyen 78cac12d8e Merge branch 'MDL-81362-403' of https://github.com/ssj365/moodle into MOODLE_403_STABLE 2024-04-11 10:59:26 +07:00
Huong Nguyen f990fcdca7 Merge branch 'MDL-81327-403' of https://github.com/laurentdavid/moodle into MOODLE_403_STABLE 2024-04-11 10:19:07 +07:00
Andrew Nicols 59cf12eca4 Merge branch 'MDL-81227_403' of https://github.com/timhunt/moodle into MOODLE_403_STABLE 2024-04-11 10:59:53 +08:00
Ilya Tregubov 3f01eeb49f Merge branch 'MDL-81491-403' of https://github.com/snake/moodle into MOODLE_403_STABLE 2024-04-11 09:51:48 +08:00
Shamiso.Jaravaza ffa09ce293 MDL-81362 mod_bigbluebuttonbn: Fix content type 2024-04-10 11:51:06 -06:00
Sara Arjona 5f8bfd504a Merge branch 'MDL-74977-403' of https://github.com/HuongNV13/moodle into MOODLE_403_STABLE 2024-04-10 15:50:58 +02:00
Huong Nguyen df99388d27 MDL-74977 core_courseformat: add expanded section update actions
This also fix the Random "Course content preferences" Behat failure
2024-04-10 18:11:45 +07:00
Sara Arjona 811b9e4187 Merge branch 'MDL-81307-fix-403' of https://github.com/sarjona/moodle into MOODLE_403_STABLE 2024-04-10 12:52:51 +02:00
Sara Arjona 4971008242 MDL-81307 theme: Fix behat failures with classic and sticky footer
Authored-by: Mikel Martín Corrales <mikel@moodle.com>
2024-04-10 12:33:46 +02:00
Jake Dallimore 510f9e3c08 MDL-81491 enrol_lti: fix incorrect login_hint parameter type validation
This was always bad, but since we only used it to verify the existence
of the param, and let library code take $_REQUEST for the real
validation, it was ok. Now, since we're redirecting to self during
necessary cookie checks added by MDL-80835, we lose the real value
originally stored in $_REQUEST. This patch just fixes the param type,
setting it to raw, which is what it should have used originally. The
raw value won't be cast from a string to an int as part of the
require_param call, so the value won't be lost any more.
2024-04-10 17:43:11 +08:00
Huong Nguyen 0cd42e33bf Merge branch 'MDL-81351-403-enfix' of https://github.com/mudrd8mz/moodle into MOODLE_403_STABLE 2024-04-10 13:55:26 +07:00
Huong Nguyen a0cf83c8f5 Merge branch 'MDL-81182_403' of https://github.com/aolley/moodle into MOODLE_403_STABLE 2024-04-10 11:24:19 +07:00
Anupama Sarjoshi cec3a6438b MDL-81482 Questions: Fix questionlib_test PHPUnit tests 2024-04-09 16:49:22 +01:00
Sara Arjona 220637457e Merge branch 'MDL-81307-403' of https://github.com/roland04/moodle into MOODLE_403_STABLE 2024-04-09 16:18:31 +02:00
Sara Arjona 725f6f35a3 Merge branch 'MDL-81451-403' of https://github.com/lucaboesch/moodle into MOODLE_403_STABLE 2024-04-09 15:31:10 +02:00
Mikel Martín 441ab036e0 MDL-81307 core_courseformat: Add bulk styles for small devices 2024-04-09 13:30:34 +02:00
Paul Holden 596b317c27 MDL-81472 mod_scorm: add missing library require to privacy class. 2024-04-09 11:38:08 +01:00
Sara Arjona c9fe574fdd weekly release 4.3.3+ 2024-04-09 12:07:24 +02:00
Laurent David e5f200616e MDL-81327 report_loglive: Apply the same fixes to loglive
* Additional fix: take into account selected group (filter)
2024-04-09 11:00:30 +02:00
Andrew Nicols 61282aa692 MDL-81327 report_log: Make group filtering logstore agnostic
The original implementation of group filtering introduced in MDL-80565
assumed that the log table existed in Moodle's own database. This is not
the case of the database logstore, or any similar logstore implemetning
the database \core\log\sql_reader interface.

Furthermore this check was also applying the SQL when the user had the
`accessallgroups` capability, or when the course was not in SEPARATE
groups mode (no groupmode and/or visible groups).

Co-authored: Laurent David <laurent.david@moodle.com>
2024-04-09 11:00:30 +02:00
Laurent David f67fa427bd MDL-81327 report_log: Fix col_fullusername
* Avoid calling get_user_fullname too many times and
deal with the case that $event->userid is empty
2024-04-09 11:00:30 +02:00
Andrew Nicols ff4d5f3caa MDL-81327 core: Remove unnecessary table filter
The \Traversable interface does not define the `valid` method.
Furthermore, the `valid` method actually checks that there is a _next_
value, which requires the value already be fetched and waiting. This is
not the case for all Iterators. For example the CallbackFilterIterator
does not load the initial value until it is requested.

It is completely unnecessary to do this check anyway as an invalid
Iterator will just not return any values.
2024-04-09 11:00:29 +02:00
Jake Dallimore 829a0d615a Merge branch 'MDL-81060-403-fix' of https://github.com/meirzamoodle/moodle into MOODLE_403_STABLE 2024-04-09 10:13:37 +08:00
Luca Bösch 9c5db33257 MDL-81451 qtype_missingtype: adopt icon size to other. 2024-04-08 17:27:03 +02:00
Luca Bösch 2f5def5d83 MDL-81451 qtype_gapfill: adopt icon size to other. 2024-04-08 17:27:03 +02:00
Sara Arjona 2f59f5b835 Merge branch 'MDL-81298-MOODLE_403_STABLE' of https://github.com/vithushau/moodle into MOODLE_403_STABLE 2024-04-08 17:11:55 +02:00
Didier 'OdyX' Raboud fecf2ee25a MDL-65381 grade_report: fix empty separate groups own grades access
On separate groups' courses, users without groups could not access
their own grade items.
2024-04-08 17:03:02 +02:00
Sara Arjona bf9e1f978e Merge branch 'MDL-78457-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-04-08 16:54:47 +02:00
Huong Nguyen 01a0c19364 Merge branch 'MDL-75037-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-04-08 16:06:38 +07:00
Marina Glancy 0b8ce298cb MDL-81256 lib: fix rounding of thumbnail image size 2024-04-08 10:01:47 +01:00
Sara Arjona e2b856751a MDL-81420 enrol: Remove duplicated header in editing form 2024-04-08 10:49:29 +02:00
Jake Dallimore 94eb59c5b8 Merge branch 'MDL-81405-403' of https://github.com/jleyva/moodle into MOODLE_403_STABLE 2024-04-08 15:35:18 +08:00
Ilya Tregubov fb74eac976 Merge branch 'MDL-81409-403' of https://github.com/snake/moodle into MOODLE_403_STABLE 2024-04-08 12:45:18 +08:00
Huong Nguyen afbec4a656 Merge branch 'MDL-80934-403' of https://github.com/laurentdavid/moodle into MOODLE_403_STABLE 2024-04-08 09:13:16 +07:00
Jake Dallimore a4a4cb1cdb MDL-81060 core: fix incorrect userquota usage in zip_packer
This change reverts much of MDL-74641, removing the erroneous userquota
checks from zip_packer. Now, during extraction, the zip_packer will exit
with an error status when it encounters a file which is larger than its
advertised size, and NOT when it determines the size of the extracted
files have exceeded the userquota (which only applies to private files
and which was used incorrectly here). It also reverts changes to the
draftfiles_ajax.php file, restoring the original (pre-MDL-74641) error
handling.
2024-04-08 09:11:48 +07:00
Huong Nguyen c056e99130 Merge branch 'MDL-81402-MOODLE_403_STABLE' of https://github.com/sh-csg/moodle into MOODLE_403_STABLE 2024-04-08 09:03:20 +07:00
Juan Leyva de6fb6e2f7 MDL-81405 tool_mobile: Set Partitioned cookie for the mobile app 2024-04-06 12:25:51 +02:00
Juan Leyva 5e7440acdc MDL-81405 session: New core cookie helper utility class 2024-04-06 12:25:51 +02:00
Paul Holden 5de37ea321 MDL-81320 datafield_checkbox: account for empty field content. 2024-04-05 15:16:40 +01:00
Sara Arjona cf01fd6bef weekly release 4.3.3+ 2024-04-05 13:22:31 +02:00
Sara Arjona 034c9d9559 Merge branch 'MDL-81359-m403' of https://github.com/sammarshallou/moodle into MOODLE_403_STABLE 2024-04-04 17:02:59 +02:00
Huong Nguyen daa896b54a Merge branch 'MDL-81386-403' of https://github.com/sarjona/moodle into MOODLE_403_STABLE 2024-04-04 21:56:29 +07:00
Stefan Hanauska bccc540bce MDL-81402 course: Add sectionnum to cm export 2024-04-04 13:02:30 +02:00
Laurent David 3071a94206 MDL-80934 core_courseformat: Expand section when selected
* Sections in the course content must be expanded when an item
from this section is selected in the course index
2024-04-04 10:41:48 +02:00
Andrew Nicols 493bef4270 MDL-81316 core: Encode anchor fragments properly to RFC 3986 2024-04-04 16:04:37 +08:00
Ilya Tregubov 4ec1c56fd6 Merge branch 'MDL-78370-403' of https://github.com/junpataleta/moodle into MOODLE_403_STABLE 2024-04-04 14:19:20 +08:00
Andrew Nicols d1d193c61a NOBUG: mod_h5pactivity - Version bump after rewrite 2024-04-04 10:36:26 +08:00
Sara Arjona 1f037c1abb Merge branch 'MDL-80765-403' of https://github.com/laurentdavid/moodle into MOODLE_403_STABLE 2024-04-03 17:18:56 +02:00
Huong Nguyen 38fc785fe4 MDL-81393 atto: Disable mediaplugin filter for Behat test 2024-04-04 00:23:28 +11:00
Paul Holden fc377530a2 MDL-75037 core: update Moodlenet repository search URLs. 2024-04-03 13:00:38 +01:00
Jake Dallimore 53176786c8 MDL-81409 enrol_lti: fixes to dynamic registration to support Blackboard
The following fixes resolve issues when used with Blackboard Learn:
- Set registration request Content-Type header to application/json
- Make registration_token optional, per the spec:
https://www.imsglobal.org/node/200666#step-1-registration-initiation-request
- Conditionally create the deployment since it's optional in
lti-tool-configuration:
https://www.imsglobal.org/node/200666#lti-configuration-0
- Remove empty custom_parameters property, since that's optional too:
https://www.imsglobal.org/node/200666#lti-configuration-0
- Remove overly-strict validation on registration_token. This was
validating the param as a JWT, but since the spec has stabilised, this
is not always the case. It's a Bearer token that's opaque to the tool
(i.e. it's just passed back unmodified), so while it could be validated
against RFC6750, it's simplest to just remove the validation entirely.
- change targetOrigin of the postMessage to '*' as per the spec:
https://www.imsglobal.org/node/200666#step-4-registration-completed-and-activation
2024-04-03 16:22:26 +08:00
Sara Arjona 075138c474 MDL-81386 output: Fix failure in PHPUnit when running filtered tests
There was a data provider to get the list of the FontAwesome icons that
was initialized when running filtered tests.
It was causing some failures with filtered tests that, for instance,
called require_login and changed the course.
To avoid this failure (and also to improve the performance), the logic
to get the icons list has been moved to the test.
2024-04-03 09:27:10 +02:00
Shamim Rezaie 18023c7dab Merge branch 'MDL-81393-403' of https://github.com/HuongNV13/moodle into MOODLE_403_STABLE 2024-04-03 16:38:40 +11:00
Andrew Nicols 22ae648dd2 Merge branch 'MDL-80818-403' of https://github.com/aanabit/moodle into MOODLE_403_STABLE 2024-04-03 13:05:36 +08:00
Huong Nguyen fbf4d35827 Merge branch 'MDL-77779-403' of https://github.com/ilyatregubov/moodle into MOODLE_403_STABLE 2024-04-03 09:12:29 +07:00
Huong Nguyen 7c9a625cf8 Merge branch 'MDL-81300-403' of https://github.com/roland04/moodle into MOODLE_403_STABLE 2024-04-03 09:06:52 +07:00
Huong Nguyen ca0020a224 Merge branch 'MDL-81072-403' of https://github.com/stevandoMoodle/moodle into MOODLE_403_STABLE 2024-04-03 08:55:11 +07:00
Huong Nguyen 0c4a148cdb Merge branch 'MDL-77838-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-04-03 08:52:23 +07:00
Sara Arjona 6294b06174 weekly release 4.3.3+ 2024-04-02 14:46:38 +02:00
Paul Holden 1d8c25f479 MDL-78457 course: remove participants navigation if cannot access. 2024-04-02 10:03:47 +01:00
Paul Holden 7f01b0369a MDL-77015 mod_data: consistent escaping of template field names.
* When creating default templates, avoid escaping the field names
  contained within [[ ]] brackets;
* When managing field, ensure the field names are escaped.
2024-04-02 10:02:07 +01:00
Mikel Martín a7a85820f2 MDL-81300 theme_boost: Fix dnd overlay in course sections
Drag and drop overlay in course page was being overlapped by divider
buttons and quick edit links.
That happens because both elements have their own z-index context.
Add z-index to the drag and drop overlay elenment to fix this issue.
2024-04-02 10:14:39 +02:00
Andrew Nicols 63f7915427 Merge branch 'MDL-79712-403' of https://github.com/snake/moodle into MOODLE_403_STABLE 2024-04-02 11:56:33 +08:00
Laurent David b7b1e20ed4 MDL-80765 mod_data: Trim values on field update
* When creating a field we usually trim values, so we should
do the same on field update
2024-04-02 05:41:39 +02:00
Jake Dallimore 8657868c97 MDL-79712 auth_lti: ensure MoodleSession uses SameSite=None Secure
The MoodleSession cookie is already set this way when using Chrome, but
not for Firefox. This is a decision made in \core\session\manager. When
Firefox eventually defaults to SameSite=Lax when the SameSite attribute
is omitted from the Set-Cookie headers, the MoodleSession cookie won't
be sent. This change ensures the cookies used during LTI launches will
continue to work if/when this happens, and despite any changes which may
be made to core's sessionlib in future.
2024-04-02 11:40:17 +08:00
Huong Nguyen 9ae3cd1be7 MDL-81393 media_videojs: Switch to use Ogv.Js tech for Chrome 2024-04-02 10:27:07 +07:00
David Mudrák 829c469c5b MDL-81351 lang: Use the fixed strings in the tests, too 2024-03-28 11:01:39 +01:00
Helen Foster 3fdae1298f MDL-81351 lang: Import fixed English strings (en_fix) 2024-03-28 10:58:24 +01:00
Stevani Andolo e67ae9fe63 MDL-81072 tool_mfa: Fixed bulk user actions capability 2024-03-28 17:35:11 +08:00
Andrew Nicols f0f1caa67d MDL-81306 core: Do not use xsendfile for request dirs 2024-03-28 09:30:07 +08:00
Andrew Nicols ee06a613b3 MDL-81306 core: Remove docs on use of xsendfile with request dirs
This reverts the change made in MDL-80648 and adds a note to not do it.
2024-03-28 09:29:26 +08:00
Ilya Tregubov bc0c77d35c Merge branch 'mdl80598-403' of https://github.com/matthewhilton/moodle into MOODLE_403_STABLE 2024-03-28 09:06:11 +08:00
Ilya Tregubov 8c41baf328 Merge branch 'MDL-80302-403' of https://github.com/aanabit/moodle into MOODLE_403_STABLE 2024-03-28 08:59:24 +08:00
Amaia Anabitarte 355e8b6cf9 MDL-80818 QuickForm: Freeze all elements with the same name
There are some radio buttons in activity Completion Conditions settings
that must be frozen altogether, because all of them have the same name.
2024-03-27 16:24:50 +01:00
Amaia Anabitarte fcbe53778d MDL-80302 completion: Improve default completion error tracking
Co-authored-by: Sara Arjona <sara@moodle.com>
2024-03-27 16:12:12 +01:00
Vithusha Uthayakumar f1bb6ea00c MDL-81298 external: fix typo error in failing testcase 2024-03-27 16:48:39 +11:00
Ilya Tregubov 013d4e7303 Merge branch 'MDL-80036-403' of https://github.com/mickhawkins/moodle into MOODLE_403_STABLE 2024-03-27 11:08:11 +08:00
Andrew Nicols 0f4478b8e5 MDL-80332 core: Coding style fixes 2024-03-26 22:44:51 +08:00
Andrew Nicols 5dfcc1aedd Merge branch 'MDL-80332-403' of https://github.com/jleyva/moodle into MOODLE_403_STABLE 2024-03-26 22:43:37 +08:00
sam marshall aa4baad9a4 MDL-81359 core_courseformat: Availability 'Show more' unwelcome outing
If the availability restriction includes a link, then 'Show more'
always appeared even when the entire text is already shown.
2024-03-26 13:40:41 +00:00
Sara Arjona 1c530d86d1 weekly release 4.3.3+ 2024-03-26 12:31:12 +01:00
Jun Pataleta 88d342fdf2 Merge branch 'MDL-80384-403' of https://github.com/junpataleta/moodle into MOODLE_403_STABLE 2024-03-26 11:16:18 +08:00
Jun Pataleta c96efdb613 MDL-80384 courseformat: Set activity icon alt text on editing mode
From a warning using WAVE:
* The title attribute value for images that lack an alt attribute value
will be presented to screen reader users. However, providing image
content in the alt attribute typically provides better accessibility,
and should be used in most cases. The title attribute will generate a
mouse hover tooltip which may or may not be desired - this tooltip will
not be presented to touch screen or keyboard users.
2024-03-26 11:02:35 +08:00
Jun Pataleta 06e13d72f0 Merge branch 'MDL-81266_403' of https://github.com/stronk7/moodle into MOODLE_403_STABLE 2024-03-26 10:33:57 +08:00
Matthew Hilton 336f311676 MDL-80598 bigbluebutton: Gracefully handle invalid customdata 2024-03-26 10:09:30 +10:00
Sara Arjona 211c8caca5 Merge branch 'MDL-81246-403' of https://github.com/HuongNV13/moodle into MOODLE_403_STABLE 2024-03-25 17:01:45 +01:00
Sara Arjona ce8ec44c40 Merge branch 'MDL-80088-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-25 16:20:31 +01:00
Juan Leyva d7a0cb8d00 MDL-80332 tool_mobile: Fallback por app launches 2024-03-25 16:05:07 +01:00
Juan Leyva 7cec5c9b71 MDL-80332 login: New hooks for complete login and passed MFA 2024-03-25 15:56:01 +01:00
Laurent David 0b3802bb0b MDL-80384 core_courseformat: Remove activty icon link
* Remove the link over the image icon for an activity and use
the title attribute for the tooltip
2024-03-25 13:25:34 +01:00
Andrew Nicols 37c7b5004b Merge branch 'MDL-80827-403' of https://github.com/stevandoMoodle/moodle into MOODLE_403_STABLE 2024-03-25 11:44:22 +08:00
Huong Nguyen 05305ea751 Merge branch 'MDL-79829-403' of https://github.com/laurentdavid/moodle into MOODLE_403_STABLE 2024-03-25 09:22:37 +07:00
Huong Nguyen 5cb5826c45 Merge branch 'MDL-81074-403' of https://github.com/andrewnicols/moodle into MOODLE_403_STABLE 2024-03-25 09:15:48 +07:00
Andrew Nicols a5e94e0a08 MDL-81074 core: Simplify strftime tests 2024-03-25 09:51:25 +08:00
Luca Bösch aa2919309f MDL-81074 core: align core_date::strftime results for numeric and string 2024-03-25 09:51:25 +08:00
Andrew Nicols 1c42deaf58 MDL-81074 core: Add assertTimeStringMatches PHPUnit Assertion
Also raised as https://github.com/sebastianbergmann/phpunit/issues/5757
2024-03-25 09:51:25 +08:00
Eloy Lafuente (stronk7) 7d01ea90fe MDL-81266 composer: Bump behat/behat to 3.14 and other deps
Only applied to 402_STABLE and up, all them requiring PHP 8.0
as a minimum. And generated following the instructions in the
dev docs.
2024-03-23 13:37:33 +01:00
Paul Holden e7a3a5982e MDL-77838 repository: preserve empty dirs in download if selected. 2024-03-22 16:22:40 +00:00
Sara Arjona 74d3c96149 weekly release 4.3.3+ 2024-03-22 14:02:56 +01:00
Jun Pataleta ef94d46a7f Merge branch 'MDL-81313-403' of https://github.com/snake/moodle into MOODLE_403_STABLE 2024-03-22 11:47:46 +08:00
Sara Arjona d537ffbeb5 Merge branch 'MDL-80488-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-21 17:30:48 +01:00
Sara Arjona 65afe1196a Merge branch 'MDL-79913-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-21 17:18:18 +01:00
Sara Arjona 23ec91a2c4 Merge branch 'MDL-81112-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-21 17:11:35 +01:00
Sara Arjona e4b036da69 Merge branch 'MDL-69688_403' of https://github.com/stronk7/moodle into MOODLE_403_STABLE 2024-03-21 15:29:53 +01:00
Huong Nguyen 4a454da7ce Merge branch 'MDL-81289-403' of https://github.com/ferranrecio/moodle into MOODLE_403_STABLE 2024-03-21 15:12:18 +07:00
Huong Nguyen 7e5bd6d54e Merge branch 'MDL-80835-403' of https://github.com/snake/moodle into MOODLE_403_STABLE 2024-03-21 14:53:31 +07:00
Jake Dallimore fff106ffc2 MDL-80835 auth_lti: fix bad cast breaking samesite LTI usage 2024-03-21 15:37:38 +08:00
Jake Dallimore b6470d6252 MDL-80835 enrol_lti: add partitioning support for OIDC state cookie
Adds the property that is required by Chrome to opt-in to its 3rd party
cookie partitioning solution, CHIPS. This specific change ensures the
'state' cookie, used in the OIDC handshake, has partitioning support.
This cookie can be partitioned unconditionally, since it's a cookie
controlled by the library and one we don't expect to be set without
partitioning elsewhere.
2024-03-21 15:37:37 +08:00
Jake Dallimore d95a5de2ac MDL-80835 auth_lti: add partitioning to post-auth MoodleSession cookie
Adds the property that is required by Chrome to opt-in to its 3rd party
cookie partitioning solution, CHIPS. This specific change to auth_lti is
to ensure the MoodleSession Set-Cookie header resulting from
complete_user_login() calls (in auth.php) have this property set.
2024-03-21 15:37:37 +08:00
Jake Dallimore b14d02d177 MDL-80835 enrol_lti: add cookies required notice to auth login endpoint
This will be displayed if the cookie checks fail, which currently occurs
in Safari only.
2024-03-21 15:37:37 +08:00
Jake Dallimore 93aac92d01 MDL-80835 enrol_lti: add partitioning support to MoodleSession cookie
Adds the property that is required by Chrome to opt-in to its 3rd party
cookie partitioning solution, CHIPS. This specific change deals with the
cookie that is set when the user is not yet auth'd with the site and is
necessary to facilitate OIDC nonce retrieval and validation.
2024-03-21 15:37:37 +08:00
Jake Dallimore 1e09e6ffa6 MDL-80835 auth_lti: add cookie helper facilitating CHIPS opt-in
To opt a cookie in to Chrome's 3rd party cookie partitioning solution,
CHIPS, the property 'Partitioned;' needs to be set. This adds a helper
class supporting this, for a given cookie(s).

Note also, PHP's native
cookie APIs (setcookie, etc) don't support this cookie property yet -
(https://github.com/php/php-src/issues/12646).

Since this class is intended to allow existing Set-Cookie headers to be
modified before being sent (e.g. allowing clients to set a property on a
cookie set elsewhere in code), it deals with the headers directly anyway
but it means that new cookies must also use this helper to opt-in,
instead of relying on setcookie(). E.g. where the intent is to add
partitioning support to a new cookie, that cookie must first be set
(setcookie) and then it may opt-in to partitioning via this helper;
partitioning support cannot be achieved directly through setcookie and
friends yet.
2024-03-21 15:37:36 +08:00
Eloy Lafuente (stronk7) 7e170eb1b3 MDL-69688 phpunit: Improve test_context_not_used
This adds a few changes to the old test_context_not_used test:

- Move it to become an advanced_test, because its mission
  is to verify that the assertEventContextNotUsed() assertion
  works as expected.
- For consistency, also move the fixtures to own phpunit fixtures.
- Add proper coverage tags, to verify that the assertion is being
  covered.
- Add a data provider to provide all the current cases and ease
  any future case that may be needed in the future. One by one
  because previously there was code never executed with the
  warning expectation causing the test to stop.
- Run them in isolation, while this is not strictly required, it's
  including external fixtures and, we'll need that isolation soon
  (for changes coming when moving the test to PHPUnit 9.6 in MDL-81266).
2024-03-21 08:30:32 +01:00
Jake Dallimore 8e9abf5bd6 MDL-81313 core: Revert "Merge branch 'MDL-80338-403'
This reverts commit 3905247a84, reversing
changes made to 4414c4598e.
2024-03-21 11:30:10 +08:00
Paul Holden d238e4fd99 MDL-80488 mod_lesson: avoid overflowing length of page titles. 2024-03-20 23:51:18 +00:00
Paul Holden d3faf37e8b MDL-81112 gradereport_grader: show user country value correctly. 2024-03-20 23:49:57 +00:00
Jun Pataleta 0f32a8b13d Merge branch 'MDL-79007-403' of https://github.com/junpataleta/moodle into MOODLE_403_STABLE 2024-03-20 15:39:23 +08:00
Jun Pataleta 6f55bc72a9 Merge branch 'MDL-81110-403' of https://github.com/roland04/moodle into MOODLE_403_STABLE 2024-03-20 15:37:51 +08:00
Mark Johnson 443656c85c MDL-79007 calendar: Make screen readers read the new month name 2024-03-20 15:08:15 +08:00
Mark Johnson aed27a6b69 MDL-79007 calendar: Make screen readers read out calendar day popovers 2024-03-20 15:05:20 +08:00
Mark Johnson 5e6a2dc020 MDL-79007 calendar: Expose block month link names to screen readers 2024-03-20 15:02:14 +08:00
Ilya Tregubov 37f5032db5 weekly release 4.3.3+ 2024-03-20 08:54:10 +08:00
Ilya Tregubov 378a2e3f32 Merge branch 'install_403_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_403_STABLE 2024-03-20 08:54:07 +08:00
ferranrecio bda0176205 MDL-81289 core_courseformat: fix get_format_string params 2024-03-19 17:34:09 +01:00
Mikel Martín 1cc3736947 MDL-81110 theme_boost: Fix scroll-margin-top in #maincontent
- Fix scroll-margin-top in #maincontent element
- Add scroll-margin-top for focused elements too
2024-03-19 14:51:58 +01:00
Paul Holden 1e7b4f6537 Merge branch 'MDL-78438-403' of https://github.com/ilyatregubov/moodle into MOODLE_403_STABLE 2024-03-19 10:45:34 +00:00
Ilya Tregubov b709cc3eee MDL-78438 gradereport_grader: Fix Behat test
Seems Oracle is too slow on pressing buttons :))
2024-03-19 17:46:47 +08:00
Michael Hawkins 03102060dd MDL-80036 core: Update security.txt expiry 2024-03-19 15:31:08 +08:00
Huong Nguyen 1c31e9722f Merge branch 'MDL-81156-403' of https://github.com/lameze/moodle into MOODLE_403_STABLE 2024-03-19 10:55:47 +07:00
Andrew Nicols a42516478d Merge branch 'MDL-77255-403' of https://github.com/HuongNV13/moodle into MOODLE_403_STABLE 2024-03-19 11:39:21 +08:00
Huong Nguyen 02300131b7 MDL-77255 block_tag_flickr: Update to match with the new API
Flickr dropped php_serial format, we need to change to JSON format
2024-03-19 10:14:46 +07:00
Ilya Tregubov 28f91d0b2d Merge branch 'MDL-80240-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-19 10:28:13 +08:00
Ilya Tregubov 40dbc075d8 Merge branch 'MDL-80919-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-19 10:14:27 +08:00
Ilya Tregubov 7050d41112 Merge branch 'MDL-78438-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-19 09:47:16 +08:00
Ilya Tregubov 609e18fbc3 Merge branch 'MDL-80793-403' of https://github.com/OxyOCE/moodle into MOODLE_403_STABLE 2024-03-19 09:12:41 +08:00
AMOS bot c038bc3d09 Automatically generated installer lang files 2024-03-19 00:08:51 +00:00
Sara Arjona 1c31846b97 Merge branch 'MDL-72923-M403' of https://github.com/michael-milette/moodle into MOODLE_403_STABLE 2024-03-18 16:57:01 +01:00
Sara Arjona a0739d133d Merge branch 'MDL-81254-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-18 16:08:15 +01:00
Laurent David 416a586a1d MDL-79829 theme: Fix Course index draggable cursor
* Make the cursor consistent for drag and drop operation
to be the same in the course content and the course index
2024-03-18 07:39:15 +01:00
Laurent David 3b8bde71fa MDL-79829 core: Reactive drag and drop cursor
* When dragging and dropping an item, the cursor should be
set to "move" and not copy.
2024-03-18 07:39:15 +01:00
Jun Pataleta 0dfe161746 Merge branch 'MDL-80279-403' of https://github.com/pedrojordao/moodle into MOODLE_403_STABLE 2024-03-18 11:22:42 +08:00
Paul Holden 2bc19b944e MDL-79913 grade: respect messaging config for user heading buttons. 2024-03-15 17:00:28 +00:00
Paul Holden 5c0e2639d4 MDL-81254 grade: ensure we always close the graded user iterator.
This ensures that the recordsets contained within are also closed
when we're done with them.
2024-03-15 16:04:07 +00:00
Huong Nguyen 45ad566de9 weekly release 4.3.3+ 2024-03-15 21:48:19 +07:00
Huong Nguyen 5fc8c89068 Merge branch 'install_403_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_403_STABLE 2024-03-15 21:48:18 +07:00
Pedro Jordao eac4b571ee MDL-80279 core: Set new attributes for user profile initials
When the user does not have a profile photo and initials are displayed,
there is no alt text for the initials which causes accessibility
issues, so with this change I added the user's full name to the title
and aria-label attributes and I set a role='img' when the element span
is rendered.
2024-03-15 07:39:23 -03:00
Jake Dallimore e2e8268afb Merge branch 'MDL-80167-403-int' of https://github.com/HuongNV13/moodle into MOODLE_403_STABLE 2024-03-15 12:54:47 +08:00
Huong Nguyen e70e73d9b7 MDL-81246 dml: Check the statement resource before freeing it 2024-03-15 11:19:43 +07:00
Huong Nguyen 7ad4a82cb3 MDL-80167 core: Skip Oracle for Oracle environment in unit tests 2024-03-15 08:46:35 +07:00
Ilya Tregubov 2c757362db Merge branch 'MDL-81190-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-15 08:42:19 +08:00
Tim Hunt bf505f1075 MDL-81227 question bank: improve the order of the action menu 2024-03-14 17:38:02 +00:00
Sara Arjona e81ce235b6 Merge branch 'MDL-80167-403' of https://github.com/HuongNV13/moodle into MOODLE_403_STABLE 2024-03-14 11:54:59 +01:00
Sara Arjona a99c603cba Merge branch 'MDL-80479-403' of https://github.com/lameze/moodle into MOODLE_403_STABLE 2024-03-14 11:47:56 +01:00
Huong Nguyen d424951ce0 MDL-80167 admin: Add environment check for Oracle database 2024-03-14 17:42:39 +07:00
Simey Lameze 585eb7d2e8 MDL-80479 behat: improve new test for quiz timer 2024-03-14 10:21:17 +08:00
Angelia Dela Cruz 745c87c8a3 MDL-80479 mod_quiz: Behat to set quiz opening and closing time 2024-03-14 10:21:17 +08:00
Ilya Tregubov 293ccf1948 MDL-77779 competency: Fix course restore from older backups.
overridegrade might be not set if Moodle < 4.1
2024-03-14 09:29:54 +08:00
Simey Lameze fa534ac13e MDL-81156 behat: coverage for force course language 2024-03-14 09:04:34 +08:00
AMOS bot be7316f99e Merge branch 'MOODLE_403_STABLE' into install_403_STABLE 2024-03-14 00:13:18 +00:00
Sara Arjona f6c86bbd1b Merge branch 'MDL-80865-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-13 12:18:53 +01:00
Ilya Tregubov d03306a1ce Merge branch 'MDL-80565-403' of https://github.com/laurentdavid/moodle into MOODLE_403_STABLE 2024-03-13 11:22:45 +08:00
AMOS bot 6d2a2edc34 Automatically generated installer lang files 2024-03-13 00:11:10 +00:00
Ilya Tregubov a30f476d18 weekly release 4.3.3+ 2024-03-13 08:09:48 +08:00
Tim Hunt 81fafbc199 MDL-81039 qtype_multichoice: better way to control feedback display 2024-03-12 12:00:05 +01:00
Tim Hunt 842dd9f729 MDL-81039 question: Improve some comments in question_display_options 2024-03-12 12:00:00 +01:00
Huong Nguyen a734d9caba Merge branch 'MDL-76805-403' of https://github.com/lameze/moodle into MOODLE_403_STABLE 2024-03-12 10:32:24 +07:00
Shamiso.Jaravaza 5fdf46bbca MDL-76805 mod_bigbluebuttonbn: Fix up behat tests 2024-03-12 10:58:33 +08:00
Ilya Tregubov 8912f31a06 Merge branch 'MDL-81035-403' of https://github.com/HuongNV13/moodle into MOODLE_403_STABLE 2024-03-12 10:40:44 +08:00
Ilya Tregubov 1cb9abbb90 Merge branch 'MDL-81127-403' of https://github.com/izendegi/moodle into MOODLE_403_STABLE 2024-03-12 10:27:13 +08:00
Ilya Tregubov 1782190cbe Merge branch 'MDL-81191-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-12 10:15:47 +08:00
Paul Holden e0fc0dc15b MDL-81191 course: define batch stealth feedback language string. 2024-03-11 17:27:17 +00:00
Paul Holden 0b82683d9d MDL-81190 mod_assign: correct context argument to format string. 2024-03-11 16:58:00 +00:00
Sara Arjona b9ca2c5320 Merge branch 'MDL-79027-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-11 17:26:56 +01:00
Laurent David 4e9c9df95a MDL-80565 report_loglive: Do not show logs from other groups
* Prevent users from seeing logs from other groups if
groupmode is in SEPARATEGROUP
2024-03-11 14:30:55 +01:00
Laurent David afd4ccf425 MDL-80565 report_log: Prevent user from seeing other logs
* When no groups are selected we forcefully prevent
users from seeing users' logs from other groups
2024-03-11 14:30:55 +01:00
Laurent David b66a627a00 MDL-80565 report_log: Fix report log selector
* When using the report log and we select a group, the group
list should show the right list of users
2024-03-11 14:30:54 +01:00
Laurent David 0f235f56c1 MDL-80565 report: Revert MDL-41465 changes
* Partial revert of changes made in MDL-41465,
while keeping the API changes made in enrol/lib.
2024-03-11 14:30:54 +01:00
Tim Hunt 36e03243da MDL-81114 question: fix selection of random questions with draft status
We need to pick the latest 'ready' version of each question
(not questions where the latest version is ready).

There are test of the behaviour of random_question_loader, and
then a test in the quiz to prove it works in use.
2024-03-11 10:45:19 +00:00
Huong Nguyen 7fe8b9e448 MDL-81035 core: Fix redirect with sub-path 2024-03-11 15:56:14 +07:00
Adam Olley 639dceb19b MDL-81182 factor_email: Correct path for config.php require
Without this, the path is incorrect and can, in some circumstances (if
config.php is a symlink for one) - result in PHP requiring the config
file twice.
2024-03-10 00:35:02 +10:30
Tim Hunt db8d3fef9c MDL-81114 question: clarify the SQL in ensure_filtered_questions_loaded
This should not change the (currently buggy) behaviour at all,
but my brain needs this to understand how to fix the bug.
2024-03-08 19:31:32 +00:00
Tim Hunt 14ce932d9a MDL-81114 question: fix coding style in random_question_loader_test 2024-03-08 19:31:32 +00:00
Paul Holden 6cde2b3bc0 Merge branch 'MDL-81128-403-STABLE' of https://github.com/davidcerezal/moodle into MOODLE_403_STABLE 2024-03-08 14:50:26 +00:00
Paul Holden aec51de205 Merge branch 'MDL-81152-403' of https://github.com/snake/moodle into MOODLE_403_STABLE 2024-03-08 14:45:22 +00:00
Iñigo Zendegi aba3268336 MDL-81127 filter: Support filters on course completion message 2024-03-08 14:05:52 +01:00
Sara Arjona fde6c3c7ef weekly release 4.3.3+ 2024-03-08 11:11:50 +01:00
Ilya Tregubov 0ded712a6b Merge branch 'MDL-78547-403-phpunit' of https://github.com/HuongNV13/moodle into MOODLE_403_STABLE 2024-03-08 13:44:56 +08:00
Huong Nguyen a28e42c0c1 MDL-78547 quiz: Remove unnecessary call to clear_regrade_date_cache()
This method is designed to clear the values but these values are never set anywhere
It's safe to remove it out of the test now
2024-03-08 12:42:04 +07:00
Huong Nguyen 2fe254dac4 Merge branch 'MDL-81082-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-08 10:28:07 +07:00
Huong Nguyen 24b0aa122e Merge branch 'MDL-81123-403' of https://github.com/andrewnicols/moodle into MOODLE_403_STABLE 2024-03-08 10:13:01 +07:00
Huong Nguyen b7d4c3cef2 Merge branch 'MDL-81046-403' of https://github.com/andrewnicols/moodle into MOODLE_403_STABLE 2024-03-08 10:07:08 +07:00
Ilya Tregubov f1feb6fcd6 Merge branch 'MDL-80550-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-08 11:05:44 +08:00
Huong Nguyen d56573e41c Merge branch 'MDL-78547_403' of https://github.com/timhunt/moodle into MOODLE_403_STABLE 2024-03-08 08:52:11 +07:00
Ilya Tregubov 7fd5907b8d Merge branch 'MDL-80943-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-08 09:22:15 +08:00
Tim Hunt d365aef387 MDL-78547 quiz: fix how deprecated add random calls structure 2024-03-07 16:13:01 +00:00
Tim Hunt dcff2a039f MDL-78547 quiz: in a preview, if a question has been edited, update it
As part of implementing this, there is a new more efficient method
get_version_information_for_questions_in_attempt to see if any
questions in a quiz need to be updated. The regrade code was updated
to take advantage of this.

Co-authored-by: Khoa Nguyen Dang <khoanguyend7@nashtechglobal.com>
2024-03-07 16:13:01 +00:00
Tim Hunt 189464ade2 MDL-78547 questions: a way to replace a attempt in a usage
... without keeping the old attempt

Co-authored-by: Khoa Nguyen Dang <khoanguyend7@nashtechglobal.com>
2024-03-07 16:13:01 +00:00
Sara Arjona c01663867a MDL-69656 backup: Remove illegal whitespaces 2024-03-07 12:05:34 +01:00
Paul Holden bf0849d9a4 MDL-80943 reportbuilder: select-type customfields should be text.
The column type determines the available aggregation options present
for the column. Custom fields of type "select" should be considered as
text fields for this purpose, because their stored value represents
the index to their available options, rather than having any distinct
meaning of it's own for display.
2024-03-07 09:47:53 +00:00
Huong Nguyen 1c2004265c Merge branch 'MDL-79990-403' of https://github.com/lameze/moodle into MOODLE_403_STABLE 2024-03-07 10:04:40 +07:00
Mihail Geshoski 90743958a7 MDL-81152 mod_lti: Custom parameter configuration in the instance form
Re-enables the configuration of custom parameters when creating or
modifying a tool instance in a course.

Backport of MDL-80914.
2024-03-07 09:57:46 +08:00
Simey Lameze c91ee7412e MDL-79990 behat: custom step to verify assign grading data
This commit also optimise the new behat test.
2024-03-07 09:44:22 +08:00
Angelia Dela Cruz 7e076e4dc8 MDL-79990 gradingform: Behat test for marking guide information display 2024-03-07 09:44:21 +08:00
Andrew Nicols 6b8243f6af Merge branch 'MDL-69656-MOODLE_403_STABLE' of https://github.com/sh-csg/moodle into MOODLE_403_STABLE 2024-03-06 23:23:27 +08:00
David Cerezal f7d052160f MDL-81128 behat: use specific step instead a workaround 2024-03-06 16:04:44 +01:00
Michael Milette 49fcf4e984 MDL-72923 message: Fixed levels of headings in messaging drawer. 2024-03-06 07:33:52 -05:00
Stefan Hanauska 8e874c5ad6 MDL-69656 backup: Replace urlencoded pluginfile urls 2024-03-06 07:40:59 +01:00
Andrew Nicols b710b9b222 Merge branch 'MDL-80183-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-06 14:04:44 +08:00
Andrew Nicols 4659884ef4 Merge branch 'MDL-80646-403' of https://github.com/mihailges/moodle into MOODLE_403_STABLE 2024-03-06 13:22:11 +08:00
Huong Nguyen c0a6ce3bed Merge branch 'MDL-80241-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-06 09:23:46 +07:00
Huong Nguyen 4a339ac3e5 Merge branch 'MDL-79278-403' of https://github.com/andelacruz/moodle into MOODLE_403_STABLE 2024-03-06 09:13:08 +07:00
Angelia Dela Cruz b2da7b06a3 MDL-79278 mod_quiz: Teacher can set how quiz questions behave behat
Co-authored-by: Simey Lameze <simey@moodle.com>
2024-03-06 09:45:26 +08:00
David Mudrák 3ec1c77fc8 MDL-80160 admin: Fix site admins selector UI if the list is hardcoded
If the list of site admins was defined in the config.php, the UI to
select / modify admins never worked. But there was no indication of the
reason which was unexpected and confusing.
2024-03-05 23:45:11 +01:00
Sara Arjona 933404f2eb Merge branch 'MDL-80930-403' of https://github.com/raortegar/moodle into MOODLE_403_STABLE 2024-03-05 15:24:56 +01:00
Andrew Nicols 9c7ca927d3 Merge branch 'MDL-80731-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-03-05 22:04:47 +08:00
Sara Arjona 970fe4d26d weekly release 4.3.3+ 2024-03-05 11:22:18 +01:00
Andrew Nicols b89e60af09 MDL-81123 tiny_html: Make empty codemirror region clickable 2024-03-05 14:12:15 +08:00
Mihail Geshoski 6784ab5334 MDL-80646 mod_lti: Enable key and secret configuration in legacy tools
When restoring a manually configured tool to a different site, the key
and secret are not being restored, which is an expected behaviour.
However, legacy (pre-4.3) manually configured tools do not have the
option to configure the key and secret, which makes these instances
unusable. To make sure these tools will continue working after a backup
and restore, we need to re-enable the key and secret fields in the
legacy instance settings form.
2024-03-05 13:23:32 +08:00
Jun Pataleta e644d854f3 Merge branch 'MDL-80819_403' of https://github.com/stronk7/moodle into MOODLE_403_STABLE 2024-03-05 11:50:01 +08:00
Huong Nguyen f9e36cc9f1 Merge branch 'MDL-80836-403' of https://github.com/snake/moodle into MOODLE_403_STABLE 2024-03-05 10:24:12 +07:00
Paul Holden 91af0db62a MDL-80240 gradereport_grader: fix userfields parameter to search sql. 2024-03-04 14:43:36 +00:00
Paul Holden 1a980f3e43 MDL-79027 block_myoverview: retrieve only course related custom fields. 2024-03-04 10:38:56 +00:00
raortegar ab1fcc6db4 MDL-80930 core: Course delete modules task handle non deletable cms 2024-03-01 13:19:06 +01:00
Jun Pataleta 994a21d4ff weekly release 4.3.3+ 2024-03-01 12:24:28 +08:00
Jun Pataleta 5bca23a73d Merge branch 'install_403_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_403_STABLE 2024-03-01 12:24:26 +08:00
Sara Arjona 9bae536652 Merge branch 'MDL-80391-403-int' of https://github.com/stevandoMoodle/moodle into MOODLE_403_STABLE 2024-02-29 08:08:57 +01:00
Stevani Andolo 151e1e8d42 MDL-80391 tiny_html: Fixed multi modal layer display 2024-02-29 13:02:21 +08:00
Huong Nguyen 6d68f5ba61 Merge branch 'MDL-80364-403' of https://github.com/pedrojordao/moodle into MOODLE_403_STABLE 2024-02-29 10:45:13 +07:00
Simey Lameze 01f67edc81 MDL-68674 behat: make content bank steps more specific 2024-02-29 09:41:52 +07:00
Andrew Nicols e3d6973ed2 Merge branch 'MDL-81042-43' of https://github.com/micaherne/moodle into MOODLE_403_STABLE 2024-02-29 09:21:42 +08:00
Sara Arjona 381de34d2a Merge branch 'MDL-81029-403' of https://github.com/aanabit/moodle into MOODLE_403_STABLE 2024-02-28 17:22:04 +01:00
Sara Arjona 8e3b63a32a Merge branch 'MDL-80391-403' of https://github.com/stevandoMoodle/moodle into MOODLE_403_STABLE 2024-02-28 17:09:48 +01:00
Sara Arjona 33f4a94841 Merge branch 'mdl-80917-403' of https://github.com/Fragonite/moodle into MOODLE_403_STABLE 2024-02-28 16:44:20 +01:00
Sara Arjona 52854e2805 Merge branch 'MDL-80834_403' of https://github.com/timhunt/moodle into MOODLE_403_STABLE 2024-02-28 16:34:39 +01:00
Sara Arjona 2fe3312360 Merge branch 'MDL-80197-403' of https://github.com/pedrojordao/moodle into MOODLE_403_STABLE 2024-02-28 16:09:41 +01:00
Jun Pataleta 4abf80757d Merge branch 'MDL-68674-403' of https://github.com/lucaboesch/moodle into MOODLE_403_STABLE 2024-02-28 23:03:58 +08:00
Sara Arjona 6ed4268891 Merge branch 'MDL-52891-MOODLE_403_STABLE' of https://github.com/opitz/moodle into MOODLE_403_STABLE 2024-02-28 15:23:24 +01:00
Paul Holden 6cd156fdbc MDL-81082 tool_mfa: format site name in email subject. 2024-02-28 10:13:07 +00:00
Andrew Nicols 3a1443ee0b MDL-80684 core: Correct location of task tests 2024-02-28 14:20:54 +08:00
Andrew Nicols c108c1ead7 Merge branch 'MDL-80684-MOODLE_403_STABLE-fail_running_tasks_on_shutdown' of https://github.com/praxisdigital/moodle into MOODLE_403_STABLE 2024-02-28 14:20:49 +08:00
Stevani Andolo 5c9a7231d7 MDL-80827 tool_xmldb: Fixed xmldb editor broken with PHP 8.1 2024-02-28 10:05:00 +08:00
AMOS bot 1ad5ef6c30 Automatically generated installer lang files 2024-02-28 00:11:07 +00:00
Harry Smith e2ffae6fb6 MDL-80793 calendar: Setup $USER with all fields 2024-02-28 10:37:59 +13:00
Pedro Jordao a5e26adf33 MDL-80364 message: Change icon and background-color when item selected
Icon and background-color had contrast issues in the selected state.
2024-02-27 15:33:22 +01:00
Sara Arjona 228c50444d Merge branch 'MDL-81036-403' of https://github.com/marinaglancy/moodle into MOODLE_403_STABLE 2024-02-27 13:04:09 +01:00
Matthias Opitz 997ca5d78e MDL-52891 calculatedsimple question: forcing new gen. values into form
* forcing new generated sets of wildcard values into the question edit form so they can be saved.
* added Behat test to check new generated values are used.
2024-02-27 11:13:26 +00:00
Huong Nguyen 2abf7ccd5f Merge branch 'MDL-78984-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-02-27 09:53:45 +07:00
Michael Aherne c3c25c7899 MDL-81042 quiz: Typo in renamed class. 2024-02-26 09:05:35 +00:00
Andrew Nicols 911bbea72e MDL-81046 core: Add hook to apis list 2024-02-26 10:12:22 +08:00
Paul Holden 885a65b33a MDL-80550 cohort: get IDs for customfields correctly in external search. 2024-02-23 15:27:35 +00:00
Amaia Anabitarte f89f680035 MDL-81029 core_course: Fix activity settings page title 2024-02-23 16:04:39 +01:00
Marina Glancy 8e08798e8e MDL-81036 behat: fix the action menu clickable area selector 2024-02-23 11:22:54 +00:00
Paul Holden 064450ce1f MDL-78984 mod_assign: observe timelimit config in override form. 2024-02-23 11:18:45 +00:00
Eloy Lafuente (stronk7) 4d1aaab4c0 MDL-80819 install: halt any install if php min version is not met
Apart from environmental stuff, the moodle_minimum_php_version_is_met()
function is executed by all installs (web, cli, database, ...)
and halts any installation (early) not meeting the PHP constraints.
2024-02-23 11:08:18 +01:00
Jun Pataleta 7576c307fb Merge branch 'MDL-77965-m403' of https://github.com/NeillM/moodle into MOODLE_403_STABLE 2024-02-23 17:59:46 +08:00
Luca Bösch f3f10bfab6 MDL-68674 theme: Have block headers have h3 class="h5". 2024-02-23 10:59:44 +01:00
Paul Holden e7bf300cdf MDL-78438 gradereport_grader: use lang string for collapsed columns. 2024-02-23 09:39:12 +00:00
Neill Magill f357b3f715 MDL-77965 unit tests: Add covers annotation 2024-02-23 09:32:45 +00:00
Neill Magill 4038a3419b MDL-77965 quiz: Improve efficiency of the data export
Before this change the query used an index scan to perform the
export, this could be really expensive on the database.

After the change the the query will use far more efficient joins,
on a large MySQL instance this can take minutes off of the query
time.

In additon the query was returning two columns that are not used
in the function. The layout column was causing the UNION to fail
on Oracle as it cannot compare text columns, removing these
columns from the returned data should further increase
performance further.
2024-02-23 09:32:45 +00:00
Neill Magill fe76a2562e MDL-77965 grade: Improve data export performance
The existing query was inefficient using an index scan and
index merges. This change splits the query up into many individually
much more efficient queries.

This should significantly improve performance on large sites.
2024-02-23 09:32:45 +00:00
Neill Magill 6f9d8ca34f MDL-77965 notes: Improve data export performance
The UNION caused the query to be run in a way that is very
inefficient on MySQL, separating the queries causes each of them to
run in a much more efficient form.

Any duplicated will be filtered out on the PHP side instead of in
the database. On large Moodle sites this is preferable as the
extract is likely to be performed on a server dedicated to running
the Moodle cron and so there will be less of effect on resources
that are used to serve end users.
2024-02-23 09:32:45 +00:00
Neill Magill 1bcd7533c4 MDL-77965 calendar: Improve performance of data extract
The UNION caused the query to be run in a way that is very
inefficient on MySQL, separating the queries causes each of them to
run in a much more efficient form.

Any duplicated will be filtered out on the PHP side instead of in
the database. On large Moodle sites this is preferable as the
extract is likely to be performed on a server dedicated to running
the Moodle cron and so there will be less of effect on resources
that are used to serve end users.
2024-02-23 09:32:45 +00:00
Neill Magill ff5f100601 MDL-77965 competency: Improve query performance and data
This change removes LEFT JOINS to improve the performance
when run against Moodle sites with very large data sets.

It will also cause some additional contexts to be returned:

1. When a user has modified a user_evidence_competency
   without modifying its associated user_evidence record
2024-02-23 09:32:45 +00:00
Neill Magill 248742095d MDL-77965 competency: Correct order of expectations
The expected value was being passed second which meant that the
messages sent by PHPUnit when this fails are the wrong way round.
2024-02-23 09:32:45 +00:00
Jun Pataleta 6219f4d2ac Merge branch 'MDL-80805-m403' of https://github.com/sammarshallou/moodle into MOODLE_403_STABLE 2024-02-23 17:31:57 +08:00
Huong Nguyen b0cd9cac9a weekly release 4.3.3+ 2024-02-23 08:37:44 +07:00
Huong Nguyen be997d91fa Merge branch 'install_403_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_403_STABLE 2024-02-23 08:37:42 +07:00
Paul Holden 77c0a7cf05 MDL-80183 message: improve ARIA labelling of total/unread counts. 2024-02-22 17:03:52 +00:00
Paul Holden b26a2d7328 MDL-80183 message: render user online status as an accessible icon. 2024-02-22 17:01:00 +00:00
Paul Holden 3f8fb93834 MDL-80731 core: ensure valid /Lang attribute in generated PDF files.
As with the changes in 4be39296, PDF files have criteria for validity
of language identifiers.
2024-02-22 16:53:03 +00:00
Huong Nguyen f278206b91 Merge branch 'MDL-80844_403' of https://github.com/stronk7/moodle into MOODLE_403_STABLE 2024-02-22 11:24:00 +07:00
Huong Nguyen 3905247a84 Merge branch 'MDL-80338-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-02-22 11:18:06 +07:00
Huong Nguyen 4414c4598e Merge branch 'MDL-62404_403' of https://github.com/timhunt/moodle into MOODLE_403_STABLE 2024-02-22 10:49:52 +07:00
Huong Nguyen 897436f056 Merge branch 'MDL-80868-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-02-22 10:43:17 +07:00
Huong Nguyen c68e966686 Merge branch 'MDL-80950-403' of https://github.com/sarjona/moodle into MOODLE_403_STABLE 2024-02-22 10:40:29 +07:00
Huong Nguyen f7ed5663a7 Merge branch 'MDL-80862-403' of https://github.com/andrewnicols/moodle into MOODLE_403_STABLE 2024-02-22 10:37:49 +07:00
Huong Nguyen 8f13279477 Merge branch 'MDL-80481_403' of https://github.com/santoshndev/moodle into MOODLE_403_STABLE 2024-02-22 09:50:51 +07:00
Santosh Nagargoje f50245a6d3 MDL-80481 report: Fixed breadcrumb menu issue 2024-02-22 02:00:19 +05:30
Sara Arjona c1b7ab67c7 Merge branch 'MDL-76441_403' of https://github.com/stronk7/moodle into MOODLE_403_STABLE 2024-02-21 17:49:56 +01:00
Sara Arjona 9c195964aa Merge branch 'wip_MDL-79802_m43_v8' of https://github.com/gjb2048/moodle into MOODLE_403_STABLE 2024-02-21 17:32:05 +01:00
Mark Johnson 39af71da2c MDL-79174 availability: Allow group condition to use hidden groups
Previously, groups with GROUP_VISIBILITY_NONE could be selected for an
availability condition, but as the group API did not return a user's own
groups with this visibility, the condition's is_available check always
failed.

This change uses the new $includehidden parameter to get all of a user's
groups from groups_get_user_groups when evaluating the condition, so
these groups work as expected. Due to the enforced hiding of conditions
containing these groups, they will never be seen by the user.
2024-02-21 15:42:47 +00:00
Mark Johnson 558de0b8b4 MDL-79174 groups: Add includehidden parameter
This allows groups_get_user_groups to return groups with
GROUP_VISIBILITY_NONE that the specified user belongs to. The parameter
is false by default, and should be use with care as membership of these
groups should not be revealed to the user.
2024-02-21 15:42:47 +00:00
Sara Arjona b56fe5fff9 Merge branch 'MDL-80605-403' of https://github.com/jbs1/moodle into MOODLE_403_STABLE 2024-02-21 16:19:19 +01:00
Sara Arjona 716113cbfb Merge branch 'MDL-80404-403' of https://github.com/andelacruz/moodle into MOODLE_403_STABLE 2024-02-21 13:23:31 +01:00
Paul Holden 9eac2f3c93 MDL-80241 grade: update user filtering by matched properties.
Invert the previous logic, which maintained a "banned" list of
fields, to instead only try to filter on those fields that are
explicitly defined to support that.
2024-02-21 10:21:36 +00:00
Sara Arjona 701e4cc18d Merge branch 'MDL-78944-43' of https://github.com/Chocolate-lightning/moodle into MOODLE_403_STABLE 2024-02-21 09:18:56 +01:00
Angelia Dela Cruz 104be7a8a7 MDL-80404 mod_data: Behat for url autolinking and opening in new window 2024-02-21 16:01:56 +08:00
Huong Nguyen adf6b7bf7f Merge branch 'MDL-79792-403' of https://github.com/andelacruz/moodle into MOODLE_403_STABLE 2024-02-21 14:25:28 +07:00
Angelia Dela Cruz aaf858c6db MDL-79792 core_badges: Behat test for awarding badge to group members 2024-02-21 14:01:24 +08:00
Jun Pataleta a8fa0e61ac Merge branch 'MDL-79003-403' of https://github.com/andrewnicols/moodle into MOODLE_403_STABLE 2024-02-21 10:36:10 +08:00
Jun Pataleta 9287d34b78 MDL-78370 block_myoverview: Conditionally show "All" paging option
To minimise performance issues when displaying courses in Summary view,
we need to conditionally show the "All" paging option. This commit
will prevent the "All" paging option to be shown if the following
conditions are met:
- The display mode is "summary"; and
- There are more than 100 courses to be shown.
2024-02-21 09:43:28 +08:00
Juan Leyva 115b8e9a6c MDL-79802 tool_mobile: Support new setting in the mobile app 2024-02-20 18:09:57 +00:00
Gareth Barnard 39fcdac6d3 MDL-79802 core_h5p: Add a new setting for adding custom H5P styles. 2024-02-20 18:09:41 +00:00
Paul Holden 90d0f55fe1 MDL-80919 course: initial check for section modules to duplicate. 2024-02-20 16:22:33 +00:00
Sara Arjona 6c1b457252 Merge branch 'MDL-80912-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-02-20 15:39:24 +01:00
Sara Arjona b0c9e538e4 Merge branch 'MDL-80383-403' of https://github.com/rjnl/moodle into MOODLE_403_STABLE 2024-02-20 15:20:06 +01:00
Paul Holden 5e968b8864 MDL-80088 mod_imscp: better uploaded package filetype validation. 2024-02-20 14:10:10 +00:00
Huong Nguyen ae17dcde01 MDL-76024 qtype calculated: Fix deprecation related to dynamic property 2024-02-20 12:29:31 +01:00
Paul Holden 191ac5c44c MDL-80865 customfield_textarea: empty check prior to exporting value.
Co-authored-by: Sara Bozzini <s.bozzini@cineca.it>
2024-02-20 10:54:53 +00:00
Huong Nguyen 2eca6f3d5f Merge branch 'MDL-80936-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-02-20 10:28:31 +07:00
Huong Nguyen 42d864754a Merge branch 'MDL-76024-MOODLE_403_STABLE' of https://github.com/opitz/moodle into MOODLE_403_STABLE 2024-02-20 10:23:35 +07:00
Andrew Nicols 7653886081 MDL-80862 testing: Reset task manager between behat tests 2024-02-20 10:54:43 +08:00
AMOS bot c86674c04d Automatically generated installer lang files 2024-02-20 00:13:56 +00:00
Pedro Jordao ab85e38732 MDL-80197 forms: Added attributes to the calendar link
The datepicker is not keyboard accessible when in a modal.

As we are using yui datepicker and the change is not simple, I added aria-hidden and tabindex properties to avoid screenreaders.
2024-02-19 21:11:52 -03:00
Jeremy Schulz c847d5ea85 MDL-80605 uploaduser: fixed username already existing when mail-matching 2024-02-19 17:24:53 +01:00
Paul Holden 30fa519fa0 MDL-80912 reportbuilder: output table caption when rendering reports.
Move the `get_name` method to the base report class, so that it can be
implemented by all report types. Provide a default implementation for
system reports based on the name of the class.

Use the name of the report as the caption. Target via Behat selectors.
2024-02-19 11:57:53 +00:00
Frederik Milling Pytlick 11a3328b06 MDL-80684 core: Fail running tasks on shutdown
When you kill a process which is executing a task, it now marks it as failed.
2024-02-19 12:06:08 +01:00
Sara Arjona dac3242a2c Merge branch 'MDL-79674-403' of https://github.com/junpataleta/moodle into MOODLE_403_STABLE 2024-02-19 10:20:47 +01:00
Sara Arjona 5f20a1efc2 Merge branch 'MDL-80613-403' of https://github.com/lameze/moodle into MOODLE_403_STABLE 2024-02-19 09:14:23 +01:00
Rodrigo Mady e258e86f15 MDL-77967 user: Add format text to custom profile fields name 2024-02-16 17:16:37 -03:00
Matthias Opitz 5e3470fd4a MDL-76024 calculated questions: improving coding style 2024-02-16 10:22:07 +00:00
Matthias Opitz c93271e1d6 MDL-76024 calculated questions: cast float type before comparison
* added calculated question for multiplication to tests/helper.php.
* added test_grading_of_negative_responses() test to question/type/calculated/tests/question_test.php
2024-02-16 10:22:07 +00:00
Eloy Lafuente (stronk7) 0ed448a277 MDL-80844 phpunit: Only some tests can use the debugging sink
The only tests that have COMPLETE* support for the debugging sink
are the advanced_testcase and the database_driver ones (store and
report). So we must ensure that the rest of tests don't use the
debugging sink at all.

Right now we are using it for storing, but later there is not
reporting, so any debugging happening within non advanced tests
is not detected.

This commit just ensures that we stop making that storing for
non advanced/database_driver tests. Nothing more, nothing less.

* Note that we have had to add a few missing bits to the
database_driver testcase because it was not 100% complete. Now
it behaves 100% the same than the advanced_testcase one regarding
the debugging sink.
2024-02-15 18:46:26 +01:00
Eloy Lafuente (stronk7) ad241529f2 MDL-76441 env: Moodle 4.2.x and 4.3.x do not support PHP 8.3 2024-02-15 18:44:52 +01:00
Sara Arjona 63d96ee2d8 MDL-80950 external: Fix PHP deprecated error for explode()
This patch fixes the following error:
PHP Deprecated:  explode(): Passing null to parameter #2 ($string)
of type string is deprecated in lib/upgradelib.php on line 1299
2024-02-15 17:52:40 +01:00
sam marshall 4f3822bba9 MDL-80805 core_form: Accessibility - add aria-required to form fields
Most types of form field will now include aria-required="true" if the
field is marked as required. This causes assistive technology to inform
users that the field is required.

Before this change, in some cases (e.g. screen reader users tabbing
through fields) users were not informed that a field is required.
2024-02-15 15:00:42 +00:00
Sara Arjona e4bbd79059 weekly release 4.3.3+ 2024-02-15 15:39:42 +01:00
Sara Arjona fe2f6bbe27 Merge branch 'install_403_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_403_STABLE 2024-02-15 15:39:38 +01:00
lihidor 7f489efdde MDL-62404 mod_quiz: Delete previews after repaginate 2024-02-15 11:00:34 +00:00
Simey Lameze a0ce4d0c52 MDL-80613 behat: convert manual completion steps to use generators 2024-02-15 18:00:06 +08:00
Huong Nguyen f7f0ab2403 Merge branch 'MDL-80823-wwwroot-redirect-MOODLE_403_STABLE' of https://github.com/brendanheywood/moodle into MOODLE_403_STABLE 2024-02-15 11:13:56 +07:00
Mathew May 3fcfe24a7e MDL-78944 gradereport_grader: Spurious behat fix 2024-02-15 10:46:34 +08:00
Jake Dallimore 194baa7436 MDL-80836 auth_lti: take user through login instead of sesspiggyback
Browsers are phasing out 3rd party cookies. Those which can be set are
partitioned to the top level embedding site, so piggybacking is
prevented. This will break the account linking process. This fix swaps
the piggyback for a login round trip, as originally intended, which
resolves the issue.
2024-02-15 10:37:39 +08:00
Stevani Andolo ffdb463148 MDL-80391 tiny_html: Fixed the loose focus in window manager 2024-02-15 10:07:57 +08:00
Alexander Van der Bellen ec0c5414b2 MDL-80917 core_backup: Clear restored course cache 2024-02-15 09:09:41 +08:00
Paul Holden 7f1caa7c57 MDL-80936 reportbuilder: fix when default customfield values are shown.
We should ensure we only show a value for the custom field (default or
otherwise) when it's actually joined to an entity it represents.
2024-02-14 12:22:47 +00:00
Sara Arjona 75b3874f97 Merge branch 'MDL-79321-403' of https://github.com/andelacruz/moodle into MOODLE_403_STABLE 2024-02-14 09:35:28 +01:00
Angelia Dela Cruz 7d5ff8512a MDL-79321 mod_quiz: Behat to set quiz as interactive with multiple tries 2024-02-13 16:41:50 +08:00
Jun Pataleta 8b009ba789 Merge branch 'MDL-80402-403' of https://github.com/andelacruz/moodle into MOODLE_403_STABLE 2024-02-13 15:52:10 +08:00
Jun Pataleta 28f306aac9 Merge branch 'MDL-80766-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-02-13 15:34:29 +08:00
Angelia Dela Cruz ab38bff9df MDL-80402 mod_quiz: Behat for interactive quiz question dependency 2024-02-13 10:44:03 +08:00
Huong Nguyen 9892a5c5fc Merge branch 'MDL-80630_403' of https://github.com/PhilippImhof/moodle into MOODLE_403_STABLE 2024-02-13 09:30:50 +07:00
Huong Nguyen 5ac04b4066 Merge branch 'MDL-80869-403' of https://github.com/paulholden/moodle into MOODLE_403_STABLE 2024-02-13 09:14:36 +07:00
Huong Nguyen b83ed4284e Merge branch 'MDL-80326-403' of https://github.com/laurentdavid/moodle into MOODLE_403_STABLE 2024-02-13 08:55:50 +07:00
AMOS bot 119dc62c26 Automatically generated installer lang files 2024-02-13 00:10:27 +00:00
AMOS bot c57f73b11f Automatically generated installer lang files 2024-02-12 00:11:21 +00:00
Paul Holden 78c1fbac60 MDL-80868 question: correct deleted format plugin definitions. 2024-02-08 16:15:31 +00:00
Paul Holden 71b2e366cd MDL-80869 mod_bigbluebuttonbn: ensure test retrieves expected record. 2024-02-08 13:22:05 +00:00
Philipp Imhof ffce63d647 MDL-80630 mod_quiz: keep timer hidden across pages
If a student hides the timer, store that setting in their user
preferences in order to keep it hidden even if they go from one page
to another in a quiz.
2024-02-08 09:43:58 +01:00
Laurent David 7a03a909a1 MDL-80326 course_format: Visibility changes only when capability is set
* When an editing teacher has not the capability to change visibility
(for example if for a given module the capability is removed), the visibility
drop down menu in the card should not be available.
2024-02-08 09:21:30 +01:00
Brendan Heywood f443edd43e MDL-80823 core: Redirect to wwwroot with full path 2024-02-08 12:08:47 +11:00
Paul Holden 120e0af3de MDL-80338 core: encode moodle_url instance anchor properties. 2024-02-07 12:21:33 +00:00
Tim Hunt a99bebe766 MDL-80834 quiz editing: fix position of the 'depends on previous' icon 2024-02-06 11:31:41 +00:00
Paul Holden c58a4359e8 MDL-80766 gradereport_grader: omit max constraint for unlimited grades. 2024-01-31 18:26:40 +00:00
Jun Pataleta d48e967e89 MDL-79674 behat: Use proper tags for testing WCAG 2.1 criteria
`wcag2a` and `wcag2aa` refer to WCAG 2.0 standards. We should be using
`wcag21a` and `wcag21aa` to test against WCAG 2.1 standards.

See:
https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#axe-core-tags
2024-01-30 16:21:02 +08:00
Jun Pataleta 09414b1184 MDL-79674 behat: axe-core version fixes
* Update \behat_accessibility::run_axe_validation_for_tags()'s
  PHPDoc block to reflect the current version.
* Fix incorrect copyright tag for the library
2024-01-30 16:21:01 +08:00
Andrew Nicols 4826318d38 MDL-79003 js: Bump stylelint and components
Stylelint 15 deprecates a heap of rules which will be removed in 16.

We can't use 16 because it's ESM and requires a big restructure of our
build process.

We can temporarily silence the deprecations and then when we update our
build tooling we can make use of @stylistic/stylelint-plugin to add the
rules back.

This commit:
- bumps stylelint and related dependencies accordingly
- fixes issues identified with newer sniffs
- temporarily silences deprecation warnings
2024-01-30 12:21:03 +08:00
Andrew Nicols 2b4ddd43ae MDL-79003 js: Move rollup ratelimit to generateBundle
There are two phases of a build: Building, and then Outputting.

We were previously listening on the final event for the build phase, but
we should be listening to the final event of the output phase.
2024-01-30 12:21:03 +08:00
Andrew Nicols c8f80a6e57 MDL-79003 js: Move browserlist config to package.json 2024-01-30 12:21:03 +08:00
Andrew Nicols 1db97f7655 MDL-79003 js: Standard package bump - glob 2024-01-30 12:21:03 +08:00
Andrew Nicols acbc1e2f19 MDL-79003 js: Standard package bump - async 2024-01-30 12:21:03 +08:00
Andrew Nicols a57d584b63 MDL-79003 js: Remove grunt-contrib-uglify
We stopped using this when we updated our processes to support ESMs in
MDL-62497. Removal of the dependency was missed at the time.
2024-01-30 12:21:02 +08:00
Andrew Nicols ef0e06808a MDL-79003 js: Standard package bump - grunt 2024-01-30 12:21:02 +08:00
Andrew Nicols eeff8efe54 MDL-79003 js: Standard package bump - watchman 2024-01-30 12:21:02 +08:00
Andrew Nicols 6bfbf54f23 MDL-79003 js: Standard package bump - ajv 2024-01-30 12:21:02 +08:00
Andrew Nicols 6038897710 MDL-79003 js: Use our own jsdoc wrapper
grunt-jsdoc is abandoned and only works with an older version of jsdoc.

This is a very simple wrapper around jsdoc itself.

This commit also includes a fix for broken docs.
2024-01-30 12:21:01 +08:00
Andrew Nicols e3ec5309f6 MDL-79003 core: Bump eslint 2024-01-30 12:21:01 +08:00
Andrew Nicols d3f1b1ccbe MDL-79003 core: Bump NodeJS to lts/iron (NodeJS 20) 2024-01-30 12:21:01 +08:00
Rajneel Totaram e00b9ca316 MDL-80383 mod_book: Make book content use full width on small screens 2024-01-08 15:35:37 +12:00
513 changed files with 10081 additions and 14184 deletions
+6 -14
View File
@@ -21,7 +21,7 @@
*/
/**
* Function to generate the destination for the uglify task
* Function to generate the destination for the minification task
* (e.g. build/file.min.js). This function will be passed to
* the rename property of files array when building dynamically:
* http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically
@@ -60,7 +60,6 @@ module.exports = grunt => {
grunt.registerTask('js', ['amd', 'yui']);
// Register NPM tasks.
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-rollup');
@@ -104,6 +103,7 @@ module.exports = grunt => {
// The queue runner will run the next `size` items in the queue.
const runQueue = (size = 1) => {
queue.splice(0, size).forEach(resolve => {
grunt.log.debug(`Item resolved. Kicking off next one.`);
resolve();
});
};
@@ -113,15 +113,17 @@ module.exports = grunt => {
// The options hook is run in parallel.
// We can return an unresolved Promise which is queued for later resolution.
options: async() => {
options: async(options) => {
return new Promise(resolve => {
queue.push(resolve);
startQueue();
return options;
});
},
// When an item in the queue completes, start the next item in the queue.
buildEnd: () => {
generateBundle: (options, bundle) => {
grunt.log.debug(`Finished output phase for ${Object.keys(bundle).join(', ')}`);
runQueue();
},
};
@@ -155,16 +157,6 @@ module.exports = grunt => {
],
presets: [
['@babel/preset-env', {
targets: {
browsers: [
">0.3%",
"last 2 versions",
"not ie >= 0",
"not op_mini all",
"not Opera > 0",
"not dead"
]
},
modules: false,
useBuiltIns: false
}]
+28 -12
View File
@@ -20,17 +20,33 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
module.exports = grunt => {
// Project configuration.
grunt.config.merge({
jsdoc: {
dist: {
options: {
configure: ".grunt/jsdoc/jsdoc.conf.js",
},
},
},
});
module.exports = (grunt) => {
const path = require('path');
grunt.loadNpmTasks('grunt-jsdoc');
grunt.registerTask('jsdoc', 'Generate JavaScript documentation using jsdoc', function() {
const done = this.async();
const configuration = path.resolve('.grunt/jsdoc/jsdoc.conf.js');
grunt.util.spawn({
cmd: 'jsdoc',
args: [
'--configure',
configuration,
]
}, function(error, result, code) {
if (result.stdout) {
grunt.log.write(result.stdout);
}
if (result.stderr) {
grunt.log.error(result.stderr);
}
if (error) {
grunt.fail.fatal(`JSDoc failed with error code ${code}`);
} else {
grunt.log.write('JSDoc completed successfully'.green);
}
done();
});
});
};
+5 -1
View File
@@ -29,6 +29,7 @@ module.exports = grunt => {
// Use a fully-qualified path.
src: files,
options: {
quietDeprecationWarnings: true,
configOverrides: {
rules: {
// These rules have to be disabled in .stylelintrc for scss compat.
@@ -45,7 +46,10 @@ module.exports = grunt => {
return {
stylelint: {
scss: {
options: {syntax: 'scss'},
options: {
quietDeprecationWarnings: true,
customSyntax: 'postcss-scss',
},
src: files,
},
},
+1 -1
View File
@@ -1 +1 @@
lts/gallium
lts/iron
+16 -4
View File
@@ -1,9 +1,14 @@
{
"customSyntax": "postcss-scss",
"plugins": [
"stylelint-csstree-validator"
],
"rules": {
"csstree/validator": true,
"csstree/validator": {
"syntaxExtensions": [
"sass"
]
},
"at-rule-empty-line-before": [ "always",
{"except": [ "blockless-after-blockless"], ignore: ["after-comment", "inside-block"]}
],
@@ -45,7 +50,7 @@
"function-name-case": "lower",
"function-parentheses-newline-inside": "always-multi-line",
"function-parentheses-space-inside": "never-single-line",
"function-url-scheme-blacklist": ["data"],
"function-url-scheme-disallowed-list": ["data"],
"function-whitespace-after": "always",
"indentation": 4,
"keyframe-declaration-no-important": true,
@@ -84,12 +89,19 @@
"selector-type-no-unknown": true,
"string-no-newline": true,
"time-min-milliseconds": 100,
"unit-blacklist": ["pt"],
"unit-disallowed-list": ["pt"],
"unit-case": "lower",
"unit-no-unknown": true,
"value-keyword-case": ["lower", {"ignoreKeywords": ["/(@|$)/"]}],
"value-list-comma-newline-after": "always-multi-line",
"value-list-comma-space-after": "always-single-line",
"value-list-comma-space-before": "never",
}
},
"overrides": [
{
"files": ["**/yui/**/*.css"],
"rules": {
}
}
]
}
+10
View File
@@ -3923,6 +3923,8 @@
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_xmlrpc_usage" level="optional">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_oracle_usage" level="optional">
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
<MOODLE version="4.2" requires="3.11.8">
@@ -3939,6 +3941,7 @@
<VENDOR name="oracle" version="19" />
</DATABASE>
<PHP version="8.0.0" level="required">
<RESTRICT function="restrict_php_version_83" message="unsupportedphpversion83" />
</PHP>
<PCREUNICODE level="optional">
<FEEDBACK>
@@ -4112,6 +4115,8 @@
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mod_assignment" level="required">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_oracle_usage" level="optional">
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
<MOODLE version="4.3" requires="3.11.8">
@@ -4128,6 +4133,7 @@
<VENDOR name="oracle" version="19" />
</DATABASE>
<PHP version="8.0.0" level="required">
<RESTRICT function="restrict_php_version_83" message="unsupportedphpversion83" />
</PHP>
<PCREUNICODE level="optional">
<FEEDBACK>
@@ -4303,6 +4309,8 @@
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_db_prefix_length" level="required">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_oracle_usage" level="optional">
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
<MOODLE version="4.4" requires="4.1.2">
@@ -4494,6 +4502,8 @@
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_db_prefix_length" level="required">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_oracle_usage" level="optional">
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
</COMPATIBILITY_MATRIX>
+13
View File
@@ -38,6 +38,19 @@ if (!is_siteadmin()) {
}
$admisselector = new core_role_admins_existing_selector();
if (array_key_exists('siteadmins', $CFG->config_php_settings)) {
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('manageadmins', 'core_role'), 3);
echo $OUTPUT->notification(get_string('siteadministratorsconfigphp', 'core_role'), \core\output\notification::NOTIFY_INFO);
echo $OUTPUT->box_start();
echo $OUTPUT->paragraph(get_string('existingadmins', 'core_role'));
$admisselector->display();
echo $OUTPUT->box_end();
echo $OUTPUT->footer();
die();
}
$potentialadmisselector = new core_role_admins_potential_selector();
if ($addusersaction) {
+14
View File
@@ -41,4 +41,18 @@ if (!empty($defaulth5plib)) {
$settings->add(new admin_settings_h5plib_handler_select('h5plibraryhandler', new lang_string('h5plibraryhandler', 'core_h5p'),
new lang_string('h5plibraryhandler_help', 'core_h5p'), $defaulth5plib));
$setting = new admin_setting_configtextarea(
'core_h5p/h5pcustomcss',
new lang_string('h5pcustomcss', 'core_h5p'),
new lang_string('h5pcustomcss_help', 'core_h5p'),
'',
PARAM_NOTAGS
);
$setting->set_updatedcallback(function () {
// Enables use of file_storage constants.
\core_h5p\local\library\autoloader::register();
\core_h5p\file_storage::generate_custom_styles();
});
$settings->add($setting);
}
@@ -65,7 +65,7 @@ class delete extends base {
* Delete the DB preset
*/
public function execute(): void {
confirm_sesskey();
require_sesskey();
$this->manager->delete_preset($this->id);
@@ -52,8 +52,6 @@ class export extends base {
* Stores a preset into the DB.
*/
public function execute(): void {
confirm_sesskey();
$url = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'export', 'mode' => 'execute']);
$this->moodleform = new export_form($url);
@@ -87,7 +85,7 @@ class export extends base {
* @throws xml_writer_exception
*/
public function download_xml(): void {
confirm_sesskey();
require_sesskey();
list($xmlstr, $filename) = $this->manager->download_preset($this->id);
@@ -40,8 +40,6 @@ class import extends base {
* Imports the xmlfile into DB
*/
public function execute(): void {
confirm_sesskey();
$url = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'import', 'mode' => 'execute']);
$this->moodleform = new import_form($url);
@@ -38,8 +38,6 @@ class load extends base {
public function execute(): void {
global $OUTPUT;
confirm_sesskey();
$url = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'load', 'mode' => 'execute']);
$this->moodleform = new load_form($url);
@@ -73,7 +73,7 @@ class rollback extends base {
public function execute(): void {
global $OUTPUT;
confirm_sesskey();
require_sesskey();
list($presetapp, $rollback, $failures) = $this->manager->revert_preset($this->id);
+7 -7
View File
@@ -97,28 +97,28 @@ if ($onlycli === false) {
switch ($action) {
case 'enable':
confirm_sesskey();
require_sesskey();
$model->enable();
redirect($returnurl);
break;
case 'disable':
confirm_sesskey();
require_sesskey();
$model->update(0, false, false);
redirect($returnurl);
break;
case 'delete':
confirm_sesskey();
require_sesskey();
$model->delete();
redirect($returnurl);
break;
case 'edit':
confirm_sesskey();
require_sesskey();
$invalidcurrenttimesplitting = $model->invalid_timesplitting_selected();
$potentialtimesplittings = $model->get_potential_timesplittings();
@@ -183,7 +183,7 @@ switch ($action) {
break;
case 'evaluate':
confirm_sesskey();
require_sesskey();
if ($model->is_static()) {
throw new moodle_exception('errornostaticevaluate', 'tool_analytics');
@@ -226,7 +226,7 @@ switch ($action) {
break;
case 'scheduledanalysis':
confirm_sesskey();
require_sesskey();
if ($onlycli) {
throw new moodle_exception('erroronlycli', 'tool_analytics');
@@ -292,7 +292,7 @@ switch ($action) {
break;
case 'clear':
confirm_sesskey();
require_sesskey();
$model->clear();
redirect($returnurl);
@@ -47,7 +47,7 @@ $string['cachedef_purpose'] = 'Data purposes';
$string['cachedef_purpose_overrides'] = 'Purpose overrides in the Data privacy tool';
$string['cachedef_contextlevel'] = 'Context levels purpose and category';
$string['cancelrequest'] = 'Cancel request';
$string['cancelrequestconfirmation'] = 'Do you really want cancel this data request?';
$string['cancelrequestconfirmation'] = 'Do you want to cancel this data request?';
$string['cannotreset'] = 'Unable to reset this request. Only rejected requests can be reset.';
$string['categories'] = 'Categories';
$string['category'] = 'Category';
@@ -75,7 +75,7 @@ class manage_competency_frameworks_page implements renderable, templatable {
);
$this->navigation[] = $addpage;
$competenciesrepository = new single_button(
new moodle_url('https://archive.moodle.net/competencies'),
new moodle_url('https://moodle.net/search', ['q' => 'competency frameworks']),
get_string('competencyframeworksrepository', 'tool_lp'),
'get'
);
@@ -49,7 +49,7 @@
}
}}
<span>
<img height="12" src="{{profileimageurlsmall}}" alt="" role="presentation">
<img height="12" src="{{profileimageurlsmall}}" alt="">
<span>{{fullname}}</span>
{{#hasidentity}}
<span><small>{{identity}}</small></span>
@@ -49,7 +49,7 @@
<p>
<ul class="inline list-inline">
{{#coursemodules}}
<li class="list-inline-item"><a href="{{url}}"><img class="icon" src="{{iconurl}}"> {{name}} </a></li>
<li class="list-inline-item"><a href="{{url}}"><img class="icon" src="{{iconurl}}" alt=""> {{name}} </a></li>
{{/coursemodules}}
{{^coursemodules}}
<li class="list-inline-item"><span class="alert">{{#str}}noactivities, tool_lp{{/str}}</span></li>
@@ -50,7 +50,7 @@
}}
<span>
<a href="{{profileurl}}" title="{{#str}}viewprofile{{/str}}">
<img height="18" src="{{profileimageurlsmall}}" alt="" role="presentation">
<img height="18" src="{{profileimageurlsmall}}" alt="">
<span>{{fullname}}</span>
{{#hasidentity}}
<span><small>{{identity}}</small></span>
+13
View File
@@ -42,7 +42,20 @@ $PAGE->set_title($pagetitle);
// Logout if it was requested.
$logout = optional_param('logout', false, PARAM_BOOL);
$sesskey = optional_param('sesskey', '_none_', PARAM_RAW);
if ($logout) {
if (!confirm_sesskey($sesskey)) {
echo $OUTPUT->header();
echo $OUTPUT->confirm(
get_string('logoutconfirm'),
new moodle_url($PAGE->url, ['logout' => 1, 'sesskey' => sesskey()]),
new moodle_url('/'),
);
echo $OUTPUT->footer();
die;
}
if (!empty($SESSION->wantsurl)) {
// If we have the wantsurl, we should redirect there, to keep it intact.
$wantsurl = $SESSION->wantsurl;
@@ -0,0 +1,51 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace tool_mfa\hook;
use core\hook\described_hook;
use core\hook\stoppable_trait;
/**
* Allow plugins to callback as soon possible after user has passed MFA.
*
* @package tool_mfa
* @copyright 2024 Juan Leyva
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class after_user_passed_mfa implements
described_hook,
\Psr\EventDispatcher\StoppableEventInterface {
use stoppable_trait;
/**
* Describes the hook purpose.
*
* @return string
*/
public static function get_hook_description(): string {
return 'Allow plugins to callback as soon possible after user has passed MFA.';
}
/**
* List of tags that describe this hook.
*
* @return string[]
*/
public static function get_hook_tags(): array {
return ['login'];
}
}
+4
View File
@@ -332,6 +332,10 @@ class manager {
$event = \tool_mfa\event\user_passed_mfa::user_passed_mfa_event($USER);
$event->trigger();
// Allow plugins to callback as soon possible after user has passed MFA.
$hook = new \tool_mfa\hook\after_user_passed_mfa();
\core\hook\manager::get_instance()->dispatch($hook);
// Add/update record in DB for users last mfa auth.
self::update_pass_time();
+4 -1
View File
@@ -16,6 +16,7 @@
namespace tool_mfa\output;
use core\context\system;
use tool_mfa\local\factor\object_factor;
use tool_mfa\local\form\login_form;
use \html_writer;
@@ -222,7 +223,8 @@ class renderer extends \plugin_renderer_base {
// Support link.
$supportemail = $CFG->supportemail;
if (!empty($supportemail)) {
$subject = get_string('email:subject', 'tool_mfa', $SITE->fullname);
$subject = get_string('email:subject', 'tool_mfa',
format_string($SITE->fullname, true, ['context' => system::instance()]));
$maillink = \html_writer::link("mailto:$supportemail?Subject=$subject", $supportemail);
$notification .= get_string('error:support', 'tool_mfa');
$notification .= \html_writer::tag('p', $maillink);
@@ -635,6 +637,7 @@ class renderer extends \plugin_renderer_base {
'hasadditionalfactors' => $hasadditionalfactors,
'additionalfactors' => $alladitionalfactors,
'authurl' => $authurl->out(),
'sesskey' => sesskey(),
'supportlink' => $this->get_support_link(),
'disablefactor' => $disablefactor
];
+1 -1
View File
@@ -24,7 +24,7 @@
*/
// Ignore coding standards for login check, this page does not require login.
// @codingStandardsIgnoreStart
require_once(__DIR__ . '../../../../../../config.php');
require_once(__DIR__ . '/../../../../../config.php');
$instanceid = required_param('instance', PARAM_INT);
$pass = optional_param('pass', '0', PARAM_INT);
$secret = optional_param('secret', 0, PARAM_INT);
+1 -2
View File
@@ -31,8 +31,7 @@ require_once($CFG->libdir.'/tablelib.php');
require_login(null, false);
require_capability('moodle/site:config', context_system::instance());
global $_SERVER;
$returnurl = $_SERVER['HTTP_REFERER'];
$returnurl = get_local_referer(false);
$PAGE->set_url('/admin/tool/mfa/index.php');
+4
View File
@@ -112,6 +112,10 @@ function tool_mfa_after_config(): void {
* @return array
*/
function tool_mfa_bulk_user_actions(): array {
if (!has_capability('moodle/site:config', context_system::instance())) {
return [];
}
return [
'tool_mfa_reset_factors' => new action_link(
new moodle_url('/admin/tool/mfa/reset_factor.php'),
@@ -83,6 +83,7 @@
<div class="d-flex justify-content-center">
<form id="cancelmfa" method="POST" action="{{{authurl}}}">
<input type="hidden" id="{{uniqid}}-logout" name="logout" value="true">
<input type="hidden" name="sesskey" value="{{sesskey}}">
<input type="submit" class="btn btn-secondary"
name="cancelmfa" id="cancelmfa_button"
value="{{#str}} cancellogin, tool_mfa {{/str}}">
+8
View File
@@ -388,6 +388,14 @@ class api {
$CFG->searchbanner, FORMAT_HTML, $context)[0];
}
if (empty($section) || $section === 'h5psettings') {
\core_h5p\local\library\autoloader::register();
$customcss = \core_h5p\file_storage::get_custom_styles();
if (!empty($customcss)) {
$settings->h5pcustomcssurl = $customcss['cssurl']->out() . '?ver=' . $customcss['cssversion'];
}
}
return $settings;
}
@@ -0,0 +1,55 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace tool_mobile;
use core\session\utility\cookie_helper;
/**
* Allows plugins to add any elements to the footer.
*
* @package tool_mobile
* @copyright 2024 Juan Leyva
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class hook_callbacks {
/**
* Callback to recover $SESSION->wantsurl.
*
* @param \core_user\hook\after_login_completed $hook
*/
public static function after_login_completed(
\core_user\hook\after_login_completed $hook,
): void {
global $SESSION, $CFG;
// Check if the user is doing a mobile app launch, if that's the case, ensure $SESSION->wantsurl is correctly set.
if (!NO_MOODLE_COOKIES && !empty($_COOKIE['tool_mobile_launch'])) {
if (empty($SESSION->wantsurl) || strpos($SESSION->wantsurl, '/tool/mobile/launch.php') === false) {
$params = json_decode($_COOKIE['tool_mobile_launch'], true);
$SESSION->wantsurl = (new \moodle_url("/$CFG->admin/tool/mobile/launch.php", $params))->out(false);
}
}
// Set Partitioned and Secure attributes to the MoodleSession cookie if the user is using the Moodle app.
if (\core_useragent::is_moodle_app()) {
cookie_helper::add_attributes_to_cookie_response_header(
'MoodleSession' . $CFG->sessioncookie,
['Secure', 'Partitioned'],
);
}
}
}
@@ -0,0 +1,49 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace tool_mobile\local\hooks\user;
/**
* Handles mobile app launches when third-party auth plugins are put in front of MFA.
*
* @package tool_mobile
* @copyright 2024 Juan Leyva
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class after_user_passed_mfa {
/**
* Callback to recover $SESSION->wantsurl.
*
* @param \tool_mfa\hook\after_user_passed_mfa $hook
*/
public static function callback(\tool_mfa\hook\after_user_passed_mfa $hook): void {
global $SESSION, $CFG;
// Check if the user is doing a mobile app launch, if that's the case, ensure $SESSION->wantsurl is correctly set.
if (!NO_MOODLE_COOKIES && !empty($_COOKIE['tool_mobile_launch'])) {
if (empty($SESSION->wantsurl) || strpos($SESSION->wantsurl, '/tool/mobile/launch.php') === false) {
$params = json_decode($_COOKIE['tool_mobile_launch'], true);
$SESSION->wantsurl = (new \moodle_url("/$CFG->admin/tool/mobile/launch.php", $params))->out(false);
$SESSION->tool_mfa_has_been_redirected = true; // Indicate MFA that they need to follow $SESSION->wantsurl.
}
// Invalidate cookie as we won't be needing it anymore.
unset($_COOKIE['tool_mobile_launch']);
if (!headers_sent()) { // Just be very cautios as this is a critical code.
setcookie('tool_mobile_launch', '', -1, $CFG->sessioncookiepath);
}
}
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Hook callbacks for Moodle app tools
*
* @package tool_mobile
* @copyright 2024 Juan Leyva
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$callbacks = [
[
'hook' => \core_user\hook\after_login_completed::class,
'callback' => \tool_mobile\hook_callbacks::class . '::after_login_completed',
'priority' => 500,
],
[
'hook' => tool_mfa\hook\after_user_passed_mfa::class,
'callback' => 'tool_mobile\local\hooks\user\after_user_passed_mfa::callback',
'priority' => 500,
],
];
+17 -7
View File
@@ -44,6 +44,18 @@ if (!$CFG->enablewebservices) {
throw new moodle_exception('enablewsdescription', 'webservice');
}
// Check if the service exists and is enabled.
$service = $DB->get_record('external_services', ['shortname' => $serviceshortname, 'enabled' => 1]);
if (empty($service)) {
throw new moodle_exception('servicenotavailable', 'webservice');
}
// Set a cookie indicating that there was a launch to authenticate via the site from the app.
$ldata = json_encode(['service' => $serviceshortname, 'passport' => $passport,
'urlscheme' => $urlscheme, 'confirmed' => (int) $confirmed, 'oauthsso' => $oauthsso]);
$expires = time() + (15 * MINSECS); // 15 minutes for authentication should be enough.
setcookie('tool_mobile_launch', $ldata, $expires, $CFG->sessioncookiepath, $CFG->sessioncookiedomain);
// We have been requested to start a SSO process via OpenID.
if (!empty($oauthsso) && is_enabled_auth('oauth2')) {
$wantsurl = new moodle_url('/admin/tool/mobile/launch.php',
@@ -63,17 +75,15 @@ if (empty($SESSION->justloggedin) &&
throw new moodle_exception('pluginnotenabledorconfigured', 'tool_mobile');
}
// Check if the service exists and is enabled.
$service = $DB->get_record('external_services', array('shortname' => $serviceshortname, 'enabled' => 1));
if (empty($service)) {
throw new moodle_exception('servicenotavailable', 'webservice');
}
require_login(0, false);
// Require an active user: not guest, not suspended.
core_user::require_active_user($USER);
// Remove cookie.
unset($_COOKIE['tool_mobile_launch']);
setcookie('tool_mobile_launch', '', -1, $CFG->sessioncookiepath);
// Get an existing token or create a new one.
$timenow = time();
$token = \core_external\util::generate_token_for_current_user($service);
@@ -126,7 +136,7 @@ if ($confirmed or $isios) {
}
$notice = get_string('clickheretolaunchtheapp', 'tool_mobile');
echo html_writer::link($location, $notice, array('id' => 'launchapp'));
echo $OUTPUT->box(html_writer::link($location, $notice, ['id' => 'launchapp']), 'generalbox warning centerpara');
echo html_writer::script(
"window.onload = function() {
document.getElementById('launchapp').click();
+12
View File
@@ -271,3 +271,15 @@ function tool_mobile_pre_processor_message_send($procname, $data) {
$data->fullmessagehtml .= html_writer::tag('p', get_string('readingthisemailgettheapp', 'tool_mobile', $url->out()));
}
}
/**
* Callback to add headers before the HTTP headers are sent.
*
*/
function tool_mobile_before_http_headers() {
global $CFG;
// Set Partitioned and Secure attributes to the MoodleSession cookie if the user is using the Moodle app.
if (\core_useragent::is_moodle_app()) {
\core\session\utility\cookie_helper::add_attributes_to_cookie_response_header('MoodleSession'.$CFG->sessioncookie, ['Secure', 'Partitioned']);
}
}
@@ -270,6 +270,18 @@ class externallib_test extends externallib_advanced_testcase {
$this->assertCount(0, $result['warnings']);
$this->assertEquals($expected, $result['settings']);
// H5P custom CSS.
set_config('h5pcustomcss', '.debug { color: #fab; }', 'core_h5p');
\core_h5p\local\library\autoloader::register();
\core_h5p\file_storage::generate_custom_styles();
$result = external::get_config();
$result = external_api::clean_returnvalue(external::get_config_returns(), $result);
$customcss = \core_h5p\file_storage::get_custom_styles();
$expected[] = ['name' => 'h5pcustomcssurl', 'value' => $customcss['cssurl']->out() . '?ver=' . $customcss['cssversion']];
$this->assertCount(0, $result['warnings']);
$this->assertEquals($expected, $result['settings']);
// Change a value and retrieve filtering by section.
set_config('commentsperpage', 1);
$expected[10]['value'] = 1;
+11
View File
@@ -688,6 +688,17 @@ class process {
break;
case UU_USER_ADD_UPDATE:
if ($this->get_match_on_email()) {
if ($usersbyname = $DB->get_records('user', ['username' => $user->username])) {
foreach ($usersbyname as $userbyname) {
if (strtolower($userbyname->email) != strtolower($user->email)) {
$this->usersskipped++;
$this->upt->track('status', get_string('usernotaddedusernameexists', 'error'), 'warning');
$skip = true;
}
}
}
}
break;
case UU_USER_UPDATE:
@@ -50,7 +50,7 @@ The example text file may also be used for testing, as you are able to preview u
$string['infoprefix'] = 'Info:';
$string['invalidupdatetype'] = 'This option cannot be selected with the chosen upload type.';
$string['invaliduserdata'] = 'Invalid data detected for user {$a} and it has been automatically cleaned.';
$string['invaliduserdatavalues'] = 'Invalid data detected for user \'{$a->username}\' ({$a->values}), which has been automatically cleaned.';
$string['invaliduserdatavalues'] = 'Incorrect data ({$a->values}) found for user {$a->username}. This data has been corrected or deleted.';
$string['invalidtheme'] = 'Theme "{$a}" is not installed and will be ignored.';
$string['linex'] = 'Line {$a}';
$string['matchemail'] = 'Match on email address';
@@ -296,3 +296,25 @@ Feature: Upload users
And I navigate to "Users > Accounts > Browse list of users" in site administration
And I should see "Bilbo Baggins"
And I should not see "Frodo Baggins"
@javascript
Scenario: Create a new user when matching them on email where where the username already exists
Given the following "users" exist:
| username | firstname | lastname | email |
| bilbob | Samwise | Gamgee | samwise@example.com |
| frodob | Frodeo | Baspins | frodo@example.com |
And I log in as "admin"
And I navigate to "Users > Accounts > Upload users" in site administration
When I upload "lib/tests/fixtures/upload_users_email_matching.csv" file to "File" filemanager
And I press "Upload users"
Then I should see "Upload users preview"
And I set the following fields to these values:
| Upload type | Add new and update existing users |
| Existing user details | Override with file |
| Match on email address | Yes |
And I press "Upload users"
And I should see "User not added - username already exists under a different email"
And I press "Continue"
And I navigate to "Users > Accounts > Browse list of users" in site administration
And I should see "Samwise Gamgee"
And I should see "Frodo Baggins"
@@ -202,8 +202,8 @@ EOF;
// We should get the debugging from the user class itself, as well as warning in the output regarding the same.
$this->assertDebuggingCalled('The property \'country\' has invalid data and has been cleaned.');
$this->assertStringContainsString('Invalid data detected for user \'student1\' (country), ' .
'which has been automatically cleaned.', $output);
$this->assertStringContainsString('Incorrect data (country) found for user student1. ' .
'This data has been corrected or deleted.', $output);
}
/**
+1 -1
View File
@@ -283,7 +283,7 @@ class manager {
'title' => get_string('importtour', 'tool_usertours'),
],
(object) [
'link' => new \moodle_url('https://archive.moodle.net/tours'),
'link' => new \moodle_url('https://moodle.net/search', ['q' => 'user tours']),
'linkproperties' => [
'target' => '_blank',
],
@@ -91,12 +91,12 @@ class edit_field_save extends XMLDBAction {
$comment = trim($comment);
$type = required_param('type', PARAM_INT);
$length = strtolower(optional_param('length', NULL, PARAM_ALPHANUM));
$length = optional_param('length', null, PARAM_INT);
$decimals = optional_param('decimals', NULL, PARAM_INT);
$notnull = optional_param('notnull', false, PARAM_BOOL);
$sequence = optional_param('sequence', false, PARAM_BOOL);
$default = optional_param('default', NULL, PARAM_PATH);
$default = trim($default);
$default = is_null($default) ? $default : trim($default);
$editeddir = $XMLDB->editeddirs[$dirpath];
$structure = $editeddir->xml_file->getStructure();
+1 -1
View File
@@ -115,7 +115,7 @@ class auth_plugin_lti extends \auth_plugin_base {
if (isloggedin()) {
// If a different user is currently logged in, authenticate the linked user instead.
global $USER;
if ((int) $USER->id !== $user->id) {
if ($USER->id !== $user->id) {
complete_user_login($user);
}
// If the linked user is already logged in, skip the call to complete_user_login() because this affects deep linking
@@ -0,0 +1,48 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace auth_lti\local\ltiadvantage\event;
use auth_lti\local\ltiadvantage\utility\cookie_helper;
use core\event\user_loggedin;
/**
* Event handler for auth_lti.
*
* @package auth_lti
* @copyright 2024 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class event_handler {
/**
* Allows the plugin to augment Set-Cookie headers when the user_loggedin event is fired as part of complete_user_login() calls.
*
* @param user_loggedin $event the event
* @return void
*/
public static function handle_user_loggedin(user_loggedin $event): void {
// The event data isn't important here. The intent of this listener is to ensure that the MoodleSession cookie is set up
// properly during LTI launches + login. This means two things:
// i) it's set with SameSite=None; Secure; where possible (since OIDC needs HTTPS this will almost always be possible).
// ii) it set with the 'Partitioned' attribute, when required.
// The former ensures cross-site cookies are sent for embedded launches. The latter is an opt-in flag needed to use Chrome's
// partitioning mechanism, CHIPS.
if (cookie_helper::cookies_supported()) {
cookie_helper::setup_session_cookie();
}
}
}
@@ -0,0 +1,173 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace auth_lti\local\ltiadvantage\utility;
use core\session\utility\cookie_helper as core_cookie_helper;
/**
* Helper class providing utils dealing with cookies in LTI, particularly 3rd party cookies.
*
* @package auth_lti
* @copyright 2024 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class cookie_helper {
/** @var int Cookies are not supported. */
public const COOKIE_METHOD_NOT_SUPPORTED = 0;
/** @var int Cookies are supported without explicit partitioning. */
public const COOKIE_METHOD_NO_PARTITIONING = 1;
/** @var int Cookies are supported via explicit partitioning. */
public const COOKIE_METHOD_EXPLICIT_PARTITIONING = 2;
/**
* Check whether cookies can be used with the current user agent and, if so, via what method they are set.
*
* Currently, this tries 2 modes of setting a test cookie:
* 1. Setting a SameSite=None, Secure cookie. This will work in any first party context, and in 3rd party contexts for
* any browsers supporting automatic partitioning of 3rd party cookies (E.g. Firefox, Brave).
* 2. If 1 fails, setting a cookie with the Chrome 'Partitioned' attribute included, opting that cookie into CHIPS. This will
* work for Chrome.
*
* Upon completion of the cookie check, the check sets a SESSION flag indicating the method used to set the cookie, and upgrades
* the session cookie ('MoodleSession') using the respective method. This ensure the session cookie will continue to be sent.
*
* Then, the following methods can be used by client code to query whether the UA supports cookies, and how:
* @see self::cookies_supported() - whether it could be set at all.
* @see self::get_cookies_supported_mode() - if a cookie could be set, what mode was used to set it.
*
* This permits client code to make sure it's setting its cookies appropriately (via the advertised method), and allows it to
* present notices - such as in the case where a given UA is found to be lacking the requisite cookie support.
* E.g.
* cookie_helper::do_cookie_check($mypageurl);
* if (!cookie_helper::cookies_supported()) {
* // Print a notice stating that cookie support is required.
* }
* // Elsewhere in other client code...
* if (cookie_helper::get_cookies_supported_mode() === cookie_helper::COOKIE_METHOD_EXPLICIT_PARTITIONING) {
* // Set a cookie, making sure to use the helper to also opt-in to partitioning.
* setcookie('myauthcookie', 'myauthcookievalue', ['samesite' => 'None', 'secure' => true]);
* cookie_helper::add_partitioning_to_cookie('myauthcookie');
* }
*
* @param \moodle_url $pageurl the URL of the page making the check, used to redirect back to after setting test cookies.
* @return void
*/
public static function do_cookie_check(\moodle_url $pageurl): void {
global $_COOKIE, $SESSION, $CFG;
$cookiecheck1 = optional_param('cookiecheck1', null, PARAM_INT);
$cookiecheck2 = optional_param('cookiecheck2', null, PARAM_INT);
if (empty($cookiecheck1)) {
// Start the cookie check. Set two test cookies - one samesite none, and one partitioned - and redirect.
// Set cookiecheck to show the check has started.
self::set_test_cookie('cookiecheck1', self::COOKIE_METHOD_NO_PARTITIONING);
self::set_test_cookie('cookiecheck2', self::COOKIE_METHOD_EXPLICIT_PARTITIONING, true);
$pageurl->params([
'cookiecheck1' => self::COOKIE_METHOD_NO_PARTITIONING,
'cookiecheck2' => self::COOKIE_METHOD_EXPLICIT_PARTITIONING,
]);
// LTI needs to guarantee the 'SameSite=None', 'Secure' (and sometimes 'Partitioned') attributes are set on the
// MoodleSession cookie. This is done via manipulation of the outgoing headers after the cookie check redirect. To
// guarantee these outgoing Set-Cookie headers will be created after the redirect, expire the current cookie.
core_cookie_helper::expire_moodlesession();
redirect($pageurl);
} else {
// Have already started a cookie check, so check the result.
$cookie1received = isset($_COOKIE['cookiecheck1']) && $_COOKIE['cookiecheck1'] == $cookiecheck1;
$cookie2received = isset($_COOKIE['cookiecheck2']) && $_COOKIE['cookiecheck2'] == $cookiecheck2;
if ($cookie1received || $cookie2received) {
// The test cookie could be set and received.
// Set a session flag storing the method used to set it, and make sure the session cookie uses this method.
$cookiemethod = $cookie1received ? self::COOKIE_METHOD_NO_PARTITIONING : self::COOKIE_METHOD_EXPLICIT_PARTITIONING;
$SESSION->auth_lti_cookie_method = $cookiemethod;
self::setup_session_cookie();
}
}
}
/**
* If a cookie check has been made, returns whether cookies could be set or not.
*
* @return bool whether cookies are supported or not.
*/
public static function cookies_supported(): bool {
return self::get_cookies_supported_method() !== self::COOKIE_METHOD_NOT_SUPPORTED;
}
/**
* If a cookie check has been made, gets the method used to set a cookie, or self::COOKIE_METHOD_NOT_SUPPORTED if not supported.
*
* For cookie methods:
* @see self::COOKIE_METHOD_NOT_SUPPORTED
* @see self::COOKIE_METHOD_NO_PARTITIONING
* @see self::COOKIE_METHOD_EXPLICIT_PARTITIONING
*
* @return int the constant representing the method by which the cookie was set, or not.
*/
public static function get_cookies_supported_method(): int {
global $SESSION;
return $SESSION->auth_lti_cookie_method ?? self::COOKIE_METHOD_NOT_SUPPORTED;
}
/**
* Sets up the session cookie according to the method used in the cookie check, and with SameSite=None; Secure attributes.
*
* @return void
*/
public static function setup_session_cookie(): void {
global $CFG;
require_once($CFG->libdir . '/sessionlib.php');
if (is_moodle_cookie_secure()) {
$atts = ['SameSite=None', 'Secure'];
if (self::get_cookies_supported_method() == self::COOKIE_METHOD_EXPLICIT_PARTITIONING) {
$atts[] = 'Partitioned';
}
core_cookie_helper::add_attributes_to_cookie_response_header('MoodleSession' . $CFG->sessioncookie, $atts);
}
}
/**
* Set a test cookie, using SameSite=None; Secure; attributes if possible, and with or without partitioning opt-in.
*
* @param string $name cookie name
* @param string $value cookie value
* @param bool $partitioned whether to try to add partitioning opt-in, which requires secure cookies (https sites).
* @return void
*/
private static function set_test_cookie(string $name, string $value, bool $partitioned = false): void {
global $CFG;
require_once($CFG->libdir . '/sessionlib.php');
$atts = ['expires' => time() + 30];
if (is_moodle_cookie_secure()) {
$atts['samesite'] = 'none';
$atts['secure'] = true;
}
setcookie($name, $value, $atts);
if (is_moodle_cookie_secure() && $partitioned) {
core_cookie_helper::add_attributes_to_cookie_response_header($name, ['Partitioned']);
}
}
}
+5 -6
View File
@@ -33,33 +33,32 @@ class renderer extends \plugin_renderer_base {
* @return string the html.
*/
public function render_account_binding_options_page(int $provisioningmode): string {
$formaction = new \moodle_url('/auth/lti/login.php');
$notification = new notification(get_string('firstlaunchnotice', 'auth_lti'), \core\notification::INFO, false);
$noauthnotice = new notification(get_string('firstlaunchnoauthnotice', 'auth_lti', get_docs_url('Publish_as_LTI_tool')),
\core\notification::WARNING, false);
$cancreateaccounts = !get_config('moodle', 'authpreventaccountcreation');
if ($provisioningmode == \auth_plugin_lti::PROVISIONING_MODE_PROMPT_EXISTING_ONLY) {
$cancreateaccounts = false;
}
$accountinfo = ['isloggedin' => isloggedin()];
$accountinfo = [];
if (isloggedin()) {
global $USER;
$accountinfo = array_merge($accountinfo, [
$accountinfo = [
'firstname' => $USER->firstname,
'lastname' => $USER->lastname,
'email' => $USER->email,
'picturehtml' => $this->output->user_picture($USER, ['size' => 35, 'class' => 'round']),
]);
];
}
$context = [
'isloggedin' => isloggedin(),
'info' => $notification->export_for_template($this),
'formaction' => $formaction->out(),
'sesskey' => sesskey(),
'accountinfo' => $accountinfo,
'cancreateaccounts' => $cancreateaccounts,
'noauthnotice' => $noauthnotice->export_for_template($this)
];
return parent::render_from_template('auth_lti/local/ltiadvantage/login', $context);
}
+33
View File
@@ -0,0 +1,33 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* LTI Auth plugin event handler definition.
*
* @package auth_lti
* @category event
* @copyright 2024 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$observers = [
[
'eventname' => '\core\event\user_loggedin',
'callback' => '\auth_lti\local\ltiadvantage\event\event_handler::handle_user_loggedin',
],
];
+1 -1
View File
@@ -35,7 +35,7 @@ $string['firstlaunchnoauthnotice'] = 'To link your existing account you must be
$string['getstartedwithnewaccount'] = 'Get started with a new account';
$string['haveexistingaccount'] = 'I have an existing account';
$string['linkthisaccount'] = 'Link this account';
$string['mustbeloggedin'] = 'You need to be logged in to your existing account';
$string['mustbeloggedin'] = 'Log in to link your existing account.';
$string['pluginname'] = 'LTI';
$string['privacy:metadata:auth_lti'] = 'LTI authentication';
$string['privacy:metadata:auth_lti:authsubsystem'] = 'This plugin is connected to the authentication subsystem.';
@@ -32,7 +32,6 @@
* info - a notification describing the first launch options
* cancreateaccounts - whether or not the user is allowed to create auth_lti accounts
* accountinfo - information about the user, importantly whether they are logged in or not.
* noauthnotice - a notification telling the user they must be authenticated to link accounts. Only relevant when not logged in.
Example context (json):
{
@@ -46,19 +45,12 @@
"issuccess": true
},
"cancreateaccounts": true,
"isloggedin": true,
"accountinfo": {
"isloggedin": true,
"firstname": "John",
"lastname": "Smith",
"email": "john@example.com",
"picturehtml": "<img src=\"http://site.example.com/pluginfile.php/5/user/icon/boost/f2?rev=99\" class=\"round\" alt=\"\" width=\"35\" height=\"35\">"
},
"noauthnotice": {
"message": "To link your existing account you must be logged in to the site...",
"extraclasses": "",
"announce": false,
"closebutton": false,
"iswarning": true
}
}
}}
@@ -79,8 +71,8 @@
<div class="card-body text-center d-flex flex-column">
<i class="fa fa-user-circle-o fa-2x link"></i>
<h4 class="card-title">{{#str}} useexistingaccount, auth_lti {{/str}}</h4>
{{#accountinfo}}
{{#isloggedin}}
{{#accountinfo}}
<p class="card-text mt-2">
<span class="text-muted">
{{#str}} currentlyloggedinas, auth_lti {{/str}}
@@ -90,14 +82,12 @@
{{firstname}} {{lastname}} ({{email}})
</p>
<input type="submit" class="btn btn-primary mt-auto" name="existing_account" value="{{#str}} linkthisaccount, auth_lti {{/str}}">
{{/accountinfo}}
{{/isloggedin}}
{{^isloggedin}}
<p class="card-text text-muted">{{#str}} mustbeloggedin, auth_lti {{/str}}</p>
{{#noauthnotice}}
{{> core/notification}}
{{/noauthnotice}}
<input type="submit" class="btn btn-primary mt-auto" name="existing_account" value="{{#str}} login, moodle {{/str}}">
{{/isloggedin}}
{{/accountinfo}}
</div>
</div>
</div>
+1 -1
View File
@@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2023100900; // The current plugin version (Date: YYYYMMDDXX).
$plugin->version = 2023100901; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2023100400; // Requires this Moodle version.
$plugin->component = 'auth_lti'; // Full name of the plugin (used for diagnostics).
+28
View File
@@ -49,6 +49,9 @@ abstract class info {
/** @var tree Availability configuration, decoded from JSON; null if unset */
protected $availabilitytree;
/** @var array The groups the current user belongs to. */
protected $groups;
/** @var array|null Array of information about current restore if any */
protected static $restoreinfo = null;
@@ -65,6 +68,7 @@ abstract class info {
$this->course = $course;
$this->visible = (bool)$visible;
$this->availability = $availability;
$this->groups = null;
}
/**
@@ -800,4 +804,28 @@ abstract class info {
}
return false;
}
/**
* Returns groups that the current user belongs to on the course. Note: If not already
* available, this may make a database query.
*
* This will include groups the user is not allowed to see themselves, so check visibility
* before displaying groups to the user.
*
* @param int $groupingid Grouping ID or 0 (default) for all groups
* @return int[] Array of int (group id) => int (same group id again); empty array if none
*/
public function get_groups(int $groupingid = 0): array {
global $USER;
if (is_null($this->groups)) {
$allgroups = groups_get_user_groups($this->course->id, $USER->id, true);
$this->groups = $allgroups;
} else {
$allgroups = $this->groups;
}
if (!isset($allgroups[$groupingid])) {
return [];
}
return $allgroups[$groupingid];
}
}
@@ -71,7 +71,7 @@ class condition extends \core_availability\condition {
$allow = true;
if (!has_capability('moodle/site:accessallgroups', $context, $userid)) {
// Get all groups the user belongs to.
$groups = $info->get_modinfo()->get_groups();
$groups = $info->get_groups();
if ($this->groupid) {
$allow = in_array($this->groupid, $groups);
} else {
@@ -108,3 +108,49 @@ Feature: availability_group
When I am on the "C1" "Course" page logged in as "student1"
Then I should see "Not available unless: You belong to G-One"
And I should not see "G-Un"
@javascript
Scenario: Condition using a hidden group
Given the following "groups" exist:
| name | course | idnumber | visibility |
| Hidden Group | C1 | GA | 3 |
And I log in as "teacher1"
And I add a page activity to course "Course 1" section "1"
And I expand all fieldsets
# Page P1 any group.
And I am on the "P1" "page activity editing" page
And I expand all fieldsets
And I click on "Add restriction..." "button"
And "Group" "button" should exist in the "Add restriction..." "dialogue"
And I click on "Group" "button" in the "Add restriction..." "dialogue"
And I set the field "Group" to "(Any group)"
And I click on ".availability-item .availability-eye img" "css_element"
And I click on "Save and return to course" "button"
# Page P2 with hidden group.
And I am on the "P2" "page activity editing" page
And I expand all fieldsets
And I click on "Add restriction..." "button"
And I click on "Group" "button" in the "Add restriction..." "dialogue"
And I set the field "Group" to "Hidden Group"
And I click on "Save and return to course" "button"
# Log back in as student.
When I am on the "Course 1" "course" page logged in as "student1"
# No pages should appear yet.
Then I should not see "P1" in the "region-main" "region"
And I should not see "P2" in the "region-main" "region"
And I should not see "Hidden Group"
# Add to groups and log out/in again.
And the following "group members" exist:
| user | group |
| student1 | GA |
And I am on "Course 1" course homepage
# P1 (any groups) and P2 should show. The user should not see the hidden group mentioned anywhere.
And I should see "P1" in the "region-main" "region"
And I should see "P2" in the "region-main" "region"
And I should not see "Hidden Group"
@@ -49,6 +49,7 @@ class condition_test extends \advanced_testcase {
$course = $generator->create_course();
$user = $generator->create_user();
$generator->enrol_user($user->id, $course->id);
$this->setUser($user);
$info = new \core_availability\mock_info($course, $user->id);
// Make 2 test groups, one in a grouping and one not.
@@ -70,7 +71,7 @@ class condition_test extends \advanced_testcase {
// Add user to groups and refresh cache.
groups_add_member($group1, $user);
groups_add_member($group2, $user);
get_fast_modinfo($course->id, 0, true);
$info = new \core_availability\mock_info($course, $user->id);
// Recheck.
$this->assertTrue($cond->is_available(false, $info, true, $user->id));
+13 -3
View File
@@ -168,6 +168,7 @@ class backup_course_task extends backup_task {
$content = self::encode_links_helper($content, 'BADGESVIEWBYID', '/badges/view.php?type=2&id=');
$content = self::encode_links_helper($content, 'USERINDEXVIEWBYID', '/user/index.php?id=');
$content = self::encode_links_helper($content, 'PLUGINFILEBYCONTEXT', '/pluginfile.php/');
$content = self::encode_links_helper($content, 'PLUGINFILEBYCONTEXTURLENCODED', '/pluginfile.php/', true);
return $content;
}
@@ -178,17 +179,26 @@ class backup_course_task extends backup_task {
* @param string $name the name of this type of encoded link.
* @param string $path the path that identifies this type of link, up
* to the ?paramname= bit.
* @param bool $urlencoded whether to use urlencode() before replacing the path.
* @return string content with one type of link encoded.
*/
static private function encode_links_helper($content, $name, $path) {
private static function encode_links_helper(string $content, string $name, string $path, bool $urlencoded = false) {
global $CFG;
// We want to convert both http and https links.
$root = $CFG->wwwroot;
$httpsroot = str_replace('http://', 'https://', $root);
$httproot = str_replace('https://', 'http://', $root);
$httpsbase = preg_quote($httpsroot . $path, '/');
$httpbase = preg_quote($httproot . $path, '/');
$httpsbase = $httpsroot . $path;
$httpbase = $httproot . $path;
if ($urlencoded) {
$httpsbase = urlencode($httpsbase);
$httpbase = urlencode($httpbase);
}
$httpsbase = preg_quote($httpsbase, '/');
$httpbase = preg_quote($httpbase, '/');
$return = preg_replace('/(' . $httpsbase . ')([0-9]+)/', '$@' . $name . '*$2@$', $content);
$return = preg_replace('/(' . $httpbase . ')([0-9]+)/', '$@' . $name . '*$2@$', $return);
@@ -167,6 +167,7 @@ class restore_course_task extends restore_task {
$rules[] = new restore_decode_rule('BADGESVIEWBYID', '/badges/view.php?type=2&id=$1', 'course');
$rules[] = new restore_decode_rule('USERINDEXVIEWBYID', '/user/index.php?id=$1', 'course');
$rules[] = new restore_decode_rule('PLUGINFILEBYCONTEXT', '/pluginfile.php/$1', 'context');
$rules[] = new restore_decode_rule('PLUGINFILEBYCONTEXTURLENCODED', '/pluginfile.php/$1', 'context', true);
return $rules;
}
+25 -1
View File
@@ -3793,7 +3793,7 @@ class restore_activity_competencies_structure_step extends restore_structure_ste
// Sortorder is ignored by precaution, anyway we should walk through the records in the right order.
$record = (object) $params;
$record->ruleoutcome = $data->ruleoutcome;
$record->overridegrade = $data->overridegrade;
$record->overridegrade = $data->overridegrade ?? 0;
$coursemodulecompetency = new \core_competency\course_module_competency(0, $record);
$coursemodulecompetency->create();
}
@@ -5434,6 +5434,25 @@ class restore_move_module_questions_categories extends restore_execution_step {
];
$params += $categoryidparams;
$DB->execute($sqlupdate, $params);
// As explained in {@see restore_quiz_activity_structure_step::process_quiz_question_legacy_instance()}
// question_set_references relating to random questions restored from old backups,
// which pick from context_module question_categores, will have been restored with the wrong questioncontextid.
// So, now, we need to find those, and updated the questioncontextid.
// We can only find them by picking apart the filter conditions, and seeign which categories they refer to.
// We need to check all the question_set_references belonging to this context_module.
$references = $DB->get_records('question_set_references', ['usingcontextid' => $newcontext->newitemid]);
foreach ($references as $reference) {
$filtercondition = json_decode($reference->filtercondition);
if (!empty($filtercondition->questioncategoryid) &&
in_array($filtercondition->questioncategoryid, $categoryids)) {
// This is one of ours, update the questionscontextid.
$DB->set_field('question_set_references',
'questionscontextid', $newcontext->newitemid,
['id' => $reference->id]);
}
}
}
// Now set the parent id for the question categories that were in the top category in the course context
@@ -6239,6 +6258,11 @@ trait restore_question_set_reference_data_trait {
if ($context = $this->get_mappingid('context', $data->questionscontextid)) {
$data->questionscontextid = $context;
} else {
$this->log('question_set_reference with old id ' . $data->id .
' referenced question context ' . $data->questionscontextid .
' which was not included in the backup. Therefore, this has been ' .
' restored with the old questionscontextid.', backup::LOG_WARNING);
}
$filtercondition['cat'] = implode(',', [
@@ -44,7 +44,18 @@ class restore_decode_rule {
protected $cregexp; // Calculated regular expresion we'll be looking for matches
public function __construct($linkname, $urltemplate, $mappings) {
/** @var bool $urlencoded Whether to use urlencode() on the final URL. */
protected bool $urlencoded;
/**
* Constructor
*
* @param string $linkname How the link has been encoded in backup (CHOICEVIEWBYID, COURSEVIEWBYID...)
* @param string $urltemplate How the original URL looks like, with dollar placeholders
* @param array|string $mappings Which backup_ids mappings do we need to apply for replacing the placeholders
* @param bool $urlencoded Whether to use urlencode() on the final URL (defaults to false)
*/
public function __construct(string $linkname, string $urltemplate, $mappings, bool $urlencoded = false) {
// Validate all the params are ok
$this->mappings = $this->validate_params($linkname, $urltemplate, $mappings);
$this->linkname = $linkname;
@@ -52,6 +63,7 @@ class restore_decode_rule {
$this->restoreid = 0;
$this->sourcewwwroot = '';
$this->targetwwwroot = ''; // yes, uses to be $CFG->wwwroot, and? ;-)
$this->urlencoded = $urlencoded;
$this->cregexp = $this->get_calculated_regexp();
}
@@ -96,6 +108,9 @@ class restore_decode_rule {
} else { // All mappings found, apply target values to the template
$toreplace = str_replace($placeholdersarr, $mappingstargetarr, $toreplace);
}
if ($this->urlencoded) {
$toreplace = urlencode($toreplace);
}
// Finally, perform the replacement in original content
$content = str_replace($tosearch, $toreplace, $content);
}
@@ -50,14 +50,18 @@ class backup_encode_content_test extends \basic_testcase {
// HTTPS root and links of both types in content.
$CFG->wwwroot = $httpsroot;
$encoded = backup_course_task::encode_content_links(
$httproot . '/course/view.php?id=123, ' .
$httpsroot . '/course/view.php?id=123, ' .
$httpsroot . '/grade/index.php?id=123, ' .
$httpsroot . '/grade/report/index.php?id=123, ' .
$httpsroot . '/badges/view.php?type=2&id=123 and ' .
$httpsroot . '/user/index.php?id=123.');
$httproot . '/course/view.php?id=123, ' .
$httpsroot . '/course/view.php?id=123, ' .
$httpsroot . '/grade/index.php?id=123, ' .
$httpsroot . '/grade/report/index.php?id=123, ' .
$httpsroot . '/badges/view.php?type=2&id=123, ' .
$httpsroot . '/user/index.php?id=123, ' .
$httpsroot . '/pluginfile.php/123 and ' .
urlencode($httpsroot . '/pluginfile.php/123') . '.'
);
$this->assertEquals('$@COURSEVIEWBYID*123@$, $@COURSEVIEWBYID*123@$, $@GRADEINDEXBYID*123@$, ' .
'$@GRADEREPORTINDEXBYID*123@$, $@BADGESVIEWBYID*123@$ and $@USERINDEXVIEWBYID*123@$.', $encoded);
'$@GRADEREPORTINDEXBYID*123@$, $@BADGESVIEWBYID*123@$, $@USERINDEXVIEWBYID*123@$, ' .
'$@PLUGINFILEBYCONTEXT*123@$ and $@PLUGINFILEBYCONTEXTURLENCODED*123@$.', $encoded);
// HTTP root and links of both types in content.
$CFG->wwwroot = $httproot;
@@ -66,10 +70,14 @@ class backup_encode_content_test extends \basic_testcase {
$httpsroot . '/course/view.php?id=123, ' .
$httproot . '/grade/index.php?id=123, ' .
$httproot . '/grade/report/index.php?id=123, ' .
$httproot . '/badges/view.php?type=2&id=123 and ' .
$httproot . '/user/index.php?id=123.');
$httproot . '/badges/view.php?type=2&id=123, ' .
$httproot . '/user/index.php?id=123, ' .
$httproot . '/pluginfile.php/123 and ' .
urlencode($httproot . '/pluginfile.php/123') . '.'
);
$this->assertEquals('$@COURSEVIEWBYID*123@$, $@COURSEVIEWBYID*123@$, $@GRADEINDEXBYID*123@$, ' .
'$@GRADEREPORTINDEXBYID*123@$, $@BADGESVIEWBYID*123@$ and $@USERINDEXVIEWBYID*123@$.', $encoded);
'$@GRADEREPORTINDEXBYID*123@$, $@BADGESVIEWBYID*123@$, $@USERINDEXVIEWBYID*123@$, ' .
'$@PLUGINFILEBYCONTEXT*123@$ and $@PLUGINFILEBYCONTEXTURLENCODED*123@$.', $encoded);
$CFG->wwwroot = $oldroot;
}
}
@@ -119,3 +119,97 @@ Feature: Award badges with separate groups
And I press "Award badge"
# Teacher 2 shouldn't be able to go further
Then I should see "Sorry, but you need to be part of a group to see this page."
@javascript
Scenario: Editing teacher can award badge to members of separate groups
Given I am on the "Course 1" course page logged in as teacher1
And I navigate to "Badges > Manage badges" in current page administration
And I follow "Course Badge"
And I press "Award badge"
When I set the field "role" to "Teacher"
# Confirm that editing teacher sees a separate groups dropdown menu.
Then "Separate groups" "select" should exist
And I should see "All participants" in the "Separate groups" "select"
# Confirm that all participants are displayed when All participants is selected.
And I should see "Student 1" in the "potentialrecipients[]" "select"
And I should see "Student 2" in the "potentialrecipients[]" "select"
And I should see "Teacher 1" in the "potentialrecipients[]" "select"
And I should see "Teacher 2" in the "potentialrecipients[]" "select"
And I set the field "Separate groups" to "Class A"
# Confirm that only members of selected group are displayed
And I should not see "Student 1" in the "potentialrecipients[]" "select"
And I should not see "Teacher 1" in the "potentialrecipients[]" "select"
And I should see "Student 2" in the "potentialrecipients[]" "select"
And I should see "Teacher 2" in the "potentialrecipients[]" "select"
And I set the field "Separate groups" to "Class B"
And I should not see "Student 2" in the "potentialrecipients[]" "select"
And I should not see "Teacher 2" in the "potentialrecipients[]" "select"
And I should see "Student 1" in the "potentialrecipients[]" "select"
And I should see "Teacher 1" in the "potentialrecipients[]" "select"
@javascript
Scenario Outline: Teacher can award badge to members of visible groups
Given I am on the "Course 1" "course editing" page logged in as teacher1
And I expand all fieldsets
# Set the group mode to visible groups.
And I set the field "Group mode" to "Visible groups"
And I press "Save and display"
When I am on the "Course 1" course page logged in as <loggedinuser>
And I navigate to "Badges > Manage badges" in current page administration
And I follow "Course Badge"
And I press "Award badge"
And I set the field "role" to "<awarder>"
# Confirm that teachers see a visible groups dropdown menu.
Then "Visible groups" "select" should exist
# Confirm that My groups option group exists.
And "optgroup[label='My groups']" "css_element" should exist in the "select[name='group']" "css_element"
# Confirm that Other groups option group exists.
And "optgroup[label='Other groups']" "css_element" should exist in the "select[name='group']" "css_element"
# Confirm that all participants are displayed when All participants is selected.
And I set the field "Visible groups" to "All participants"
And I should see "Student 1" in the "potentialrecipients[]" "select"
And I should see "Student 2" in the "potentialrecipients[]" "select"
And I should see "Teacher 1" in the "potentialrecipients[]" "select"
And I should see "Teacher 2" in the "potentialrecipients[]" "select"
# Confirm that only members of selected group are displayed.
And I set the field "Visible groups" to "Class A"
And I should not see "Student 1" in the "potentialrecipients[]" "select"
And I should not see "Teacher 1" in the "potentialrecipients[]" "select"
And I should see "Student 2" in the "potentialrecipients[]" "select"
And I should see "Teacher 2" in the "potentialrecipients[]" "select"
And I set the field "Visible groups" to "Class B"
And I should not see "Student 2" in the "potentialrecipients[]" "select"
And I should not see "Teacher 2" in the "potentialrecipients[]" "select"
And I should see "Student 1" in the "potentialrecipients[]" "select"
And I should see "Teacher 1" in the "potentialrecipients[]" "select"
Examples:
| loggedinuser | awarder |
| teacher1 | Teacher |
| teacher2 | Non-editing teacher |
@javascript
Scenario Outline: Teacher can award badge to members when group mode is set to no groups
Given I am on the "Course 1" "course editing" page logged in as teacher1
And I expand all fieldsets
# Set the group mode to no groups.
And I set the field "Group mode" to "No groups"
And I press "Save and display"
When I am on the "Course 1" course page logged in as <loggedinuser>
And I navigate to "Badges > Manage badges" in current page administration
And I follow "Course Badge"
And I press "Award badge"
And I set the field "role" to "<awarder>"
# Confirm that no group dropdowns don't exist.
Then "Separate groups" "select" should not exist
And "Visible groups" "select" should not exist
# Confirm all participants are displayed.
And I should see "Student 1" in the "potentialrecipients[]" "select"
And I should see "Student 2" in the "potentialrecipients[]" "select"
And I should see "Teacher 1" in the "potentialrecipients[]" "select"
And I should see "Teacher 2" in the "potentialrecipients[]" "select"
Examples:
| loggedinuser | awarder |
| teacher1 | Teacher |
| teacher2 | Non-editing teacher |
@@ -34,8 +34,7 @@ Feature: Delete course badge already awarded
# Navigate to Manage Badges page in order to delete the badge
And I navigate to "Badges > Manage badges" in current page administration
# Delete the badge
And I open the action menu in "<badgename>" "table_row"
And I choose "Delete" in the open action menu
And I press "Delete" action in the "<badgename>" report row
And I press "<deleteoption>"
And I am on the "Course 1" "enrolled users" page
And I click on "Student 1" "link"
+5 -9
View File
@@ -54,16 +54,14 @@ Feature: Manage badges
And I set the field "Manager" to "1"
And I press "Save"
And I navigate to "Badges > Manage badges" in site administration
And I open the action menu in "Badge #1" "table_row"
And I choose "Enable access" in the open action menu
And I press "Enable access" action in the "Badge #1" report row
And I should see "Changes in badge access"
And I press "Continue"
And I should see "Access to the badges was successfully enabled"
Then the following should exist in the "reportbuilder-table" table:
| Name | Badge status |
| Badge #1 | Available |
And I open the action menu in "Badge #1" "table_row"
And I choose "Disable access" in the open action menu
And I press "Disable access" action in the "Badge #1" report row
And I should see "Access to the badges was successfully disabled"
And the following should exist in the "reportbuilder-table" table:
| Name | Badge status |
@@ -78,14 +76,12 @@ Feature: Manage badges
And I set the field "Manager" to "1"
And I press "Save"
And I navigate to "Badges > Manage badges" in site administration
And I open the action menu in "Badge #1" "table_row"
And I choose "Enable access" in the open action menu
And I press "Enable access" action in the "Badge #1" report row
And I press "Continue"
And I open the action menu in "Badge #1" "table_row"
And I choose "Award badge" in the open action menu
And I press "Award badge" action in the "Badge #1" report row
And I set the field "potentialrecipients[]" to "Admin User (moodle@example.com)"
And I press "Award badge"
And I navigate to "Badges > Manage badges" in site administration
And the following should exist in the "reportbuilder-table" table:
Then the following should exist in the "reportbuilder-table" table:
| Name | Badge status | Recipients |
| Badge #1 | Available | 1 |
@@ -10,8 +10,8 @@ Feature: View a site event on the dashboard
| student1 | Student | 1 | student1@example.com | S1 |
And I log in as "admin"
And I create a calendar event with form data:
| id_eventtype | Site |
| id_name | Site Event |
| id_eventtype | Site |
| id_name | Site Event |
And I log out
@javascript
@@ -29,5 +29,5 @@ Feature: View a site event on the dashboard
# We need to give the browser a couple seconds to re-render the page after the screen has been resized.
And I wait "1" seconds
And I should not see "Site Event"
And I hover over today in the mini-calendar block
When I hover over today in the mini-calendar block responsive view
And I should see "Site Event"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+5 -1
View File
@@ -547,7 +547,11 @@ const itemsPerPageFunc = (pagingLimit, root) => {
// Filter out all pagination options which are too large for the amount of courses user is enrolled in.
const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10);
return itemsPerPage.filter(pagingOption => {
return pagingOption.value < totalCourseCount || pagingOption.value === 0;
if (pagingOption.value === 0 && totalCourseCount > 100) {
// To minimise performance issues, do not show the "All" option if the user is enrolled in more than 100 courses.
return false;
}
return pagingOption.value < totalCourseCount;
});
};
+8 -1
View File
@@ -363,7 +363,14 @@ class main implements renderable, templatable {
if (!$this->displaygroupingcustomfield) {
return [];
}
$fieldid = $DB->get_field('customfield_field', 'id', ['shortname' => $this->customfiltergrouping]);
// Get the relevant customfield ID within the core_course/course component/area.
$fieldid = $DB->get_field_sql("
SELECT f.id
FROM {customfield_field} f
JOIN {customfield_category} c ON c.id = f.categoryid
WHERE f.shortname = :shortname AND c.component = 'core_course' AND c.area = 'course'
", ['shortname' => $this->customfiltergrouping]);
if (!$fieldid) {
return [];
}
@@ -51,8 +51,7 @@
<div class="text-xs-center text-center mt-3" data-region="empty-message">
<img class="empty-placeholder-image-lg mt-1"
src="{{nocoursesimg}}"
alt="{{{ title }}}"
role="presentation"
alt=""
>
<h5 class="h5 mt-3 mb-0">{{{ title }}}</h5>
<p class="mt-3 mb-0">{{{ intro }}}</p>
@@ -70,3 +70,8 @@ Feature: Zero state on my overview block
And "Create course" "button" should exist
And I click on "Create course" "button"
And I should see "Add a new course"
@accessibility
Scenario: Evaluate the accessibility of the My courses (zero state)
When I am on the "My courses" page logged in as "manager"
Then the page should meet accessibility standards
@@ -27,9 +27,8 @@
<div class="text-xs-center text-center mt-4" data-region="empty-message">
<img
src="{{noitemsimgurl}}"
alt="{{#str}} noitems, block_recentlyaccesseditems {{/str}}"
role="presentation"
alt=""
style="height: 70px; width: 70px;"
>
<p class="text-muted mt-3 mb-0">{{#str}} noitems, block_recentlyaccesseditems {{/str}}</p>
</div>
</div>
@@ -42,19 +42,21 @@
<div class="card-deck dashboard-card-deck one-row" data-region="items-list" role="list">
{{#items}}
<a class="card dashboard-card mb-1" href="{{{viewurl}}}" title="{{{name}}}" role="listitem">
<div class="card-body p-2 m-1">
<div class="d-flex text-truncate">
<div class="d-flex align-self-center activityiconcontainer {{purpose}}">
{{{icon}}}
</div>
<div class="w-100 line-height-3 text-truncate ml-2">
<h6 class="mb-0 text-truncate"><span class="clickable">{{{name}}}</span></h6>
<small class="text-truncate mb-0">{{{coursename}}}</small>
<div class="card dashboard-card" role="listitem">
<a class="mb-1" href="{{{viewurl}}}" title="{{{name}}}">
<div class="card-body p-2 m-1">
<div class="d-flex text-truncate">
<div class="d-flex align-self-center activityiconcontainer {{purpose}}">
{{{icon}}}
</div>
<div class="w-100 line-height-3 text-truncate ml-2">
<h6 class="mb-0 text-truncate"><span class="clickable">{{{name}}}</span></h6>
<small class="text-truncate mb-0">{{{coursename}}}</small>
</div>
</div>
</div>
</div>
</a>
</a>
</div>
{{/items}}
</div>
{{#hasmoreitems}}
@@ -23,15 +23,19 @@ Feature: The recently accessed items block allows users to easily access their m
| name | Test forum name |
And I log in as "student1"
@accessibility
Scenario: User has not accessed any item
Then I should see "No recent items" in the "Recently accessed items" "block"
And the page should meet accessibility standards
@accessibility
Scenario: User has accessed some items
Given I change window size to "large"
When I am on the "Test forum name" "forum activity" page
And I follow "Dashboard"
Then I should see "Test forum name" in the "Recently accessed items" "block"
And I should not see "Show more items" in the "Recently accessed items" "block"
And the page should meet accessibility standards
Scenario: User has accessed more than 3 items
Given the following "activities" exist:
@@ -0,0 +1,42 @@
@block_starredcourses
Feature: Starred courses
In order for me to quickly navigate to my favourite courses
As a user
I must be able to add them to the Starred courses block
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| shortname | fullname |
| C1 | Course 1 |
| C2 | Course 2 |
| C3 | Course 3 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student1 | C2 | student |
| student1 | C3 | student |
And the following "blocks" exist:
| blockname | contextlevel | reference | pagetypepattern | defaultregion |
| starredcourses | User | student1 | my-index | content |
@accessibility @javascript
Scenario: User has no starred courses
Given I log in as "student1"
Then I should see "No starred courses"
And the page should meet accessibility standards
@accessibility @javascript
Scenario: User has starred courses
Given I am on the "My courses" page logged in as "student1"
And I click on "Actions for course Course 1" "button"
And I click on "Star for Course 1" "link"
And I click on "Actions for course Course 3" "button"
And I click on "Star for Course 3" "link"
When I follow "Dashboard"
Then the page should meet accessibility standards
And I should see "Course 1" in the "Starred courses" "block"
And I should see "Course 3" in the "Starred courses" "block"
But I should not see "Course 2" in the "Starred courses" "block"
+12 -10
View File
@@ -101,13 +101,14 @@ class block_tag_flickr extends block_base {
$request .= '&api_key='.FLICKR_DEV_KEY;
$request .= '&photoset_id='.$this->config->photoset;
$request .= '&per_page='.$numberofphotos;
$request .= '&format=php_serial';
$request .= '&format=json';
// We need to add nojsoncallback=? here, otherwise, Flickr will return the jsonFlickrApi object.
$request .= '&nojsoncallback=?';
$response = $this->fetch_request($request);
$search = @unserialize($response);
if ($search === false && $search != serialize(false)) {
// The response didn't appear to be anything serialized, exit...
$search = @json_decode($response, true);
if (!is_array($search) || json_last_error() !== JSON_ERROR_NONE) {
// The response didn't appear to be in correct format.
return;
}
@@ -126,13 +127,14 @@ class block_tag_flickr extends block_base {
$request .= '&tags='.$tagscsv;
$request .= '&per_page='.$numberofphotos;
$request .= '&sort='.$sortby;
$request .= '&format=php_serial';
$request .= '&format=json';
// We need to add nojsoncallback=? here, otherwise, Flickr will return the jsonFlickrApi object.
$request .= '&nojsoncallback=?';
$response = $this->fetch_request($request);
$search = @unserialize($response);
if ($search === false && $search != serialize(false)) {
// The response didn't appear to be anything serialized, exit...
$search = @json_decode($response, true);
if (!is_array($search) || json_last_error() !== JSON_ERROR_NONE) {
// The response didn't appear to be in correct format.
return;
}
$photos = array_values($search['photos']['photo']);
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -6,6 +6,6 @@ define("core_calendar/popover",["theme_boost/popover","jquery","core_calendar/se
* @copyright 2021 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 4.0
*/function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj},CalendarSelectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CalendarSelectors);const isPopoverConfigured=new Map,showPopover=target=>{if(!isPopoverConfigured.has(target)){const dateEle=(0,_jquery.default)(target);dateEle.popover({trigger:"manual",placement:"top",html:!0,content:()=>{const source=dateEle.find(CalendarSelectors.elements.dateContent),content=(0,_jquery.default)("<div>");if(source.length){const temptContent=source.find(".hidden").clone(!1);content.html(temptContent.html())}return content.html()}}),isPopoverConfigured.set(target,!0)}var dateContainer;dateContainer=target,"none"===window.getComputedStyle(dateContainer.querySelector(CalendarSelectors.elements.dateContent)).display&&((0,_jquery.default)(target).popover("show"),target.addEventListener("mouseleave",hidePopover),target.addEventListener("focusout",hidePopover))},hidePopover=e=>{const target=e.target,dateContainer=e.target.closest(CalendarSelectors.elements.dateContainer);if(dateContainer&&isPopoverConfigured.has(dateContainer)){const isTargetActive=target.contains(document.activeElement),isTargetHover=target.matches(":hover");isTargetActive||isTargetHover||((0,_jquery.default)(dateContainer).popover("hide"),dateContainer.removeEventListener("mouseleave",hidePopover),dateContainer.removeEventListener("focusout",hidePopover))}};let listenersRegistered=!1;listenersRegistered||((()=>{const showPopoverHandler=e=>{const dateContainer=e.target.closest(CalendarSelectors.elements.dateContainer);dateContainer&&(e.preventDefault(),showPopover(dateContainer))};document.addEventListener("mouseover",showPopoverHandler),document.addEventListener("focusin",showPopoverHandler)})(),listenersRegistered=!0)}));
*/function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj},CalendarSelectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CalendarSelectors);const isPopoverConfigured=new Map,showPopover=target=>{const dateContainer=target.closest(CalendarSelectors.elements.dateContainer);if(!isPopoverConfigured.has(dateContainer)){(0,_jquery.default)(target).popover({trigger:"manual",placement:"top",html:!0,title:dateContainer.dataset.title,content:()=>{const source=(0,_jquery.default)(dateContainer).find(CalendarSelectors.elements.dateContent),content=(0,_jquery.default)("<div>");if(source.length){const temptContent=source.find(".hidden").clone(!1);content.html(temptContent.html())}return content.html()}}),isPopoverConfigured.set(dateContainer,!0)}(dateContainer=>"none"===window.getComputedStyle(dateContainer.querySelector(CalendarSelectors.elements.dateContent)).display)(dateContainer)&&((0,_jquery.default)(target).popover("show"),target.addEventListener("mouseleave",hidePopover),target.addEventListener("focusout",hidePopover))},hidePopover=e=>{const target=e.target,dateContainer=e.target.closest(CalendarSelectors.elements.dateContainer);if(dateContainer&&isPopoverConfigured.has(dateContainer)){const isTargetActive=target.contains(document.activeElement),isTargetHover=target.matches(":hover");isTargetActive||isTargetHover||((0,_jquery.default)(target).popover("hide"),target.removeEventListener("mouseleave",hidePopover),target.removeEventListener("focusout",hidePopover))}};let listenersRegistered=!1;listenersRegistered||((()=>{const showPopoverHandler=e=>{const dayLink=e.target.closest(CalendarSelectors.links.dayLink);dayLink&&(e.preventDefault(),showPopover(dayLink))};document.addEventListener("mouseover",showPopoverHandler),document.addEventListener("focusin",showPopoverHandler)})(),listenersRegistered=!0)}));
//# sourceMappingURL=popover.min.js.map
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -5,6 +5,6 @@
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define("core_calendar/selectors",[],(function(){return{eventFilterItem:"[data-action='filter-event-type']",eventType:{site:"[data-eventtype-site]",category:"[data-eventtype-category]",course:"[data-eventtype-course]",group:"[data-eventtype-group]",user:"[data-eventtype-user]",other:"[data-eventtype-other]"},popoverType:{site:"[data-popover-eventtype-site]",category:"[data-popover-eventtype-category]",course:"[data-popover-eventtype-course]",group:"[data-popover-eventtype-group]",user:"[data-popover-eventtype-user]",other:"[data-popover-eventtype-other]"},calendarPeriods:{month:"[data-period='month']"},courseSelector:'select[name="course"]',viewSelector:'div[data-region="view-selector"]',actions:{create:'[data-action="new-event-button"]',edit:'[data-action="edit"]',remove:'[data-action="delete"]',viewEvent:'[data-action="view-event"]',deleteSubscription:'[data-action="delete-subscription"]'},elements:{courseSelector:'select[name="course"]',dateContainer:".clickable.hasevent",dateContent:'[data-region="day-content"]',monthDetailed:".calendarmonth.calendartable"},today:".today",day:'[data-region="day"]',calendarMain:'[data-region="calendar"]',wrapper:".calendarwrapper",eventItem:'[data-type="event"]',links:{navLink:".calendarwrapper .arrow_link",eventLink:"[data-region='event-item']",miniDayLink:"[data-region='mini-day-link']"},containers:{loadingIcon:'[data-region="overlay-icon-container"]'},mainCalendar:".maincalendar .heightcontainer",fullCalendarView:"page-calendar-view"}}));
define("core_calendar/selectors",[],(function(){return{eventFilterItem:"[data-action='filter-event-type']",eventType:{site:"[data-eventtype-site]",category:"[data-eventtype-category]",course:"[data-eventtype-course]",group:"[data-eventtype-group]",user:"[data-eventtype-user]",other:"[data-eventtype-other]"},popoverType:{site:"[data-popover-eventtype-site]",category:"[data-popover-eventtype-category]",course:"[data-popover-eventtype-course]",group:"[data-popover-eventtype-group]",user:"[data-popover-eventtype-user]",other:"[data-popover-eventtype-other]"},calendarPeriods:{month:"[data-period='month']"},courseSelector:'select[name="course"]',viewSelector:'div[data-region="view-selector"]',actions:{create:'[data-action="new-event-button"]',edit:'[data-action="edit"]',remove:'[data-action="delete"]',viewEvent:'[data-action="view-event"]',deleteSubscription:'[data-action="delete-subscription"]'},elements:{courseSelector:'select[name="course"]',dateContainer:".clickable.hasevent",dateContent:'[data-region="day-content"]',monthDetailed:".calendarmonth.calendartable"},today:".today",day:'[data-region="day"]',calendarMain:'[data-region="calendar"]',wrapper:".calendarwrapper",eventItem:'[data-type="event"]',links:{navLink:".calendarwrapper .arrow_link",eventLink:"[data-region='event-item']",miniDayLink:"[data-region='mini-day-link']",dayLink:"[data-action='view-day-link']"},containers:{loadingIcon:'[data-region="overlay-icon-container"]'},mainCalendar:".maincalendar .heightcontainer",fullCalendarView:"page-calendar-view"}}));
//# sourceMappingURL=selectors.min.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"selectors.min.js","sources":["../src/selectors.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 * CSS selectors for the calendar.\n *\n * @module core_calendar/selectors\n * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n eventFilterItem: \"[data-action='filter-event-type']\",\n eventType: {\n site: \"[data-eventtype-site]\",\n category: \"[data-eventtype-category]\",\n course: \"[data-eventtype-course]\",\n group: \"[data-eventtype-group]\",\n user: \"[data-eventtype-user]\",\n other: \"[data-eventtype-other]\",\n },\n popoverType: {\n site: \"[data-popover-eventtype-site]\",\n category: \"[data-popover-eventtype-category]\",\n course: \"[data-popover-eventtype-course]\",\n group: \"[data-popover-eventtype-group]\",\n user: \"[data-popover-eventtype-user]\",\n other: \"[data-popover-eventtype-other]\",\n },\n calendarPeriods: {\n month: \"[data-period='month']\",\n },\n courseSelector: 'select[name=\"course\"]',\n viewSelector: 'div[data-region=\"view-selector\"]',\n actions: {\n create: '[data-action=\"new-event-button\"]',\n edit: '[data-action=\"edit\"]',\n remove: '[data-action=\"delete\"]',\n viewEvent: '[data-action=\"view-event\"]',\n deleteSubscription: '[data-action=\"delete-subscription\"]',\n },\n elements: {\n courseSelector: 'select[name=\"course\"]',\n dateContainer: '.clickable.hasevent',\n dateContent: '[data-region=\"day-content\"]',\n monthDetailed: '.calendarmonth.calendartable',\n },\n today: '.today',\n day: '[data-region=\"day\"]',\n calendarMain: '[data-region=\"calendar\"]',\n wrapper: '.calendarwrapper',\n eventItem: '[data-type=\"event\"]',\n links: {\n navLink: '.calendarwrapper .arrow_link',\n eventLink: \"[data-region='event-item']\",\n miniDayLink: \"[data-region='mini-day-link']\",\n },\n containers: {\n loadingIcon: '[data-region=\"overlay-icon-container\"]',\n },\n mainCalendar: '.maincalendar .heightcontainer',\n fullCalendarView: 'page-calendar-view',\n };\n});\n"],"names":["define","eventFilterItem","eventType","site","category","course","group","user","other","popoverType","calendarPeriods","month","courseSelector","viewSelector","actions","create","edit","remove","viewEvent","deleteSubscription","elements","dateContainer","dateContent","monthDetailed","today","day","calendarMain","wrapper","eventItem","links","navLink","eventLink","miniDayLink","containers","loadingIcon","mainCalendar","fullCalendarView"],"mappings":";;;;;;;AAsBAA,iCAAO,IAAI,iBACA,CACHC,gBAAiB,oCACjBC,UAAW,CACPC,KAAM,wBACNC,SAAU,4BACVC,OAAQ,0BACRC,MAAO,yBACPC,KAAM,wBACNC,MAAO,0BAEXC,YAAa,CACTN,KAAM,gCACNC,SAAU,oCACVC,OAAQ,kCACRC,MAAO,iCACPC,KAAM,gCACNC,MAAO,kCAEXE,gBAAiB,CACbC,MAAO,yBAEXC,eAAgB,wBAChBC,aAAc,mCACdC,QAAS,CACLC,OAAQ,mCACRC,KAAM,uBACNC,OAAQ,yBACRC,UAAW,6BACXC,mBAAoB,uCAExBC,SAAU,CACNR,eAAgB,wBAChBS,cAAe,sBACfC,YAAa,8BACbC,cAAe,gCAEnBC,MAAO,SACPC,IAAK,sBACLC,aAAc,2BACdC,QAAS,mBACTC,UAAW,sBACXC,MAAO,CACHC,QAAS,+BACTC,UAAW,6BACXC,YAAa,iCAEjBC,WAAY,CACRC,YAAa,0CAEjBC,aAAc,iCACdC,iBAAkB"}
{"version":3,"file":"selectors.min.js","sources":["../src/selectors.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 * CSS selectors for the calendar.\n *\n * @module core_calendar/selectors\n * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n eventFilterItem: \"[data-action='filter-event-type']\",\n eventType: {\n site: \"[data-eventtype-site]\",\n category: \"[data-eventtype-category]\",\n course: \"[data-eventtype-course]\",\n group: \"[data-eventtype-group]\",\n user: \"[data-eventtype-user]\",\n other: \"[data-eventtype-other]\",\n },\n popoverType: {\n site: \"[data-popover-eventtype-site]\",\n category: \"[data-popover-eventtype-category]\",\n course: \"[data-popover-eventtype-course]\",\n group: \"[data-popover-eventtype-group]\",\n user: \"[data-popover-eventtype-user]\",\n other: \"[data-popover-eventtype-other]\",\n },\n calendarPeriods: {\n month: \"[data-period='month']\",\n },\n courseSelector: 'select[name=\"course\"]',\n viewSelector: 'div[data-region=\"view-selector\"]',\n actions: {\n create: '[data-action=\"new-event-button\"]',\n edit: '[data-action=\"edit\"]',\n remove: '[data-action=\"delete\"]',\n viewEvent: '[data-action=\"view-event\"]',\n deleteSubscription: '[data-action=\"delete-subscription\"]',\n },\n elements: {\n courseSelector: 'select[name=\"course\"]',\n dateContainer: '.clickable.hasevent',\n dateContent: '[data-region=\"day-content\"]',\n monthDetailed: '.calendarmonth.calendartable',\n },\n today: '.today',\n day: '[data-region=\"day\"]',\n calendarMain: '[data-region=\"calendar\"]',\n wrapper: '.calendarwrapper',\n eventItem: '[data-type=\"event\"]',\n links: {\n navLink: '.calendarwrapper .arrow_link',\n eventLink: \"[data-region='event-item']\",\n miniDayLink: \"[data-region='mini-day-link']\",\n dayLink: \"[data-action='view-day-link']\",\n },\n containers: {\n loadingIcon: '[data-region=\"overlay-icon-container\"]',\n },\n mainCalendar: '.maincalendar .heightcontainer',\n fullCalendarView: 'page-calendar-view',\n };\n});\n"],"names":["define","eventFilterItem","eventType","site","category","course","group","user","other","popoverType","calendarPeriods","month","courseSelector","viewSelector","actions","create","edit","remove","viewEvent","deleteSubscription","elements","dateContainer","dateContent","monthDetailed","today","day","calendarMain","wrapper","eventItem","links","navLink","eventLink","miniDayLink","dayLink","containers","loadingIcon","mainCalendar","fullCalendarView"],"mappings":";;;;;;;AAsBAA,iCAAO,IAAI,iBACA,CACHC,gBAAiB,oCACjBC,UAAW,CACPC,KAAM,wBACNC,SAAU,4BACVC,OAAQ,0BACRC,MAAO,yBACPC,KAAM,wBACNC,MAAO,0BAEXC,YAAa,CACTN,KAAM,gCACNC,SAAU,oCACVC,OAAQ,kCACRC,MAAO,iCACPC,KAAM,gCACNC,MAAO,kCAEXE,gBAAiB,CACbC,MAAO,yBAEXC,eAAgB,wBAChBC,aAAc,mCACdC,QAAS,CACLC,OAAQ,mCACRC,KAAM,uBACNC,OAAQ,yBACRC,UAAW,6BACXC,mBAAoB,uCAExBC,SAAU,CACNR,eAAgB,wBAChBS,cAAe,sBACfC,YAAa,8BACbC,cAAe,gCAEnBC,MAAO,SACPC,IAAK,sBACLC,aAAc,2BACdC,QAAS,mBACTC,UAAW,sBACXC,MAAO,CACHC,QAAS,+BACTC,UAAW,6BACXC,YAAa,gCACbC,QAAS,iCAEbC,WAAY,CACRC,YAAa,0CAEjBC,aAAc,iCACdC,iBAAkB"}
+11 -1
View File
@@ -34,6 +34,7 @@ define([
'core_calendar/crud',
'core_calendar/selectors',
'core/config',
'core/str',
],
function(
$,
@@ -45,6 +46,7 @@ function(
CalendarCrud,
CalendarSelectors,
Config,
Str,
) {
var SELECTORS = {
@@ -57,7 +59,9 @@ function(
CALENDAR_MONTH_WRAPPER: ".calendarwrapper",
TODAY: '.today',
DAY_NUMBER_CIRCLE: '.day-number-circle',
DAY_NUMBER: '.day-number'
DAY_NUMBER: '.day-number',
SCREEN_READER_ANNOUNCEMENTS: '.calendar-announcements',
CURRENT_MONTH: '.calendar-controls .current'
};
/**
@@ -151,6 +155,12 @@ function(
body.on(CalendarEvents.eventMoved, function() {
CalendarViewManager.reloadCurrentMonth(root);
});
// Announce the newly loaded month to screen readers.
body.on(CalendarEvents.monthChanged, root, async function() {
const monthName = body.find(SELECTORS.CURRENT_MONTH).text();
const monthAnnoucement = await Str.get_string('newmonthannouncement', 'calendar', monthName);
body.find(SELECTORS.SCREEN_READER_ANNOUNCEMENTS).html(monthAnnoucement);
});
CalendarCrud.registerEditListeners(root, eventFormModalPromise);
};
+12 -10
View File
@@ -38,14 +38,16 @@ const isPopoverAvailable = (dateContainer) => {
const isPopoverConfigured = new Map();
const showPopover = target => {
if (!isPopoverConfigured.has(target)) {
const dateContainer = target.closest(CalendarSelectors.elements.dateContainer);
if (!isPopoverConfigured.has(dateContainer)) {
const dateEle = jQuery(target);
dateEle.popover({
trigger: 'manual',
placement: 'top',
html: true,
title: dateContainer.dataset.title,
content: () => {
const source = dateEle.find(CalendarSelectors.elements.dateContent);
const source = jQuery(dateContainer).find(CalendarSelectors.elements.dateContent);
const content = jQuery('<div>');
if (source.length) {
const temptContent = source.find('.hidden').clone(false);
@@ -55,10 +57,10 @@ const showPopover = target => {
}
});
isPopoverConfigured.set(target, true);
isPopoverConfigured.set(dateContainer, true);
}
if (isPopoverAvailable(target)) {
if (isPopoverAvailable(dateContainer)) {
jQuery(target).popover('show');
target.addEventListener('mouseleave', hidePopover);
target.addEventListener('focusout', hidePopover);
@@ -75,9 +77,9 @@ const hidePopover = e => {
const isTargetActive = target.contains(document.activeElement);
const isTargetHover = target.matches(':hover');
if (!isTargetActive && !isTargetHover) {
jQuery(dateContainer).popover('hide');
dateContainer.removeEventListener('mouseleave', hidePopover);
dateContainer.removeEventListener('focusout', hidePopover);
jQuery(target).popover('hide');
target.removeEventListener('mouseleave', hidePopover);
target.removeEventListener('focusout', hidePopover);
}
}
};
@@ -87,13 +89,13 @@ const hidePopover = e => {
*/
const registerEventListeners = () => {
const showPopoverHandler = (e) => {
const dateContainer = e.target.closest(CalendarSelectors.elements.dateContainer);
if (!dateContainer) {
const dayLink = e.target.closest(CalendarSelectors.links.dayLink);
if (!dayLink) {
return;
}
e.preventDefault();
showPopover(dateContainer);
showPopover(dayLink);
};
document.addEventListener('mouseover', showPopoverHandler);
+1
View File
@@ -66,6 +66,7 @@ define([], function() {
navLink: '.calendarwrapper .arrow_link',
eventLink: "[data-region='event-item']",
miniDayLink: "[data-region='mini-day-link']",
dayLink: "[data-action='view-day-link']",
},
containers: {
loadingIcon: '[data-region="overlay-icon-container"]',
@@ -78,7 +78,8 @@ trait eventtype {
if (!empty($eventtypes['user']) && count($options) == 1) {
$mform->addElement('hidden', 'eventtype');
$mform->setType('eventtype', PARAM_TEXT);
$mform->setDefault('eventtype', 'user');
$mform->hardFreeze('eventtype');
$mform->setConstant('eventtype', 'user');
return;
} else {
$mform->addElement('select', 'eventtype', get_string('eventkind', 'calendar'), $options);
@@ -106,7 +106,7 @@ class managesubscriptions extends \moodleform {
$courseid = (!empty($data[$coursekey])) ? $data[$coursekey] : null;
$eventtypes = calendar_get_allowed_event_types($courseid);
if (empty($eventtype) || !isset($eventtypes[$eventtype])) {
if (empty($eventtype) || !isset($eventtypes[$eventtype]) || $eventtypes[$eventtype] == false) {
$errors['eventtype'] = get_string('invalideventtype', 'calendar');
}
+3 -3
View File
@@ -118,9 +118,9 @@ class provider implements
(e.courseid = ctx.instanceid AND e.eventtype = 'course' AND ctx.contextlevel = :coursecontext) OR
(e.courseid = ctx.instanceid AND e.eventtype = 'group' AND ctx.contextlevel = :groupcontext) OR
(e.userid = ctx.instanceid AND e.eventtype = 'user' AND ctx.contextlevel = :usercontext)
WHERE e.userid = :cuserid
UNION
SELECT ctx.id
WHERE e.userid = :cuserid";
$contextlist->add_from_sql($sql, $params);
$sql = "SELECT ctx.id
FROM {context} ctx
JOIN {course_modules} cm ON cm.id = ctx.instanceid AND ctx.contextlevel = :modulecontext
JOIN {modules} m ON m.id = cm.module
+2 -5
View File
@@ -39,12 +39,9 @@ if (empty($CFG->enablecalendarexport)) {
die('no export');
}
// Fetch basic user information to correctly log the user.
$fields = 'id,username,password,firstname,lastname';
$checkuserid = !empty($userid) && $user = $DB->get_record('user', array('id' => $userid), $fields);
$checkuserid = !empty($userid) && $user = \core_user::get_user($userid);
// Allowing for fallback check of old url - MDL-27542.
$checkusername = !empty($username) && $user = $DB->get_record('user', array('username' => $username), $fields);
$checkusername = !empty($username) && $user = \core_user::get_user_by_username($username);
if ((!$checkuserid && !$checkusername) || !$user) {
//No such user
die('Invalid authentication');
+11 -1
View File
@@ -902,7 +902,17 @@ class core_calendar_external extends external_api {
$courseid = (!empty($data[$coursekey])) ? $data[$coursekey] : null;
$editoroptions = \core_calendar\local\event\forms\create::build_editor_options($context);
$formoptions = ['editoroptions' => $editoroptions, 'courseid' => $courseid];
$formoptions['eventtypes'] = calendar_get_allowed_event_types($courseid);
$allowedeeventtypes = calendar_get_allowed_event_types($courseid);
// Event type validation.
if (in_array(true, $allowedeeventtypes, true) === false) {
throw new \moodle_exception('nopermissiontoupdatecalendar');
}
if (empty($eventtype) || !isset($allowedeeventtypes[$eventtype]) || $allowedeeventtypes[$eventtype] == false) {
return ['validationerror' => true];
}
$formoptions['eventtypes'] = $allowedeeventtypes;
if ($courseid) {
require_once($CFG->libdir . '/grouplib.php');
$groupcoursedata = groups_get_course_data($courseid);
+2 -1
View File
@@ -39,4 +39,5 @@
{{{filter_selector}}}
{{/filter_selector}}
{{> core_calendar/add_event_button}}
</div>
</div>
<div aria-live="polite" class="sr-only calendar-announcements"></div>
+33 -11
View File
@@ -48,6 +48,10 @@ class behat_calendar extends behat_base {
new behat_component_named_selector('mini calendar block', [".//*[@data-block='calendar_month']"]),
new behat_component_named_selector('full calendar page', [".//*[@id='page-calendar-view']"]),
new behat_component_named_selector('calendar day', [".//*[@data-region='day'][@data-day=%locator%]"]),
new behat_component_named_selector(
'responsive calendar day',
[".//*[@data-region='day'][@data-day=%locator%]/div[contains(@class, 'hidden-desktop')]"]
),
];
}
@@ -93,33 +97,51 @@ class behat_calendar extends behat_base {
/**
* Hover over a specific day in the mini-calendar.
*
* @Given /^I hover over day "(?P<dayofmonth>\d+)" of this month in the mini-calendar block$/
* @Given /^I hover over day "(?P<dayofmonth>\d+)" of this month in the mini-calendar block(?P<responsive> responsive view|)$/
* @param int $day The day of the current month
* @param string $responsive If not null, find the responsive version of the link.
*/
public function i_hover_over_day_of_this_month_in_mini_calendar_block(int $day): void {
$this->execute("behat_general::i_hover_in_the",
[$day, 'core_calendar > calendar day', '', 'core_calendar > mini calendar block']);
public function i_hover_over_day_of_this_month_in_mini_calendar_block(int $day, string $responsive = ''): void {
$this->execute(
"behat_general::i_hover_in_the",
[
$day,
empty($responsive) ? 'core_calendar > calendar day' : 'core_calendar > responsive calendar day',
'',
'core_calendar > mini calendar block',
],
);
}
/**
* Hover over a specific day in the full calendar page.
*
* @Given /^I hover over day "(?P<dayofmonth>\d+)" of this month in the full calendar page$/
* @Given /^I hover over day "(?P<dayofmonth>\d+)" of this month in the full calendar page(?P<responsive> responsive view|)$/
* @param int $day The day of the current month
* @param string $responsive If not empty, use the repsonsive view.
*/
public function i_hover_over_day_of_this_month_in_full_calendar_page(int $day): void {
$this->execute("behat_general::i_hover_in_the",
[$day, 'core_calendar > calendar day', '', 'core_calendar > full calendar page']);
public function i_hover_over_day_of_this_month_in_full_calendar_page(int $day, string $responsive = ''): void {
$this->execute(
"behat_general::i_hover_in_the",
[
$day,
empty($responsive) ? 'core_calendar > calendar day' : 'core_calendar > responsive calendar day',
'',
'core_calendar > full calendar page',
],
);
}
/**
* Hover over today in the mini-calendar.
*
* @Given /^I hover over today in the mini-calendar block$/
* @Given /^I hover over today in the mini-calendar block( responsive view|)$/
*
* @param string $responsive If not empty, use the responsive calendar link.
*/
public function i_hover_over_today_in_mini_calendar_block(): void {
public function i_hover_over_today_in_mini_calendar_block(string $responsive = ''): void {
$todaysday = date('j');
$this->i_hover_over_day_of_this_month_in_mini_calendar_block($todaysday);
$this->i_hover_over_day_of_this_month_in_mini_calendar_block($todaysday, $responsive);
}
/**
+1 -1
View File
@@ -278,7 +278,7 @@ Feature: Perform basic calendar functionality
# We need to give the browser a couple seconds to re-render the page after the screen has been resized.
And I wait "1" seconds
And I should not see "Event 1:1"
And I hover over day "1" of this month in the full calendar page
And I hover over day "1" of this month in the full calendar page responsive view
And I should see "Event 1:1"
@javascript
+1
View File
@@ -42,6 +42,7 @@ use core_privacy\local\request\approved_userlist;
*
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \core_calendar\privacy\provider
*/
class provider_test extends provider_testcase {
+2 -2
View File
@@ -402,7 +402,7 @@ class core_cohort_external extends external_api {
$results = cohort_get_cohorts($context->id, $limitfrom, $limitnum, $query);
$results = $results['cohorts'];
if (!$context instanceof context_system) {
$results = array_merge($results, cohort_get_available_cohorts($context, COHORT_ALL, $limitfrom, $limitnum, $query));
$results = $results + cohort_get_available_cohorts($context, COHORT_ALL, $limitfrom, $limitnum, $query);
}
} else if ($includes == 'all') {
$results = cohort_get_all_cohorts($limitfrom, $limitnum, $query);
@@ -414,7 +414,7 @@ class core_cohort_external extends external_api {
$cohorts = array();
if (!empty($results)) {
$cohortids = array_keys($results);
$cohortids = array_column($results, 'id');
$customfieldsdata = self::get_custom_fields_data($cohortids);
}
+8 -5
View File
@@ -716,11 +716,16 @@ class externallib_test extends externallib_advanced_testcase {
$this->assertCount(1, $result['cohorts']);
$this->assertEquals('Cohortsearch 4', $result['cohorts'][$cohort4->id]->name);
$result = core_cohort_external::search_cohorts("Cohortsearch 4", $syscontext, 'parents');
// A user with permissions in the system, searching category context.
$result = core_cohort_external::search_cohorts("Cohortsearch 4", $catcontext, 'parents');
$this->assertCount(1, $result['cohorts']);
$this->assertEquals('Cohortsearch 4', $result['cohorts'][$cohort4->id]->name);
$this->assertIsArray($result['cohorts'][$cohort4->id]->customfields);
$this->assertCount(2, $result['cohorts'][$cohort4->id]->customfields);
$this->assertEqualsCanonicalizing([
'Test value 1',
'Test value 2',
], array_column($result['cohorts'][$cohort4->id]->customfields, 'value'));
$actual = [];
foreach ($result['cohorts'][$cohort4->id]->customfields as $customfield) {
$this->assertArrayHasKey('name', $customfield);
@@ -730,8 +735,6 @@ class externallib_test extends externallib_advanced_testcase {
$this->assertArrayHasKey('value', $customfield);
$actual[$customfield['shortname']] = $customfield;
}
$this->assertEquals('Test value 1', $actual['testfield1']['value']);
$this->assertEquals('Test value 2', $actual['testfield2']['value']);
// A user with permissions in the category.
$this->setUser($catcreator);
+183 -65
View File
@@ -223,16 +223,28 @@ class provider implements
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . competency_framework::TABLE . "} cf
ON cf.contextid = ctx.id
LEFT JOIN {" . competency::TABLE . "} c
ON c.competencyframeworkid = cf.id
LEFT JOIN {" . related_competency::TABLE . "} cr
ON cr.competencyid = c.id
WHERE cf.usermodified = :userid1
OR c.usermodified = :userid2
OR cr.usermodified = :userid3";
$params = ['userid1' => $userid, 'userid2' => $userid, 'userid3' => $userid];
JOIN {" . competency_framework::TABLE . "} cf ON cf.contextid = ctx.id
WHERE cf.usermodified = :userid1";
$params = ['userid1' => $userid];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . competency_framework::TABLE . "} cf ON cf.contextid = ctx.id
JOIN {" . competency::TABLE . "} c ON c.competencyframeworkid = cf.id
WHERE c.usermodified = :userid2";
$params = ['userid2' => $userid];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . competency_framework::TABLE . "} cf ON cf.contextid = ctx.id
JOIN {" . competency::TABLE . "} c ON c.competencyframeworkid = cf.id
JOIN {" . related_competency::TABLE . "} cr ON cr.competencyid = c.id
WHERE cr.usermodified = :userid3";
$params = ['userid3' => $userid];
$contextlist->add_from_sql($sql, $params);
// Find the contexts of the templates, and related data, modified by the user.
@@ -241,16 +253,30 @@ class provider implements
FROM {context} ctx
JOIN {" . template::TABLE . "} tpl
ON tpl.contextid = ctx.id
LEFT JOIN {" . template_cohort::TABLE . "} tch
WHERE tpl.usermodified = :userid1";
$params = ['userid1' => $userid];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . template::TABLE . "} tpl
ON tpl.contextid = ctx.id
JOIN {" . template_cohort::TABLE . "} tch
ON tch.templateid = tpl.id
AND tch.usermodified = :userid2
LEFT JOIN {" . template_competency::TABLE . "} tc
AND tch.usermodified = :userid2";
$params = ['userid2' => $userid];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . template::TABLE . "} tpl
ON tpl.contextid = ctx.id
JOIN {" . template_competency::TABLE . "} tc
ON tc.templateid = tpl.id
AND tc.usermodified = :userid3
WHERE tpl.usermodified = :userid1
OR tch.id IS NOT NULL
OR tc.id IS NOT NULL";
$params = ['userid1' => $userid, 'userid2' => $userid, 'userid3' => $userid];
AND tc.usermodified = :userid3";
$params = ['userid3' => $userid];
$contextlist->add_from_sql($sql, $params);
// Find the possible course contexts.
@@ -302,21 +328,52 @@ class provider implements
JOIN {context} ctx
ON ctx.instanceid = p.userid
AND ctx.contextlevel = :userlevel
LEFT JOIN {" . plan_competency::TABLE . "} pc
ON pc.planid = p.id
AND pc.usermodified = :userid3
LEFT JOIN {" . user_competency_plan::TABLE . "} upc
ON upc.planid = p.id
AND upc.usermodified = :userid4
WHERE p.usermodified = :userid1
OR p.reviewerid = :userid2
OR pc.id IS NOT NULL
OR upc.id IS NOT NULL";
WHERE p.usermodified = :userid1";
$params = [
'userlevel' => CONTEXT_USER,
'userid1' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {" . plan::TABLE . "} p
JOIN {context} ctx
ON ctx.instanceid = p.userid
AND ctx.contextlevel = :userlevel
WHERE p.reviewerid = :userid2";
$params = [
'userlevel' => CONTEXT_USER,
'userid2' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {" . plan::TABLE . "} p
JOIN {context} ctx
ON ctx.instanceid = p.userid
AND ctx.contextlevel = :userlevel
JOIN {" . plan_competency::TABLE . "} pc
ON pc.planid = p.id
AND pc.usermodified = :userid3";
$params = [
'userlevel' => CONTEXT_USER,
'userid3' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {" . plan::TABLE . "} p
JOIN {context} ctx
ON ctx.instanceid = p.userid
AND ctx.contextlevel = :userlevel
JOIN {" . user_competency_plan::TABLE . "} upc
ON upc.planid = p.id
AND upc.usermodified = :userid4";
$params = [
'userlevel' => CONTEXT_USER,
'userid4' => $userid,
];
$contextlist->add_from_sql($sql, $params);
@@ -325,32 +382,78 @@ class provider implements
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
LEFT JOIN {" . user_competency::TABLE . "} uc
JOIN {" . user_competency::TABLE . "} uc
ON uc.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel1
LEFT JOIN {" . evidence::TABLE . "} e
ON e.usercompetencyid = uc.id
AND (e.usermodified = :userid3 OR e.actionuserid = :userid4)
LEFT JOIN {" . user_evidence::TABLE . "} ue
ON ue.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel2
AND ue.usermodified = :userid5
LEFT JOIN {" . user_evidence_competency::TABLE . "} uec
ON uec.userevidenceid = ue.id
AND uec.usermodified = :userid6
WHERE uc.usermodified = :userid1
OR uc.reviewerid = :userid2
OR e.id IS NOT NULL
OR ue.id IS NOT NULL
OR uec.id IS NOT NULL";
WHERE uc.usermodified = :userid1";
$params = [
'userlevel1' => CONTEXT_USER,
'userlevel2' => CONTEXT_USER,
'userid1' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_competency::TABLE . "} uc
ON uc.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel1
WHERE uc.reviewerid = :userid2";
$params = [
'userlevel1' => CONTEXT_USER,
'userid2' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_competency::TABLE . "} uc
ON uc.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel1
JOIN {" . evidence::TABLE . "} e
ON e.usercompetencyid = uc.id
AND e.usermodified = :userid3";
$params = [
'userlevel1' => CONTEXT_USER,
'userid3' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_competency::TABLE . "} uc
ON uc.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel1
JOIN {" . evidence::TABLE . "} e
ON e.usercompetencyid = uc.id
AND e.actionuserid = :userid4";
$params = [
'userlevel1' => CONTEXT_USER,
'userid4' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_evidence::TABLE . "} ue
ON ue.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel2
AND ue.usermodified = :userid5";
$params = [
'userlevel2' => CONTEXT_USER,
'userid5' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_evidence::TABLE . "} ue
ON ue.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel2
JOIN {" . user_evidence_competency::TABLE . "} uec
ON uec.userevidenceid = ue.id
AND uec.usermodified = :userid6";
$params = [
'userlevel2' => CONTEXT_USER,
'userid6' => $userid,
];
$contextlist->add_from_sql($sql, $params);
@@ -360,33 +463,48 @@ class provider implements
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
LEFT JOIN {" . plan::TABLE . "} p
JOIN {" . plan::TABLE . "} p
ON p.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel1
LEFT JOIN {" . user_competency::TABLE . "} uc
ON uc.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel2
AND uc.userid = :userid2
LEFT JOIN {" . user_evidence::TABLE . "} ue
ON ue.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel3
AND ue.userid = :userid3
LEFT JOIN {" . user_competency_course::TABLE . "} ucc
ON ucc.courseid = ctx.instanceid
AND ctx.contextlevel = :courselevel
AND ucc.userid = :userid4
WHERE p.userid = :userid1
OR uc.id IS NOT NULL
OR ue.id IS NOT NULL
OR ucc.id IS NOT NULL";
WHERE p.userid = :userid1";
$params = [
'userlevel1' => CONTEXT_USER,
'userlevel2' => CONTEXT_USER,
'userlevel3' => CONTEXT_USER,
'courselevel' => CONTEXT_COURSE,
'userid1' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_competency::TABLE . "} uc
ON uc.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel2
AND uc.userid = :userid2";
$params = [
'userlevel2' => CONTEXT_USER,
'userid2' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_evidence::TABLE . "} ue
ON ue.userid = ctx.instanceid
AND ctx.contextlevel = :userlevel3
AND ue.userid = :userid3";
$params = [
'userlevel3' => CONTEXT_USER,
'userid3' => $userid,
];
$contextlist->add_from_sql($sql, $params);
$sql = "
SELECT DISTINCT ctx.id
FROM {context} ctx
JOIN {" . user_competency_course::TABLE . "} ucc
ON ucc.courseid = ctx.instanceid
AND ctx.contextlevel = :courselevel
AND ucc.userid = :userid4";
$params = [
'courselevel' => CONTEXT_COURSE,
'userid4' => $userid,
];
$contextlist->add_from_sql($sql, $params);
+19 -2
View File
@@ -46,6 +46,7 @@ use core_competency\privacy\provider;
* @copyright 2018 Frédéric Massart
* @author Frédéric Massart <fred@branchup.tech>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \core_competency\privacy\provider
*/
class provider_test extends provider_testcase {
@@ -666,6 +667,7 @@ class provider_test extends provider_testcase {
$u2 = $dg->create_user();
$u3 = $dg->create_user();
$u4 = $dg->create_user();
$u5 = $dg->create_user();
$c1ctx = \context_course::instance($c1->id);
$u1ctx = \context_user::instance($u1->id);
@@ -681,30 +683,45 @@ class provider_test extends provider_testcase {
$this->assert_contextlist(provider::get_contexts_for_userid($u2->id), []);
$this->assert_contextlist(provider::get_contexts_for_userid($u3->id), []);
$this->assert_contextlist(provider::get_contexts_for_userid($u4->id), []);
$this->assert_contextlist(provider::get_contexts_for_userid($u5->id), []);
$ccg->create_plan(['userid' => $u1->id]);
$this->assert_contextlist(provider::get_contexts_for_userid($u1->id), [$u1ctx]);
$this->assert_contextlist(provider::get_contexts_for_userid($u2->id), []);
$this->assert_contextlist(provider::get_contexts_for_userid($u3->id), []);
$this->assert_contextlist(provider::get_contexts_for_userid($u4->id), []);
$this->assert_contextlist(provider::get_contexts_for_userid($u5->id), []);
$ccg->create_user_competency(['userid' => $u2->id, 'competencyid' => $comp1->get('id')]);
$this->assert_contextlist(provider::get_contexts_for_userid($u1->id), [$u1ctx]);
$this->assert_contextlist(provider::get_contexts_for_userid($u2->id), [$u2ctx]);
$this->assert_contextlist(provider::get_contexts_for_userid($u3->id), []);
$this->assert_contextlist(provider::get_contexts_for_userid($u4->id), []);
$this->assert_contextlist(provider::get_contexts_for_userid($u5->id), []);
$ccg->create_user_competency_course(['userid' => $u3->id, 'competencyid' => $comp1->get('id'), 'courseid' => $c1->id]);
$this->assert_contextlist(provider::get_contexts_for_userid($u1->id), [$u1ctx]);
$this->assert_contextlist(provider::get_contexts_for_userid($u2->id), [$u2ctx]);
$this->assert_contextlist(provider::get_contexts_for_userid($u3->id), [$c1ctx]);
$this->assert_contextlist(provider::get_contexts_for_userid($u4->id), []);
$this->assert_contextlist(provider::get_contexts_for_userid($u5->id), []);
$ccg->create_user_evidence(['userid' => $u4->id]);
$ue = $ccg->create_user_evidence(['userid' => $u4->id]);
$this->assert_contextlist(provider::get_contexts_for_userid($u1->id), [$u1ctx]);
$this->assert_contextlist(provider::get_contexts_for_userid($u2->id), [$u2ctx]);
$this->assert_contextlist(provider::get_contexts_for_userid($u3->id), [$c1ctx]);
$this->assert_contextlist(provider::get_contexts_for_userid($u4->id), [$u4ctx]);
$this->assert_contextlist(provider::get_contexts_for_userid($u5->id), []);
// A user editing a context relationship.
$this->setUser($u5);
$ccg->create_user_evidence_competency(['userevidenceid' => $ue->get('id'), 'competencyid' => $comp1->get('id')]);
$this->setAdminUser();
$this->assert_contextlist(provider::get_contexts_for_userid($u1->id), [$u1ctx]);
$this->assert_contextlist(provider::get_contexts_for_userid($u2->id), [$u2ctx]);
$this->assert_contextlist(provider::get_contexts_for_userid($u3->id), [$c1ctx]);
$this->assert_contextlist(provider::get_contexts_for_userid($u4->id), [$u4ctx]);
$this->assert_contextlist(provider::get_contexts_for_userid($u5->id), [$u4ctx]);
}
public function test_get_users_in_context_with_actual_data_and_actual_data_is_goooood() {
@@ -2598,7 +2615,7 @@ class provider_test extends provider_testcase {
$expectedids = array_unique(array_map(function($item) {
return $item instanceof \context ? $item->id : $id;
}, $expectedcontextsorids));
$this->assert_array_match($contextids, $expectedids);
$this->assert_array_match($expectedids, $contextids);
}
/**
+6 -29
View File
@@ -14,15 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Bulk edit activity completion form
*
* @package core_completion
* @copyright 2017 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
use core_completion\manager;
/**
* Bulk edit activity completion form
@@ -80,26 +72,11 @@ class core_completion_bulkedit_form extends core_completion_edit_base_form {
}
$cm = reset($this->cms);
$course = $this->course;
$modname = $cm->modname;
$modmoodleform = "$CFG->dirroot/mod/$modname/mod_form.php";
if (file_exists($modmoodleform)) {
require_once($modmoodleform);
} else {
throw new \moodle_exception('noformdesc');
}
list($cmrec, $context, $module, $data, $cw) = get_moduleinfo_data($cm, $course);
$data->return = 0;
$data->sr = 0;
$data->update = $modname;
// Initialise the form but discard all JS requirements it adds, our form has already added them.
$mformclassname = 'mod_'.$modname.'_mod_form';
$PAGE->start_collecting_javascript_requirements();
$this->_moduleform = new $mformclassname($data, 0, $cmrec, $course);
$PAGE->end_collecting_javascript_requirements();
$this->_moduleform = manager::get_module_form(
modname: $cm->modname,
course: $this->course,
cm: $cm,
);
return $this->_moduleform;
}
+7 -23
View File
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
use core_completion\manager;
/**
* Default activity completion form
*
@@ -68,34 +70,16 @@ class core_completion_defaultedit_form extends core_completion_edit_base_form {
* @return moodleform_mod|null
*/
protected function get_module_form() {
global $CFG, $PAGE;
if ($this->_moduleform) {
return $this->_moduleform;
}
$modnames = array_keys($this->get_module_names());
$modname = $modnames[0];
$course = $this->course;
$modmoodleform = "$CFG->dirroot/mod/$modname/mod_form.php";
if (file_exists($modmoodleform)) {
require_once($modmoodleform);
} else {
throw new \moodle_exception('noformdesc');
}
list($module, $context, $cw, $cmrec, $data) = prepare_new_moduleinfo_data($course, $modname, 0, $this->get_suffix());
$data->return = 0;
$data->sr = 0;
$data->add = $modname;
// Initialise the form but discard all JS requirements it adds, our form has already added them.
$mformclassname = 'mod_'.$modname.'_mod_form';
$PAGE->start_collecting_javascript_requirements();
$this->_moduleform = new $mformclassname($data, 0, $cmrec, $course);
$this->_moduleform->set_suffix('_' . $modname);
$PAGE->end_collecting_javascript_requirements();
$this->_moduleform = manager::get_module_form(
modname: $modnames[0],
course: $this->course,
suffix: $this->get_suffix(),
);
return $this->_moduleform;
}
+6 -1
View File
@@ -109,10 +109,15 @@ abstract class core_completion_edit_base_form extends moodleform {
$component = "mod_{$modnames[0]}";
$itemnames = \core_grades\component_gradeitems::get_itemname_mapping_for_component($component);
$hascustomrules = count($itemnames) > 1;
$customcompletionelements = [];
try {
// Add completion rules from the module form to this form.
$moduleform = $this->get_module_form();
// If the module doesn't return a form for any reason, we don't continue checking.
if (!$moduleform) {
return [false, []];
}
$moduleform->_form = $this->_form;
if ($customcompletionelements = $moduleform->{$function}()) {
$hascustomrules = true;
@@ -135,7 +140,7 @@ abstract class core_completion_edit_base_form extends moodleform {
} catch (Exception $e) {
debugging('Could not add custom completion rule of module ' . $modnames[0] .
' to this form, this has to be fixed by the developer', DEBUG_DEVELOPER);
return [$hascustomrules, $customcompletionelements];
return [false, $customcompletionelements];
}
}
+61 -3
View File
@@ -238,7 +238,12 @@ class manager {
$context = $this->get_context();
$canmanage = has_capability('moodle/course:manageactivities', $context);
$course = get_course($this->courseid);
$availablemodules = [];
foreach ($data->modules as $module) {
$libfile = "$CFG->dirroot/mod/$module->name/lib.php";
if (!file_exists($libfile)) {
continue;
}
$module->icon = $OUTPUT->image_url('monologo', $module->name)->out();
$module->formattedname = format_string(get_string('modulename', 'mod_' . $module->name),
true, ['context' => $context]);
@@ -248,13 +253,13 @@ class manager {
$defaults->modname = $module->name;
$module->completionstatus = $this->get_completion_detail($defaults);
}
$availablemodules[] = $module;
}
// Order modules by displayed name.
$modules = (array) $data->modules;
usort($modules, function($a, $b) {
usort($availablemodules, function($a, $b) {
return strcmp($a->formattedname, $b->formattedname);
});
$data->modules = $modules;
$data->modules = $availablemodules;
return $data;
}
@@ -627,4 +632,57 @@ class manager {
return $data;
}
/**
* Return a mod_form of the given module.
*
* @param string $modname Module to get the form from.
* @param stdClass $course Course object.
* @param ?cm_info $cm cm_info object to use.
* @param string $suffix The suffix to add to the name of the completion rules.
* @return ?\moodleform_mod The moodleform_mod object if everything goes fine. Null otherwise.
*/
public static function get_module_form(
string $modname,
stdClass $course,
?cm_info $cm = null,
string $suffix = ''
): ?\moodleform_mod {
global $CFG, $PAGE;
$modmoodleform = "$CFG->dirroot/mod/$modname/mod_form.php";
if (file_exists($modmoodleform)) {
require_once($modmoodleform);
} else {
throw new \moodle_exception('noformdesc');
}
if ($cm) {
[$cmrec, $context, $module, $data, $cw] = get_moduleinfo_data($cm, $course);
$data->update = $modname;
} else {
[$module, $context, $cw, $cmrec, $data] = prepare_new_moduleinfo_data($course, $modname, 0, $suffix);
$data->add = $modname;
}
$data->return = 0;
$data->sr = 0;
// Initialise the form but discard all JS requirements it adds, our form has already added them.
$mformclassname = 'mod_'.$modname.'_mod_form';
$PAGE->start_collecting_javascript_requirements();
try {
$moduleform = new $mformclassname($data, 0, $cmrec, $course);
if (!$cm) {
$moduleform->set_suffix('_' . $modname);
}
} catch (\Exception $e) {
// The form class has thrown an error when instantiating.
// This could happen because some conditions for the module are not met.
$moduleform = null;
} finally {
$PAGE->end_collecting_javascript_requirements();
}
return $moduleform;
}
}
+3 -1
View File
@@ -179,8 +179,10 @@ class completion_completion extends data_object {
// Notify user.
$course = get_course($data->course);
$messagesubject = get_string('coursecompleted', 'completion');
$options = new stdClass();
$options->context = context_course::instance($course->id);
$a = [
'coursename' => get_course_display_name_for_list($course),
'coursename' => format_string(get_course_display_name_for_list($course), true, $options),
'courselink' => (string) new moodle_url('/course/view.php', array('id' => $course->id)),
];
$messagebody = get_string('coursecompletedmessage', 'completion', $a);
@@ -5,8 +5,8 @@ Feature: Allow to mark course as completed without cron for activity completion
Background:
Given the following "courses" exist:
| fullname | shortname | category |
| Completion course | CC1 | 0 |
| fullname | shortname | category | enablecompletion |
| Completion course | CC1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | First | student1@example.com |
@@ -25,11 +25,7 @@ Feature: Allow to mark course as completed without cron for activity completion
And the following "blocks" exist:
| blockname | contextlevel | reference | pagetypepattern | defaultregion |
| completionstatus | Course | CC1 | course-view-* | side-pre |
And I am on the "Completion course" "course editing" page logged in as admin
And I expand all fieldsets
And I set the field "Enable completion tracking" to "Yes"
And I click on "Save and display" "button"
And I am on the "Test assignment name" "assign activity editing" page
And I am on the "Test assignment name" "assign activity editing" page logged in as admin
And I click on "Expand all" "link" in the "region-main" "region"
And I set the field "Add requirements" to "1"
And I set the field "completionusegrade" to "1"

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