Compare commits

..

277 Commits

Author SHA1 Message Date
Andrew Nicols db07c09afc Moodle release 4.4.1 2024-06-07 14:55:41 +08:00
Jun Pataleta 70c8bfa038 weekly release 4.4+ 2024-06-06 22:30:32 +08:00
Jun Pataleta ccca8adbc1 NOBUG: Fixed file access permissions 2024-06-06 22:30:32 +08:00
Jun Pataleta 7e01d53de3 Merge branch 'MDL-81750-404' of https://github.com/davewoloszyn/moodle into MOODLE_404_STABLE 2024-06-06 22:06:30 +08:00
Jun Pataleta 3317a05ec9 Merge branch 'MDL-82045-404' of https://github.com/andrewnicols/moodle into MOODLE_404_STABLE 2024-06-06 22:06:29 +08:00
Leon Stringer b0dddbb02a MDL-81146 forms: Disable submit buttons in Chrome 2024-06-06 22:06:29 +08:00
Huong Nguyen 322dae8b9c MDL-81875 mod_data: Fix incorrect version number 2024-06-06 22:06:29 +08:00
Andrew Nicols f6be7e2a3e Merge branch 'MDL-81794-404' of https://github.com/meirzamoodle/moodle into MOODLE_404_STABLE 2024-06-06 22:06:29 +08:00
Huong Nguyen aca5d1d5c6 Merge branch 'MDL-81875-404' of https://github.com/laurentdavid/moodle into MOODLE_404_STABLE 2024-06-06 22:06:29 +08:00
Huong Nguyen 07367f22f1 Merge branch 'MDL-81749-404' of https://github.com/aanabit/moodle into MOODLE_404_STABLE 2024-06-06 22:06:28 +08:00
Huong Nguyen 0c47aa6a92 Merge branch 'MDL-81989-404' of https://github.com/lameze/moodle into MOODLE_404_STABLE 2024-06-06 22:06:28 +08:00
meirzamoodle e23f603c41 MDL-81890 assign: confirm_sesskey fixed in view_fix_rescaled_null_grades 2024-06-06 22:06:28 +08:00
meirzamoodle 9af9711c0a MDL-81890 enrol_lti: Fix sesskey checks 2024-06-06 22:06:28 +08:00
meirzamoodle 30fadc3686 MDL-81890 course: Fix sesskey checks 2024-06-06 22:06:28 +08:00
meirzamoodle 756090ed79 MDL-81890 tool_moodlenet: Fix sesskey checks 2024-06-06 22:06:27 +08:00
Jun Pataleta 2630591338 MDL-81631 backup: Simplify logic using null coalescing operator 2024-06-06 22:06:27 +08:00
Jun Pataleta e00e4487a4 Merge branch 'MDL-81631_404' of https://github.com/santoshndev/moodle into MOODLE_404_STABLE 2024-06-06 22:06:27 +08:00
Jun Pataleta 64f1fd7d7b Merge branch 'MDL-81894-404' of https://github.com/andrewnicols/moodle into MOODLE_404_STABLE 2024-06-06 22:06:27 +08:00
Jun Pataleta 09b06bdb28 Merge branch 'MDL-78198-404' of https://github.com/andrewnicols/moodle into MOODLE_404_STABLE 2024-06-06 22:06:27 +08:00
Huong Nguyen 9e1723f4b8 Merge branch 'MDL-81865-404' of https://github.com/lucaboesch/moodle into MOODLE_404_STABLE 2024-06-06 22:06:26 +08:00
Jun Pataleta 3b4e2ae357 Merge branch 'MDL-82086-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-06-06 22:06:26 +08:00
Huong Nguyen d35b9dc7bb Merge branch 'MDL-81397-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-06-06 22:06:26 +08:00
Paul Holden 500cec5757 MDL-81778 mod_bigbluebuttonbn: access checks when getting meeting URL. 2024-06-06 22:06:26 +08:00
Stevani Andolo 891e9994b4 MDL-81412 calendar: Sanitise calendar event names 2024-06-06 22:05:53 +08:00
Andrew Nicols 83b3ee6212 MDL-82045 course: Destroy copy course modal instead of hiding it 2024-06-06 10:04:10 +08:00
Andrew Nicols 253c16b64d MDL-81894 core: system_clock should use the server timezone 2024-06-06 00:18:52 +08:00
Amaia Anabitarte af96d4eebe MDL-81749 core_completion: Let custom rules to return failed state 2024-06-05 15:55:28 +02:00
lameze 4e6de24f31 MDL-81989 install: remove unnecessary closing div tags 2024-06-05 14:02:56 +08:00
Paul Holden 7d1563333b MDL-82055 output: replace usage of named templatable interface.
It's not available during, and breaks, site installation. The output
class doesn't necessarily need it in 78db6bbce8, so switch to simple
templatable interface instead.
2024-06-05 14:02:54 +08:00
Cameron Ball f7988538b2 MDL-81774 curl: Strip auth headers when redirecting to different host 2024-06-05 02:41:19 +00:00
Jun Pataleta 6ba71820b1 Merge branch 'MDL-81982-404' of https://github.com/junpataleta/moodle into MOODLE_404_STABLE 2024-06-05 10:29:17 +08:00
David Mudrák 61c5863b1a MDL-81982 lang: Fix strings order
Fixes the strings order in files where CI has detected the wrong order
of modified strings.
2024-06-05 10:25:06 +08:00
David Mudrák 0091b1a153 MDL-81982 lang: Use fixed strings in tests, too 2024-06-05 10:25:06 +08:00
Helen Foster b12afc89e4 MDL-81982 lang: Import fixed English strings (en_fix) 2024-06-05 10:25:06 +08:00
Huong Nguyen 844aef124c Merge branch 'MDL-81970-plagiarism_upgrade_txt-404' of https://github.com/leonstr/moodle into MOODLE_404_STABLE 2024-06-05 09:13:49 +07:00
Huong Nguyen 476a8fcb72 Merge branch 'MDL-81751-MOODLE_404_STABLE' of https://github.com/durzo/moodle into MOODLE_404_STABLE 2024-06-05 08:56:10 +07:00
David Woloszyn fe22fe1c83 MDL-81750 form: Add selector to match filemanager in group 2024-06-05 09:34:57 +10:00
Laurent David cb9527cd4f MDL-81875 mod_data: Revert database autolink removal
Autolinks are used in the database for text fields via
the auto-linking filter.
The revert is partial here because of changes made
in MDL-70384 to all field forms/templates

This reverts commit 42edaba5 from MDL-80949
2024-06-04 16:09:31 +02:00
Paul Holden e71fc4ff3b MDL-82086 theme_boost: consistent selector for textarea form controls.
In a similar vein to 31f6039635, we shouldn't assume form controls are
always descendents of `.mform` (e.g. the messaging drawer input).
2024-06-04 14:37:37 +01:00
Paul Holden 89fae608a6 MDL-81397 courseformat: define section editing capabilities.
This ensures consistency for current page instance, when rendering
content in response to fragment AJAX requests.
2024-06-04 09:00:17 +01:00
Huong Nguyen 34bcb7e70e Merge branch 'MDL-81936-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-06-04 13:16:01 +07:00
Huong Nguyen 608f7d6aa0 Merge branch 'MDL-81955-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-06-04 10:33:46 +07:00
Andrew Nicols c43d07d0cf Merge branch 'MDL-80208-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-06-04 11:07:20 +08:00
Juan Leyva ad46a97f53 MDL-80959 tool_mobile: Use different user keys for QR and auto login 2024-06-04 10:33:40 +08:00
Andrew Nicols da4e626ffa Merge branch 'MDL-82047-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-06-04 10:29:18 +08:00
Andrew Nicols cb4904ea25 Merge branch 'MDL-82055-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-06-04 09:50:36 +08:00
Huong Nguyen 0efd3066a2 Merge branch 'MDL-81544-404' of https://github.com/rezaies/moodle into MOODLE_404_STABLE 2024-06-03 12:05:35 +07:00
Huong Nguyen 002b1aed6a Merge branch 'MDL-73735-M404_duplicate-line-course-rest-php' of https://github.com/ziegenberg/moodle into MOODLE_404_STABLE 2024-06-03 11:53:25 +07:00
Huong Nguyen c73c1bafc1 Merge branch 'MDL-82009-404' of https://github.com/sarjona/moodle into MOODLE_404_STABLE 2024-06-03 11:36:47 +07:00
Sara Arjona e56c732209 MDL-82009 course: Course blocks should be displayed in section page too 2024-05-31 15:16:55 +02:00
Paul Holden cff22cd5ab MDL-82055 output: replace usage of named templatable interface.
It's not available during, and breaks, site installation. The output
class doesn't necessarily need it in 78db6bbce8, so switch to simple
templatable interface instead.
2024-05-31 13:04:30 +01:00
Sara Arjona 2ec3b297d7 Merge branch 'MDL-81850-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-31 10:44:41 +02:00
Ilya Tregubov 1e480e2851 Merge branch 'MDL-77685-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-31 09:04:44 +08:00
Luca Bösch a22d64816d MDL-81865 mod_quiz: Display modal for 'Add random question' as mform.
This is a backport of MDL-81713.
2024-05-30 21:56:03 +02:00
Paul Holden f540fab1dc MDL-82047 analytics: remove 32-bit integer checks for maximum time. 2024-05-30 19:00:25 +01:00
Sara Arjona 1291869f90 weekly release 4.4+ 2024-05-30 17:00:26 +02:00
Jun Pataleta 7a420b6a34 Merge branch 'MDL-80831-404' of https://github.com/HuongNV13/moodle into MOODLE_404_STABLE 2024-05-30 18:25:50 +08:00
Huong Nguyen ff963d9feb MDL-80831 core: Add missing classname for privacy url 2024-05-30 17:17:13 +07:00
Paul Holden ad693b830a MDL-81850 availability_group: correctly retrieve groups for given user. 2024-05-30 10:15:47 +01:00
Huong Nguyen c2b0463539 Merge branch 'MDL-80973-404' of https://github.com/jleyva/moodle into MOODLE_404_STABLE 2024-05-30 10:38:06 +07:00
Andrew Nicols f0445d5cac MDL-68262 assignfeedback_editpdf: Correct args to make_request_directory 2024-05-30 09:54:08 +07:00
Sara Arjona 2debc731ef Merge branch 'MDL-81983_404' of https://github.com/lostrogit/moodle into MOODLE_404_STABLE 2024-05-29 14:07:31 +02:00
Sara Arjona 56aaf7a998 Merge branch 'MDL-81901-progress-cli-MOODLE_404_STABLE' of https://github.com/brendanheywood/moodle into MOODLE_404_STABLE 2024-05-29 13:32:31 +02:00
Sara Arjona 86cb89c5fc Merge branch 'MDL-80911-404' of https://github.com/HuongNV13/moodle into MOODLE_404_STABLE 2024-05-29 13:19:22 +02:00
Jun Pataleta 9a75b24e1c Merge branch 'MDL-81779-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-29 17:02:01 +08:00
Andrew Nicols b19ce46723 MDL-68262 assignfeedback_editpdf: Update unit test for latest coding style 2024-05-29 16:19:41 +08:00
Andrew Nicols 370843c967 MDL-68262 assignfeedback_editpdf: Switch to request dirs 2024-05-29 16:19:41 +08:00
Andrew Nicols da15787ed0 Merge branch 'MDL-80831-404' of https://github.com/junpataleta/moodle into MOODLE_404_STABLE 2024-05-29 14:27:20 +08:00
Jun Pataleta 801a2b9497 MDL-80831 theme: Set a vertical padding for footer links
This is to increase the target size of links within the footer and
meet WCAG 2.2 Level AA Success Criterion 2.5.8
2024-05-29 10:40:11 +08:00
Jun Pataleta 7a5bdf76b6 MDL-80831 gradereport_grader: Remove unnecessary setting of aria-hidden
- If an element is being hidden visually with `display: none;` (e.g. by
using the class `.d-none`), there's no need to set `aria-hidden` because
it is already hidden from the accessibility tree.
- Setting `aria-hidden=false` is also not recommended and it's better to
remove the `aria-hidden` attribute instead of setting it to false.
2024-05-29 10:40:11 +08:00
Jun Pataleta 63f7c6cc6a MDL-80831 behat: Re-enable target-size rule for accessibility tests 2024-05-29 10:40:11 +08:00
Brendan Heywood b35b012e9e MDL-81901 core: Fix progress bar bug in cli in non tty mode 2024-05-29 11:15:05 +10:00
Andrew Nicols 0204fbb810 Merge branch 'MDL-82015-404' of https://github.com/ak4t0sh/moodle into MOODLE_404_STABLE 2024-05-29 09:11:05 +08:00
Andrew Nicols 5cf7efb761 Merge branch 'MDL-81597-404' of https://github.com/roland04/moodle into MOODLE_404_STABLE 2024-05-28 23:49:59 +08:00
Andrew Nicols 2016d4c6dd MDL-81733 mod_lesson: Coding style fixes 2024-05-28 23:45:50 +08:00
Andrew Nicols aae18f59aa Merge branch 'MDL-81733-404' of https://github.com/izendegi/moodle into MOODLE_404_STABLE 2024-05-28 23:45:47 +08:00
Mikel Martín 4d23fe8a58 MDL-81597 theme_boost: Fix header bar alignment
- Refactot context_header class to implement named templatable so
render_context_header in core and theme_boost can be removed
- Refactor context_header to use templates
- Fix context header layout and styles
2024-05-28 16:33:21 +02:00
Arnaud Trouvé fcfcdd6508 MDL-82015 mod_bigbluebuttonbn: Fix date formatting during import 2024-05-28 15:24:26 +02:00
Andrew Nicols 45808a99b4 Merge branch 'MDL-82004-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-28 14:31:42 +08:00
Andrew Nicols e4acde27c9 Merge branch 'MDL-81822-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-28 14:05:58 +08:00
Andrew Nicols 5182b60391 Merge branch 'MDL-81870-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-28 14:00:15 +08:00
Huong Nguyen 23ab7d4973 Merge branch 'MDL-78768-M404_import-multiple-new-grade-items' of https://github.com/ziegenberg/moodle into MOODLE_404_STABLE 2024-05-28 12:32:26 +07:00
Andrew Nicols af8f13bac5 MDL-81897 tool_mobile: Coding style fix 2024-05-28 11:41:16 +08:00
Andrew Nicols e5b2b5e0ba Merge branch 'MDL-81897-404' of https://github.com/jleyva/moodle into MOODLE_404_STABLE 2024-05-28 11:40:53 +08:00
Andrew Nicols 9a6b07f0de Merge branch 'MDL-81757-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-28 10:25:26 +08:00
Andrew Nicols 11e53bf4b0 Merge branch 'MDL-81698_404' of https://github.com/stronk7/moodle into MOODLE_404_STABLE 2024-05-28 10:12:50 +08:00
Huong Nguyen 176caab765 Merge branch 'MDL-81915_404' of https://github.com/AnupamaSarjoshi/moodle into MOODLE_404_STABLE 2024-05-28 08:57:45 +07:00
Andrew Nicols bd03886512 Merge branch 'MDL-78942-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-28 09:17:28 +08:00
Juan Leyva 6f6d2d9a0f MDL-80973 tool_policy: Fix edge case 2024-05-27 17:20:22 +02:00
Mikel Martín e6af0ea94e MDL-81597 renderer: Add page getter method to renderer_base 2024-05-27 13:27:05 +02:00
Eloy Lafuente (stronk7) 86b45212e8 MDL-81698 phpunit: Convert some skipped tests into useful ones
After some tests, it seems that we can safely cover
phpunit_util::reset_all_data() executing it within
own basic_test self tests.

That way we can confirm that the reset code is doing its job
and detecting unexpected changes at various levels (database,
globals, ...).

Note that, in order to catch the E_USER_WARNINGS, for PHPUnit 9.6
and up, we have to convert them to exceptions, because the notice/
warning/error expectations have been deprecated and will be removed
in PHPUnit 10. So we are using a trick, already used also by
advanced_test. And, no matter that we are repeating the trick
a few times, that's ok in order to have all its uses controlled.
2024-05-25 17:19:52 +02:00
Eloy Lafuente (stronk7) b2a485e244 MDL-81698 phpunit: Apply various fixes towards 1by1 execution
The changes here are heterogeneous:
- Include stuff that is not available (other test has included it).
  Sometimes local to a unit test, others in setupBeforeClass() or
  globally, ... depends on every case.
- Rename some tests (namespaces, test name, ...) towards getting it
  running.
- Amend small bits here and there.

Important note: I've left any "cosmetic" warning out from the
changes, only a few errors (like long array syntax) have been fixed.
2024-05-25 17:19:52 +02:00
Eloy Lafuente (stronk7) 80f521eed0 MDL-81698 phpunit: Uncomment some tests and make them skipped
There are a few tests in core that are commented since the
beginning. That's not useful at all, so with this commit we
are un-commenting them instead.

Note that this is an initial step to have them back with skipped
outcome. Later in this issue we'll decide about to keep them or
completely remove them (each test can have a different outcome).
2024-05-25 17:19:51 +02:00
Eloy Lafuente (stronk7) f14fdf23e3 MDL-81698 phpunit: New workflow to run tests 1by1 @ GitHub
This workflow just finds all the individual
tests available in core (all the test_ methods
in _test.php file) and runs them one by one,
with a total isolation, because each one is
executed by a different PHPUnit invocation..

Any test ending with error, will be reported
as part of the output.

Note that we are not using PHPUnit's own
isolation here, just running them one by one.
2024-05-25 17:19:51 +02:00
Anupama Sarjoshi 25724ebfbb MDL-81915 admin: Fix test_admin_output_new_settings_by_page unit test 2024-05-24 11:24:32 +01:00
Iñigo Zendegi c73623506d MDL-81733 lesson: Support filters on lesson activity link 2024-05-24 09:42:57 +02:00
meirzamoodle 7450f0e33f MDL-81794 cachestore_redis: Fix the TLS issue on the Redis 2024-05-24 14:28:10 +07:00
meirzamoodle 55163a6ba9 MDL-81794 core: Fix the TLS issue on the Redis 2024-05-24 14:28:10 +07:00
Ilya Tregubov 4e04339cbd weekly release 4.4+ 2024-05-24 10:51:59 +08:00
Andrew Nicols bf3562d13e MDL-78198 file: Stop sending the Accept-Charset header in curl
See notes on:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Charset

Warning: Do not use this header. Browsers omit this header and servers
should ignore it.

The Accept-Charset request HTTP header was a header that advertised a
client's supported character encodings. It is no longer widely used.

UTF-8 is well-supported and the overwhelmingly preferred choice for
character encoding. To guarantee better privacy through less
configuration-based entropy, all browsers omit the Accept-Charset
header.
2024-05-23 22:18:08 +08:00
Paul Holden aca3ad2008 MDL-82004 assignsubmission_onlinetext: no concatenation in lang strings. 2024-05-23 13:08:22 +01:00
Paul Holden f34e050e9b MDL-81870 core: re-factor duplicate plugin savepoint implementations. 2024-05-23 09:09:26 +01:00
Paul Holden c2eb42fc7b MDL-81870 core: validate component during upgrade task savepoint. 2024-05-23 09:04:24 +01:00
Huong Nguyen 625e015ff8 Merge branch 'MDL-81407_404' of https://github.com/AnupamaSarjoshi/moodle into MOODLE_404_STABLE 2024-05-23 10:38:42 +07:00
Huong Nguyen 9d74cebd74 Merge branch 'MDL-81992_404' of https://github.com/timhunt/moodle into MOODLE_404_STABLE 2024-05-23 09:45:26 +07:00
Huong Nguyen 64ad970e5e Merge branch 'MDL-81862-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-23 08:47:48 +07:00
Tim Hunt f0fb54d907 MDL-81992 question bank: disable create question button until ready
Previously, you could click the button before it was ready, and that
just led to an error, which was very silly usability.
2024-05-22 16:26:05 +01:00
Paul Holden 5da8226f72 MDL-80208 cachestore_redis: ensure empty set many with TTL don't throw. 2024-05-21 22:36:46 +01:00
Santosh Nagargoje 2fcd7f93b9 MDL-81631 backup: Added null parameter fix 2024-05-21 17:06:21 +05:30
Daniel Ziegenberg 9b30118456 MDL-73735 course: remove duplicate line of code
Signed-off-by: Daniel Ziegenberg <daniel@ziegenberg.at>
Co-authored-by: Davo Smith <davo.smith@synergy-learning.com>
2024-05-21 13:30:24 +02:00
Huong Nguyen dd87304fcf Merge branch 'MDL-81652-404' of https://github.com/lucaboesch/moodle into MOODLE_404_STABLE 2024-05-21 17:33:50 +07:00
Huong Nguyen b08fbf99db Merge branch 'MDL-76765-404' of https://github.com/lucaboesch/moodle into MOODLE_404_STABLE 2024-05-21 17:29:22 +07:00
Carlos Castillo b3f708d4d1 MDL-81983 admin: Remove double capability check 2024-05-21 12:14:21 +02:00
Huong Nguyen 4572a114a9 Merge branch 'MDL-81538-404' of https://github.com/rezaies/moodle into MOODLE_404_STABLE 2024-05-21 16:31:51 +07:00
Paul Holden c889381be2 MDL-81862 mod_assign: add activity field files for backup/restore. 2024-05-21 09:06:01 +01:00
Ilya Tregubov 1e07a5eef6 Merge branch 'MDL-81318-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-21 12:49:20 +08:00
Shamim Rezaie 2c40c8554b Merge branch 'MDL-80599-404-2' of https://github.com/ilyatregubov/moodle into MOODLE_404_STABLE 2024-05-21 14:23:15 +10:00
Huong Nguyen 955f4f3bb7 Merge branch 'MDL-81948-404' of https://github.com/ferranrecio/moodle into MOODLE_404_STABLE 2024-05-21 10:03:12 +07:00
Huong Nguyen 1dca48371d Merge branch 'MDL-81904-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-21 09:43:45 +07:00
Huong Nguyen c0b9a52430 Merge branch 'MDL-80583-404' of https://github.com/stevandoMoodle/moodle into MOODLE_404_STABLE 2024-05-21 09:24:34 +07:00
Luca Bösch 462b8f9861 MDL-76765 qtype_ddmarker: Show multiple tries options correctly. 2024-05-20 22:13:40 +02:00
Luca Bösch 242ab5d5aa MDL-76765 question: Show multiple tries options correctly. 2024-05-20 22:13:39 +02:00
Luca Bösch bf8a28b220 MDL-81652 qtype_multianswer: Tackle Bootstrap 5 compatibility. 2024-05-20 21:30:18 +02:00
Paul Holden a76a1e3f5c MDL-81822 tool_licensemanager: slightly relax license shortname type.
The "*-4.0" license versions could not be managed currently.
2024-05-20 17:44:47 +01:00
Leon Stringer 78b3369ca0 MDL-81970 plagiarism: Callbacks wrong way round 2024-05-20 11:01:33 +01:00
Stevani Andolo 2f0513b91c MDL-80583 communication_matrix: Ignore unreachable matrix server 2024-05-20 16:46:58 +08:00
Paul Holden b25405fd56 MDL-81904 mod_feedback: show export questions only if any present. 2024-05-20 09:27:38 +01:00
Huong Nguyen 5565f47e9b MDL-80911 Course: Fix missing required files 2024-05-20 10:08:02 +07:00
Huong Nguyen 2848c7d6be Merge branch 'MDL-81929-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-20 09:20:11 +07:00
Anupama Sarjoshi 4e151ddf30 MDL-81407 qbank_columnsortorder: Fix error displaying hiddencols
When the question custom fields were deleted, if there were any
hidden columns in qbank Column sort order referring those fields they
were throwing 'Custom field does not exist' exception. Changes done to
ignore such fields and display valid hiddencols, as these references
were breaking the qbank Column sort order and Question bank pages.
2024-05-19 08:58:45 +01:00
Daniel Ziegenberg 4b9de301c3 MDL-78768 grade: fix import multiple new grade items
Prior to this change, when importing grades from .csv (or another
method) and you choose to map the grades to a 'New grade item', you get
the following error message:
```
"Mapping collision detected - two fields maps to the same grade item
new."
```
A workaround existed that required users to import one column at a time,
which was cumbersome.

This change fixes importing multiple new grade items and adds a behat
test.

Co-authored-by: hehe009 <max_kan@hotmail.com>
Signed-off-by: Daniel Ziegenberg <daniel@ziegenberg.at>
2024-05-17 21:22:09 +02:00
Paul Holden dbffeb334b MDL-81955 assignfeedback_editpdf: ensure ghostscript present in tests. 2024-05-17 10:25:27 +01:00
ferranrecio 165709ff7d MDL-81948 theme: fix fallback layout logic
Finding the page layout fallback was trying to get a default layout from
the ancient theme called "base". This default theme do not exists
anymore and defining a wrong layout in a theme is a code mistake that
should not be bypassed.
2024-05-17 10:43:35 +02:00
Ilya Tregubov 0a75d57b13 MDL-80599 enrol_guest: Support password update in csv course upload 2024-05-17 11:01:22 +08:00
Ilya Tregubov 3182c71093 MDL-80599 enrol_self: Support password update in csv course upload 2024-05-17 11:01:22 +08:00
Ilya Tregubov 56fb55683a MDL-80599 core: Refactor enrol methods
Move update instance logic to its own method and cover with tests.
Also update enrol plugins to work woth new methods.
2024-05-17 11:01:22 +08:00
Ilya Tregubov 78229db6a1 MDL-80599 enrol_self: Check group key regardless of policy used
Password policy is not connected to using of group key enrolment
2024-05-17 11:01:22 +08:00
Paul Holden d04044bb0a MDL-81936 editor_tiny: handle default filepicker/draftitem config.
If they aren't passed in editor options config, ensure they still
observe the registered option processor (type) for each.
2024-05-16 18:49:46 +01:00
Jordan Tomkinson c1b4ccc4c6 MDL-81751 ddl: new reserved keywords in Aurora MySQL database engine 2024-05-16 13:41:29 +01:00
Paul Holden 5db3c95e29 MDL-81929 user: reset the selected bulk action when nothing selected.
In the case of bulk user actions that are implemented via Javascript,
we should ensure that once they are complete the selected action is
reset to its default state of "Choose..." so that it can be selected
again.
2024-05-16 13:00:26 +01:00
Huong Nguyen bcae2164ac weekly release 4.4+ 2024-05-16 18:25:19 +07:00
Huong Nguyen 207eed1190 Merge branch 'install_404_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_404_STABLE 2024-05-16 18:25:16 +07:00
Huong Nguyen c948d9e40e Merge branch 'MDL-81598-404' of https://github.com/laurentdavid/moodle into MOODLE_404_STABLE 2024-05-16 09:51:05 +07:00
Huong Nguyen 9ccdee7687 Merge branch 'MDL-81543-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-16 09:38:53 +07:00
Huong Nguyen 1a3e68a956 Merge branch 'MDL-79838-404' of https://github.com/andelacruz/moodle into MOODLE_404_STABLE 2024-05-16 09:21:54 +07:00
Paul Holden ffe67fc6df MDL-81318 assignsubmission_onlinetext: ignore blank content download. 2024-05-15 09:33:18 +01:00
Angelia Dela Cruz 8a60437bea MDL-79838 mod_h5pactivity: Behat for H5P activity grade settings control 2024-05-15 14:33:21 +08:00
Shamim Rezaie c62143a5df Merge branch 'MDL-81530-404' of https://github.com/HuongNV13/moodle into MOODLE_404_STABLE 2024-05-15 15:12:59 +10:00
Huong Nguyen 691f2c8950 Merge branch 'MDL-51360-404-2' of https://github.com/ilyatregubov/moodle into MOODLE_404_STABLE 2024-05-15 10:44:18 +07:00
Ilya Tregubov 6ae2c78e9f Merge branch 'MDL-70441-404-fix' of https://github.com/lameze/moodle into MOODLE_404_STABLE 2024-05-15 09:30:40 +08:00
Simey Lameze 7a4ea4dd65 MDL-70441 mod_assign: change condition to check if variable is not empty 2024-05-15 09:15:16 +08:00
Shamim Rezaie 217a7af933 MDL-81544 theme_boost: Don't change the display of .visibleifjs elements 2024-05-15 04:13:24 +10:00
Sara Arjona df176b292a Merge branch 'MDL-81805-404' of https://github.com/lameze/moodle into MOODLE_404_STABLE 2024-05-14 17:05:38 +02:00
Juan Leyva fa5a564a17 MDL-81897 tool_mobile: Force partitioned cookies on inapp browser 2024-05-14 13:44:15 +02:00
Ilya Tregubov dd1d91486d Merge branch 'MDL-70441-404' of https://github.com/kevpercy/moodle into MOODLE_404_STABLE 2024-05-14 14:09:05 +08:00
David Balch f9ab045794 MDL-70441 assign: Freeze workflow states not available to user.
Prior to this, a custom grader role that has mod/assign:grade but not
mod/assign:releasegrades or mod/assign:managegrades would see a select
with only the states available to them - not the current state, and
could reset a grade from released to Not marked/In marking/Marking
completed.
2024-05-14 12:55:00 +08:00
Huong Nguyen 32bf836fec Merge branch 'MDL-81434-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-14 08:52:47 +07:00
Paul Holden e205d5accc MDL-81543 qtype_calculated: correct component of language string. 2024-05-13 10:43:44 +01:00
Laurent David 9febdf9b0f MDL-81598 theme_boost: Fix quiz review options tooltips 2024-05-13 08:26:29 +02:00
Ilya Tregubov ce8695ff1f Merge branch 'MDL-81867-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-13 09:29:09 +08:00
AMOS bot 3e80d6ee79 Automatically generated installer lang files 2024-05-12 00:14:32 +00:00
Paul Holden 513bafbcfd MDL-81867 qbank_columnsortorder: correct/cleanup previous upgrade step. 2024-05-10 09:16:46 +01:00
Ilya Tregubov 7fc818ebb8 MDL-51360 core_grades: Improve documentation of grade_get_grades(). 2024-05-10 09:17:03 +08:00
Ilya Tregubov 0219831af4 MDL-51360 mod_quiz: Don't do full course recalculation.
It seems it was never needed. We only need to know if just one
grade is overridden and its value.
2024-05-10 09:17:03 +08:00
Ilya Tregubov 477aa7c750 weekly release 4.4+ 2024-05-10 09:09:33 +08:00
Paul Holden 47e844d0f4 MDL-81757 editor_tiny: preserve unicode character escape in errors.
Removing backslashes from the XHR response led to errors including
unicode escape codes being broken.
2024-05-09 20:13:14 +01:00
Amaia Anabitarte c44c1b5345 MDL-81601 core_course: Add course index completion status behats 2024-05-09 19:43:20 +08:00
Jun Pataleta 28e9e58577 Merge branch 'MDL-81713-404' of https://github.com/junpataleta/moodle into MOODLE_404_STABLE 2024-05-09 12:02:19 +08:00
Jun Pataleta 6a628bcd3a MDL-81713 mod_quiz: Use accessible fieldset name for Behat test 2024-05-09 11:56:22 +08:00
Luca Bösch 10e44e40d9 MDL-81713 mod_quiz: Add random question 'Parent category' help icon. 2024-05-09 11:56:09 +08:00
Jun Pataleta a625583b11 Merge branch 'MDL-80972-404' of https://github.com/lameze/moodle into MOODLE_404_STABLE 2024-05-09 11:43:23 +08:00
info@eWallah.net 810ab99d7d MDL-80972 behat: Behat check_server_status can provide better info. 2024-05-09 11:07:31 +08:00
Huong Nguyen 82a76a1b69 Merge branch 'MDL-81423_404' of https://github.com/timhunt/moodle into MOODLE_404_STABLE 2024-05-09 09:55:24 +07:00
Huong Nguyen ddca055e0d Merge branch 'MDL-80453-404' of https://github.com/andelacruz/moodle into MOODLE_404_STABLE 2024-05-09 09:47:55 +07:00
Ilya Tregubov b0a11d12ca Merge branch 'MDL-81601-404' of https://github.com/aanabit/moodle into MOODLE_404_STABLE 2024-05-09 10:46:17 +08:00
Huong Nguyen 996564f803 Merge branch 'MDL-79702-404' of https://github.com/lameze/moodle into MOODLE_404_STABLE 2024-05-09 09:33:39 +07:00
Paul Holden 2512e6b09e MDL-77685 grade: remove Behat specific report rendering hacks.
These changes follow the same approach as that taken elsewhere in
the Gradebook in 3643f48e0f.
2024-05-08 12:29:43 +01:00
Angelia Dela Cruz d5d6b0f7f9 MDL-80453 mod_glossary: Behat for different glossary format display 2024-05-08 17:26:13 +08:00
Amaia Anabitarte 52b4f3bb1d MDL-81601 core_course: Fix course index completion status 2024-05-08 08:25:57 +02:00
Paul Holden 1c2f5dc1df MDL-81434 reportbuilder: ensure filter/condition parameter uniqueness.
This change fixes an edge case that could be triggered by creating a
custom report that contained a filter instance that was active as both
a filter and condition, where the filter instance provides parameters
to it's SQL fragment.

There is only one such filter present currently with which we can test
this, see 2f9001cbe9.
2024-05-07 15:03:16 +01:00
Paul Holden 75d95bc4b8 Merge branch 'MDL-81764_404' of https://github.com/lostrogit/moodle into MOODLE_404_STABLE 2024-05-07 14:43:03 +01:00
Paul Holden a6c17af4c7 Merge branch 'MDL-81776-404' of https://github.com/dravek/moodle into MOODLE_404_STABLE 2024-05-07 14:33:10 +01:00
Huong Nguyen 7ac2d7f598 MDL-81530 editor_tiny: Better browsers compatibility
The TinyMCE.remove() function is killing other events on Firefox
We need to modify the setupForElementId() to make TinyMCE to
remove itself outside of the event loop
2024-05-07 14:21:22 +07:00
Simey Lameze c4533967d1 MDL-81805 behat: remove unnecessary @javascript from tests 2024-05-07 09:10:19 +08:00
Paul Holden 5bd4a81e09 MDL-81779 theme_boost: more liberal selector for form-control elements.
There are various pages (e.g. many related to permissions), where the
form element is not a descendent of `.mform` - so the previous selector
never matched them and led to unstyled inputs.

Co-authored-by: Mikel Martín <mikel@moodle.com>
2024-05-06 20:34:41 +01:00
Simey Lameze 72f0a11f0d MDL-79702 behat: improvements and fixes to new tests
This commit adds the handling of tags as string separated by comma
to forum and glossary modules.
2024-05-06 20:02:23 +08:00
Angelia Dela Cruz 7b873db958 MDL-79702 core_tag: Behat for activity tags deletion during course reset 2024-05-06 20:02:23 +08:00
Huong Nguyen b83cd14d25 Merge branch 'MDL-80589-404' of https://github.com/lameze/moodle into MOODLE_404_STABLE 2024-05-06 09:37:09 +07:00
Ilya Tregubov 4b0ca64617 Merge branch 'MDL-81656-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-06 09:55:34 +08:00
David Carrillo 7741ae3094 MDL-81776 reportbuilder: Check tagarea enabled when check availability
- When checking availability for a column/filter we need to assert
tagarea is enabled because it can return also null value.
2024-05-03 12:22:46 +02:00
Tim Hunt aa1dc46bc8 MDL-81423 assign: improve valdation message for submissionstatement 2024-05-03 10:33:46 +01:00
Shamim Rezaie 27dd2e751f MDL-81538 theme_boost: Fix popover arrow direction in RTL 2024-05-03 13:03:30 +10:00
Ilya Tregubov f88dbfcafc weekly release 4.4+ 2024-05-03 08:22:32 +08:00
Ilya Tregubov aaf45097b7 Merge branch 'install_404_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_404_STABLE 2024-05-03 08:22:29 +08:00
AMOS bot adb0352ef4 Automatically generated installer lang files 2024-05-03 00:10:48 +00:00
Carlos Castillo da4486b202 MDL-81764 reportbuilder: Include full name filter to Users system report 2024-05-02 13:56:47 +02:00
Simey Lameze 778470818a MDL-80589 behat: fixes and improvements to new tests 2024-05-02 13:33:44 +08:00
Angelia Dela Cruz 4625eeb998 MDL-80589 qtype_numerical: Behat to add, edit, preview numeric question 2024-05-02 13:33:44 +08:00
Huong Nguyen 4e7d555c18 Merge branch 'MDL-81587_404' of https://github.com/timhunt/moodle into MOODLE_404_STABLE 2024-05-02 09:37:58 +07:00
Huong Nguyen 8b7f262aca Merge branch 'MDL-81611_404_STABLE' of https://github.com/marxjohnson/moodle into MOODLE_404_STABLE 2024-05-02 09:02:11 +07:00
Huong Nguyen d33dd13fd5 Merge branch 'MDL-79851-404' of https://github.com/lameze/moodle into MOODLE_404_STABLE 2024-05-01 16:10:54 +07:00
Angelia Dela Cruz a548bccf18 MDL-79851 mod_h5pactivity: Behat for h5p activity access control 2024-05-01 14:26:10 +08:00
Huong Nguyen 9977c07a69 Merge branch 'MDL-81621-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-05-01 10:59:03 +07:00
Sara Arjona eedb5fde7a Merge branch 'MDL-81724-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-04-30 16:38:04 +02:00
Tim Hunt eabc15f983 MDL-81587 quiz: fix layout of the add/edit random UI
Some of the removed CSS seems no longer relevant now that we have
customisation options for the layout of the question bank.
2024-04-30 15:25:11 +01:00
Sara Arjona 18ac43934a Merge branch 'MDL-78889-404' of https://github.com/ilyatregubov/moodle into MOODLE_404_STABLE 2024-04-30 16:05:12 +02:00
Sara Arjona 35914200b5 MDL-80571 tiny_premium: Fix version 2024-04-30 15:45:39 +02:00
Sara Arjona 48c064505e Merge branch 'MDL-81632-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-04-30 15:09:34 +02:00
Sara Arjona 417fbe1534 Merge branch 'MDL-80571-404' of https://github.com/davewoloszyn/moodle into MOODLE_404_STABLE 2024-04-30 15:05:56 +02:00
Paul Holden c70ceb5f7b MDL-81724 editor_tiny: remove whitespace from block formats syntax.
Ensures that the values are subsequently translated correctly. Workaround
for upstream bug: https://github.com/tinymce/tinymce/pull/9599

Co-authored-by: Andrew Nicols <andrew@nicols.co.uk>
2024-04-30 09:26:17 +01:00
Huong Nguyen c4735904e5 Merge branch 'MDL-79149-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-04-30 11:48:13 +07:00
Huong Nguyen 0792e7bdd9 Merge branch 'MDL-81717-404' of https://github.com/sarjona/moodle into MOODLE_404_STABLE 2024-04-30 11:38:11 +07:00
Huong Nguyen eaf09ebbc4 Merge branch 'MDL-81707-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-04-30 10:14:44 +07:00
Ilya Tregubov d620ee8cdf Merge branch 'MDL-77272-404' of https://github.com/SergioComeron/moodle into MOODLE_404_STABLE 2024-04-30 10:18:21 +08:00
Huong Nguyen f7124a4a95 MDL-81610 course: Update upgrade.txt 2024-04-30 09:12:17 +07:00
Huong Nguyen 34f1f22b00 Merge branch 'MDL-81610_404_STABLE' of https://github.com/marxjohnson/moodle into MOODLE_404_STABLE 2024-04-30 09:01:19 +07:00
David Woloszyn a95ac513bf MDL-80571 tiny_premium: Premium plugins can be individually enabled 2024-04-30 09:40:42 +10:00
Sara Arjona a829242d47 MDL-81717 h5p: Improve robustness content type fetching
Strengthen code to ensure retrieval of all content types, despite
failures.
2024-04-29 12:33:38 +02:00
Paul Holden 2479bc2042 MDL-81707 availability_grade: fix alignment of condition checkboxes. 2024-04-29 10:38:41 +01:00
Paul Holden 67376511ac MDL-81656 h5p: correct content editing URL parameter handling. 2024-04-29 09:22:21 +01:00
Paul Holden b65b4892ae MDL-81621 mod_bigbluebuttonbn: remove reflection setAccessible call.
See 361dfe8145 for context.
2024-04-29 09:21:19 +01:00
Ilya Tregubov be159037d4 MDL-78889 gradereport_user: Fix pass/fail icon for totals
When the category has a 'grade to pass' set, the pass/fail icon
was displayed even when the category total grade values was hidden.
2024-04-29 14:44:28 +08:00
Ilya Tregubov eccadb67b7 MDL-81380 qbank_columnsortorder: Add type and return value hint 2024-04-29 10:47:59 +08:00
Ilya Tregubov 94d8b7f040 Merge branch 'MDL-81380_404' of https://github.com/AnupamaSarjoshi/moodle into MOODLE_404_STABLE 2024-04-29 10:47:06 +08:00
Huong Nguyen c6bf1efac6 Merge branch 'MDL-80849-404' of https://github.com/davewoloszyn/moodle into MOODLE_404_STABLE 2024-04-29 08:55:27 +07:00
Ilya Tregubov 50c7842d73 Merge branch 'MDL-80597-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-04-29 09:46:14 +08:00
Ilya Tregubov 48597285cd Merge branch 'MDL-81697-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-04-29 09:22:27 +08:00
David Woloszyn e13fe01c7b MDL-80849 communication_matrix: Prevent numeric usernames 2024-04-29 10:49:32 +10:00
Anupama Sarjoshi 1542a9f9e1 MDL-81380 qbank_columnsortorder: Add upgrade.php
Before Moodle 4.3, config_plugins settings for qbank_columnsortorder
(disabledcol, enabledcol) had a value like
qbank_statistics\columns\facility_index,
qbank_statistics\columns\discriminative_efficiency, ...
In Moodle 4.3, the values are stored as
qbank_statistics\columns\facility_index-facility_index.
So updating the old values to match the new format.
2024-04-26 17:12:45 +01:00
Paul Holden 310b932482 MDL-79149 report_participation: more resilient activity access test.
Use the "Forum" module rather than the "Book" module, because the latter
emits two events when a student views it - if these two events happened
either side of a single second, then the report  would count two distinct
views (when it was trying to assert only one).
2024-04-26 16:08:11 +01:00
Paul Holden edf923e38e MDL-80597 mod_quiz: simplify filtering by tags Behat assertions.
The initial filtering for category was redundant, as it's applied
by default. While present it also caused reproducible failures on
CI due to timing issues when trying to apply multiple filters.
2024-04-26 10:27:06 +01:00
Huong Nguyen 95a5cb8038 Merge branch 'MDL-78433-404' of https://github.com/aanabit/moodle into MOODLE_404_STABLE 2024-04-26 16:19:53 +07:00
Huong Nguyen 771786ad4d Merge branch 'MDL-81641_404' of https://github.com/timhunt/moodle into MOODLE_404_STABLE 2024-04-26 16:03:54 +07:00
Sara Arjona 5f6b32c4cb Merge branch 'MDL-81581_404' of https://github.com/stronk7/moodle into MOODLE_404_STABLE 2024-04-26 10:48:29 +02:00
Sara Arjona 8ee732f391 Merge branch 'MDL-77445-404' of https://github.com/andelacruz/moodle into MOODLE_404_STABLE 2024-04-26 10:40:29 +02:00
Paul Holden 21e6eea20e MDL-81697 qtype_ordering: correctly reference feedback file areas. 2024-04-25 21:38:15 +01:00
Eloy Lafuente (stronk7) 1dbc78253c MDL-81581 phpunit: Apply the new method to all remaining cases
After discussing it in the issue, we have agreed to apply
the new phpunit_util::normalise_line_endings() method to
all the remaining cases (within tests!) where the manual
replacement was being done. This commits achieves that.

Note that I've looked for both str_replace() and preg_replace()
cases, but only the former had cases worth converting. All the
later ones are different and cannot be replaced by the new
utility method.
2024-04-25 18:52:25 +02:00
Eloy Lafuente (stronk7) c9d48f6f25 MDL-81581 phpunit: Create the normalise_line_endings() method
And apply it to all the obvious places related with the issue.

Note that surely there are way more in code base, but it's
out of scope for this issue.
2024-04-25 18:52:25 +02:00
Eloy Lafuente (stronk7) 894ee17f53 MDL-81581 phpunit: Move multiple assert_same_xml() to common one 2024-04-25 18:52:25 +02:00
Mark Johnson cbdfc83b69 MDL-81610 course: Optimise core_courseformat\external\get_state
core_courseformat\external\get_state calls export_for_tempalte for each
course module, which in turn calls completion_info::is_tracked_user()
for the current user on the current course. The result of this call will
be the same for every course module, so does not need to be called what
may be several hundred times.

This change allows the result of is_tracked_user to be passed to the cm
state object, then calls it once and passes it to each cm's object. If
it is not passed in, it will be computed on demand as before.
2024-04-25 16:15:21 +01:00
Mark Johnson 6c484f9fc8 MDL-81611 assign: Close session early in gradingpanel fragment 2024-04-25 16:14:26 +01:00
SergioComeron 6de35b2623 MDL-77272 report: Enable module level report options 2024-04-25 17:13:03 +02:00
Sara Arjona f0c1a3789d weekly release 4.4+ 2024-04-25 15:43:27 +02:00
Sara Arjona 8f0f474882 Merge branch 'install_404_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_404_STABLE 2024-04-25 15:43:24 +02:00
Philipp Imhof 452cf5c72d MDL-80087 Questions: warn if preload_all_step_users was not called
Co-authored-by: Tim Hunt <T.J.Hunt@open.ac.uk>
2024-04-25 09:37:47 +07:00
Paul Holden dd88b540c2 MDL-81632 block_recentlyaccesseditems: deterministic item ordering.
Behat tests of this functionality could trigger the unlikely (in real
world usage) scenario where a user has an identical "timeaccess" value
for multiple course activities.

This led to random failures in said tests when the DB ordered items in
apparently random order, where the "timeaccess" value was equal.
2024-04-24 20:57:36 +01:00
Sara Arjona 6a04c121af Merge branch 'MDL-77998-404' of https://github.com/andelacruz/moodle into MOODLE_404_STABLE 2024-04-24 14:47:59 +02:00
Angelia Dela Cruz 83eb2a624c MDL-77998 Behat: Add behat coverage for preferences navigation 2024-04-24 19:38:50 +08:00
Huong Nguyen 1d6bee488d Merge branch 'MDL-81627_404' of https://github.com/timhunt/moodle into MOODLE_404_STABLE 2024-04-24 15:26:05 +07:00
Tim Hunt 14574fdb56 MDL-81301 quiz: improve performance of deleting a quiz 2024-04-24 15:19:23 +07:00
Angelia Dela Cruz aa213563e7 MDL-77445 Behat: Coverage for activity chooser navigation 2024-04-24 15:38:05 +08:00
Jun Pataleta 1d01c74390 Merge branch 'MDL-81570-404' of https://github.com/sarjona/moodle into MOODLE_404_STABLE 2024-04-24 14:40:39 +08:00
ferranrecio 0e70a88d03 MDL-81575 core_courseformat: fix interactive section visibility badge 2024-04-24 12:37:32 +08:00
Jun Pataleta 49b666b670 Merge branch 'MDL-81567-404' of https://github.com/lucaboesch/moodle into MOODLE_404_STABLE 2024-04-24 12:21:59 +08:00
Mikel Martín 4c1beb921f MDL-81460 theme_boost: Fix loading spinner wobbles 2024-04-24 12:11:01 +08:00
Huong Nguyen dcb43cf3b0 Merge branch 'MOODLE_404_MDL-81613' of https://github.com/danielneis/moodle into MOODLE_404_STABLE 2024-04-24 09:59:59 +07:00
Huong Nguyen aa71fcfb7f Merge branch 'MDL-81564-404' of https://github.com/HuongNV13/moodle into MOODLE_404_STABLE 2024-04-24 08:58:03 +07:00
Laurent David 53557c3be1 MDL-81564 mod_data: Remove duplicate error messages
* When a field is missing the error is duplicated for all field
subsequently added to the database
2024-04-24 08:54:58 +07:00
Ilya Tregubov 16f7008c68 Merge branch 'MDL-81274-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-04-24 09:44:17 +08:00
Huong Nguyen 291a5449eb Merge branch 'MDL-80107-404' of https://github.com/andelacruz/moodle into MOODLE_404_STABLE 2024-04-24 08:35:50 +07:00
Angelia Dela Cruz 42d52002ee MDL-80107 gradingform: Behat test for defining marking guide 2024-04-24 09:03:58 +08:00
Tim Hunt c9b7b67fad MDL-81641 question: fix random question loader draft state tests 2024-04-23 18:25:24 +01:00
Daniel Neis Araujo 8a2929c1a9 MDL-81613 report_log: fix user fullname export for log report 2024-04-23 14:05:07 -03:00
Amaia Anabitarte 8f53f30e02 MDL-78433 course: Remove duplicated course search form 2024-04-23 16:23:13 +02:00
Sara Arjona 9ea7607613 Merge branch 'MDL-81209-main' of https://github.com/andrewnicols/moodle into MOODLE_404_STABLE 2024-04-23 15:36:42 +02:00
Sara Arjona 26a607e8a3 Merge branch 'MDL-81578-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-04-23 15:13:46 +02:00
Sara Arjona 4378d1dadc Merge branch 'MDL-81457-main' of https://github.com/andrewnicols/moodle into MOODLE_404_STABLE 2024-04-23 14:57:03 +02:00
Sara Arjona 582eb88404 MDL-81570 tiny_premium: Add missing lang string 2024-04-23 12:43:40 +02:00
Sara Arjona ec0e1d74a0 Merge branch 'MDL-81603-404' of https://github.com/paulholden/moodle into MOODLE_404_STABLE 2024-04-23 12:38:44 +02:00
Tim Hunt 82eacb2e48 MDL-81627 quiz restore: fix restore of grade item for each slot 2024-04-23 11:34:16 +01:00
Jun Pataleta 8b976e77ce MDL-81616 upgrade: add the 4.4.0 separation line to all upgrade scripts 2024-04-23 15:24:02 +10:00
Paul Holden cab8184fcb MDL-81274 mod_data: observe current approved value on record import. 2024-04-22 12:48:54 +01:00
Paul Holden 7b18faed92 MDL-78942 tool_mfa: replace deprecated coding standards annotation.
Replace with equivalent `phpcs` annotation as appropriate, same as
done previously in a5c91474ee (see that commit for further context).
2024-04-22 12:29:15 +01:00
Paul Holden 4adb2a7695 MDL-81578 user: disable form change checker for bulk user actions.
When bulk user actions are used from the "Browse users" report.
2024-04-22 12:22:57 +01:00
Paul Holden 410b333e47 MDL-81603 reportbuilder: don't rely on real entity in datasource test. 2024-04-22 11:24:10 +01:00
AMOS bot a8908c1a50 Automatically generated installer lang files 2024-04-22 00:10:33 +00:00
Luca Bösch b0fec7d82b MDL-81567 mod_quiz: Adopt edit section title pencil icons size. 2024-04-20 23:05:11 +02:00
11757 changed files with 196620 additions and 371946 deletions
-3
View File
@@ -1,6 +1,3 @@
**/yui/build/** -diff
**/amd/build/** -diff
lib/dml/oci_native_moodle_package.sql text eol=lf
**.js.map -diff
**-min.js -diff
**.min.js -diff
+1 -1
View File
@@ -1,2 +1,2 @@
# Primary donations pages.
custom: ["https://moodle.com/donations/", "https://fund.moodle.org"]
custom: ["https://moodle.com/donations/", moodle.org]
+4 -6
View File
@@ -4,6 +4,7 @@ on:
push:
branches-ignore:
- main
- master
- MOODLE_[0-9]+_STABLE
tags-ignore:
- v[0-9]+.[0-9]+.[0-9]+*
@@ -19,7 +20,7 @@ env:
jobs:
Grunt:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
steps:
- name: Checking out code
@@ -61,12 +62,12 @@ jobs:
matrix:
include:
# MySQL builds always run with the lowest PHP supported version.
- os: ubuntu-24.04
- os: ubuntu-22.04
php: 8.1
extensions:
db: mysqli
# PostgreSQL builds always run with the highest PHP supported version.
- os: ubuntu-24.04
- os: ubuntu-22.04
php: 8.3
db: pgsql
@@ -122,6 +123,3 @@ jobs:
dbtype: ${{ matrix.db }}
phpunit_options: ${{ secrets.phpunit_options }}
run: vendor/bin/phpunit $phpunit_options ${{ inputs.phpunit_extra_options }}
- name: Git status
run: git status
-23
View File
@@ -1,23 +0,0 @@
name: Web Installer Testing
on:
workflow_dispatch:
jobs:
InstallationTest:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
php: [8.1, 8.3]
steps:
- name: Construct repository URL
run: echo "REPOSITORY_URL=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" >> $GITHUB_ENV
shell: bash
- uses: moodlehq/moodle-webinstaller-test@main
with:
repository: ${{ env.REPOSITORY_URL }}
branch: ${{ github.ref_name }}
php: ${{ matrix.php }}
+1 -1
View File
@@ -128,7 +128,7 @@ jobs:
- name: Setting up redis service
run: |
choco install redis --version 5.0.7 --no-progress
choco install redis-64 --version 3.0.503 --no-progress
nssm install redis redis-server
nssm start redis
+1 -4
View File
@@ -26,9 +26,7 @@
/.patches/
/.idea/
.phpstorm.*
!/.phpstorm.meta.php/
/.phpstorm.meta.php/*
!/.phpstorm.meta.php/di.php
!.phpstorm.meta.php
/nbproject/
CVS
.DS_Store
@@ -58,4 +56,3 @@ moodle-plugin-ci.phar
.hugo_build.lock
phpcs.xml
jsconfig.json
UPGRADING-CURRENT.md
+2 -27
View File
@@ -40,20 +40,9 @@ const fetchComponentData = () => {
if (!Object.entries(componentData).length) {
componentData.subsystems = {};
componentData.pathList = [];
componentData.components = {};
componentData.standardComponents = {};
// Fetch the component definiitions from the distributed JSON file.
const components = JSON.parse(fs.readFileSync(`${gruntFilePath}/lib/components.json`));
const pluginData = JSON.parse(fs.readFileSync(`${gruntFilePath}/lib/plugins.json`));
componentData.pluginTypes = components.plugintypes;
const standardPlugins = Object.entries(pluginData.standard).map(
([pluginType, pluginNames]) => {
return pluginNames.map(pluginName => `${pluginType}_${pluginName}`);
}
).reduce((acc, val) => acc.concat(val), []);
// Build the list of moodle subsystems.
componentData.subsystems.lib = 'core';
@@ -66,8 +55,8 @@ const fetchComponentData = () => {
}
}
// The list of components includes the list of subsystems.
componentData.components = {...componentData.subsystems};
// The list of components incldues the list of subsystems.
componentData.components = componentData.subsystems;
// Go through each of the plugintypes.
Object.entries(components.plugintypes).forEach(([pluginType, pluginTypePath]) => {
@@ -98,20 +87,6 @@ const fetchComponentData = () => {
});
});
// Create a list of the standard subsystem and plugins.
componentData.standardComponents = Object.fromEntries(
Object.entries(componentData.components).filter(([, name]) => {
if (name === 'core' || name.startsWith('core_')) {
return true;
}
return standardPlugins.indexOf(name) !== -1;
})
);
componentData.componentMapping = Object.fromEntries(
Object.entries(componentData.components).map(([path, name]) => [name, path])
);
}
return componentData;
-138
View File
@@ -1,138 +0,0 @@
// 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/>.
import * as Components from '../../components.js';
const componentData = Components.fetchComponentData();
/**
* The standard components shipped with core Moodle.
*
* @type {Object}
*/
export const standardComponents = componentData.standardComponents;
/**
* All components of the current Moodle instance.
*
* @type {Object}
*/
export const allComponents = componentData.components;
/**
* Get all components of the current Moodle instance.
*
* @returns {Array}
*/
export const getAllComponents = () => {
let components = new Map(Object.entries(componentData.pluginTypes).map(([value, path]) => ([path,{
path,
value,
name: `${value} (plugin type)`,
}])));
Object
.entries(componentData.components)
.filter(([path, value]) => Object.values(componentData.standardComponents).includes(value))
.forEach(([path, value]) => {
const entry = {
path,
value,
name: value,
};
if (Object.values(componentData.subsystems).includes(value)) {
if (components.has(path)) {
entry.name = `${value} (subsystem / plugintype)`;
} else {
entry.name = `${value} (subsystem)`;
}
}
components.set(path, entry);
});
return Array.from(components.values());
};
/**
* Whether the specified component is a standard component shipped with core Moodle.
*
* @param {string} componentName
* @returns {boolean}
*/
export const isStandardComponent = (componentName) => {
if (Object.values(componentData.standardComponents).includes(componentName)) {
return true;
}
if (Object.keys(componentData.pluginTypes).includes(componentName)) {
return true;
}
return false;
};
export const rewritePlugintypeAsSubsystem = (componentName) => {
if (Object.keys(componentData.pluginTypes).includes(componentName)) {
const pluginTypePath = componentData.pluginTypes[componentName];
if (Object.keys(componentData.subsystems).includes(pluginTypePath)) {
return true;
}
}
return false;
};
/**
* Whether the specified component is a community component.
*
* @param {string} componentName
* @returns {boolean}
*/
export const isCommunityComponent = (componentName) => {
if (isStandardComponent(componentName)) {
return false;
}
return Object.values(componentData.components).indexOf(componentName) !== -1;
};
/**
* Sort method for components.
*
* This method sorts components putting `core` first, followed by core subsystems, then everything else.
*
* @param {String} a
* @param {String} b
* @returns {Number}
*/
export const sortComponents = (a, b) => {
// Always put 'core' first.
if (a === 'core') {
return -1;
} else if (b === 'core') {
return 1;
}
// Put core subsystems next.
if (a.startsWith('core_') && !b.startsWith('core_')) {
return -1;
} else if (b.startsWith('core_') && !a.startsWith('core_')) {
return 1;
}
// Sort alphabetically for everything else.
return a.localeCompare(b);
};
-85
View File
@@ -1,85 +0,0 @@
#!/usr/bin/env node
// 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/>.
import inquirer from 'inquirer';
import chalk from 'chalk';
import { createNote } from './note.mjs';
import { getInitialValues } from './helpers.mjs';
import * as Prompts from './prompts.mjs';
import logger from './logger.mjs';
export default async (options) => {
// Processs the initial values.
const initialValues = getInitialValues(options);
// Fetch information.
const messages = [];
const { issueNumber } = await inquirer.prompt([
Prompts.getIssuePrompt(),
], initialValues);
let selection = {};
let notePath;
do {
selection = {};
selection = await inquirer.prompt([
Prompts.getComponentsPrompt(),
Prompts.getTypePrompt(),
Prompts.getMessagePromptInput(),
], initialValues);
if (selection.message === '') {
selection = Object.assign(
selection,
await inquirer.prompt([
Prompts.getMessagePromptEditor(),
]),
);
}
logger.info(`
Creating upgrade note with the following options:
- Issue: ${chalk.bold(issueNumber)}
- Component: ${chalk.bold(selection.components)}
- Type: ${chalk.bold(selection.type)}
- Message:
${chalk.bold(selection.message)}
`);
messages.push({
components: [selection.components],
type: selection.type,
message: selection.message,
});
// Save the note so far.
if (notePath) {
await createNote(issueNumber, messages, notePath);
logger.info(`Updated note at: ${chalk.underline(chalk.bold(notePath))}`);
} else {
notePath = await createNote(issueNumber, messages);
logger.info(`Note created at: ${chalk.underline(chalk.bold(notePath))}`);
}
selection = Object.assign(
selection,
await inquirer.prompt([
Prompts.getAddAnotherPrompt(),
], initialValues),
);
} while (selection.addAnother);
};
-357
View File
@@ -1,357 +0,0 @@
#!/usr/bin/env node
// 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/>.
import chalk from 'chalk';
import { getAllComponents } from './components.mjs';
import { getCombinedNotesByComponent, deleteAllNotes } from './note.mjs';
import { getNoteName } from './noteTypes.mjs';
import { writeFile, readFile, unlink } from 'fs/promises';
import { join as joinPath } from 'path';
import logger from './logger.mjs';
import { getCurrentVersion } from './helpers.mjs';
/**
* Helper to fetch the current notes from a file.
*
* @param {string} file
* @returns {Promise<string>}
*/
const getCurrentNotes = async (file) => {
try {
return await readFile(file, 'utf8');
} catch (error) {
return null;
}
}
/**
* Update the UPGRADING.md file.
*
* @param {string} upgradeNotes
* @param {Object} options
* @param {boolean} options.deleteNotes
* @returns {Promise<void>}
*/
const updateUpgradeNotes = async (upgradeNotes, options) => {
const fileName = 'UPGRADING.md';
// Write the notes to a file.
logger.info(`Writing notes to ${chalk.underline(chalk.bold(fileName))}`);
// Prepend to the existing file.
const existingContent = await getCurrentNotes(fileName);
if (existingContent) {
await writeFile(fileName, getUpdatedNotes(existingContent, upgradeNotes));
} else {
// This should not normally happen.
await writeFile(fileName, upgradeNotes);
}
if (options.deleteNotes) {
logger.warn(`>>> Deleting all notes <<<`)
// Delete the notes.
deleteAllNotes();
}
};
/**
* Create the current summary notes.
*
* @param {string} upgradeNotes
* @returns {Promise<void>}
*/
const createCurrentSummary = async (upgradeNotes) => {
const fileName = 'UPGRADING-CURRENT.md';
const notes = `# Moodle upgrade notes\n\n${upgradeNotes}`;
await writeFile(fileName, notes);
logger.info(`Running upgrade notes written to ${chalk.underline(chalk.bold(fileName))}`);
};
/**
* Get the indexes of the lines that contain the version headings.
*
* @param {array<string>} lines
* @returns {array<object>}
*/
const getVersionLineIndexes = (lines) => {
const h2Indexes = [];
lines.forEach((line, index) => {
const matches = line.match(/^##\s(?<version>.*)$/);
if (matches) {
h2Indexes.push({
index,
line,
version: matches.groups.version,
});
}
});
return h2Indexes;
};
/**
* Find the index of the Unreleased heading.
*
* @param {array<object>} versionHeadings
* @returns {number}
*/
const findUnreleasedHeadingIndex = (versionHeadings) => versionHeadings.findIndex((heading) => {
if (heading.version === 'Unreleased') {
// Used if version cannot be guessed.
return true;
}
if (heading.version.endsWith('+')) {
// Weekly release for a stable branch.
return true;
}
if (heading.version.match(/beta|rc\d/)) {
// Beta and RC rolls are treated as weeklies.
return true;
}
if (heading.version.endsWith('dev')) {
// Development version.
return true;
}
return false;
});
/**
* Get the before and after content, to facilitate replacing any existing Unreleased notes.
*
* @param {array<string>} lines
* @returns {Object} {beforeContent: string, afterContent: string}
*/
const getBeforeAndAfterContent = (lines) => {
const existingLines = lines.split('\n');
const versionHeadings = getVersionLineIndexes(existingLines);
if (versionHeadings.length > 0) {
const unreleasedHeadingIndex = findUnreleasedHeadingIndex(versionHeadings);
if (unreleasedHeadingIndex !== -1) {
const beforeContent = existingLines.slice(0, versionHeadings[unreleasedHeadingIndex].index).join('\n');
if (versionHeadings.length > unreleasedHeadingIndex + 1) {
const afterContent = existingLines.slice(versionHeadings[unreleasedHeadingIndex + 1].index).join('\n');
return {
beforeContent,
afterContent,
};
}
return {
beforeContent,
afterContent: '',
};
}
return {
beforeContent: existingLines.slice(0, versionHeadings[0].index).join('\n'),
afterContent: existingLines.slice(versionHeadings[0].index).join('\n'),
};
}
return {
beforeContent: existingLines.join('\n'),
afterContent: '',
}
};
/**
* Get the notes for the component.
*
* @param {string} types
* @param {Number} headingLevel
* @returns {string}
*/
const getNotesForComponent = (types, headingLevel) => {
let upgradeNotes = '';
Object.entries(types).forEach(([type, notes]) => {
upgradeNotes += '#'.repeat(headingLevel);
upgradeNotes += ` ${getNoteName(type)}\n\n`;
notes.forEach(({ message, issueNumber }) => {
// Split the message into lines, removing empty lines.
const messageLines = message
.split('\n')
// Remove empty lines between tables, and list entries, but not after lists.
.filter((line, index, lines) => {
if (line.trim().length === 0) {
// This line is empty.
// If it's the first line in the file, remove it.
if (index === 0) {
return false;
}
// This is the last line in the file, remove it.
if (index === lines.length - 1) {
return false;
}
// If the previous line relates to a table, remove this line.
if (lines[index - 1].match(/^\s*\|/)) {
return false;
}
// If the next line is also empty, do not remove this line.
if (lines[index + 1].trim().length === 0) {
return true;
}
// Do not remove the line if the previous line was a list item.
if (lines[index - 1].match(/^\s*[-*]\s/)) {
return true;
}
if (lines[index - 1].match(/^\s*\d+\.\s/)) {
return true;
}
// Preserve all other empty lines by default.
return true;
}
// Keep any line which has content.
return true;
});
const firstLine = messageLines.shift().trim();
upgradeNotes += `- ${firstLine}\n`;
messageLines
.forEach((line) => {
upgradeNotes += ` ${line}`.trimEnd() + `\n`;
});
upgradeNotes += `\n For more information see [${issueNumber}](https://tracker.moodle.org/browse/${issueNumber})\n`;
});
upgradeNotes += '\n';
});
return upgradeNotes;
};
/**
* Get the updated notes mixed with existing content.
*
* @param {string} existingContent
* @param {string} upgradeNotes
*/
const getUpdatedNotes = (existingContent, upgradeNotes) => {
const { beforeContent, afterContent } = getBeforeAndAfterContent(existingContent);
const newContent = `${beforeContent}\n${upgradeNotes}\n${afterContent}`
.split('\n')
.filter((line, index, lines) => {
if (line === '' && lines[index - 1] === '') {
// Remove multiple consecutive empty lines.
return false;
}
return true;
})
.join('\n');
return newContent;
};
/**
* Update the notes for each component.
*/
const updateComponentNotes = (
notes,
version,
notesFileName = 'UPGRADING.md',
removeEmpty = false,
) => {
return getAllComponents().map(async (component) => {
logger.verbose(`Updating notes for ${component.name} into ${component.path}`);
const fileName = joinPath(component.path, notesFileName);
const existingContent = await getCurrentNotes(fileName);
if (!existingContent) {
if (!notes[component.value]) {
// No existing notes, and no new notes to add.
return;
}
} else {
if (!notes[component.value]) {
// There is existing content, but nothing to add.
if (removeEmpty) {
logger.verbose(`Removing empty notes file ${fileName}`);
await unlink(fileName);
}
return;
}
}
const componentNotes = notes[component.value];
let upgradeNotes = `## ${version}\n\n`;
upgradeNotes += getNotesForComponent(componentNotes, 3);
if (existingContent) {
await writeFile(fileName, getUpdatedNotes(existingContent, upgradeNotes));
} else {
await writeFile(
fileName,
`# ${component.name} Upgrade notes\n\n${upgradeNotes}`,
);
}
});
}
/**
* Generate the upgrade notes for a new release.
*
* @param {string|undefined} version
* @param {Object} options
* @param {boolean} options.generateUpgradeNotes
* @param {boolean} options.deleteNotes
* @returns {Promise<void>}
*/
export default async (version, options = {}) => {
const notes = await getCombinedNotesByComponent();
if (Object.keys(notes).length === 0) {
logger.warn('No notes to generate');
return;
}
if (!version) {
version = await getCurrentVersion();
}
// Generate the upgrade notes for this release.
// We have
// - a title with the release name
// - the change types
// - which contain the components
// - which document each change
let upgradeNotes = `## ${version}\n\n`;
Object.entries(notes).forEach(([component, types]) => {
upgradeNotes += `### ${component}\n\n`;
upgradeNotes += getNotesForComponent(types, 4);
});
await Promise.all([
createCurrentSummary(upgradeNotes),
...updateComponentNotes(notes, version, 'UPGRADING-CURRENT.md', true),
]);
if (options.generateUpgradeNotes) {
await Promise.all(updateComponentNotes(notes, version));
await updateUpgradeNotes(upgradeNotes, options);
}
};
-230
View File
@@ -1,230 +0,0 @@
// 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/>.
import chalk from 'chalk';
import { isStandardComponent, isCommunityComponent, rewritePlugintypeAsSubsystem } from './components.mjs';
import { isValidNoteName } from './noteTypes.mjs';
import logger from './logger.mjs';
import { readFile } from 'fs/promises';
/**
* Validate an issue number input
*
* @param {string} input
* @returns {string|boolean}
*/
export const validateIssueNumber = (input) => {
if (!input) {
return 'You must provide a tracker issue number';
}
if (input.match(/^[a-zA-Z]*-\d+$/)) {
return true;
}
if (input.match(/^\d+$/)) {
return true;
}
return 'The issue number was not recognised as a valid issue number';
};
/**
* Format an issue number input.
*
* @param {string} input
* @returns {string}
*/
export const formatIssueNumber = (input) => {
if (input.match(/^[a-zA-Z]*-\d+$/)) {
return input;
}
if (input.match(/^\d+$/)) {
return `MDL-${input}`;
}
return input;
};
/**
* Validate a component.
*
* @param {string} input
* @returns {string|boolean}
*/
export const validateComponent = (input) => {
if (isStandardComponent(input)) {
return true;
}
if (isCommunityComponent(input)) {
return 'Currently only core plugins are supported.';
}
return 'The component was not recognised as a standard component';
};
export const formatComponent = (input) => {
if (rewritePlugintypeAsSubsystem(input)) {
return `core_${input}`;
}
return input;
}
/**
* Get the initial values from the options.
*
* @param {object} options
* @returns {object}
*/
export const getInitialValues = (options) => {
const initialValues = {};
const type = getInitialTypeValue(options);
if (type) {
initialValues.type = type;
}
const issueNumber = getInitialIssueValue(options);
if (issueNumber) {
initialValues.issueNumber = issueNumber;
}
const component = getInitialComponentValue(options);
if (component) {
initialValues.components = component;
}
const message = getInitialMessageValue(options);
if (message) {
initialValues.message = message
initialValues.addAnother = false;
}
return initialValues;
};
/**
* Get the initial type value.
*
* @param {Object} options
* @returns {string|undefined}
*/
const getInitialTypeValue = (options) => {
if (!options.type) {
return;
}
options.type = options.type.trim().toLowerCase();
if (isValidNoteName(options.type)) {
return options.type;
}
logger.warn(`Note type "${chalk.underline(chalk.red(options.type))}" is not valid.`);
};
/**
* Get the initial issue number value.
*
* @param {Object} options
* @returns {string|undefined}
*/
const getInitialIssueValue = (options) => {
if (!options.issue) {
return;
}
options.issue = options.issue.trim().toUpperCase();
const issueNumberValidated = validateIssueNumber(options.issue);
if (issueNumberValidated === true) {
const issueNumber = formatIssueNumber(options.issue);
if (issueNumber !== options.issue) {
logger.warn(
`Issue number "${chalk.underline(chalk.red(options.issue))}" was updated to ` +
`"${chalk.underline(chalk.green(issueNumber))}"`
);
}
return issueNumber;
} else {
logger.warn(`Issue number "${chalk.underline(chalk.red(options.issue))}" is not valid: ${issueNumberValidated}`);
}
};
/**
* Get the initial component value.
*
* @param {Object} options
* @returns {string|undefined}
*/
const getInitialComponentValue = (options) => {
if (!options.component) {
return;
}
options.component = options.component.trim().toLowerCase();
const componentValidated = validateComponent(options.component);
if (componentValidated === true) {
const component = formatComponent(options.component);
if (component !== options.component) {
logger.warn(
`Component "${chalk.underline(chalk.red(options.component))}" was updated to ` +
`"${chalk.underline(chalk.green(component))}"`
);
}
return component;
} else {
logger.warn(`Component "${chalk.underline(chalk.red(options.component))}" is not valid: ${componentValidated}`);
}
};
/**
* Get the initial message value.
*
* @param {Object} options
* @returns {string|undefined}
*/
const getInitialMessageValue = (options) => {
if (!options.message) {
return;
}
return options.message.trim();
};
/**
* Get the current version from the project /version.php file.
*
* @returns {Promise<string>}
*/
export const getCurrentVersion = async () => {
const versionRegex = new RegExp(/^ *\$release *= *['\"](?<release>[^ \+]+\+?) *\(Build:.*/m);
try {
const versionFile = await readFile('version.php', 'utf8');
const match = versionFile.match(versionRegex);
if (match) {
return match.groups.release;
}
} catch(error) {
logger.error('Unable to read the version file');
}
return "Unreleased";
}
-30
View File
@@ -1,30 +0,0 @@
#!/usr/bin/env node
// 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/>.
import winston from 'winston';
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
),
transports: [
new winston.transports.Console(),
],
});
export default logger;
-194
View File
@@ -1,194 +0,0 @@
// 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/>.
import yaml from 'js-yaml';
import path from 'path';
import { writeFile, mkdir, readdir, readFile, unlink } from 'fs/promises';
import { isValidNoteName, sortNoteTypes } from './noteTypes.mjs';
import { sortComponents } from './components.mjs';
const unreleasedPath = path.resolve('.upgradenotes');
/**
* Get the filename for the note.
*
* @param {string} issueNumnber The issue number
* @returns {string}
*/
const getFilename = (issueNumber) => {
const dateTimeFormat = new Intl.DateTimeFormat(undefined, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
fractionalSecondDigits: 2,
timeZone: 'UTC',
});
const date = Object.fromEntries(
dateTimeFormat.formatToParts(new Date())
.filter((p) => p.type !== 'literal')
.map((p) => ([p.type, p.value]))
);
const dateString = [
date.year,
date.month,
date.day,
date.hour,
date.minute,
date.second,
date.fractionalSecond,
].join('');
return `${issueNumber}-${dateString}.yml`;
};
/**
* Create a new note.
*
* @param {string} issueNumber
* @param {[type: string]: {message: string}} messages
* @returns {string} The path to the note on disk
*/
export const createNote = async (
issueNumber,
messages,
notePath,
) => {
const note = {
issueNumber,
notes: {},
};
messages.forEach(({components, type, message}) => {
note.notes[components] = note.notes[components] || [];
note.notes[components].push({message, type});
});
if (!notePath) {
notePath = path.resolve(unreleasedPath, getFilename(issueNumber));
}
const noteContent = yaml.dump(note);
await mkdir(unreleasedPath, {recursive: true});
await writeFile(notePath, noteContent);
return notePath;
};
/**
* Get all unreleased notes.
*
* @returns {Promise<{issueNumber: string, components: string[], types: {[type: string]: {message: string}[]}}[]>
*/
export const getAllNotes = async () => {
const files = await readdir(unreleasedPath);
const notes = files
.filter((file) => file.endsWith('.yml'))
.map(async (file) => {
const filePath = path.resolve(unreleasedPath, file);
const fileContent = await readFile(filePath, 'utf8');
return {
...yaml.load(fileContent),
filePath,
};
});
return Promise.all(notes);
};
/**
* Get the list of notes, grouped by note type, then component.
*
* @returns {Promise<{[type: string]: {[components: string]: {message: string, issueNumber: string}[]}}>}
*/
export const getCombinedNotes = async () => {
const notes = await getAllNotes();
const combinedNotes = {};
notes.forEach((note) => {
Object.entries(note.notes).forEach(([components, data]) => {
data.forEach((entry) => {
if (!isValidNoteName(entry.type)) {
throw new Error(`Invalid note type: "${entry.type}" in file ${note.filePath}`);
}
combinedNotes[entry.type] = combinedNotes[entry.type] || {};
combinedNotes[entry.type][components] = combinedNotes[entry.type][components] || [];
combinedNotes[entry.type][components].push({message: entry.message, issueNumber: note.issueNumber});
});
});
});
return combinedNotes;
};
/**
* Get the list of notes, grouped by component, then by note type.
*
* @returns {Promise<{[component: string]: {[type: string]: {message: string, issueNumber: string}[]}}>}
*/
export const getCombinedNotesByComponent = async () => {
const notes = await getAllNotes();
const combinedNotes = {};
notes.forEach((note) => {
Object.entries(note.notes).forEach(([component, data]) => {
combinedNotes[component] = combinedNotes[component] || {};
data.forEach((entry) => {
if (!isValidNoteName(entry.type)) {
throw new Error(`Invalid note type: "${entry.type}" in file ${note.filePath}`);
}
combinedNotes[component][entry.type] = combinedNotes[component][entry.type] || [];
combinedNotes[component][entry.type].push({
component,
message: entry.message,
issueNumber: note.issueNumber,
type: entry.type,
});
});
});
});
// Sort notes by note type.
Object.entries(combinedNotes).forEach(([component, types]) => {
combinedNotes[component] = Object.fromEntries(
Object.entries(types).sort(([a], [b]) => sortNoteTypes(a, b))
);
});
// Sort components.
return Object.fromEntries(
Object.entries(combinedNotes).sort(([a], [b]) => sortComponents(a, b))
);
};
/**
* Delete all unreleased notes.
*
* @returns {Promise<void>}
*/
export const deleteAllNotes = async () => {
const files = await readdir(unreleasedPath);
return Promise.all(
files
.filter((item, index) => files.indexOf(item) === index)
.filter((file) => file.endsWith('.yml'))
.map((file) => unlink(`${unreleasedPath}/${file}`))
);
};
-80
View File
@@ -1,80 +0,0 @@
// 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/>.
/**
* The note types.
*
* Matches the suggested order in https://keepachangelog.com/en/1.1.0/.
* @type {Object<string, string>}
*/
const noteTypes = {
'improved': 'Added',
'changed': 'Changed',
'deprecated': 'Deprecated',
'removed': 'Removed',
'fixed': 'Fixed',
};
/**
* The preferred order of note types.
*
* @type {string[]}
*/
const preferredOrder = Object.keys(noteTypes);
/**
* Comparison method to sort note types.
*
* @param {String} a
* @param {String} b
* @returns {Number}
*/
export const sortNoteTypes = (a, b) => {
const aIndex = preferredOrder.indexOf(a);
const bIndex = preferredOrder.indexOf(b);
if (aIndex === -1) {
return 1;
}
if (bIndex === -1) {
return -1;
}
return aIndex - bIndex;
};
/**
* Get the note names.
*
* @returns {string[]}
*/
export const getNoteNames = () => Object.keys(noteTypes);
/**
* Get the human-readable note name.
*
* @param {string} type
* @returns {string}
*/
export const getNoteName = (type) => noteTypes[type];
/**
* Whether the note name is valid.
*
* @param {string} type
* @returns {boolean}
*/
export const isValidNoteName = (type) => noteTypes[type] !== undefined;
-153
View File
@@ -1,153 +0,0 @@
// 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/>.
import inquirer from 'inquirer';
import SearchList from 'inquirer-search-list';
import { getNoteNames } from './noteTypes.mjs';
import { getAllComponents } from './components.mjs';
import {
formatComponent,
formatIssueNumber,
validateComponent,
validateIssueNumber,
} from './helpers.mjs';
/**
* A Search List which accepts an initial value.
*/
class SearchListWithInitialValue extends SearchList {
constructor(options, ...args) {
super(options, ...args);
if (options.default) {
const pointer = this.filterList.findIndex((item) => {
return item.value === options.default;
});
if (pointer > -1) {
this.pointer = pointer;
}
}
}
}
inquirer.registerPrompt('search-list', SearchListWithInitialValue);
/**
* Get the issue type prompt.
*
* @param {string} defaultData The initially selected value
* @returns {Object}
*/
export const getTypePrompt = (defaultData) => ({
default: defaultData,
type: 'search-list',
message: 'Type of change',
name: 'type',
choices: getNoteNames(),
validate: (selection) => {
if (selection.length < 1) {
return 'You must select at least one type of change';
}
return true;
},
});
/**
* Get the component prompt.
*
* @param {string} [defaultValue='core'] The initally selected value.
* @returns
*/
export const getComponentsPrompt = (defaultValue) => {
if (!defaultValue ) {
defaultValue = 'core';
}
return {
choices: getAllComponents(),
default: defaultValue,
type: 'search-list',
message: 'Component',
name: 'components',
validate: validateComponent,
filter: formatComponent,
};
};
/**
* Get the issue number prompt as an inline input.
*
* @param {string} defaultData
* @returns {object}
*/
export const getIssuePrompt = (defaultData) => ({
default: defaultData,
type: 'input',
message: 'Tracker issue number',
name: 'issueNumber',
validate: validateIssueNumber,
filter: formatIssueNumber,
});
/**
* Get a message prompt.
*
* @param {string} defaultData
* @returns
*/
export const getMessagePromptEditor = (defaultData) => ({
default: defaultData,
type: process.stdin.isTTY ? 'editor' : 'input',
postfix: '.md',
message: 'Message',
name: 'message',
waitUserInput: false,
validate: (input) => {
if (!input) {
return 'You must provide a message';
}
return true;
},
// Remove any trailing whitespace.
filter: (input) => input.split('\n').map((line) => line.trimEnd()).join('\n'),
});
/**
* Get a message prompt.
*
* @param {string} defaultData
* @returns
*/
export const getMessagePromptInput = (defaultData) => ({
default: defaultData,
type: 'input',
message: 'Message (leave empty to use editor)',
name: 'message',
filter: (input) => input.trim(),
});
/**
* Get a prompt to ask the user if they wish to add another entry.
*
* @returns {Object}
*/
export const getAddAnotherPrompt = () => ({
type: 'confirm',
message: 'Do you want to add another note?',
default: false,
name: 'addAnother',
});
-103
View File
@@ -1,103 +0,0 @@
#!/usr/bin/env node
// 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/>.
import { Argument, Option, program } from 'commander';
import chalk from 'chalk';
import { getNoteNames } from './notes/src/noteTypes.mjs';
import createAction from './notes/src/create.mjs';
import generateAction from './notes/src/generate.mjs';
import logger from './notes/src/logger.mjs';
console.log(`
${chalk.bold(chalk.underline(chalk.green('Moodle Upgrade Notes Generator')))}
This tool is used to generate the upgrade notes for changes you make in Moodle.
Please remember that the intended audience of these changes is
${chalk.italic('plugin developers')} who need to know how to update their plugins
for a new Moodle version.
Upgrade notes should not be used to document changes for site administrators, or
for internal API changes which are not expected to be used outside of the
relevant component.
`)
program.configureHelp({
helpWidth: 100,
});
program.on('option:verbose', () => {
logger.level = 'verbose';
});
program.addOption(
new Option(
'-v, --verbose',
'Output more information during the generation process',
)
.default(false)
);
// Define the command line options.
program
.command('create')
.summary('Generate a new upgrade note')
.addOption(
new Option('-t, --type <type>', `The type of change to document. Valid types are: ${getNoteNames().join(', ')}`)
)
.addOption(new Option('-i, --issue <issue>', 'The tracker issue number'))
.addOption(new Option('-c, --component <component>', 'The component to write a note for'))
.addOption(new Option(
'-m, --message <message>',
'The message to use for the upgrade note',
))
.action((options) => createAction(options));
program
.command('summary')
.summary('Generate a local copy of the upgrade notes summary')
.addArgument(
new Argument('[version]', 'The Moodle version to create the summary notes for')
)
.action((version) => generateAction(version));
program
.command('release')
.summary('Generate the markdown copies of the upgrade notes for a Moodle release')
.addArgument(
new Argument('[version]', 'The Moodle version to create the release notes for')
)
.addOption(
new Option(
'--generate-upgrade-notes',
'Generate the UPGRADING.md notes for the release. ' +
'Note: This option is intended for use by the release manager when generating the upgrade notes.',
)
.default(true)
)
.addOption(
new Option(
'-d, --delete-notes',
'Delete the notes after generating the UPGRADING.md notes for the release. ' +
'Note: This option is intended for use by the release manager when generating the upgrade notes.' +
'This option has no effect unless --generate-upgrade-notes is also set.'
)
.default(false)
)
.action((version, options) => generateAction(version, options));
program.parse();
+1 -1
View File
@@ -1 +1 @@
lts/jod
lts/iron
View File
-1560
View File
File diff suppressed because it is too large Load Diff
-9
View File
@@ -1,9 +0,0 @@
# core_admin (subsystem) Upgrade notes
## 4.5rc1
### Removed
- The HTTP Server setting "Use slash arguments" (slasharguments) configuration setting and related to it has been removed. Calling the option with $CFG->slasharguments or get_config('slasharguments') is no longer available.
For more information see [MDL-62640](https://tracker.moodle.org/browse/MDL-62640)
-65
View File
@@ -1,65 +0,0 @@
<?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/>.
/**
* Manage AI subsystem plugins settings.
*
* @package core_admin
* @copyright 2024 Matt Porritt <matt.porritt@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once('../config.php');
require_once($CFG->libdir . '/adminlib.php');
$action = required_param('action', PARAM_ALPHANUMEXT);
$type = required_param('type', PARAM_PLUGIN); // Must be provider or placement.
$name = required_param('name', PARAM_PLUGIN); // Plugin name, e.g openai.
$syscontext = context_system::instance();
$PAGE->set_url('/admin/ai.php');
$PAGE->set_context($syscontext);
require_admin();
require_sesskey();
$return = new moodle_url('/admin/settings.php', ['section' => "aisettings$type"]);
$plugins = core_plugin_manager::instance()->get_plugins_of_type("ai$type");
$sortorder = array_flip(array_keys($plugins));
if (!isset($plugins[$name])) {
throw new moodle_exception('aipluginnotfound', 'core_ai', $return, $name);
}
$plugintypename = $plugins[$name]->type . '_' . $plugins[$name]->name;
switch ($action) {
case 'disable':
if ($plugins[$name]->is_enabled()) {
$class = core_plugin_manager::resolve_plugininfo_class("aisettings$type");
$class::enable_plugin($name, false);
}
break;
case 'enable':
if (!$plugins[$name]->is_enabled()) {
$class = core_plugin_manager::resolve_plugininfo_class("aisettings$type");
$class::enable_plugin($name, true);
}
break;
}
redirect($return);
-12
View File
@@ -1,12 +0,0 @@
define("core_admin/expand_hash",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=_exports.expandSection=void 0;
/**
* Expand the collapse section element.
*
* @module core_admin/expand_hash
* @copyright Meirza <meirza.arson@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 4.5
*/
const SELECTORS_COLLAPSE_ELEMENTS='[data-toggle="collapse"]',SELECTORS_FOCUSTHENEXPAND_ELEMENTS=".focus-expand";_exports.init=()=>{const focusexpand=document.querySelector(SELECTORS_FOCUSTHENEXPAND_ELEMENTS);null==focusexpand||focusexpand.addEventListener("click",(()=>{expandSection("".concat(focusexpand.getAttribute("href")))}))};const expandSection=hash=>{const container=document.querySelector(hash),targetContainer=null==container?void 0:container.querySelector(SELECTORS_COLLAPSE_ELEMENTS);if("false"===(null==targetContainer?void 0:targetContainer.getAttribute("aria-expanded"))){const collapseId=targetContainer.getAttribute("aria-controls"),collapseContainer=document.getElementById(collapseId);collapseContainer.classList.remove("collapse"),collapseContainer.classList.add("show"),targetContainer.setAttribute("aria-expanded","true"),targetContainer.classList.remove("collapsed");const collapseElement=document.querySelector(".collapseexpand.collapsemenu");collapseElement.setAttribute("aria-expanded","true"),collapseElement.classList.remove("collapsed")}};_exports.expandSection=expandSection}));
//# sourceMappingURL=expand_hash.min.js.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"expand_hash.min.js","sources":["../src/expand_hash.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 * Expand the collapse section element.\n *\n * @module core_admin/expand_hash\n * @copyright Meirza <meirza.arson@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.5\n */\n\nconst SELECTORS = {\n COLLAPSE_ELEMENTS: '[data-toggle=\"collapse\"]',\n FOCUSTHENEXPAND_ELEMENTS: '.focus-expand',\n};\n\n/**\n * Initializes the focus and expand functionality.\n */\nexport const init = () => {\n // Select all collapsible elements only.\n const focusexpand = document.querySelector(SELECTORS.FOCUSTHENEXPAND_ELEMENTS);\n\n // Add click event listener to the anchor element\n focusexpand?.addEventListener('click', () => {\n expandSection(`${focusexpand.getAttribute('href')}`);\n });\n};\n\n/**\n * Expands a section based on the provided URL hash.\n *\n * This function takes a hash string, finds the corresponding element in the DOM,\n * and expands it if it is currently collapsed. It updates the necessary ARIA\n * attributes and classes to reflect the expanded state.\n *\n * @param {string} hash - The hash (e.g., '#elementId') of the element to expand.\n */\nexport const expandSection = (hash) => {\n const container = document.querySelector(hash);\n const targetContainer = container?.querySelector(SELECTORS.COLLAPSE_ELEMENTS);\n\n if (targetContainer?.getAttribute('aria-expanded') === 'false') {\n const collapseId = targetContainer.getAttribute('aria-controls');\n const collapseContainer = document.getElementById(collapseId);\n\n // Show the content.\n collapseContainer.classList.remove('collapse');\n collapseContainer.classList.add('show');\n\n // Update aria-expanded attribute to reflect the new state.\n targetContainer.setAttribute('aria-expanded', 'true');\n targetContainer.classList.remove('collapsed');\n\n // Get collapse expand menu element.\n const collapseElement = document.querySelector('.collapseexpand.collapsemenu');\n // Ensure it gets noticed to make it work.\n collapseElement.setAttribute('aria-expanded', 'true');\n collapseElement.classList.remove('collapsed');\n }\n};\n"],"names":["SELECTORS","focusexpand","document","querySelector","addEventListener","expandSection","getAttribute","hash","container","targetContainer","collapseId","collapseContainer","getElementById","classList","remove","add","setAttribute","collapseElement"],"mappings":";;;;;;;;;MAwBMA,4BACiB,2BADjBA,mCAEwB,8BAMV,WAEVC,YAAcC,SAASC,cAAcH,oCAG3CC,MAAAA,aAAAA,YAAaG,iBAAiB,SAAS,KACnCC,wBAAiBJ,YAAYK,aAAa,oBAarCD,cAAiBE,aACpBC,UAAYN,SAASC,cAAcI,MACnCE,gBAAkBD,MAAAA,iBAAAA,UAAWL,cAAcH,gCAEM,WAAnDS,MAAAA,uBAAAA,gBAAiBH,aAAa,kBAA8B,OACtDI,WAAaD,gBAAgBH,aAAa,iBAC1CK,kBAAoBT,SAASU,eAAeF,YAGlDC,kBAAkBE,UAAUC,OAAO,YACnCH,kBAAkBE,UAAUE,IAAI,QAGhCN,gBAAgBO,aAAa,gBAAiB,QAC9CP,gBAAgBI,UAAUC,OAAO,mBAG3BG,gBAAkBf,SAASC,cAAc,gCAE/Cc,gBAAgBD,aAAa,gBAAiB,QAC9CC,gBAAgBJ,UAAUC,OAAO"}
+1 -1
View File
@@ -1,3 +1,3 @@
define("core_admin/plugin_management_table",["exports","core_table/dynamic","core_table/local/dynamic/selectors","core/ajax","core/pending","core/notification"],(function(_exports,_dynamic,Selectors,_ajax,_pending,_notification){var obj;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)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Selectors=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}(Selectors),_pending=(obj=_pending)&&obj.__esModule?obj:{default:obj};let watching=!1;return _exports.default=class{constructor(){!function(obj,key,value){key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}(this,"clickHandlers",[]),this.addClickHandler(this.handleStateToggle),this.addClickHandler(this.handleMoveUpDown),this.registerEventListeners()}static init(){watching||(watching=!0,new this)}addClickHandler(handler){this.clickHandlers.push(handler.bind(this))}registerEventListeners(){document.addEventListener("click",function(e){const tableRoot=this.getTableRoot(e);tableRoot&&this.clickHandlers.forEach((handler=>handler(tableRoot,e)))}.bind(this))}getTableRoot(e){const tableRoot=e.target.closest(Selectors.main.region);return tableRoot||!1}setPluginState(methodname,plugin,state){return(0,_ajax.call)([{methodname:methodname,args:{plugin:plugin,state:state}}])[0]}setPluginOrder(methodname,plugin,direction){return(0,_ajax.call)([{methodname:methodname,args:{plugin:plugin,direction:direction}}])[0]}async handleStateToggle(tableRoot,e){const stateToggle=e.target.closest('[data-action="togglestate"][data-toggle-method]');if(stateToggle){e.preventDefault();const pendingPromise=new _pending.default("core_table/dynamic:togglestate");await this.setPluginState(stateToggle.dataset.toggleMethod,stateToggle.dataset.plugin,"1"===stateToggle.dataset.state?0:1);const[updatedRoot]=await Promise.all([(0,_dynamic.refreshTableContent)(tableRoot),(0,_notification.fetchNotifications)()]);updatedRoot.querySelector('[data-action="togglestate"][data-plugin="'.concat(stateToggle.dataset.plugin,'"]')).focus(),this.removeTooltips(),pendingPromise.resolve()}}async handleMoveUpDown(tableRoot,e){const actionLink=e.target.closest('[data-action="move"][data-method][data-direction]');if(!actionLink)return;e.preventDefault();const pendingPromise=new _pending.default("core_table/dynamic:processAction");await this.setPluginOrder(actionLink.dataset.method,actionLink.dataset.plugin,"up"===actionLink.dataset.direction?-1:1);const[updatedRoot]=await Promise.all([(0,_dynamic.refreshTableContent)(tableRoot),(0,_notification.fetchNotifications)()]),exactMatch=updatedRoot.querySelector('[data-action="move"][data-plugin="'.concat(actionLink.dataset.plugin,'"][data-direction="').concat(actionLink.dataset.direction,'"]'));var _updatedRoot$querySel;exactMatch?exactMatch.focus():null===(_updatedRoot$querySel=updatedRoot.querySelector('[data-action="move"][data-plugin="'.concat(actionLink.dataset.plugin,'"]')))||void 0===_updatedRoot$querySel||_updatedRoot$querySel.focus();pendingPromise.resolve()}removeTooltips(){const tooltips=document.querySelectorAll('[id*="tooltip"]');tooltips.length>0&&tooltips.forEach((tooltip=>{tooltip.remove()}))}},_exports.default}));
define("core_admin/plugin_management_table",["exports","core_table/dynamic","core_table/local/dynamic/selectors","core/ajax","core/pending","core/notification"],(function(_exports,_dynamic,Selectors,_ajax,_pending,_notification){var obj;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)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Selectors=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}(Selectors),_pending=(obj=_pending)&&obj.__esModule?obj:{default:obj};let watching=!1;return _exports.default=class{constructor(){!function(obj,key,value){key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}(this,"clickHandlers",[]),this.addClickHandler(this.handleStateToggle),this.addClickHandler(this.handleMoveUpDown),this.registerEventListeners()}static init(){watching||(watching=!0,new this)}addClickHandler(handler){this.clickHandlers.push(handler.bind(this))}registerEventListeners(){document.addEventListener("click",function(e){const tableRoot=this.getTableRoot(e);tableRoot&&this.clickHandlers.forEach((handler=>handler(tableRoot,e)))}.bind(this))}getTableRoot(e){const tableRoot=e.target.closest(Selectors.main.region);return tableRoot||!1}setPluginState(methodname,plugin,state){return(0,_ajax.call)([{methodname:methodname,args:{plugin:plugin,state:state}}])[0]}setPluginOrder(methodname,plugin,direction){return(0,_ajax.call)([{methodname:methodname,args:{plugin:plugin,direction:direction}}])[0]}async handleStateToggle(tableRoot,e){const stateToggle=e.target.closest('[data-action="togglestate"][data-toggle-method]');if(stateToggle){e.preventDefault();const pendingPromise=new _pending.default("core_table/dynamic:togglestate");await this.setPluginState(stateToggle.dataset.toggleMethod,stateToggle.dataset.plugin,"1"===stateToggle.dataset.state?0:1);const[updatedRoot]=await Promise.all([(0,_dynamic.refreshTableContent)(tableRoot),(0,_notification.fetchNotifications)()]);updatedRoot.querySelector('[data-action="togglestate"][data-plugin="'.concat(stateToggle.dataset.plugin,'"]')).focus(),pendingPromise.resolve()}}async handleMoveUpDown(tableRoot,e){const actionLink=e.target.closest('[data-action="move"][data-method][data-direction]');if(!actionLink)return;e.preventDefault();const pendingPromise=new _pending.default("core_table/dynamic:processAction");await this.setPluginOrder(actionLink.dataset.method,actionLink.dataset.plugin,"up"===actionLink.dataset.direction?-1:1);const[updatedRoot]=await Promise.all([(0,_dynamic.refreshTableContent)(tableRoot),(0,_notification.fetchNotifications)()]),exactMatch=updatedRoot.querySelector('[data-action="move"][data-plugin="'.concat(actionLink.dataset.plugin,'"][data-direction="').concat(actionLink.dataset.direction,'"]'));var _updatedRoot$querySel;exactMatch?exactMatch.focus():null===(_updatedRoot$querySel=updatedRoot.querySelector('[data-action="move"][data-plugin="'.concat(actionLink.dataset.plugin,'"]')))||void 0===_updatedRoot$querySel||_updatedRoot$querySel.focus();pendingPromise.resolve()}},_exports.default}));
//# sourceMappingURL=plugin_management_table.min.js.map
File diff suppressed because one or more lines are too long
-11
View File
@@ -1,11 +0,0 @@
define("core_admin/plugins_overview",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(){const filters=document.querySelectorAll(SELECTORS_PLUGIN_FILTERS),pluginRows=document.querySelectorAll(SELECTORS_PLUGIN_ROWS),pluginTypeRows=document.querySelectorAll(SELECTORS_PLUGIN_TYPE_ROWS),filterPlugins=target=>{const filterBy=target.getAttribute("data-filterby"),headerVisibility={};for(const row of pluginRows){const type=[...row.classList].find((s=>s.startsWith("type-"))),visible="all"===filterBy||row.classList.contains(filterBy);row.style.display=visible?null:"none",visible&&type&&(headerVisibility[type]=!0)}for(const row of pluginTypeRows){const type=[...row.classList].find((s=>s.startsWith("type-")));if(type){const visible="all"===filterBy||headerVisibility[type];row.style.display=visible?null:"none"}}filters.forEach((el=>el.classList.remove("active"))),target.classList.add("active")};if(filters.forEach((target=>target.addEventListener("click",(e=>{e.preventDefault(),window.history.replaceState({},null,e.target.href),filterPlugins(target)})))),window.location.hash.length>1){const anchor=window.location.hash.substring(1),target=[...filters].find((t=>t.getAttribute("data-filterby")===anchor));target&&filterPlugins(target)}};
/**
* Allows to filter the plugin list on plugins overview page
*
* @module core_admin/plugins_overview
* @copyright 2024 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
const SELECTORS_PLUGIN_FILTERS="#plugins-overview-panel [data-filterby]",SELECTORS_PLUGIN_ROWS="table#plugins-control-panel tbody tr:not(.plugintypeheader)",SELECTORS_PLUGIN_TYPE_ROWS="table#plugins-control-panel tbody tr.plugintypeheader"}));
//# sourceMappingURL=plugins_overview.min.js.map
@@ -1 +0,0 @@
{"version":3,"file":"plugins_overview.min.js","sources":["../src/plugins_overview.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 * Allows to filter the plugin list on plugins overview page\n *\n * @module core_admin/plugins_overview\n * @copyright 2024 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst SELECTORS = {\n PLUGIN_FILTERS: '#plugins-overview-panel [data-filterby]',\n PLUGIN_ROWS: 'table#plugins-control-panel tbody tr:not(.plugintypeheader)',\n PLUGIN_TYPE_ROWS: 'table#plugins-control-panel tbody tr.plugintypeheader',\n};\n\n/**\n * Initialise filters for the \"Plugins overview\" page\n */\nexport function init() {\n const filters = document.querySelectorAll(SELECTORS.PLUGIN_FILTERS);\n const pluginRows = document.querySelectorAll(SELECTORS.PLUGIN_ROWS);\n const pluginTypeRows = document.querySelectorAll(SELECTORS.PLUGIN_TYPE_ROWS);\n\n const filterPlugins = (target) => {\n const filterBy = target.getAttribute('data-filterby');\n const headerVisibility = {};\n\n // Hide all plugin rows in the plugin table that do not match the filter and show all others.\n for (const row of pluginRows) {\n const type = [...row.classList].find(s => s.startsWith('type-'));\n const visible = filterBy === 'all' ? true : row.classList.contains(filterBy);\n row.style.display = visible ? null : 'none';\n if (visible && type) {\n headerVisibility[type] = true;\n }\n }\n\n // Hide all the plugin type headers that do not have any visible plugins and show all others.\n for (const row of pluginTypeRows) {\n const type = [...row.classList].find(s => s.startsWith('type-'));\n if (type) {\n const visible = filterBy === 'all' || headerVisibility[type];\n row.style.display = visible ? null : 'none';\n }\n }\n\n // Toggle 'active' class for the selected filter.\n filters.forEach(el => el.classList.remove('active'));\n target.classList.add('active');\n };\n\n // Add event listeners for the links changing plugins filters.\n filters\n .forEach(target => target.addEventListener('click', (e) => {\n e.preventDefault();\n window.history.replaceState({}, null, e.target.href);\n filterPlugins(target);\n }));\n\n // Pre-filter plugins based on the current url anchor.\n if (window.location.hash.length > 1) {\n const anchor = window.location.hash.substring(1);\n const target = [...filters].find(t => t.getAttribute('data-filterby') === anchor);\n if (target) {\n filterPlugins(target);\n }\n }\n}\n"],"names":["filters","document","querySelectorAll","SELECTORS","pluginRows","pluginTypeRows","filterPlugins","target","filterBy","getAttribute","headerVisibility","row","type","classList","find","s","startsWith","visible","contains","style","display","forEach","el","remove","add","addEventListener","e","preventDefault","window","history","replaceState","href","location","hash","length","anchor","substring","t"],"mappings":"4JAiCUA,QAAUC,SAASC,iBAAiBC,0BACpCC,WAAaH,SAASC,iBAAiBC,uBACvCE,eAAiBJ,SAASC,iBAAiBC,4BAE3CG,cAAiBC,eACbC,SAAWD,OAAOE,aAAa,iBAC/BC,iBAAmB,OAGpB,MAAMC,OAAOP,WAAY,OACpBQ,KAAO,IAAID,IAAIE,WAAWC,MAAKC,GAAKA,EAAEC,WAAW,WACjDC,QAAuB,QAAbT,UAA4BG,IAAIE,UAAUK,SAASV,UACnEG,IAAIQ,MAAMC,QAAUH,QAAU,KAAO,OACjCA,SAAWL,OACXF,iBAAiBE,OAAQ,OAK5B,MAAMD,OAAON,eAAgB,OACxBO,KAAO,IAAID,IAAIE,WAAWC,MAAKC,GAAKA,EAAEC,WAAW,cACnDJ,KAAM,OACAK,QAAuB,QAAbT,UAAsBE,iBAAiBE,MACvDD,IAAIQ,MAAMC,QAAUH,QAAU,KAAO,QAK7CjB,QAAQqB,SAAQC,IAAMA,GAAGT,UAAUU,OAAO,YAC1ChB,OAAOM,UAAUW,IAAI,cAIzBxB,QACCqB,SAAQd,QAAUA,OAAOkB,iBAAiB,SAAUC,IACjDA,EAAEC,iBACFC,OAAOC,QAAQC,aAAa,GAAI,KAAMJ,EAAEnB,OAAOwB,MAC/CzB,cAAcC,aAIdqB,OAAOI,SAASC,KAAKC,OAAS,EAAG,OAC3BC,OAASP,OAAOI,SAASC,KAAKG,UAAU,GACxC7B,OAAS,IAAIP,SAASc,MAAKuB,GAAKA,EAAE5B,aAAa,mBAAqB0B,SACtE5B,QACAD,cAAcC;;;;;;;;MAvDpBJ,yBACc,0CADdA,sBAEW,8DAFXA,2BAGgB"}
-74
View File
@@ -1,74 +0,0 @@
// 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/>.
/**
* Expand the collapse section element.
*
* @module core_admin/expand_hash
* @copyright Meirza <meirza.arson@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 4.5
*/
const SELECTORS = {
COLLAPSE_ELEMENTS: '[data-toggle="collapse"]',
FOCUSTHENEXPAND_ELEMENTS: '.focus-expand',
};
/**
* Initializes the focus and expand functionality.
*/
export const init = () => {
// Select all collapsible elements only.
const focusexpand = document.querySelector(SELECTORS.FOCUSTHENEXPAND_ELEMENTS);
// Add click event listener to the anchor element
focusexpand?.addEventListener('click', () => {
expandSection(`${focusexpand.getAttribute('href')}`);
});
};
/**
* Expands a section based on the provided URL hash.
*
* This function takes a hash string, finds the corresponding element in the DOM,
* and expands it if it is currently collapsed. It updates the necessary ARIA
* attributes and classes to reflect the expanded state.
*
* @param {string} hash - The hash (e.g., '#elementId') of the element to expand.
*/
export const expandSection = (hash) => {
const container = document.querySelector(hash);
const targetContainer = container?.querySelector(SELECTORS.COLLAPSE_ELEMENTS);
if (targetContainer?.getAttribute('aria-expanded') === 'false') {
const collapseId = targetContainer.getAttribute('aria-controls');
const collapseContainer = document.getElementById(collapseId);
// Show the content.
collapseContainer.classList.remove('collapse');
collapseContainer.classList.add('show');
// Update aria-expanded attribute to reflect the new state.
targetContainer.setAttribute('aria-expanded', 'true');
targetContainer.classList.remove('collapsed');
// Get collapse expand menu element.
const collapseElement = document.querySelector('.collapseexpand.collapsemenu');
// Ensure it gets noticed to make it work.
collapseElement.setAttribute('aria-expanded', 'true');
collapseElement.classList.remove('collapsed');
}
};
-16
View File
@@ -138,10 +138,6 @@ export default class {
// Refocus on the link that as pressed in the first place.
updatedRoot.querySelector(`[data-action="togglestate"][data-plugin="${stateToggle.dataset.plugin}"]`).focus();
// When clicking the toggle and it remains focused, a new tooltip will be generated.
// Therefore, the old tooltip should be removed.
this.removeTooltips();
pendingPromise.resolve();
}
}
@@ -180,16 +176,4 @@ export default class {
pendingPromise.resolve();
}
/**
* Remove tooltips.
*/
removeTooltips() {
const tooltips = document.querySelectorAll('[id*="tooltip"]');
if (tooltips.length > 0) {
tooltips.forEach(tooltip => {
tooltip.remove();
});
}
}
}
-82
View File
@@ -1,82 +0,0 @@
// 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/>.
/**
* Allows to filter the plugin list on plugins overview page
*
* @module core_admin/plugins_overview
* @copyright 2024 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
const SELECTORS = {
PLUGIN_FILTERS: '#plugins-overview-panel [data-filterby]',
PLUGIN_ROWS: 'table#plugins-control-panel tbody tr:not(.plugintypeheader)',
PLUGIN_TYPE_ROWS: 'table#plugins-control-panel tbody tr.plugintypeheader',
};
/**
* Initialise filters for the "Plugins overview" page
*/
export function init() {
const filters = document.querySelectorAll(SELECTORS.PLUGIN_FILTERS);
const pluginRows = document.querySelectorAll(SELECTORS.PLUGIN_ROWS);
const pluginTypeRows = document.querySelectorAll(SELECTORS.PLUGIN_TYPE_ROWS);
const filterPlugins = (target) => {
const filterBy = target.getAttribute('data-filterby');
const headerVisibility = {};
// Hide all plugin rows in the plugin table that do not match the filter and show all others.
for (const row of pluginRows) {
const type = [...row.classList].find(s => s.startsWith('type-'));
const visible = filterBy === 'all' ? true : row.classList.contains(filterBy);
row.style.display = visible ? null : 'none';
if (visible && type) {
headerVisibility[type] = true;
}
}
// Hide all the plugin type headers that do not have any visible plugins and show all others.
for (const row of pluginTypeRows) {
const type = [...row.classList].find(s => s.startsWith('type-'));
if (type) {
const visible = filterBy === 'all' || headerVisibility[type];
row.style.display = visible ? null : 'none';
}
}
// Toggle 'active' class for the selected filter.
filters.forEach(el => el.classList.remove('active'));
target.classList.add('active');
};
// Add event listeners for the links changing plugins filters.
filters
.forEach(target => target.addEventListener('click', (e) => {
e.preventDefault();
window.history.replaceState({}, null, e.target.href);
filterPlugins(target);
}));
// Pre-filter plugins based on the current url anchor.
if (window.location.hash.length > 1) {
const anchor = window.location.hash.substring(1);
const target = [...filters].find(t => t.getAttribute('data-filterby') === anchor);
if (target) {
filterPlugins(target);
}
}
}
-4
View File
@@ -118,10 +118,6 @@ foreach ($settingspage->children as $childpage) {
}
$outputhtml .= html_writer::tag('div', '<!-- -->', array('class' => 'clearer'));
$outputhtml .= $setting->output_html($data);
if ($childpage->has_dependencies()) {
$opts = ['dependencies' => $childpage->get_dependencies_for_javascript()];
$PAGE->requires->js_call_amd('core/showhidesettings', 'init', [$opts]);
}
}
$outputhtml .= html_writer::end_tag('fieldset');
}
+1 -1
View File
@@ -62,7 +62,7 @@ class filesize extends \admin_setting {
* @param int|null $defaultunit GB, MB, etc. (in bytes)
*/
public function __construct(string $name, string $visiblename, string $description,
?int $defaultvalue = null, ?int $defaultunit = null) {
int $defaultvalue = null, int $defaultunit = null) {
$defaultsetting = self::parse_bytes($defaultvalue);
@@ -105,7 +105,7 @@ class task_log extends base {
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$tablealias}.classname")
->add_field("$tablealias.classname")
->set_is_sortable(true)
->add_callback(static function(string $classname): string {
$output = '';
@@ -183,7 +183,15 @@ class task_log extends base {
->set_type(column::TYPE_FLOAT)
->add_field("{$tablealias}.timeend - {$tablealias}.timestart", 'duration')
->set_is_sortable(true)
->add_callback([format::class, 'format_time'], 2);
->add_callback(static function(float $value): string {
$duration = round($value, 2);
if (empty($duration)) {
// The format_time function returns 'now' when the difference is exactly 0.
// Note: format_time performs concatenation in exactly this fashion so we should do this for consistency.
return '0 ' . get_string('secs', 'moodle');
}
return format_time($duration);
});
// Hostname column.
$columns[] = (new column(
@@ -203,8 +211,11 @@ class task_log extends base {
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_INTEGER)
->add_field("{$tablealias}.pid")
->set_is_sortable(true);
->set_is_sortable(true)
// Although this is an integer column, it doesn't make sense to perform numeric aggregation on it.
->set_disabled_aggregation(['avg', 'count', 'countdistinct', 'max', 'min', 'sum']);
// Database column.
$columns[] = (new column(
@@ -213,14 +224,17 @@ class task_log extends base {
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_INTEGER)
->add_fields("{$tablealias}.dbreads, {$tablealias}.dbwrites")
->set_is_sortable(true, ["{$tablealias}.dbreads", "{$tablealias}.dbwrites"])
->add_callback(static function($value, stdClass $row): string {
->add_callback(static function(int $value, stdClass $row): string {
$output = '';
$output .= \html_writer::div(get_string('task_stats:dbreads', 'admin', $row->dbreads));
$output .= \html_writer::div(get_string('task_stats:dbwrites', 'admin', $row->dbwrites));
return $output;
});
})
// Although this is an integer column, it doesn't make sense to perform numeric aggregation on it.
->set_disabled_aggregation(['avg', 'count', 'countdistinct', 'max', 'min', 'sum']);
// Database reads column.
$columns[] = (new column(
@@ -20,12 +20,10 @@ use context_system;
use core_admin\reportbuilder\local\entities\task_log;
use core_reportbuilder\local\entities\user;
use core_reportbuilder\local\report\action;
use core_reportbuilder\system_report;
use html_writer;
use lang_string;
use moodle_url;
use pix_icon;
use stdClass;
use core_reportbuilder\system_report;
/**
* Task logs system report class implementation
@@ -91,9 +89,7 @@ class task_logs extends system_report {
* unique identifier
*/
public function add_columns(): void {
$entitymainalias = $this->get_entity('task_log')->get_table_alias('task_log');
$this->add_columns_from_entities([
$columns = [
'task_log:name',
'task_log:type',
'user:fullname',
@@ -103,18 +99,14 @@ class task_logs extends system_report {
'task_log:pid',
'task_log:database',
'task_log:result',
]);
];
// Wrap the task name in a link.
$this->get_column('task_log:name')
->add_field("{$entitymainalias}.id")
->add_callback(static function(string $output, stdClass $row): string {
return html_writer::link(new moodle_url('/admin/tasklogs.php', ['logid' => $row->id]), $output);
});
$this->add_columns_from_entities($columns);
// Rename the user fullname column.
$this->get_column('user:fullname')
->set_title(new lang_string('user', 'admin'));
// It's possible to override the display name of a column, if you don't want to use the value provided by the entity.
if ($column = $this->get_column('user:fullname')) {
$column->set_title(new lang_string('user', 'admin'));
}
// It's possible to set a default initial sort direction for one column.
$this->set_initial_sort_column('task_log:starttime', SORT_DESC);
@@ -127,14 +119,16 @@ class task_logs extends system_report {
* unique identifier
*/
protected function add_filters(): void {
$this->add_filters_from_entities([
$filters = [
'task_log:name',
'task_log:type',
'task_log:output',
'task_log:result',
'task_log:timestart',
'task_log:duration',
]);
];
$this->add_filters_from_entities($filters);
}
/**
@@ -149,7 +143,7 @@ class task_logs extends system_report {
new moodle_url('/admin/tasklogs.php', ['logid' => ':id']),
new pix_icon('e/search', ''),
[],
false,
true,
new lang_string('view'),
)));
@@ -159,11 +159,11 @@ class users extends system_report {
->add_callback(static function(string $fullname, \stdClass $row): string {
if ($row->suspended) {
$fullname .= ' ' . \html_writer::tag('span', get_string('suspended', 'moodle'),
['class' => 'badge badge-secondary ms-1']);
['class' => 'badge badge-secondary ml-1']);
}
if (!$row->confirmed) {
$fullname .= ' ' . \html_writer::tag('span', get_string('confirmationpending', 'admin'),
['class' => 'badge badge-danger ms-1']);
['class' => 'badge badge-danger ml-1']);
}
return $fullname;
});
@@ -1,85 +0,0 @@
<?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 core_admin\table;
use moodle_url;
use stdClass;
use html_writer;
use get_string_manager;
/**
* Availability admin settings.
*
* @package core_admin
* @copyright 2024 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class availability_management_table extends plugin_management_table {
protected function get_table_id(): string {
return 'availabilityconditions_administration_table';
}
protected function get_plugintype(): string {
return 'availability';
}
protected function get_action_url(array $params = []): moodle_url {
return new moodle_url('/admin/tool/availabilityconditions/', $params);
}
public function guess_base_url(): void {
$this->define_baseurl(
new moodle_url('/admin/tool/availabilityconditions/')
);
}
protected function get_column_list(): array {
return [
'name' => get_string('plugin'),
'version' => get_string('version'),
'enabled' => get_string('enabled', 'admin'),
'defaultdisplaymode' => get_string('defaultdisplaymode', 'tool_availabilityconditions'),
];
}
public function setup(): void {
$this->set_attribute('id', 'availabilityconditions_administration_table');
$this->set_attribute('class', 'admintable generaltable');
parent::setup();
}
protected function col_name(stdClass $row): string {
return html_writer::span(
get_string('pluginname', 'availability_' . $row->plugininfo->name)
);
}
protected function col_defaultdisplaymode(stdClass $row): string {
global $OUTPUT, $CFG;
$displaymode = get_config('availability_' . $row->plugininfo->name, 'defaultdisplaymode') ? 'show' : 'hide';
$paramsdisplaymode = [
'sesskey' => sesskey(),
'plugin' => $row->plugininfo->name,
'displaymode' => $displaymode,
];
$urldisplaymode = new moodle_url('/' . $CFG->admin . '/tool/availabilityconditions/', $paramsdisplaymode);
return html_writer::link($urldisplaymode, $OUTPUT->pix_icon('t/' . $displaymode,
get_string($displaymode)), ['class' => 'display-mode-' . $row->plugininfo->name]);
}
}
+19 -31
View File
@@ -303,29 +303,28 @@ abstract class plugin_management_table extends flexible_table implements dynamic
global $OUTPUT;
$enabled = $row->plugininfo->is_enabled();
if ($enabled) {
$labelstr = get_string('disableplugin', 'core_admin', $row->plugininfo->displayname);
} else {
$labelstr = get_string('enableplugin', 'core_admin', $row->plugininfo->displayname);
}
$params = [
'id' => 'admin-toggle-' . $row->plugininfo->name,
'checked' => $enabled,
'dataattributes' => [
'name' => 'id',
'value' => $row->plugininfo->name,
'toggle-method' => $this->get_toggle_service(),
'action' => 'togglestate',
'plugin' => $row->plugin,
'state' => $enabled ? 1 : 0,
],
'title' => $labelstr,
'label' => $labelstr,
'labelclasses' => 'sr-only',
'sesskey' => sesskey(),
'plugin' => $row->plugininfo->name,
'action' => $enabled ? 'disable' : 'enable',
];
return $OUTPUT->render_from_template('core_admin/setting_configtoggle', $params);
if ($enabled) {
$icon = $OUTPUT->pix_icon('t/hide', get_string('disableplugin', 'core_admin', $row->plugininfo->displayname));
} else {
$icon = $OUTPUT->pix_icon('t/show', get_string('enableplugin', 'core_admin', $row->plugininfo->displayname));
}
return html_writer::link(
$this->get_action_url($params),
$icon,
[
'data-toggle-method' => $this->get_toggle_service(),
'data-action' => 'togglestate',
'data-plugin' => $row->plugin,
'data-state' => $enabled ? 1 : 0,
],
);
}
protected function col_order(stdClass $row): string {
@@ -501,15 +500,4 @@ abstract class plugin_management_table extends flexible_table implements dynamic
protected function supports_ordering(): bool {
return $this->plugininfoclass::plugintype_supports_ordering();
}
/**
* Check if the user has the capability to access this table.
*
* Default implementation for plugin management tables is to require 'moodle/site:config' capability
*
* @return bool Return true if capability check passed.
*/
public function has_capability(): bool {
return has_capability('moodle/site:config', $this->get_context());
}
}
+11 -7
View File
@@ -172,16 +172,11 @@ $CFG->debugdeveloper = true;
$parts = explode('/', str_replace('\\', '/', dirname(__DIR__)));
$CFG->admin = array_pop($parts);
// Point pear include path to moodles lib/pear so that includes and requires will search there for files before anywhere else
// the problem is that we need specific version of quickforms and hacked excel files :-(.
//point pear include path to moodles lib/pear so that includes and requires will search there for files before anywhere else
//the problem is that we need specific version of quickforms and hacked excel files :-(
ini_set('include_path', $CFG->libdir.'/pear' . PATH_SEPARATOR . ini_get('include_path'));
// The core_component class can be used in any scripts, it does not need anything else.
require_once($CFG->libdir.'/classes/component.php');
// Register our classloader.
\core\component::register_autoloader();
require_once($CFG->libdir.'/classes/text.php');
require_once($CFG->libdir.'/classes/string_manager.php');
require_once($CFG->libdir.'/classes/string_manager_install.php');
@@ -195,6 +190,15 @@ require_once($CFG->libdir.'/moodlelib.php');
require_once($CFG->libdir.'/deprecatedlib.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/componentlib.class.php');
require_once($CFG->dirroot.'/cache/lib.php');
// Register our classloader, in theory somebody might want to replace it to load other hacked core classes.
// Required because the database checks below lead to session interaction which is going to lead us to requiring autoloaded classes.
if (defined('COMPONENT_CLASSLOADER')) {
spl_autoload_register(COMPONENT_CLASSLOADER);
} else {
spl_autoload_register('core_component::classloader');
}
require($CFG->dirroot.'/version.php');
$CFG->target_release = $release;
+1 -1
View File
@@ -50,6 +50,6 @@ Example:
exit(0);
}
\core\session\manager::destroy_all();
\core\session\manager::kill_all_sessions();
exit(0);
+9 -18
View File
@@ -69,7 +69,7 @@ if ($options['enablelater']) {
}
$time = time() + ($options['enablelater']*60);
set_config('maintenance_later', $time, null, true);
set_config('maintenance_later', $time);
echo get_string('clistatusenabledlater', 'admin', userdate($time))."\n";
return 0;
@@ -77,32 +77,23 @@ if ($options['enablelater']) {
} else if ($options['enable']) {
if (file_exists("$CFG->dataroot/climaintenance.html")) {
// The maintenance is already enabled, nothing to do.
exit(0);
}
enable_cli_maintenance_mode();
set_config('maintenance_enabled', 'cli mode', null, true);
if (isset($CFG->maintenance_later)) {
unset_config('maintenance_later', null, true);
} else {
enable_cli_maintenance_mode();
}
set_config('maintenance_enabled', 0);
unset_config('maintenance_later');
echo get_string('sitemaintenanceoncli', 'admin')."\n";
exit(0);
} else if ($options['enableold']) {
set_config('maintenance_enabled', 1, null, true);
if (isset($CFG->maintenance_later)) {
unset_config('maintenance_later', null, true);
}
set_config('maintenance_enabled', 1);
unset_config('maintenance_later');
echo get_string('sitemaintenanceon', 'admin')."\n";
exit(0);
} else if ($options['disable']) {
if ($CFG->maintenance_enabled !== '0') {
set_config('maintenance_enabled', 0, null, true);
}
if (isset($CFG->maintenance_later)) {
unset_config('maintenance_later', null, true);
}
set_config('maintenance_enabled', 0);
unset_config('maintenance_later');
if (file_exists("$CFG->dataroot/climaintenance.html")) {
unlink("$CFG->dataroot/climaintenance.html");
}
-1
View File
@@ -24,7 +24,6 @@
*/
define('CLI_SCRIPT', true);
define('IGNORE_COMPONENT_CACHE', true);
require(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/clilib.php');
+16 -39
View File
@@ -55,14 +55,13 @@ If courseid is set, course module/s will be added into the course.
Options:
-f, --file=STRING Path to the backup file.
-c, --categoryid=INT ID of the course category to restore to. This option is ignored when restoring an activity and courseid is set.
-C, --courseid=INT ID of the course to restore to. This option is ignored when restoring a course and the categoryid is set.
-c, --categoryid=INT ID of the course category to restore to.
-C, --courseid=INT ID of the course to restore to. This option is ignored if categoryid is set.
-s, --showdebugging Show developer level debugging information
-h, --help Print out this help.
Example:
\$sudo -u www-data /usr/bin/php admin/cli/restore_backup.php --file=/path/to/backup/coursebackup.mbz --categoryid=1\n
\$sudo -u www-data /usr/bin/php admin/cli/restore_backup.php --file=/path/to/backup/activitybackup.mbz --courseid=1\n
\$sudo -u www-data /usr/bin/php admin/cli/restore_backup.php --file=/path/to/backup/file.mbz --categoryid=1\n
EOL;
echo $help;
@@ -85,15 +84,11 @@ if ($options['categoryid']) {
if (!$category = $DB->get_record('course_categories', ['id' => $options['categoryid']], 'id')) {
throw new \moodle_exception('invalidcategoryid');
}
}
if ($options['courseid']) {
} else if ($options['courseid']) {
if (!$course = $DB->get_record('course', ['id' => $options['courseid']], 'id')) {
throw new \moodle_exception('invalidcourseid');
}
}
if (empty($category) && empty($course)) {
} else {
throw new \moodle_exception('invalidoption');
}
@@ -105,46 +100,28 @@ $fp = get_file_packer('application/vnd.moodle.backup');
$fp->extract_to_pathname($options['file'], $path);
cli_heading(get_string('preprocessingbackupfile'));
try {
// Create a temporary restore controller to determine the restore type.
$tmprc = new restore_controller($backupdir, SITEID, backup::INTERACTIVE_NO,
backup::MODE_GENERAL, $admin->id, backup::TARGET_EXISTING_ADDING);
// Restore the backup into a new course if:
// - It is a course backup and the category is set.
// - It is an activity backup and the course is not set.
$restoreasnewcourse = ($tmprc->get_type() === backup::TYPE_1COURSE && !empty($category)) ||
($tmprc->get_type() !== backup::TYPE_1COURSE && empty($course));
// Make sure to clean up the temporary restore controller.
$tmprc->destroy();
list($fullname, $shortname) = restore_dbops::calculate_course_names(0, get_string('restoringcourse', 'backup'),
get_string('restoringcourseshortname', 'backup'));
if ($restoreasnewcourse) {
list($fullname, $shortname) = restore_dbops::calculate_course_names(0, get_string('restoringcourse', 'backup'),
get_string('restoringcourseshortname', 'backup'));
$courseid = restore_dbops::create_new_course($fullname, $shortname, $category->id);
$rc = new restore_controller($backupdir, $courseid, backup::INTERACTIVE_NO,
backup::MODE_GENERAL, $admin->id, backup::TARGET_NEW_COURSE);
} else {
if (!empty($course)) {
$courseid = $course->id;
$rc = new restore_controller($backupdir, $courseid, backup::INTERACTIVE_NO,
backup::MODE_GENERAL, $admin->id, backup::TARGET_EXISTING_ADDING);
} else {
$courseid = restore_dbops::create_new_course($fullname, $shortname, $category->id);
$rc = new restore_controller($backupdir, $courseid, backup::INTERACTIVE_NO,
backup::MODE_GENERAL, $admin->id, backup::TARGET_NEW_COURSE);
}
$rc->execute_precheck();
$rc->execute_plan();
$rc->destroy();
// Rename the course's full and short names with the backup file's original names if the backup file is an activity backup
// that is restored to a new course.
if ($restoreasnewcourse && $rc->get_type() !== backup::TYPE_1COURSE) {
// Rename course name if the backup is from course module and restore to category.
if (empty($course)) {
$course = get_course($courseid);
$backupinfo = $rc->get_info();
$tmpfullname = $backupinfo->original_course_fullname ?? get_string('restoretonewcourse', 'backup');
$tmpshortname = $backupinfo->original_course_shortname ?? get_string('newcourse');
list($fullname, $shortname) = restore_dbops::calculate_course_names(
courseid: 0,
fullname: $tmpfullname,
shortname: $tmpshortname,
);
list($fullname, $shortname) = restore_dbops::calculate_course_names(0, get_string('restoretonewcourse', 'backup'),
get_string('newcourse'));
$course->fullname = $fullname;
$course->shortname = $shortname;
$course->visible = 1;
+3 -3
View File
@@ -158,13 +158,13 @@ if ($options['plugins']) {
if (is_null($plugin)) {
cli_writeln('Unknown plugin: ' . $component);
} else {
$pluginstring = $component . "\t" . $plugin->displayname;
$pluginstring = $plugin->component . "\t" . $plugin->displayname;
if ($pluginman->can_uninstall_plugin($component)) {
if ($pluginman->can_uninstall_plugin($plugin->component)) {
if ($options['run']) {
cli_writeln('Uninstalling: ' . $pluginstring);
$progress = new progress_trace_buffer(new text_progress_trace(), true);
$pluginman->uninstall_plugin($component, $progress);
$pluginman->uninstall_plugin($plugin->component, $progress);
$progress->finished();
cli_write($progress->get_buffer());
} else {
-592
View File
@@ -4327,7 +4327,6 @@
<VENDOR name="oracle" version="19" />
</DATABASE>
<PHP version="8.1.0" level="required">
<RESTRICT function="restrict_php_version_84" message="unsupportedphpversion84" />
</PHP>
<PCREUNICODE level="optional">
<FEEDBACK>
@@ -4509,595 +4508,4 @@
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
<MOODLE version="4.5" requires="4.1.2">
<UNICODE level="required">
<FEEDBACK>
<ON_ERROR message="unicoderequired" />
</FEEDBACK>
</UNICODE>
<DATABASE level="required">
<VENDOR name="mariadb" version="10.6.7" />
<VENDOR name="mysql" version="8.0" />
<VENDOR name="postgres" version="13" />
<VENDOR name="mssql" version="14.0" />
<VENDOR name="oracle" version="19" />
</DATABASE>
<PHP version="8.1.0" level="required">
<RESTRICT function="restrict_php_version_84" message="unsupportedphpversion84" />
</PHP>
<PCREUNICODE level="optional">
<FEEDBACK>
<ON_CHECK message="pcreunicodewarning" />
</FEEDBACK>
</PCREUNICODE>
<PHP_EXTENSIONS>
<PHP_EXTENSION name="iconv" level="required">
<FEEDBACK>
<ON_ERROR message="iconvrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="mbstring" level="required">
<FEEDBACK>
<ON_ERROR message="mbstringrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="curl" level="required">
<FEEDBACK>
<ON_ERROR message="curlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="openssl" level="required">
<FEEDBACK>
<ON_ERROR message="opensslrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="tokenizer" level="optional">
<FEEDBACK>
<ON_CHECK message="tokenizerrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="soap" level="optional">
<FEEDBACK>
<ON_CHECK message="soaprecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="ctype" level="required">
<FEEDBACK>
<ON_ERROR message="ctyperequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="zip" level="required">
<FEEDBACK>
<ON_ERROR message="ziprequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="zlib" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="gd" level="required">
<FEEDBACK>
<ON_ERROR message="gdrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="simplexml" level="required">
<FEEDBACK>
<ON_ERROR message="simplexmlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="spl" level="required">
<FEEDBACK>
<ON_ERROR message="splrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="pcre" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="dom" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="xml" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="xmlreader" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="intl" level="required">
<FEEDBACK>
<ON_ERROR message="intlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="json" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="hash" level="required"/>
<PHP_EXTENSION name="fileinfo" level="required"/>
<PHP_EXTENSION name="sodium" level="required"/>
<PHP_EXTENSION name="exif" level="optional"/>
</PHP_EXTENSIONS>
<PHP_SETTINGS>
<PHP_SETTING name="memory_limit" value="96M" level="required">
<FEEDBACK>
<ON_ERROR message="settingmemorylimit" />
</FEEDBACK>
</PHP_SETTING>
<PHP_SETTING name="file_uploads" value="1" level="optional">
<FEEDBACK>
<ON_CHECK message="settingfileuploads" />
</FEEDBACK>
</PHP_SETTING>
<PHP_SETTING name="opcache.enable" value="1" level="optional">
<FEEDBACK>
<ON_CHECK message="opcacherecommended" />
</FEEDBACK>
</PHP_SETTING>
</PHP_SETTINGS>
<CUSTOM_CHECKS>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddbstorageengine" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
<FEEDBACK>
<ON_ERROR message="quizattemptsupgradedmessage" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
<FEEDBACK>
<ON_CHECK message="slashargumentswarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
<FEEDBACK>
<ON_CHECK message="unsupporteddbtablerowformat" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
<FEEDBACK>
<ON_CHECK message="unoconvwarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
<FEEDBACK>
<ON_CHECK message="libcurlwarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddbfileformat" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddbfilepertable" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddblargeprefix" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
<FEEDBACK>
<ON_CHECK message="ishttpswarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
<FEEDBACK>
<ON_CHECK message="incompleteunicodesupport" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="required">
<FEEDBACK>
<ON_ERROR message="sixtyfourbitsrequired" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_max_input_vars" level="optional">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_admin_dir_usage" level="optional">
<FEEDBACK>
<ON_CHECK message="iscustomadminwarnings" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_xmlrpc_usage" level="optional">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mod_assignment" level="required">
</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_CHECK file="lib/upgradelib.php" function="check_async_backup" level="recommended">
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
<MOODLE version="5.0" requires="4.2.3">
<UNICODE level="required">
<FEEDBACK>
<ON_ERROR message="unicoderequired" />
</FEEDBACK>
</UNICODE>
<DATABASE level="required">
<VENDOR name="mariadb" version="10.11.0" />
<VENDOR name="mysql" version="8.4" />
<VENDOR name="postgres" version="14" />
<VENDOR name="mssql" version="14.0" />
<VENDOR name="auroramysql" version="8.0" />
</DATABASE>
<PHP version="8.2.0" level="required">
</PHP>
<PCREUNICODE level="optional">
<FEEDBACK>
<ON_CHECK message="pcreunicodewarning" />
</FEEDBACK>
</PCREUNICODE>
<PHP_EXTENSIONS>
<PHP_EXTENSION name="iconv" level="required">
<FEEDBACK>
<ON_ERROR message="iconvrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="mbstring" level="required">
<FEEDBACK>
<ON_ERROR message="mbstringrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="curl" level="required">
<FEEDBACK>
<ON_ERROR message="curlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="openssl" level="required">
<FEEDBACK>
<ON_ERROR message="opensslrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="tokenizer" level="optional">
<FEEDBACK>
<ON_CHECK message="tokenizerrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="soap" level="optional">
<FEEDBACK>
<ON_CHECK message="soaprecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="ctype" level="required">
<FEEDBACK>
<ON_ERROR message="ctyperequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="zip" level="required">
<FEEDBACK>
<ON_ERROR message="ziprequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="zlib" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="gd" level="required">
<FEEDBACK>
<ON_ERROR message="gdrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="simplexml" level="required">
<FEEDBACK>
<ON_ERROR message="simplexmlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="spl" level="required">
<FEEDBACK>
<ON_ERROR message="splrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="pcre" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="dom" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="xml" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="xmlreader" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="intl" level="required">
<FEEDBACK>
<ON_ERROR message="intlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="json" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="hash" level="required"/>
<PHP_EXTENSION name="fileinfo" level="required"/>
<PHP_EXTENSION name="sodium" level="required"/>
<PHP_EXTENSION name="exif" level="optional"/>
</PHP_EXTENSIONS>
<PHP_SETTINGS>
<PHP_SETTING name="memory_limit" value="96M" level="required">
<FEEDBACK>
<ON_ERROR message="settingmemorylimit" />
</FEEDBACK>
</PHP_SETTING>
<PHP_SETTING name="file_uploads" value="1" level="optional">
<FEEDBACK>
<ON_CHECK message="settingfileuploads" />
</FEEDBACK>
</PHP_SETTING>
<PHP_SETTING name="opcache.enable" value="1" level="optional">
<FEEDBACK>
<ON_CHECK message="opcacherecommended" />
</FEEDBACK>
</PHP_SETTING>
</PHP_SETTINGS>
<CUSTOM_CHECKS>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddbstorageengine" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
<FEEDBACK>
<ON_ERROR message="quizattemptsupgradedmessage" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
<FEEDBACK>
<ON_CHECK message="slashargumentswarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
<FEEDBACK>
<ON_CHECK message="unsupporteddbtablerowformat" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
<FEEDBACK>
<ON_CHECK message="unoconvwarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
<FEEDBACK>
<ON_CHECK message="libcurlwarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddbfileformat" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddbfilepertable" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddblargeprefix" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
<FEEDBACK>
<ON_CHECK message="ishttpswarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
<FEEDBACK>
<ON_CHECK message="incompleteunicodesupport" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="required">
<FEEDBACK>
<ON_ERROR message="sixtyfourbitsrequired" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_max_input_vars" level="optional">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_admin_dir_usage" level="optional">
<FEEDBACK>
<ON_CHECK message="iscustomadminwarnings" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_xmlrpc_usage" level="optional">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mod_assignment" level="required">
</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_async_backup" level="recommended">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_aurora_version" level="optional">
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
<MOODLE version="5.1" requires="4.2.3">
<UNICODE level="required">
<FEEDBACK>
<ON_ERROR message="unicoderequired" />
</FEEDBACK>
</UNICODE>
<DATABASE level="required">
<VENDOR name="mariadb" version="10.11.0" />
<VENDOR name="mysql" version="8.4" />
<VENDOR name="postgres" version="15" />
<VENDOR name="mssql" version="14.0" />
<VENDOR name="auroramysql" version="8.0" />
</DATABASE>
<PHP version="8.2.0" level="required">
</PHP>
<PCREUNICODE level="optional">
<FEEDBACK>
<ON_CHECK message="pcreunicodewarning" />
</FEEDBACK>
</PCREUNICODE>
<PHP_EXTENSIONS>
<PHP_EXTENSION name="iconv" level="required">
<FEEDBACK>
<ON_ERROR message="iconvrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="mbstring" level="required">
<FEEDBACK>
<ON_ERROR message="mbstringrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="curl" level="required">
<FEEDBACK>
<ON_ERROR message="curlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="openssl" level="required">
<FEEDBACK>
<ON_ERROR message="opensslrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="tokenizer" level="optional">
<FEEDBACK>
<ON_CHECK message="tokenizerrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="soap" level="optional">
<FEEDBACK>
<ON_CHECK message="soaprecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="ctype" level="required">
<FEEDBACK>
<ON_ERROR message="ctyperequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="zip" level="required">
<FEEDBACK>
<ON_ERROR message="ziprequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="zlib" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="gd" level="required">
<FEEDBACK>
<ON_ERROR message="gdrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="simplexml" level="required">
<FEEDBACK>
<ON_ERROR message="simplexmlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="spl" level="required">
<FEEDBACK>
<ON_ERROR message="splrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="pcre" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="dom" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="xml" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="xmlreader" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="intl" level="required">
<FEEDBACK>
<ON_ERROR message="intlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="json" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="hash" level="required"/>
<PHP_EXTENSION name="fileinfo" level="required"/>
<PHP_EXTENSION name="sodium" level="required"/>
<PHP_EXTENSION name="exif" level="optional"/>
</PHP_EXTENSIONS>
<PHP_SETTINGS>
<PHP_SETTING name="memory_limit" value="96M" level="required">
<FEEDBACK>
<ON_ERROR message="settingmemorylimit" />
</FEEDBACK>
</PHP_SETTING>
<PHP_SETTING name="file_uploads" value="1" level="optional">
<FEEDBACK>
<ON_CHECK message="settingfileuploads" />
</FEEDBACK>
</PHP_SETTING>
<PHP_SETTING name="opcache.enable" value="1" level="optional">
<FEEDBACK>
<ON_CHECK message="opcacherecommended" />
</FEEDBACK>
</PHP_SETTING>
<PHP_SETTING name="zend.exception_ignore_args" value="1" level="optional">
<FEEDBACK>
<ON_CHECK message="settingzendexceptionignoreargs" />
</FEEDBACK>
</PHP_SETTING>
</PHP_SETTINGS>
<CUSTOM_CHECKS>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddbstorageengine" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
<FEEDBACK>
<ON_ERROR message="quizattemptsupgradedmessage" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
<FEEDBACK>
<ON_CHECK message="slashargumentswarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
<FEEDBACK>
<ON_CHECK message="unsupporteddbtablerowformat" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
<FEEDBACK>
<ON_CHECK message="unoconvwarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
<FEEDBACK>
<ON_CHECK message="libcurlwarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddbfileformat" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddbfilepertable" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddblargeprefix" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
<FEEDBACK>
<ON_CHECK message="ishttpswarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
<FEEDBACK>
<ON_CHECK message="incompleteunicodesupport" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="required">
<FEEDBACK>
<ON_ERROR message="sixtyfourbitsrequired" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_max_input_vars" level="optional">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_admin_dir_usage" level="optional">
<FEEDBACK>
<ON_CHECK message="iscustomadminwarnings" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_xmlrpc_usage" level="optional">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mod_assignment" level="required">
</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_async_backup" level="recommended">
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_aurora_version" level="optional">
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
</COMPATIBILITY_MATRIX>
+7
View File
@@ -40,6 +40,13 @@ if (!function_exists('iconv')) {
die();
}
// Make sure php5-json is available.
if (!function_exists('json_encode') || !function_exists('json_decode')) {
// This also shouldn't happen.
echo 'Moodle requires the json PHP extension. Please install or enable the json extension.';
die();
}
// Make sure xml extension is available.
if (!extension_loaded('xml')) {
echo 'Moodle requires the xml PHP extension. Please install or enable the xml extension.';
+7 -4
View File
@@ -29,6 +29,8 @@ require_once($CFG->libdir . '/adminlib.php');
require_once($CFG->libdir . '/filelib.php');
$fetchupdates = optional_param('fetchupdates', false, PARAM_BOOL); // Check for available plugins updates.
$updatesonly = optional_param('updatesonly', false, PARAM_BOOL); // Show updateable plugins only.
$contribonly = optional_param('contribonly', false, PARAM_BOOL); // Show additional plugins only.
$uninstall = optional_param('uninstall', '', PARAM_COMPONENT); // Uninstall the plugin.
$delete = optional_param('delete', '', PARAM_COMPONENT); // Delete the plugin folder after it is uninstalled.
$confirmed = optional_param('confirm', false, PARAM_BOOL); // Confirm the uninstall/delete action.
@@ -45,7 +47,8 @@ require_admin();
$syscontext = context_system::instance();
// URL params we want to maintain on redirects.
$pageurl = new moodle_url('/admin/plugins.php');
$pageparams = array('updatesonly' => $updatesonly, 'contribonly' => $contribonly);
$pageurl = new moodle_url('/admin/plugins.php', $pageparams);
$pluginman = core_plugin_manager::instance();
@@ -54,7 +57,7 @@ $PAGE->set_primary_active_tab('siteadminnode');
if ($uninstall) {
if (!$confirmed) {
admin_externalpage_setup('pluginsoverview');
admin_externalpage_setup('pluginsoverview', '', $pageparams);
} else {
$PAGE->set_url($pageurl);
$PAGE->set_context($syscontext);
@@ -198,7 +201,7 @@ if ($installupdate and $installupdateversion) {
}
}
admin_externalpage_setup('pluginsoverview');
admin_externalpage_setup('pluginsoverview', '', $pageparams);
/** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
@@ -211,4 +214,4 @@ if ($fetchupdates) {
redirect($PAGE->url);
}
echo $output->plugin_management_page($pluginman, $checker);
echo $output->plugin_management_page($pluginman, $checker, $pageparams);
+2 -2
View File
@@ -25,7 +25,7 @@ namespace core_adminpresets;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass core_adminpresets_generator
*/
final class generator_test extends \advanced_testcase {
class generator_test extends \advanced_testcase {
/**
* Test the behaviour of create_preset() method.
@@ -180,7 +180,7 @@ final class generator_test extends \advanced_testcase {
*
* @return array
*/
public static function create_preset_provider(): array {
public function create_preset_provider(): array {
return [
'Default values' => [
],
+8 -8
View File
@@ -23,9 +23,9 @@ namespace core_adminpresets;
* @category test
* @copyright 2021 Sara Arjona (sara@moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \core_adminpresets\helper
* @coversDefaultClass helper
*/
final class helper_test extends \advanced_testcase {
class helper_test extends \advanced_testcase {
/**
* Test the behaviour of create_preset() method.
@@ -84,7 +84,7 @@ final class helper_test extends \advanced_testcase {
*
* @return array
*/
public static function create_preset_provider(): array {
public function create_preset_provider(): array {
return [
'Default values' => [
],
@@ -177,7 +177,7 @@ final class helper_test extends \advanced_testcase {
*
* @return array
*/
public static function add_item_provider(): array {
public function add_item_provider(): array {
return [
'Setting without plugin' => [
'name' => 'settingname',
@@ -239,7 +239,7 @@ final class helper_test extends \advanced_testcase {
*
* @return array
*/
public static function add_plugin_provider(): array {
public function add_plugin_provider(): array {
return [
'Plugin: enabled (using int)' => [
'type' => 'plugintype',
@@ -321,7 +321,7 @@ final class helper_test extends \advanced_testcase {
*
* @return array
*/
public static function change_default_preset_provider(): array {
public function change_default_preset_provider(): array {
return [
'Starter preset' => [
'preset' => 'starter',
@@ -364,7 +364,7 @@ final class helper_test extends \advanced_testcase {
'preset' => 'unexisting',
],
'Valid XML file' => [
'preset' => self::get_fixture_path(__NAMESPACE__, 'import_settings_plugins.xml'),
'preset' => __DIR__ . '/fixtures/import_settings_plugins.xml',
'settings' => [
'allowemojipicker' => 1,
'enableportfolios' => 1,
@@ -377,7 +377,7 @@ final class helper_test extends \advanced_testcase {
],
],
'Invalid XML file' => [
'preset' => self::get_fixture_path(__NAMESPACE__, 'invalid_xml_file.xml'),
'preset' => __DIR__ . '/fixtures/invalid_xml_file.xml',
],
'Unexisting XML file' => [
'preset' => __DIR__ . '/fixtures/unexisting.xml',
@@ -25,7 +25,7 @@ namespace core_adminpresets\local\setting;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \core_adminpresets\local\setting\adminpresets_admin_setting_bloglevel
*/
final class adminpresets_admin_setting_bloglevel_test extends \advanced_testcase {
class adminpresets_admin_setting_bloglevel_test extends \advanced_testcase {
/**
* Test the behaviour of save_value() method.
@@ -72,7 +72,7 @@ final class adminpresets_admin_setting_bloglevel_test extends \advanced_testcase
*
* @return array
*/
public static function save_value_provider(): array {
public function save_value_provider(): array {
return [
'Save the bloglevel and set blog_menu block visibility to true' => [
'setttingvalue' => BLOG_USER_LEVEL,
@@ -25,7 +25,7 @@ namespace core_adminpresets\local\setting;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \core_adminpresets\local\setting\adminpresets_admin_setting_sitesettext
*/
final class adminpresets_admin_setting_sitesettext_test extends \advanced_testcase {
class adminpresets_admin_setting_sitesettext_test extends \advanced_testcase {
/**
* Test the behaviour of save_value() method.
@@ -66,7 +66,7 @@ final class adminpresets_admin_setting_sitesettext_test extends \advanced_testca
*
* @return array
*/
public static function save_value_provider(): array {
public function save_value_provider(): array {
return [
'Fullname: different value' => [
'settingname' => 'fullname',
@@ -25,7 +25,7 @@ namespace core_adminpresets\local\setting;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \core_adminpresets\local\setting\adminpresets_setting
*/
final class adminpresets_setting_test extends \advanced_testcase {
class adminpresets_setting_test extends \advanced_testcase {
/**
* Test the behaviour of save_value() method.
@@ -77,7 +77,7 @@ final class adminpresets_setting_test extends \advanced_testcase {
*
* @return array
*/
public static function save_value_provider(): array {
public function save_value_provider(): array {
return [
'Core setting with the same value is not saved' => [
'category' => 'optionalsubsystems',
@@ -167,7 +167,7 @@ final class adminpresets_setting_test extends \advanced_testcase {
*
* @return array
*/
public static function save_attributes_values_provider(): array {
public function save_attributes_values_provider(): array {
return [
'Plugin setting with the same value is not saved' => [
'category' => 'modsettinglesson',
+12 -26
View File
@@ -28,14 +28,13 @@ use stdClass;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \core_adminpresets\manager
*/
final class manager_test extends \advanced_testcase {
class manager_test extends \advanced_testcase {
/**
* Include required libraries.
*/
public static function setUpBeforeClass(): void {
global $CFG;
require_once($CFG->libdir.'/adminlib.php');
parent::setUpBeforeClass();
}
/**
@@ -368,7 +367,7 @@ final class manager_test extends \advanced_testcase {
*
* @return array
*/
public static function export_preset_provider(): array {
public function export_preset_provider(): array {
return [
'Export settings and plugins, excluding sensible' => [
'includesensible' => false,
@@ -424,7 +423,7 @@ final class manager_test extends \advanced_testcase {
* @param string|null $expectedpresetname Expected preset name.
*/
public function test_import_preset(string $filecontents, bool $expectedpreset, bool $expectedsettings = false,
bool $expectedplugins = false, bool $expecteddebugging = false, ?string $expectedexception = null,
bool $expectedplugins = false, bool $expecteddebugging = false, string $expectedexception = null,
string $expectedpresetname = 'Imported preset'): void {
global $DB;
@@ -531,32 +530,26 @@ final class manager_test extends \advanced_testcase {
*
* @return array
*/
public static function import_preset_provider(): array {
public function import_preset_provider(): array {
return [
'Import settings from an empty file' => [
'filecontents' => '',
'expectedpreset' => false,
],
'Import settings and plugins from a valid XML file' => [
'filecontents' => file_get_contents(
filename: self::get_fixture_path(__NAMESPACE__, 'import_settings_plugins.xml')
),
'filecontents' => file_get_contents(__DIR__ . '/fixtures/import_settings_plugins.xml'),
'expectedpreset' => true,
'expectedsettings' => true,
'expectedplugins' => true,
],
'Import only settings from a valid XML file' => [
'filecontents' => file_get_contents(
filename: self::get_fixture_path(__NAMESPACE__, 'import_settings.xml')
),
'filecontents' => file_get_contents(__DIR__ . '/fixtures/import_settings.xml'),
'expectedpreset' => true,
'expectedsettings' => true,
'expectedplugins' => false,
],
'Import settings and plugins from a valid XML file with Starter name, which will be marked as non-core' => [
'filecontents' => file_get_contents(
filename: self::get_fixture_path(__NAMESPACE__, 'import_starter_name.xml')
),
'filecontents' => file_get_contents(__DIR__ . '/fixtures/import_starter_name.xml'),
'expectedpreset' => true,
'expectedsettings' => true,
'expectedplugins' => true,
@@ -565,9 +558,7 @@ final class manager_test extends \advanced_testcase {
'expectedpresetname' => 'Starter',
],
'Import settings from an invalid XML file' => [
'filecontents' => file_get_contents(
filename: self::get_fixture_path(__NAMESPACE__, 'invalid_xml_file.xml')
),
'filecontents' => file_get_contents(__DIR__ . '/fixtures/invalid_xml_file.xml'),
'expectedpreset' => false,
'expectedsettings' => false,
'expectedplugins' => false,
@@ -575,26 +566,20 @@ final class manager_test extends \advanced_testcase {
'expectedexception' => \Exception::class,
],
'Import unexisting settings category' => [
'filecontents' => file_get_contents(
filename: self::get_fixture_path(__NAMESPACE__, 'unexisting_category.xml')
),
'filecontents' => file_get_contents(__DIR__ . '/fixtures/unexisting_category.xml'),
'expectedpreset' => false,
'expectedsettings' => false,
'expectedplugins' => false,
],
'Import unexisting setting' => [
'filecontents' => file_get_contents(
filename: self::get_fixture_path(__NAMESPACE__, 'unexisting_setting.xml')
),
'filecontents' => file_get_contents(__DIR__ . '/fixtures/unexisting_setting.xml'),
'expectedpreset' => false,
'expectedsettings' => false,
'expectedplugins' => false,
'expecteddebugging' => true,
],
'Import valid settings with one unexisting setting too' => [
'filecontents' => file_get_contents(
filename: self::get_fixture_path(__NAMESPACE__, 'import_settings_with_unexisting_setting.xml'),
),
'filecontents' => file_get_contents(__DIR__ . '/fixtures/import_settings_with_unexisting_setting.xml'),
'expectedpreset' => true,
'expectedsettings' => false,
'expectedplugins' => false,
@@ -603,6 +588,7 @@ final class manager_test extends \advanced_testcase {
];
}
/**
* Test the behaviour of delete_preset() method when the preset id doesn't exist.
*
@@ -30,13 +30,13 @@ use core_privacy\tests\provider_testcase;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \core_adminpresets\privacy\provider
*/
final class privacy_provider_test extends provider_testcase {
class privacy_provider_test extends provider_testcase {
/**
* Test for provider::get_metadata().
* @covers ::get_metadata
*/
public function test_get_metadata(): void {
public function test_get_metadata() {
$collection = new collection('core_adminpresets');
$newcollection = provider::get_metadata($collection);
$itemcollection = $newcollection->get_collection();
@@ -49,7 +49,7 @@ final class privacy_provider_test extends provider_testcase {
* Test for provider::get_contexts_for_userid() doesn't return any context.
* @covers ::get_contexts_for_userid
*/
public function test_get_contexts_for_userid(): void {
public function test_get_contexts_for_userid() {
global $USER;
$this->resetAfterTest();
@@ -67,7 +67,7 @@ final class privacy_provider_test extends provider_testcase {
* Test for provider::get_users_in_context() doesn't return any user.
* @covers ::get_users_in_context
*/
public function test_get_users_in_context(): void {
public function test_get_users_in_context() {
global $USER;
$this->resetAfterTest();
@@ -88,7 +88,7 @@ final class privacy_provider_test extends provider_testcase {
* Test for provider::export_user_data().
* @covers ::export_user_data
*/
public function test_export_user_data(): void {
public function test_export_user_data() {
global $USER;
$this->resetAfterTest();
@@ -121,7 +121,7 @@ final class privacy_provider_test extends provider_testcase {
* Test for provider::delete_data_for_all_users_in_context().
* @covers ::delete_data_for_all_users_in_context
*/
public function test_delete_data_for_all_users_in_context(): void {
public function test_delete_data_for_all_users_in_context() {
global $DB, $USER;
$this->resetAfterTest();
@@ -146,7 +146,7 @@ final class privacy_provider_test extends provider_testcase {
* Test for provider::delete_data_for_user().
* @covers ::delete_data_for_user
*/
public function test_delete_data_for_user(): void {
public function test_delete_data_for_user() {
global $DB, $USER;
$this->resetAfterTest();
-2
View File
@@ -23,8 +23,6 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('IGNORE_COMPONENT_CACHE', true);
require_once('../config.php');
require_once($CFG->libdir.'/adminlib.php');
+5 -10
View File
@@ -34,10 +34,8 @@ admin_externalpage_setup('registrationmoodleorg');
$unregistration = optional_param('unregistration', false, PARAM_BOOL);
$confirm = optional_param('confirm', false, PARAM_BOOL);
// Consider the site 'registered' if records exist locally and at the hub.
$siteisregistered = \core\hub\registration::is_registered() && core\hub\api::is_site_registered_in_hub();
if ($unregistration && $siteisregistered) {
if ($unregistration && \core\hub\registration::is_registered()) {
if ($confirm) {
require_sesskey();
\core\hub\registration::unregister(false, false);
@@ -62,7 +60,7 @@ if (!$returnurl = optional_param('returnurl', null, PARAM_LOCALURL)) {
$returnurl = $isinitialregistration ? '/admin/index.php' : '/admin/registration/index.php';
}
$siteregistrationform = new \core\hub\site_registration_form(null, ['registered' => $siteisregistered]);
$siteregistrationform = new \core\hub\site_registration_form();
$siteregistrationform->set_data(['returnurl' => $returnurl]);
if ($fromform = $siteregistrationform->get_data()) {
@@ -88,7 +86,7 @@ echo $OUTPUT->header();
// Current status of registration.
$notificationtype = \core\output\notification::NOTIFY_ERROR;
if ($siteisregistered) {
if (\core\hub\registration::is_registered()) {
$lastupdated = \core\hub\registration::get_last_updated();
if ($lastupdated == 0) {
$registrationmessage = get_string('pleaserefreshregistrationunknown', 'admin');
@@ -106,7 +104,7 @@ if ($siteisregistered) {
}
// Heading.
if ($siteisregistered) {
if (\core\hub\registration::is_registered()) {
echo $OUTPUT->heading(get_string('registerwithmoodleorgupdate', 'core_hub'));
} else if ($isinitialregistration) {
echo $OUTPUT->heading(get_string('registerwithmoodleorgcomplete', 'core_hub'));
@@ -119,7 +117,7 @@ echo $renderer->moodleorg_registration_message();
$siteregistrationform->display();
if ($siteisregistered) {
if (\core\hub\registration::is_registered()) {
// Unregister link.
$unregisterhuburl = new moodle_url("/admin/registration/index.php", ['unregistration' => 1]);
echo html_writer::div(html_writer::link($unregisterhuburl, get_string('unregister', 'hub')), 'unregister mt-2');
@@ -127,7 +125,4 @@ if ($siteisregistered) {
echo html_writer::div(html_writer::link(new moodle_url($returnurl), get_string('skipregistration', 'hub')),
'skipregistration mt-2');
}
$PAGE->requires->js_call_amd('core_admin/expand_hash', 'init');
echo $OUTPUT->footer();
+76 -82
View File
@@ -295,7 +295,6 @@ class core_admin_renderer extends plugin_renderer_base {
$output .= $this->header();
$output .= $this->output->heading(get_string('notifications', 'admin'));
$output .= $this->upgrade_news_message();
$output .= $this->maturity_info($maturity);
$output .= empty($CFG->disableupdatenotifications) ? $this->available_updates($availableupdates, $availableupdatesfetch) : '';
$output .= $this->insecure_dataroot_warning($insecuredataroot);
@@ -755,22 +754,6 @@ class core_admin_renderer extends plugin_renderer_base {
return $this->box($copyrighttext, 'copyright');
}
/**
* Display a transient notification for important upgrades messages for
* specific releases.
*
* @return string HTML to output.
*/
protected function upgrade_news_message() {
return $this->notification(
get_string('importantupdates_content', 'admin'),
'info',
false,
get_string('importantupdates_title', 'admin'),
'i/circleinfo, core'
);
}
/**
* Display a warning about installing development code if necesary.
* @param int $maturity
@@ -860,14 +843,14 @@ class core_admin_renderer extends plugin_renderer_base {
$registerbutton = $this->single_button(new moodle_url('/admin/registration/index.php'),
get_string('register', 'admin'));
$str = 'registrationwarning';
$type = 'error alert alert-danger';
} else {
$registerbutton = '';
$str = 'registrationwarningcontactadmin';
$type = 'info';
}
return $this->warning( get_string($str, 'admin') . '&nbsp;' . $registerbutton , $type);
return $this->warning( get_string($str, 'admin')
. '&nbsp;' . $this->help_icon('registration', 'admin') . $registerbutton ,
'error alert alert-danger');
}
return '';
@@ -891,8 +874,8 @@ class core_admin_renderer extends plugin_renderer_base {
protected function mobile_configuration_warning($mobileconfigured) {
$output = '';
if (!$mobileconfigured) {
$settingslink = new moodle_url('/admin/search.php', ['query' => 'enablemobilewebservice']);
$configurebutton = $this->single_button($settingslink, get_string('enablemobilewebservice', 'admin'), 'get');
$settingslink = new moodle_url('/admin/settings.php', ['section' => 'mobilesettings']);
$configurebutton = $this->single_button($settingslink, get_string('enablemobilewebservice', 'admin'));
$output .= $this->warning(get_string('mobilenotconfiguredwarning', 'admin') . '&nbsp;' . $configurebutton);
}
@@ -1021,7 +1004,7 @@ class core_admin_renderer extends plugin_renderer_base {
function upgrade_reload($url) {
return html_writer::empty_tag('br') .
html_writer::tag('div',
html_writer::link($url, $this->pix_icon('i/reload', '', '', ['class' => 'icon']) .
html_writer::link($url, $this->pix_icon('i/reload', '', '', array('class' => 'icon icon-pre')) .
get_string('reload'), array('title' => get_string('reload'))),
array('class' => 'continuebutton')) . html_writer::empty_tag('br');
}
@@ -1258,7 +1241,7 @@ class core_admin_renderer extends plugin_renderer_base {
new moodle_url($this->page->url, array('installupdatex' => 1)),
get_string('updateavailableinstallall', 'core_admin', count($installableupdates)),
'post',
array('class' => 'singlebutton updateavailableinstallall me-1')
array('class' => 'singlebutton updateavailableinstallall mr-1')
);
}
@@ -1267,7 +1250,7 @@ class core_admin_renderer extends plugin_renderer_base {
new moodle_url($this->page->url, array('abortinstallx' => 1, 'confirmplugincheck' => 0)),
get_string('cancelinstallall', 'core_plugin', count($installabortable)),
'post',
array('class' => 'singlebutton cancelinstallall me-1')
array('class' => 'singlebutton cancelinstallall mr-1')
);
}
@@ -1276,17 +1259,17 @@ class core_admin_renderer extends plugin_renderer_base {
new moodle_url($this->page->url, array('abortupgradex' => 1)),
get_string('cancelupgradeall', 'core_plugin', count($upgradeabortable)),
'post',
array('class' => 'singlebutton cancelupgradeall me-1')
array('class' => 'singlebutton cancelupgradeall mr-1')
);
}
$out .= html_writer::div(html_writer::link(new moodle_url($this->page->url, array('showallplugins' => 0)),
get_string('plugincheckattention', 'core_plugin')).' '.html_writer::span($sumattention, 'badge bg-light text-dark'),
'btn btn-link me-1');
'btn btn-link mr-1');
$out .= html_writer::div(html_writer::link(new moodle_url($this->page->url, array('showallplugins' => 1)),
get_string('plugincheckall', 'core_plugin')).' '.html_writer::span($sumtotal, 'badge bg-light text-dark'),
'btn btn-link me-1');
'btn btn-link mr-1');
$out .= $this->output->container_end(); // End of .actions container.
$out .= $this->output->container_end(); // End of #plugins-check-info container.
@@ -1305,7 +1288,7 @@ class core_admin_renderer extends plugin_renderer_base {
* @param null|moodle_url $cancel URL for the cancel link, defaults to the current page
* @return string HTML
*/
public function plugins_management_confirm_buttons(?moodle_url $continue=null, ?moodle_url $cancel=null) {
public function plugins_management_confirm_buttons(moodle_url $continue=null, moodle_url $cancel=null) {
$out = html_writer::start_div('plugins-management-confirm-buttons');
@@ -1393,13 +1376,13 @@ class core_admin_renderer extends plugin_renderer_base {
new moodle_url($this->page->url, array('installdepx' => 1)),
get_string('dependencyinstallmissing', 'core_plugin', count($installable)),
'post',
array('class' => 'singlebutton dependencyinstallmissing d-inline-block me-1')
array('class' => 'singlebutton dependencyinstallmissing d-inline-block mr-1')
);
}
$out .= html_writer::div(html_writer::link(new moodle_url('/admin/tool/installaddon/'),
get_string('dependencyuploadmissing', 'core_plugin'), array('class' => 'btn btn-link')),
'dependencyuploadmissing d-inline-block me-1');
'dependencyuploadmissing d-inline-block mr-1');
$out .= $this->output->container_end(); // End of .plugins-check-dependencies-actions container.
@@ -1465,29 +1448,29 @@ class core_admin_renderer extends plugin_renderer_base {
$info .= html_writer::div(
html_writer::link('https://moodle.org/plugins/view.php?plugin='.$plugin->component,
get_string('misdepinfoplugin', 'core_plugin')),
'misdepinfoplugin d-inline-block me-3 mb-1'
'misdepinfoplugin d-inline-block mr-3 mb-1'
);
$info .= html_writer::div(
html_writer::link('https://moodle.org/plugins/pluginversion.php?id='.$plugin->version->id,
get_string('misdepinfoversion', 'core_plugin')),
'misdepinfoversion d-inline-block me-3 mb-1'
'misdepinfoversion d-inline-block mr-3 mb-1'
);
$info .= html_writer::div(html_writer::link($plugin->version->downloadurl, get_string('download')),
'misdepdownload d-inline-block me-3 mb-1');
'misdepdownload d-inline-block mr-3 mb-1');
if ($pluginman->is_remote_plugin_installable($plugin->component, $plugin->version->version, $reason)) {
$info .= $this->output->single_button(
new moodle_url($this->page->url, array('installdep' => $plugin->component)),
get_string('dependencyinstall', 'core_plugin'),
'post',
array('class' => 'singlebutton dependencyinstall me-3 mb-1')
array('class' => 'singlebutton dependencyinstall mr-3 mb-1')
);
} else {
$reasonhelp = $this->info_remote_plugin_not_installable($reason);
if ($reasonhelp) {
$info .= html_writer::div($reasonhelp, 'reasonhelp dependencyinstall d-inline-block me-3 mb-1');
$info .= html_writer::div($reasonhelp, 'reasonhelp dependencyinstall d-inline-block mr-3 mb-1');
}
}
@@ -1673,9 +1656,8 @@ class core_admin_renderer extends plugin_renderer_base {
public function plugins_overview_panel(core_plugin_manager $pluginman, array $options = array()) {
$plugininfo = $pluginman->get_plugins();
$this->page->requires->js_call_amd('core_admin/plugins_overview', 'init');
$numtotal = $numextension = $numupdatable = $numinstallable = $nummissing = $numnew = 0;
$numtotal = $numextension = $numupdatable = $numinstallable = 0;
foreach ($plugininfo as $type => $plugins) {
foreach ($plugins as $name => $plugin) {
@@ -1689,12 +1671,8 @@ class core_admin_renderer extends plugin_renderer_base {
}
}
if ($plugin->get_status() === core_plugin_manager::PLUGIN_STATUS_MISSING) {
$nummissing++;
continue;
}
if ($plugin->get_status() === core_plugin_manager::PLUGIN_STATUS_NEW) {
$numnew++;
}
$numtotal++;
if (!$plugin->is_standard()) {
$numextension++;
@@ -1703,50 +1681,36 @@ class core_admin_renderer extends plugin_renderer_base {
}
$infoall = html_writer::link(
$this->page->url,
new moodle_url($this->page->url, array('contribonly' => 0, 'updatesonly' => 0)),
get_string('overviewall', 'core_plugin'),
['title' => get_string('filterall', 'core_plugin'), 'data-filterby' => 'all', 'class' => 'active']
array('title' => get_string('filterall', 'core_plugin'))
).' '.html_writer::span($numtotal, 'badge number number-all');
$infoext = html_writer::link(
new moodle_url($this->page->url, [], 'additional'),
new moodle_url($this->page->url, array('contribonly' => 1, 'updatesonly' => 0)),
get_string('overviewext', 'core_plugin'),
['title' => get_string('filtercontribonly', 'core_plugin'), 'data-filterby' => 'additional']
array('title' => get_string('filtercontribonly', 'core_plugin'))
).' '.html_writer::span($numextension, 'badge number number-additional');
if ($numupdatable) {
$infoupdatable = html_writer::link(
new moodle_url($this->page->url, [], 'updatable'),
new moodle_url($this->page->url, array('contribonly' => 0, 'updatesonly' => 1)),
get_string('overviewupdatable', 'core_plugin'),
['title' => get_string('filterupdatesonly', 'core_plugin'), 'data-filterby' => 'updatable']
array('title' => get_string('filterupdatesonly', 'core_plugin'))
).' '.html_writer::span($numupdatable, 'badge bg-info text-white number number-updatable');
} else {
// No updates, or the notifications disabled.
$infoupdatable = '';
}
if ($numnew) {
$infonew = html_writer::link(
new moodle_url($this->page->url, [], 'newplugin'),
get_string('status_new', 'plugin'),
['title' => get_string('filternewpluginsonly', 'core_plugin'), 'data-filterby' => 'newplugin']
).' '.html_writer::span($numnew, 'badge bg-success text-white number number-newplugin');
} else {
$infonew = '';
}
$out = html_writer::start_div('', array('id' => 'plugins-overview-panel'));
if ($nummissing) {
$infomissing = html_writer::link(
new moodle_url($this->page->url, [], 'missing'),
get_string('status_missing', 'plugin'),
['title' => get_string('filtermissingonly', 'core_plugin'), 'data-filterby' => 'missing']
).' '.html_writer::span($nummissing, 'badge bg-danger text-white number number-missing');
} else {
$infomissing = '';
if (!empty($options['updatesonly'])) {
$out .= $this->output->heading(get_string('overviewupdatable', 'core_plugin'), 3);
} else if (!empty($options['contribonly'])) {
$out .= $this->output->heading(get_string('overviewext', 'core_plugin'), 3);
}
$out = html_writer::start_div('', ['id' => 'plugins-overview-panel']);
if ($numinstallable) {
$out .= $this->output->single_button(
new moodle_url($this->page->url, array('installupdatex' => 1)),
@@ -1757,10 +1721,8 @@ class core_admin_renderer extends plugin_renderer_base {
}
$out .= html_writer::div($infoall, 'info info-all').
html_writer::div($infoext, 'info info-additional').
html_writer::div($infoupdatable, 'info info-updatable').
html_writer::div($infonew, 'info info-newplugin').
html_writer::div($infomissing, 'info info-missing');
html_writer::div($infoext, 'info info-ext').
html_writer::div($infoupdatable, 'info info-updatable');
$out .= html_writer::end_div(); // End of #plugins-overview-panel block.
@@ -1780,6 +1742,38 @@ class core_admin_renderer extends plugin_renderer_base {
$plugininfo = $pluginman->get_plugins();
// Filter the list of plugins according the options.
if (!empty($options['updatesonly'])) {
$updateable = array();
foreach ($plugininfo as $plugintype => $pluginnames) {
foreach ($pluginnames as $pluginname => $pluginfo) {
$pluginavailableupdates = $pluginfo->available_updates();
if (!empty($pluginavailableupdates)) {
foreach ($pluginavailableupdates as $pluginavailableupdate) {
$updateable[$plugintype][$pluginname] = $pluginfo;
}
}
}
}
$plugininfo = $updateable;
}
if (!empty($options['contribonly'])) {
$contribs = array();
foreach ($plugininfo as $plugintype => $pluginnames) {
foreach ($pluginnames as $pluginname => $pluginfo) {
if (!$pluginfo->is_standard()) {
$contribs[$plugintype][$pluginname] = $pluginfo;
}
}
}
$plugininfo = $contribs;
}
if (empty($plugininfo)) {
return '';
}
$table = new html_table();
$table->id = 'plugins-control-panel';
$table->head = array(
@@ -1877,15 +1871,13 @@ class core_admin_renderer extends plugin_renderer_base {
$row->attributes['class'] .= ' standard';
$source = '';
} else {
$row->attributes['class'] .= ' additional';
$source = html_writer::div(get_string('sourceext', 'core_plugin'), 'source badge mr-1 bg-info text-white');
$row->attributes['class'] .= ' extension';
$source = html_writer::div(get_string('sourceext', 'core_plugin'), 'source badge bg-info text-white');
}
if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
$row->attributes['class'] .= ' missing';
$msg = html_writer::div(get_string('status_missing', 'core_plugin'), 'statusmsg badge bg-danger text-white');
} else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
$row->attributes['class'] .= ' newplugin';
$msg = html_writer::div(get_string('status_new', 'core_plugin'), 'statusmsg badge bg-success text-white');
} else {
$msg = '';
@@ -1901,7 +1893,6 @@ class core_admin_renderer extends plugin_renderer_base {
$updateinfo = '';
if (is_array($plugin->available_updates())) {
$row->attributes['class'] .= ' updatable';
foreach ($plugin->available_updates() as $availableupdate) {
$updateinfo .= $this->plugin_available_update_info($pluginman, $availableupdate);
}
@@ -2189,11 +2180,10 @@ class core_admin_renderer extends plugin_renderer_base {
$output = '';
$output .= $this->header();
$output .= $this->container_start('upgradekeyreq');
$output .= $this->heading(get_string('upgradekeyreq', 'core_admin'));
$output .= $this->container_start('upgradekeyreq w-25');
$output .= html_writer::start_tag('form', array('method' => 'POST', 'action' => $url));
$output .= html_writer::empty_tag('input', [
'id' => 'upgradekey',
'name' => 'upgradekey',
'type' => 'password',
'class' => 'form-control w-auto',
@@ -2216,10 +2206,14 @@ class core_admin_renderer extends plugin_renderer_base {
* @return string
*/
public function moodleorg_registration_message() {
$a = new stdClass();
$a->moreinformation = '#id_sitestats'; // More information anchor.
$a->moodleapp = MOODLE_PRODUCTURL . '/solutions/moodle-app/';
$out = format_text(get_string('registerwithmoodleorginfo', 'core_hub', $a), FORMAT_MARKDOWN);
$out = format_text(get_string('registerwithmoodleorginfo', 'core_hub'), FORMAT_MARKDOWN);
$out .= html_writer::link(
MOODLE_PRODUCTURL.'/solutions/moodle-app/',
$this->output->pix_icon('i/info', '').' '.get_string('registerwithmoodleorginfoapp', 'core_hub'),
['class' => 'btn btn-link', 'role' => 'opener', 'target' => '_href']
);
$out .= html_writer::link(
HUB_MOODLEORGHUBURL,
-22
View File
@@ -1,22 +0,0 @@
# core_role (subsystem) Upgrade notes
## 4.5
### Added
- All session management has been moved to the `\core\session\manager` class.
This removes the dependancy to use the `sessions` table.
Session management plugins (like Redis) should now inherit
the base `\core\session\handler` class, which implements
`SessionHandlerInterface`, and override methods as required.
The following methods in `\core\session\manager` have been deprecated:
| Old method name | New method name |
| --- | --- |
| `kill_all_sessions` | `destroy_all` |
| `kill_session` | `destroy` |
| `kill_sessions_for_auth_plugin` | `destroy_by_auth_plugin` |
| `kill_user_sessions` | `destroy_user_sessions` |
For more information see [MDL-66151](https://tracker.moodle.org/browse/MDL-66151)
@@ -655,7 +655,7 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
echo "</label>\n";
}
if ($helpicon) {
echo '<span class="float-sm-end text-nowrap">'.$helpicon.'</span>';
echo '<span class="float-sm-right text-nowrap">'.$helpicon.'</span>';
}
echo '</div>';
if (isset($this->errors[$name])) {
@@ -18,12 +18,13 @@ declare(strict_types=1);
namespace core_role\reportbuilder\local\entities;
use core\{context, context_helper};
use core\lang_string;
use context;
use context_helper;
use lang_string;
use stdClass;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\select;
use core_reportbuilder\local\report\{column, filter};
use stdClass;
/**
* Role entity
@@ -95,6 +96,7 @@ class role extends base {
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$rolealias}.name, {$rolealias}.shortname, {$rolealias}.id, {$contextalias}.id AS contextid")
->add_fields(context_helper::get_preload_record_columns_sql($contextalias))
// The sorting is on name, unless empty (determined by single space - thanks Oracle) then we use shortname.
@@ -104,7 +106,7 @@ class role extends base {
ELSE {$rolealias}.name
END",
])
->add_callback(static function(?string $name, stdClass $role): string {
->set_callback(static function($name, stdClass $role): string {
if ($name === null) {
return '';
}
@@ -122,6 +124,7 @@ class role extends base {
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$rolealias}.name, {$rolealias}.shortname")
// The sorting is on name, unless empty (determined by single space - thanks Oracle) then we use shortname.
->set_is_sortable(true, [
@@ -130,9 +133,12 @@ class role extends base {
ELSE {$rolealias}.name
END",
])
->add_callback(fn(?string $name, stdClass $role) => match ($name) {
null => '',
default => role_get_name($role, null, ROLENAME_ORIGINAL),
->set_callback(static function($name, stdClass $role): string {
if ($name === null) {
return '';
}
return role_get_name($role, null, ROLENAME_ORIGINAL);
});
// Short name column.
@@ -142,22 +148,8 @@ class role extends base {
$this->get_entity_name()
))
->add_joins($this->get_joins())
->add_field("{$rolealias}.shortname")
->set_is_sortable(true);
// Archetype column.
$columns[] = (new column(
'archetype',
new lang_string('archetype', 'core_role'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->add_field("{$rolealias}.archetype")
->add_callback(fn(?string $archetype) => match ($archetype) {
null => '',
'' => get_string('none'),
default => get_string("archetype{$archetype}", 'core_role'),
})
->set_type(column::TYPE_TEXT)
->add_fields("{$rolealias}.shortname")
->set_is_sortable(true);
// Description column.
@@ -174,9 +166,12 @@ class role extends base {
->set_type(column::TYPE_LONGTEXT)
->add_field($descriptionfieldsql, 'description')
->add_field("{$rolealias}.shortname")
->add_callback(fn(?string $description, stdClass $role) => match ($description) {
null => '',
default => role_get_description($role),
->set_callback(static function($description, stdClass $role): string {
if ($description === null) {
return '';
}
return role_get_description($role);
});
return $columns;
@@ -203,22 +198,6 @@ class role extends base {
return role_get_names(null, ROLENAME_ORIGINAL, true);
});
// Archetype filter.
$filters[] = (new filter(
select::class,
'archetype',
new lang_string('archetype', 'core_role'),
$this->get_entity_name(),
"{$rolealias}.archetype",
))
->add_joins($this->get_joins())
->set_options_callback(static function(): array {
return array_map(
fn(string $archetype) => get_string("archetype{$archetype}", 'core_role'),
get_role_archetypes(),
);
});
return $filters;
}
}
@@ -102,6 +102,7 @@ class role_assignment extends base {
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$raalias}.component")
->set_is_sortable(true);
@@ -112,6 +113,7 @@ class role_assignment extends base {
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_INTEGER)
->add_field("{$raalias}.itemid")
->set_is_sortable(true);
+1 -1
View File
@@ -165,7 +165,7 @@ if (!empty($capabilities)) {
$attrs = array('type'=>'submit', 'name'=>'savechanges', 'value'=>get_string('savechanges'), 'class'=>'btn btn-primary');
echo html_writer::empty_tag('input', $attrs);
$attrs = array('type' => 'submit', 'name' => 'cancel', 'value' => get_string('cancel'),
'class' => 'btn btn-secondary ms-1');
'class' => 'btn btn-secondary ml-1');
echo html_writer::empty_tag('input', $attrs);
echo html_writer::end_tag('div');
echo html_writer::end_tag('div');
+4 -4
View File
@@ -26,8 +26,8 @@ use core_role_preset;
* @copyright 2013 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class preset_test extends \advanced_testcase {
public function test_xml(): void {
class preset_test extends \advanced_testcase {
public function test_xml() {
global $DB;
$roles = $DB->get_records('role');
@@ -81,10 +81,10 @@ final class preset_test extends \advanced_testcase {
* Tests covered method.
* @covers \core_role_preset::parse_preset
*/
public function test_mixed_levels(): void {
public function test_mixed_levels() {
// The problem here is that we cannot guarantee plugin contexts
// have unique short names, so we have to also support level numbers.
$xml = file_get_contents(self::get_fixture_path(__NAMESPACE__, 'mixed_levels.xml'));
$xml = file_get_contents(__DIR__ . '/fixtures/mixed_levels.xml');
$this->assertTrue(\core_role_preset::is_valid_preset($xml));
$preset = \core_role_preset::parse_preset($xml);
+10 -10
View File
@@ -40,12 +40,12 @@ use core_privacy\local\request\approved_userlist;
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class provider_test extends provider_testcase {
class provider_test extends provider_testcase {
/**
* Test to check export_user_preferences.
* returns user preferences data.
*/
public function test_export_user_preferences(): void {
public function test_export_user_preferences() {
$this->resetAfterTest();
$this->setAdminUser();
$user = $this->getDataGenerator()->create_user();
@@ -64,7 +64,7 @@ final class provider_test extends provider_testcase {
/**
* Check all contexts are returned if there is any user data for this user.
*/
public function test_get_contexts_for_userid(): void {
public function test_get_contexts_for_userid() {
global $DB;
$this->resetAfterTest();
@@ -114,7 +114,7 @@ final class provider_test extends provider_testcase {
/**
* Test that user data is exported correctly.
*/
public function test_export_user_data(): void {
public function test_export_user_data() {
global $DB;
$this->resetAfterTest();
@@ -243,7 +243,7 @@ final class provider_test extends provider_testcase {
/**
* Test for provider::delete_data_for_all_users_in_context().
*/
public function test_delete_data_for_all_users_in_context(): void {
public function test_delete_data_for_all_users_in_context() {
global $DB;
$this->resetAfterTest();
@@ -329,7 +329,7 @@ final class provider_test extends provider_testcase {
/**
* Test for provider::delete_data_for_user().
*/
public function test_delete_data_for_user(): void {
public function test_delete_data_for_user() {
global $DB;
$this->resetAfterTest();
@@ -387,7 +387,7 @@ final class provider_test extends provider_testcase {
/**
* Export for a user with a key against a script where no instance is specified.
*/
public function test_export_user_role_to_cohort(): void {
public function test_export_user_role_to_cohort() {
global $DB;
$this->resetAfterTest();
@@ -443,7 +443,7 @@ final class provider_test extends provider_testcase {
/**
* Test for provider::delete_user_role_to_cohort().
*/
public function test_delete_user_role_to_cohort(): void {
public function test_delete_user_role_to_cohort() {
global $DB;
$this->resetAfterTest();
@@ -478,7 +478,7 @@ final class provider_test extends provider_testcase {
/**
* Test that only users within a course context are fetched.
*/
public function test_get_users_in_context(): void {
public function test_get_users_in_context() {
global $DB;
$this->resetAfterTest();
@@ -599,7 +599,7 @@ final class provider_test extends provider_testcase {
/**
* Test that data for users in approved userlist is deleted.
*/
public function test_delete_data_for_users(): void {
public function test_delete_data_for_users() {
global $DB;
$this->resetAfterTest();
@@ -20,14 +20,19 @@ namespace core_role\reportbuilder\datasource;
use core\context\course;
use core_reportbuilder_generator;
use core_reportbuilder_testcase;
use core_reportbuilder\local\filters\{date, select, text};
use core_reportbuilder\tests\core_reportbuilder_testcase;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php");
/**
* Unit tests for roles datasource
*
* @package core_role
* @covers \core_role\reportbuilder\datasource\roles
* @covers \core_role\reportbuilder\datasource\roles;
* @copyright 2024 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
@@ -99,7 +104,6 @@ final class roles_test extends core_reportbuilder_testcase {
// Role.
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:name']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:shortname']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:archetype']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:description']);
// Role assignment.
@@ -110,20 +114,11 @@ final class roles_test extends core_reportbuilder_testcase {
$content = $this->get_custom_report_content($report->get('id'));
$this->assertCount(1, $content);
[
$rolename,
$roleshortname,
$rolearchetype,
$roledescription,
$timemodified,
$component,
$itemid,
] = array_values($content[0]);
[$rolename, $roleshortname, $roledescription, $timemodified, $component, $itemid] = array_values($content[0]);
// Role.
$this->assertEquals('Moocher (Manager)', $rolename);
$this->assertEquals('manager', $roleshortname);
$this->assertEquals('ARCHETYPE: Manager', $rolearchetype);
$this->assertEquals('Managers can access courses and modify them, but usually do not participate in them.',
$roledescription);
@@ -151,14 +146,6 @@ final class roles_test extends core_reportbuilder_testcase {
'role:name_operator' => select::EQUAL_TO,
'role:name_value' => -1,
], false],
'Filter role archetype' => ['role:archetype', [
'role:archetype_operator' => select::EQUAL_TO,
'role:archetype_value' => 'student',
], true],
'Filter role archetype (no match)' => ['role:archetype', [
'role:archetype_operator' => select::EQUAL_TO,
'role:archetype_value' => 'teacher',
], false],
// Role assignment.
'Filter role assignment time modified' => ['role_assignment:timemodified', [
-69
View File
@@ -1,69 +0,0 @@
<?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/>.
/**
* Adds settings links to admin tree.
*
* AI gets top billing in general because it's the future.
*
* @package core_admin
* @copyright 2024 Matt Porritt <matt.porritt@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
if ($hassiteconfig) {
// Add settings page for AI provider settings.
$providers = new admin_settingpage('aiprovider', new lang_string('aiproviders', 'ai'));
$providers->add(new admin_setting_heading('availableproviders',
get_string('availableproviders', 'core_ai'),
get_string('availableproviders_desc', 'core_ai')));
$providers->add(new \core_admin\admin\admin_setting_plugin_manager(
'aiprovider',
\core_ai\table\aiprovider_management_table::class,
'manageaiproviders',
new lang_string('manageaiproviders', 'core_ai'),
));
$ADMIN->add('ai', $providers);
// Load settings for all AI providers.
$plugins = core_plugin_manager::instance()->get_plugins_of_type('aiprovider');
foreach ($plugins as $plugin) {
/** @var \core\plugininfo\aiprovider $plugin */
$plugin->load_settings($ADMIN, 'ai', $hassiteconfig);
}
// Add settings page for AI placement settings.
$placements = new admin_settingpage('aiplacement', new lang_string('aiplacements', 'ai'));
$placements->add(new admin_setting_heading('availableplacements',
get_string('availableplacements', 'core_ai'),
get_string('availableplacements_desc', 'core_ai')));
$placements->add(new \core_admin\admin\admin_setting_plugin_manager(
'aiplacement',
\core_ai\table\aiplacement_management_table::class,
'manageaiplacements',
new lang_string('manageaiplacements', 'core_ai'),
));
$ADMIN->add('ai', $placements);
// Load settings for all placements.
$plugins = core_plugin_manager::instance()->get_plugins_of_type('aiplacement');
foreach ($plugins as $plugin) {
/** @var \core\plugininfo\aiprovider $plugin */
$plugin->load_settings($ADMIN, 'ai', $hassiteconfig);
}
}
-6
View File
@@ -152,12 +152,6 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) { // sp
}
$choices[HOMEPAGE_MYCOURSES] = new lang_string('mycourses', 'admin');
$choices[HOMEPAGE_USER] = new lang_string('userpreference', 'admin');
// Allow hook callbacks to extend options.
$hook = new \core_user\hook\extend_default_homepage();
\core\di::get(\core\hook\manager::class)->dispatch($hook);
$choices += $hook->get_options();
$temp->add(new admin_setting_configselect('defaulthomepage', new lang_string('defaulthomepage', 'admin'),
new lang_string('configdefaulthomepage', 'admin'), get_default_home_page(), $choices));
if (!isset($CFG->enabledashboard) || $CFG->enabledashboard) {
+1 -16
View File
@@ -65,21 +65,6 @@ if (($hassiteconfig || has_any_capability(array(
new lang_string('allowexternalbackpack', 'badges'),
new lang_string('allowexternalbackpack_desc', 'badges'), 1));
$defaultcanvasregions = [
'Australia|https://au.badgr.io|https://api.au.badgr.io/v2',
'Canada|https://ca.badgr.io|https://api.ca.badgr.io/v2',
'Europe|https://eu.badgr.io|https://api.eu.badgr.io/v2',
'Singapore|https://sg.badgr.io|https://api.sg.badgr.io/v2',
'United States|https://badgr.io|https://api.badgr.io/v2',
];
$globalsettings->add(new admin_setting_configtextarea(
'badges_canvasregions',
new lang_string('canvasregions', 'badges'),
new lang_string('canvasregions_desc', 'badges'),
implode("\n", $defaultcanvasregions),
PARAM_RAW,
));
$ADMIN->add('badges', $globalsettings);
$ADMIN->add('badges',
@@ -102,7 +87,7 @@ if (($hassiteconfig || has_any_capability(array(
$ADMIN->add('badges',
new admin_externalpage('newbadge',
new lang_string('newbadge', 'badges'),
new moodle_url('/badges/edit.php', ['action' => 'new']),
new moodle_url('/badges/newbadge.php', array('type' => BADGE_TYPE_SITE)),
array('moodle/badges:createbadge'), empty($CFG->enablebadges)
)
);
+1 -30
View File
@@ -420,12 +420,6 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
new lang_string('generalgroups', 'backup'), new lang_string('configgeneralgroups', 'backup'),
array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_general_competencies', new lang_string('generalcompetencies','backup'), new lang_string('configgeneralcompetencies','backup'), array('value'=>1, 'locked'=>0)));
$temp->add(new admin_setting_configcheckbox_with_lock(
'backup/backup_general_customfield',
new lang_string('generalcustomfield', 'backup'),
new lang_string('configgeneralcustomfield', 'backup'),
['value' => 1, 'locked' => 0],
));
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_general_contentbankcontent',
new lang_string('generalcontentbankcontent', 'backup'),
new lang_string('configgeneralcontentbankcontent', 'backup'),
@@ -437,6 +431,7 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
['value' => 1, 'locked' => 0])
);
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_general_legacyfiles',
new lang_string('generallegacyfiles', 'backup'),
new lang_string('configlegacyfiles', 'backup'), array('value' => 1, 'locked' => 0)));
@@ -459,24 +454,12 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_activities', new lang_string('generalactivities','backup'), new lang_string('configgeneralactivities','backup'), array('value'=>1, 'locked'=>0)));
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_blocks', new lang_string('generalblocks','backup'), new lang_string('configgeneralblocks','backup'), array('value'=>1, 'locked'=>0)));
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_filters', new lang_string('generalfilters','backup'), new lang_string('configgeneralfilters','backup'), array('value'=>1, 'locked'=>0)));
$temp->add(new admin_setting_configcheckbox_with_lock(
'backup/backup_import_badges',
new lang_string('generalbadges', 'backup'),
new lang_string('configgeneralbadges', 'backup'),
['value' => 0, 'locked' => 0],
));
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_calendarevents', new lang_string('generalcalendarevents','backup'), new lang_string('configgeneralcalendarevents','backup'), array('value'=>1, 'locked'=>0)));
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_questionbank', new lang_string('generalquestionbank','backup'), new lang_string('configgeneralquestionbank','backup'), array('value'=>1, 'locked'=>0)));
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_groups',
new lang_string('generalgroups', 'backup'), new lang_string('configgeneralgroups', 'backup'),
array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_competencies', new lang_string('generalcompetencies','backup'), new lang_string('configgeneralcompetencies','backup'), array('value'=>1, 'locked'=>0)));
$temp->add(new admin_setting_configcheckbox_with_lock(
'backup/backup_import_customfield',
new lang_string('generalcustomfield', 'backup'),
new lang_string('configgeneralcustomfield', 'backup'),
['value' => 1, 'locked' => 0],
));
$temp->add(new admin_setting_configcheckbox_with_lock(
'backup/backup_import_contentbankcontent',
new lang_string('generalcontentbankcontent', 'backup'),
@@ -606,12 +589,6 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_groups', new lang_string('generalgroups', 'backup'),
new lang_string('configgeneralgroups', 'backup'), 1));
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_competencies', new lang_string('generalcompetencies','backup'), new lang_string('configgeneralcompetencies','backup'), 1));
$temp->add(new admin_setting_configcheckbox(
'backup/backup_auto_customfield',
new lang_string('generalcustomfield', 'backup'),
new lang_string('configgeneralcustomfield', 'backup'),
1,
));
$temp->add(new admin_setting_configcheckbox(
'backup/backup_auto_contentbankcontent',
new lang_string('generalcontentbankcontent', 'backup'),
@@ -691,12 +668,6 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_competencies',
new lang_string('generalcompetencies', 'backup'),
new lang_string('configrestorecompetencies', 'backup'), array('value' => 1, 'locked' => 0)));
$temp->add(new admin_setting_configcheckbox_with_lock(
'restore/restore_general_customfield',
new lang_string('generalcustomfield', 'backup'),
new lang_string('configrestorecustomfield', 'backup'),
['value' => 1, 'locked' => 0],
));
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_contentbankcontent',
new lang_string('generalcontentbankcontent', 'backup'),
new lang_string('configrestorecontentbankcontent', 'backup'), array('value' => 1, 'locked' => 0)));
+1 -10
View File
@@ -109,16 +109,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
// Add the 'profiling' page to admin block.
$ADMIN->add('development', $temp);
$ADMIN->add(
parentname: 'development',
something: new admin_externalpage(
name: 'swaggerui',
visiblename: new lang_string('swaggerui', 'admin'),
url: "$CFG->wwwroot/admin/swaggerui.php",
),
);
// Web service test clients DO NOT COMMIT : THE EXTERNAL WEB PAGE IS NOT AN ADMIN PAGE !!!!!
// Web service test clients DO NOT COMMIT : THE EXTERNAL WEB PAGE IS NOT AN ADMIN PAGE !!!!!
$ADMIN->add('development', new admin_externalpage('testclient', new lang_string('testclient', 'webservice'), "$CFG->wwwroot/$CFG->admin/webservice/testclient.php"));
-44
View File
@@ -1,44 +0,0 @@
<?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/>.
/**
* Configure the settings for file redaction service.
*
* @package core_admin
* @copyright Meirza <meirza.arson@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
if ($hassiteconfig) {
if (!$ADMIN->locate('file_redactor')) {
$ADMIN->add('server', new admin_category('file_redactor', get_string('redactor', 'core_files')));
}
$manager = \core\di::get(\core_files\redactor\manager::class);
// Get settings from each service.
foreach ($manager->get_service_classnames() as $servicename => $service) {
$servicesettings = new admin_settingpage(
$servicename,
new lang_string("redactor:{$servicename}", 'core_files'),
);
$service::add_settings($servicesettings);
$ADMIN->add('file_redactor', $servicesettings);
}
}
+3 -30
View File
@@ -39,45 +39,18 @@ if ($hassiteconfig) {
$temp->add(new admin_setting_configtext('defaultcity', new lang_string('defaultcity', 'core_admin'),
new lang_string('defaultcity_help', 'core_admin'), ''));
$temp->add(new admin_setting_countrycodes('allcountrycodes', new lang_string('allcountrycodes', 'core_admin'),
new lang_string('configallcountrycodes', 'core_admin')));
$temp->add(new admin_setting_heading('iplookup', new lang_string('iplookup', 'core_admin'),
new lang_string('iplookupinfo', 'core_admin')));
$temp->add(new admin_setting_configfile('geoip2file', new lang_string('geoipfile', 'core_admin'),
new lang_string('configgeoipfile', 'core_admin', $CFG->dataroot . '/geoip/'),
$CFG->dataroot . '/geoip/GeoIP-City.mmdb'));
$temp->add(new admin_setting_configselect('geoipdbedition',
new lang_string('geoipdbedition', 'core_admin'),
new lang_string('geoipdbedition_desc', 'core_admin'), 'GeoLite2-City',
['GeoLite2-City' => 'GeoLite2-City', 'GeoIP2-City' => 'GeoIP2-City']));
$temp->add(new admin_setting_configtext('geoipmaxmindaccid',
new lang_string('geoipmaxmindaccid', 'core_admin'),
new lang_string('geoipmaxmindaccid_desc', 'core_admin'),
'',
PARAM_TEXT,
));
$temp->add(new admin_setting_configtext('geoipmaxmindlicensekey',
new lang_string('geoipmaxmindlicensekey', 'core_admin'),
new lang_string('geoipmaxmindlicensekey_desc', 'core_admin'),
'',
PARAM_TEXT,
));
$CFG->dataroot . '/geoip/GeoLite2-City.mmdb'));
$temp->add(new admin_setting_configtext('googlemapkey3', new lang_string('googlemapkey3', 'core_admin'),
new lang_string('googlemapkey3_help', 'core_admin'), '', PARAM_RAW, 60));
$temp->add(new admin_setting_configtext(
'geopluginapikey',
new lang_string('geopluginapikey', 'core_admin'),
new lang_string('geopluginapikey_desc', 'core_admin'),
'',
PARAM_TEXT,
));
$temp->add(new admin_setting_countrycodes('allcountrycodes', new lang_string('allcountrycodes', 'core_admin'),
new lang_string('configallcountrycodes', 'core_admin')));
}
$ADMIN->add('location', $temp);
+1 -52
View File
@@ -102,8 +102,6 @@ if ($hassiteconfig) {
new lang_string('limitconcurrentlogins_desc', 'core_auth'), 0, $options));
$temp->add(new admin_setting_configtext('alternateloginurl', new lang_string('alternateloginurl', 'auth'),
new lang_string('alternatelogin', 'auth', htmlspecialchars(get_login_url(), ENT_COMPAT)), ''));
$temp->add(new admin_setting_configcheckbox('showloginform', new lang_string('showloginform', 'core_auth'),
new lang_string('showloginform_desc', 'core_auth'), 1));
$temp->add(new admin_setting_configtext('forgottenpasswordurl', new lang_string('forgottenpasswordurl', 'auth'),
new lang_string('forgottenpassword', 'auth'), '', PARAM_URL));
$temp->add(new admin_setting_confightmleditor('auth_instructions', new lang_string('instructions', 'auth'),
@@ -287,7 +285,7 @@ if ($hassiteconfig) {
$items[] = new admin_setting_configcheckbox('filtermatchonepertext', new lang_string('filtermatchonepertext', 'admin'), new lang_string('configfiltermatchonepertext', 'admin'), 0);
$items[] = new admin_setting_configcheckbox('filternavigationwithsystemcontext',
new lang_string('filternavigationwithsystemcontext', 'admin'),
new lang_string('configfilternavigationwithsystemcontext', 'admin'), 1);
new lang_string('configfilternavigationwithsystemcontext', 'admin'), 0);
foreach ($items as $item) {
$item->set_updatedcallback('reset_text_filters_cache');
$temp->add($item);
@@ -544,18 +542,6 @@ if ($hassiteconfig || has_capability('moodle/question:config', $systemcontext))
$settings->add(new admin_setting_configselect('question_preview/history',
get_string('responsehistory', 'question'), '', 0, $hiddenofvisible));
// Question editing settings.
$settings = new admin_settingpage('qediting',
get_string('questionediting', 'question'),
'moodle/question:config');
$ADMIN->add('qtypesettings', $settings);
$settings->add(new admin_setting_heading('qediting_options',
'', get_string('questionediting_desc', 'question')));
$settings->add(new admin_setting_configcheckbox('questiondefaultssave',
get_string('questiondefaultssave', 'question'), get_string('questiondefaultssave_desc', 'question'), 1));
// Settings for particular question types.
$plugins = core_plugin_manager::instance()->get_plugins_of_type('qtype');
core_collator::asort_objects_by_property($plugins, 'displayname');
@@ -836,49 +822,12 @@ if ($hassiteconfig && core_communication\api::is_available()) {
}
}
// SMS plugins.
if ($hassiteconfig) {
$ADMIN->add(
'modules',
new admin_category(
'sms',
new lang_string('sms', 'core_sms'),
),
);
$ADMIN->add(
'sms',
new admin_externalpage(
'smsgateway',
new lang_string('manage_sms_gateways', 'core_sms'),
$CFG->wwwroot . '/sms/sms_gateways.php',
),
);
foreach (core_component::get_plugin_list('smsgateway') as $plugin => $path) {
$settingspath = $path . '/settings.php';
if (file_exists($settingspath)) {
$settings = new admin_settingpage(
'smsgateway_' . $plugin . '_settings',
new lang_string('pluginname', 'smsgateway_' . $plugin),
'moodle/site:config',
);
include($settingspath);
$ADMIN->add('smsgateway', $settings);
}
}
}
// Content bank content types.
if ($hassiteconfig) {
$ADMIN->add('modules', new admin_category('contentbanksettings', new lang_string('contentbank')));
$temp = new admin_settingpage('managecontentbanktypes', new lang_string('managecontentbanktypes'));
$temp->add(new admin_setting_managecontentbankcontenttypes());
$ADMIN->add('contentbanksettings', $temp);
$ADMIN->add('contentbanksettings',
new admin_externalpage('contentbank', new lang_string('contentbankcustomfields', 'contentbank'),
$CFG->wwwroot . '/contentbank/customfield.php',
'moodle/contentbank:configurecustomfields'
)
);
$plugins = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
foreach ($plugins as $plugin) {
/** @var \core\plugininfo\contentbank $plugin */
+2 -4
View File
@@ -126,7 +126,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
$temp->add($adminsetting);
$temp->add(new admin_setting_configcheckbox('passwordchangelogout',
new lang_string('passwordchangelogout', 'admin'),
new lang_string('passwordchangelogout_desc', 'admin'), 1));
new lang_string('passwordchangelogout_desc', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('passwordchangetokendeletion',
new lang_string('passwordchangetokendeletion', 'admin'),
@@ -152,9 +152,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
$sensiblesettingsdefault .= 'server_password@@search_solr, ssl_keypassword@@search_solr, ';
$sensiblesettingsdefault .= 'alternateserver_password@@search_solr, alternatessl_keypassword@@search_solr, ';
$sensiblesettingsdefault .= 'test_password@@cachestore_redis, password@@mlbackend_python, ';
$sensiblesettingsdefault .= 'badges_badgesalt@@none, calendar_exportsalt@@none, ';
$sensiblesettingsdefault .= 'bigbluebuttonbn_shared_secret@@none, apikey@@tiny_premium, ';
$sensiblesettingsdefault .= 'matrixaccesstoken@@communication_matrix, api_secret@@factor_sms';
$sensiblesettingsdefault .= 'badges_badgesalt@@none, calendar_exportsalt@@none';
$temp->add(new admin_setting_configtextarea('adminpresets/sensiblesettings',
get_string('sensiblesettings', 'core_adminpresets'),
get_string('sensiblesettingstext', 'core_adminpresets'),
+2 -10
View File
@@ -181,16 +181,8 @@ if ($hassiteconfig) {
new lang_string('configproxyuser', 'admin'), ''));
$temp->add(new admin_setting_configpasswordunmask('proxypassword', new lang_string('proxypassword', 'admin'),
new lang_string('configproxypassword', 'admin'), ''));
$setting = new admin_setting_configtext('proxybypass', new lang_string('proxybypass', 'admin'),
new lang_string('configproxybypass', 'admin'), 'localhost,127.0.0.1');
$setting->set_updatedcallback(function() {
// Normalize $CFG->proxybypass value.
$normalizedvalue = \core\ip_utils::normalize_internet_address_list(get_config('core', 'proxybypass'));
set_config('proxybypass', $normalizedvalue);
});
$temp->add($setting);
$temp->add(new admin_setting_configtext('proxybypass', new lang_string('proxybypass', 'admin'),
new lang_string('configproxybypass', 'admin'), 'localhost, 127.0.0.1'));
$temp->add(new admin_setting_configcheckbox('proxylogunsafe', new lang_string('proxylogunsafe', 'admin'),
new lang_string('configproxylogunsafe_help', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('proxyfixunsafe', new lang_string('proxyfixunsafe', 'admin'),
-1
View File
@@ -25,7 +25,6 @@ if ($hassiteconfig) {
$ADMIN->add('root', new admin_category('users', new lang_string('users','admin')));
$ADMIN->add('root', new admin_category('courses', new lang_string('courses','admin')));
$ADMIN->add('root', new admin_category('grades', new lang_string('grades')));
$ADMIN->add('root', new admin_category('ai', new lang_string('ai', 'ai')));
$ADMIN->add('root', new admin_category('analytics', new lang_string('analytics', 'analytics')));
$ADMIN->add('root', new admin_category('competencies', new lang_string('competencies', 'core_competency')));
$ADMIN->add('root', new admin_category('badges', new lang_string('badges'), empty($CFG->enablebadges)));
-87
View File
@@ -1,87 +0,0 @@
<?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/>.
/**
* Swagger UI for Moodle
*
* @package core_admin
* @copyright Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require('../config.php');
require_once($CFG->libdir . '/adminlib.php');
$swaggerversion = '5.17.14';
$PAGE->set_url('/admin/swaggerui.php');
admin_externalpage_setup('swaggerui');
$PAGE->requires->css(new moodle_url("https://unpkg.com/swagger-ui-dist@{$swaggerversion}/swagger-ui.css"));
echo $OUTPUT->header();
// These have to be manually added for now because they must be made cross-origin. The `js` method does not yet support this.
echo html_writer::tag(
tagname: 'script',
contents: '',
attributes: [
'src' => new moodle_url("https://unpkg.com/swagger-ui-dist@{$swaggerversion}/swagger-ui-bundle.js"),
'crossorigin' => 'crossorigin',
],
);
echo html_writer::tag(
tagname: 'script',
contents: '',
attributes: [
'src' => new moodle_url("https://unpkg.com/swagger-ui-plugin-hierarchical-tags"),
'crossorigin' => 'crossorigin',
],
);
$openapipath = moodle_url::routed_path('/api/rest/v2/openapi.json')->out();
$swaggerinit = <<<JS
window.ui = SwaggerUIBundle({
url: "{$openapipath}",
dom_id: '#swagger-ui',
// Enable the "Try it out" button by default.
tryItOutEnabled: true,
// Show snippets different OS options.
requestSnippetsEnabled: true,
deepLinking: true,
plugins: [
HierarchicalTagsPlugin,
],
hierarchicalTagSeparator: /[_]/
});
JS;
$PAGE->requires->js_init_code(
jscode: $swaggerinit,
ondomready: true,
);
echo html_writer::div('', '', [
'id' => 'swagger-ui',
]);
echo $OUTPUT->footer();
+1 -34
View File
@@ -24,7 +24,6 @@
require_once(__DIR__ . '/../config.php');
require_once("{$CFG->libdir}/adminlib.php");
require_once("tool/task/lib.php");
use core_admin\reportbuilder\local\systemreports\task_logs;
use core_reportbuilder\system_report_factory;
@@ -50,41 +49,9 @@ if (null !== $logid) {
if ($download) {
$filename = str_replace('\\', '_', $log->classname) . "-{$log->id}.log";
header("Content-Disposition: attachment; filename=\"{$filename}\"");
readstring_accel($log->output, 'text/plain');
exit;
}
try {
$class = new $log->classname;
$title = $class->get_name();
} catch (Exception $e) {
$title = $log->classname;
}
$title .= " ($log->id)";
$PAGE->navbar->add($title, '');
echo $OUTPUT->header();
echo html_writer::start_tag('pre', ['class' => 'task-output', 'style' => 'min-height: 24lh']);
echo tool_task_mtrace_wrapper($log->output);
echo html_writer::end_tag('pre');
echo $OUTPUT->action_link(
new moodle_url('/admin/tasklogs.php'),
$strheading,
null,
null,
new pix_icon('i/log', ''),
);
echo ' ';
echo $OUTPUT->action_link(
new moodle_url('/admin/tasklogs.php', ['logid' => $log->id, 'download' => true]),
new lang_string('download'),
null,
null,
new pix_icon('t/download', ''),
);
echo $OUTPUT->footer();
readstring_accel($log->output, 'text/plain');
exit;
}
+1 -1
View File
@@ -48,7 +48,7 @@
Setting.
}}
<div class="form-item row" id="{{id}}">
<div class="form-label col-sm-3 text-sm-end">
<div class="form-label col-sm-3 text-sm-right">
{{#customcontrol}}
<p {{#labelfor}}id="{{labelfor}}_label"{{/labelfor}}>
{{{title}}}
@@ -38,6 +38,6 @@
}
}}
<div class="form-checkbox defaultsnext">
{{^readonly}}<input type="hidden" name="{{name}}" value="{{no}}">{{/readonly}}
<input type="hidden" name="{{name}}" value="{{no}}">
<input {{#readonly}}disabled{{/readonly}} type="checkbox" name="{{name}}" value="{{value}}" id="{{id}}" {{#checked}}checked{{/checked}}>
</div>
@@ -35,7 +35,7 @@
}
}}
<div class="form-multicheckbox">
{{^readonly}}<input type="hidden" name="{{name}}[xxxxx]" value="1">{{/readonly}}
<input type="hidden" name="{{name}}[xxxxx]" value="1">
{{#hasoptions}}
<ul>
{{#options}}
@@ -40,7 +40,7 @@
Setting configmultiselect.
}}
<div class="form-select">
{{^readonly}}<input type="hidden" name="{{name}}[xxxxx]" value="1">{{/readonly}}
<input type="hidden" name="{{name}}[xxxxx]" value="1">
<select {{#readonly}}disabled{{/readonly}} id="{{id}}" name="{{name}}[]" size="{{size}}" class="form-control" multiple>
{{#options}}
<option value="{{value}}" {{#selected}}selected{{/selected}}>{{name}}</option>
@@ -59,7 +59,7 @@
Setting configmultiselect with optgroup support.
}}
<div class="form-select">
{{^readonly}}<input type="hidden" name="{{name}}[xxxxx]" value="1">{{/readonly}}
<input type="hidden" name="{{name}}[xxxxx]" value="1">
<select {{#readonly}}disabled{{/readonly}} id="{{id}}" name="{{name}}[]" size="{{size}}" class="form-control" multiple>
{{#options}}
<option value="{{value}}" {{#selected}}selected{{/selected}}>{{name}}</option>
@@ -1,59 +0,0 @@
{{!
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/>.
}}
{{!
@template core_admin/setting_configtoggle
Template for toggle element in admin settings.
Context variables required for this template:
* id - element id
* checked - bool, true if the toggle is enabled
* name - form element name
* value - form for name field
* action - action to be performed when the toggle is clicked
* plugin - plugin name
* state - state of the toggle
* title - title of the toggle tooltip
* label - label of the toggle
* labelclasses - classes for the label (sr-only for screen readers)
Example context (json):
{
"id": "reality-toggle-3",
"checked": true,
"dataattributes": [{
"name": "id",
"value": "toggle-reality",
"action": "togglestate",
"plugin": "tool_reality",
"state": 1
}],
"title": "Title example",
"label": "Enable/disable reality",
"labelclasses": "sr-only"
}
}}
<div id="container-{{id}}"
data-toggle-method="{{dataattributes.toggle-method}}"
data-action="{{dataattributes.action}}"
data-plugin="{{dataattributes.plugin}}"
data-state="{{dataattributes.state}}"
data-value="{{dataattributes.value}}"
>
{{> core/toggle }}
</div>
+1 -1
View File
@@ -35,7 +35,7 @@
Setting description.
}}
<div class="form-item row">
<div class="form-label col-sm-3 text-sm-end">
<div class="form-label col-sm-3 text-sm-right">
<label>
{{{title}}}
</label>
@@ -1,33 +0,0 @@
{{!
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/>.
}}
{{!
@template core_admin/setting_savebutton
Admin save button settings template.
Example context (json):
{
}
}}
{{!
Settings.
}}
<div class="row">
<div class="offset-sm-3 col-sm-3">
<button type="submit" class="btn btn-primary">{{#str}}savechanges, admin{{/str}}</button>
</div>
</div>
-39
View File
@@ -1,39 +0,0 @@
{{!
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/>.
}}
{{!
@template core_admin/table/namedesc
Admin setting template to display an name and description
in a single table cell.
Context variables required for this template:
* name - Name of the table setting
* description - Description of the setting
Example context (json):
{
"name": "Fancy Setting",
"description": "Text that describes this setting well"
}
}}
{{!
Name and description.
}}
<div>
<div><strong>{{name}}</strong></div>
<div>{{description}}</div>
</div>
@@ -58,7 +58,7 @@
<a
href="{{reporturl}}"
id="theme-usage-report-{{choose}}"
class="btn btn-link p-0 ms-2"
class="btn btn-link p-0 ml-2"
title="{{#str}}themeusagereportname, admin, {{name}}{{/str}}">
<i class="icon fa fa-area-chart m-0" aria-hidden="true"></i>
<span class="sr-only">{{#str}}themeusagereportname, admin, {{name}}{{/str}}</span>
@@ -68,7 +68,7 @@
<a
href="{{settingsurl}}"
id="theme-settings-{{choose}}"
class="btn btn-link p-0 ms-2"
class="btn btn-link p-0 ml-2"
title="{{#str}}themeeditsettingsname, admin, {{name}}{{/str}}">
<i class="icon fa fa-cog m-0" aria-hidden="true"></i>
<span class="sr-only">{{#str}}themeeditsettingsname, admin, {{name}}{{/str}}</span>
@@ -36,7 +36,7 @@
<div class="lead">{{tokenname}}</div>
<div class="d-flex justify-content-start align-middle">
<div class="lead text-break pt-1" id="copytoclipboardtoken">{{token}}</div>
<button class="btn btn-primary ms-2" data-action="copytoclipboard" data-clipboard-target="#copytoclipboardtoken" data-clipboard-success-message="{{#str}}tokencopied, webservice{{/str}}">
<button class="btn btn-primary ml-2" data-action="copytoclipboard" data-clipboard-target="#copytoclipboardtoken" data-clipboard-success-message="{{#str}}tokencopied, webservice{{/str}}">
{{#pix}}t/copy, core {{/pix}}{{#str}}copytoclipboard{{/str}}</button>
</div>
</div>
-4
View File
@@ -81,8 +81,6 @@ class behat_admin extends behat_base {
$this->execute('behat_forms::i_set_the_field_with_xpath_to', [$fieldxpath, $value]);
$this->execute("behat_general::i_click_on", [get_string('savechanges'), 'button']);
// Wait for the page to be redirected.
$this->execute("behat_general::i_wait_to_be_redirected");
}
}
@@ -92,8 +90,6 @@ class behat_admin extends behat_base {
* @Given /^the following config values are set as admin:$/
* @param TableNode $table
*/
#[\core\attribute\example('And the following config values are set as admin:
| sendcoursewelcomemessage | 0 | enrol_manual |')]
public function the_following_config_values_are_set_as_admin(TableNode $table) {
if (!$data = $table->getRowsHash()) {
+7 -7
View File
@@ -18,7 +18,7 @@ Feature: An administrator can browse user accounts
When I navigate to "Users > Accounts > Browse list of users" in site administration
# Name field always present, email field is default for showidentity.
Then the following should exist in the "reportbuilder-table" table:
| First name | Email address |
| First name / Last name | Email address |
| User One | one@example.com |
| User Two | two@example.com |
# Should not see other identity fields or non-default name fields.
@@ -34,7 +34,7 @@ Feature: An administrator can browse user accounts
| alternativefullnameformat | firstnamephonetic lastname |
When I navigate to "Users > Accounts > Browse list of users" in site administration
Then the following should exist in the "reportbuilder-table" table:
| First name - phonetic | Email address |
| First name - phonetic / Last name | Email address |
| Yewzer One | one@example.com |
| Yoozare Two | two@example.com |
@@ -43,7 +43,7 @@ Feature: An administrator can browse user accounts
| showuseridentity | department,profile_field_frog |
When I navigate to "Users > Accounts > Browse list of users" in site administration
Then the following should exist in the "reportbuilder-table" table:
| First name | Favourite frog | Department |
| First name / Last name | Favourite frog | Department |
| User One | Kermit | Attack |
| User Two | Tree | Defence |
And I should not see "Email address" in the "table" "css_element"
@@ -79,7 +79,7 @@ Feature: An administrator can browse user accounts
Scenario: Delete a user account
Given I navigate to "Users > Accounts > Browse list of users" in site administration
And I press "Delete" action in the "User One" report row
And I should see "Are you sure you want to delete user User One" in the "Delete user" "dialogue"
And I should see "Are you absolutely sure you want to completely delete the user User One" in the "Delete user" "dialogue"
And I click on "Delete" "button" in the "Delete user" "dialogue"
Then I should see "Deleted user User One"
And I should not see "User One" in the "reportbuilder-table" "table"
@@ -103,7 +103,7 @@ Feature: An administrator can browse user accounts
| user3 | User | Three | three@example.com | Glass |
And I navigate to "Users > Accounts > Browse list of users" in site administration
Then the following should exist in the "reportbuilder-table" table:
| First name | Email address |
| First name / Last name | Email address |
| User One | one@example.com |
| User Two | two@example.com |
| User Three | three@example.com |
@@ -137,7 +137,7 @@ Feature: An administrator can browse user accounts
| user2 | C1 | student |
And I navigate to "Users > Accounts > Browse list of users" in site administration
Then the following should exist in the "reportbuilder-table" table:
| First name | Email address |
| First name / Last name | Email address |
| User One | one@example.com |
| User Two | two@example.com |
And I click on "Filters" "button"
@@ -166,7 +166,7 @@ Feature: An administrator can browse user accounts
| user1 | coursecreator | system | |
And I navigate to "Users > Accounts > Browse list of users" in site administration
Then the following should exist in the "reportbuilder-table" table:
| First name | Email address |
| First name / Last name | Email address |
| User One | one@example.com |
| User Two | two@example.com |
| User Three | three@example.com |
+20 -8
View File
@@ -8,14 +8,26 @@ Feature: An administrator can manage Block plugins
Scenario: An administrator can control the enabled state of block plugins using JavaScript
Given I am logged in as "admin"
And I navigate to "Plugins > Blocks > Manage blocks" in site administration
When I toggle the "Disable Latest badges" admin switch "off"
And I should see "Latest badges disabled."
And I reload the page
Then I should see "Enable Latest badges"
And I toggle the "Enable Latest badges" admin switch "on"
And I should see "Latest badges enabled."
And I reload the page
Then I should see "Disable Latest badges"
When I click on "Disable Latest badges" "link"
Then I should see "Latest badges disabled."
And "Disable Latest badges" "link" should not exist
But "Enable Latest badges" "link" should exist
When I click on "Enable Latest badges" "link"
Then I should see "Latest badges enabled."
And "Enable Latest badges" "link" should not exist
But "Disable Latest badges" "link" should exist
Scenario: An administrator can control the enabled state of block plugins without JavaScript
Given I am logged in as "admin"
And I navigate to "Plugins > Blocks > Manage blocks" in site administration
When I click on "Disable Latest badges" "link"
Then I should see "Latest badges disabled."
And "Disable Latest badges" "link" should not exist
But "Enable Latest badges" "link" should exist
When I click on "Enable Latest badges" "link"
Then I should see "Latest badges enabled."
And "Enable Latest badges" "link" should not exist
But "Disable Latest badges" "link" should exist
@javascript
Scenario: An administrator can control the protected state of block plugins using JavaScript
+20 -9
View File
@@ -6,14 +6,25 @@ Feature: An administrator can manage Media plugins
@javascript
Scenario: An administrator can control the enabled state of media plugins using JavaScript
The state change should be reflected in the UI and persist across page reloads
Given I am logged in as "admin"
And I navigate to "Plugins > Media players > Manage media players" in site administration
When I toggle the "Disable YouTube" admin switch "off"
And I should see "YouTube disabled."
And I reload the page
Then I should see "Enable YouTube"
And I toggle the "Enable YouTube" admin switch "on"
And I should see "YouTube enabled."
And I reload the page
Then I should see "Disable YouTube"
When I click on "Disable YouTube" "link"
Then I should see "YouTube disabled."
And "Disable YouTube" "link" should not exist
But "Enable YouTube" "link" should exist
When I click on "Enable YouTube" "link"
Then I should see "YouTube enabled."
And "Enable YouTube" "link" should not exist
But "Disable YouTube" "link" should exist
Scenario: An administrator can control the enabled state of media plugins without JavaScript
Given I am logged in as "admin"
And I navigate to "Plugins > Media players > Manage media players" in site administration
When I click on "Disable YouTube" "link"
Then I should see "YouTube disabled."
And "Disable YouTube" "link" should not exist
But "Enable YouTube" "link" should exist
When I click on "Enable YouTube" "link"
Then I should see "YouTube enabled."
And "Enable YouTube" "link" should not exist
But "Disable YouTube" "link" should exist
+103
View File
@@ -0,0 +1,103 @@
@core @core_admin
Feature: Manage external services tokens
In order to manage external service usage
As an admin
I need to be able to create, filter and delete tokens
Background:
Given the following "users" exist:
| username | password | firstname | lastname |
| user1 | user1 | Firstname1 | Lastname1 |
| user2 | user2 | Firstname2 | Lastname2 |
| user3 | user3 | Firstname3 | Lastname3 |
| user4 | user4 | Firstname4 | Lastname4 |
And I change window size to "small"
@javascript
Scenario: Add a token to user identified by name and then delete that token
Given I log in as "admin"
And I am on site homepage
And I navigate to "Server > Web services > Manage tokens" in site administration
And I press "Create token"
And I set the field "Name" to "Webservice1"
And I set the field "User" to "Firstname1 Lastname1"
And I set the field "Service" to "Moodle mobile web service"
And I set the field "IP restriction" to "127.0.0.1"
When I press "Save changes"
Then the following should exist in the "generaltable" table:
| Name | First name | Service | IP restriction | Last access |
| Webservice1 | Firstname1 Lastname1 | Moodle mobile web service | 127.0.0.1 | Never |
# Verify the message and the "Copy to clipboard" button.
And I should see "Copy the token now. It won't be shown again once you leave this page."
And "Copy to clipboard" "button" should exist
# New token can only read once.
And I reload the page
And I should not see "Copy the token now. It won't be shown again once you leave this page."
And "Copy to clipboard" "button" should not exist
# Delete token.
And I click on "Delete" "link" in the "Webservice1" "table_row"
And I should see "Do you really want to delete this web service token for Firstname1 Lastname1 on the service Moodle mobile web service?"
And I press "Delete"
And "Webservice1" "table_row" should not exist
@javascript @skip_chrome_zerosize
Scenario: Tokens can be filtered by name (case-insensitive), by user and by service
Given the following "core_webservice > Service" exists:
| name | Site information |
| shortname | siteinfo |
| enabled | 1 |
And the following "core_webservice > Service function" exists:
| service | siteinfo |
| functions | core_webservice_get_site_info |
And the following "core_webservice > Tokens" exist:
| user | service | name |
| user2 | siteinfo | WEBservice1 |
| user3 | moodle_mobile_app | webservicE3 |
| user4 | siteinfo | New service2 |
When I log in as "admin"
And I navigate to "Server > Web services > Manage tokens" in site administration
# All created tokens are shown by default.
And "Firstname1 Lastname1" "table_row" should not exist
And I should see "Site information" in the "Firstname2 Lastname2" "table_row"
And I should see "Moodle mobile web service" in the "Firstname3 Lastname3" "table_row"
And I should see "Site information" in the "Firstname4 Lastname4" "table_row"
# Filter tokens by by name (case-insensitive).
And I click on "Tokens filter" "link"
And I set the field "Name" to "webservice"
And I press "Show only matching tokens"
And I should see "Site information" in the "Firstname2 Lastname2" "table_row"
And I should see "Moodle mobile web service" in the "Firstname3 Lastname3" "table_row"
And "Firstname4 Lastname4" "table_row" should not exist
# Reset the filter.
And I press "Show all tokens"
And I should see "Site information" in the "Firstname2 Lastname2" "table_row"
And I should see "Moodle mobile web service" in the "Firstname3 Lastname3" "table_row"
And I should see "Site information" in the "Firstname4 Lastname4" "table_row"
# Filter tokens by user (note we can select the user by the identity field here).
When I click on "Tokens filter" "link"
And I set the field "User" to "user2@example.com"
And I press "Show only matching tokens"
Then "Firstname3 Lastname3" "table_row" should not exist
And "Firstname4 Lastname4" "table_row" should not exist
And I should see "Site information" in the "Firstname2 Lastname2" "table_row"
# Reset the filter.
And I press "Show all tokens"
And I should see "Site information" in the "Firstname2 Lastname2" "table_row"
And I should see "Moodle mobile web service" in the "Firstname3 Lastname3" "table_row"
And I should see "Site information" in the "Firstname4 Lastname4" "table_row"
# Filter tokens by service.
And I click on "Tokens filter" "link"
And I set the field "Service" to "Site information"
And I press "Show only matching tokens"
And I should see "Site information" in the "Firstname2 Lastname2" "table_row"
And I should see "Site information" in the "Firstname4 Lastname4" "table_row"
And "Firstname3 Lastname3" "table_row" should not exist
@@ -1,40 +0,0 @@
@core @core_admin
Feature: Plugins overview page
In order to manage plugin and settings
As an admin
I can access the plugins overview page
Background:
Given I am logged in as "admin"
And I navigate to "Plugins > Plugins overview" in site administration
Scenario: An administrator can view plugins overview page
Then "Plugins overview" "text" should exist
And "Check for available updates" "button" should exist
And "All plugins" "link" should exist
And "Additional plugins" "link" should exist
Scenario: An administrator can access list of activities when activity modules cog icon is pressed
When I click on "Settings" "link" in the "Activity modules" "table_row"
Then "Activities" "text" should exist
And "Manage activities" "text" should exist in the ".breadcrumb" "css_element"
And "Activity modules" "link" should exist in the ".breadcrumb" "css_element"
Scenario: An administrator can access plugin settings
When I click on "Settings" "link" in the "Assignment" "table_row"
Then "Assignment settings" "text" should exist
And "Assignment settings" "text" should exist in the ".breadcrumb" "css_element"
And "Assignment" "link" should exist in the ".breadcrumb" "css_element"
And "Activity modules" "link" should exist in the ".breadcrumb" "css_element"
Scenario: Plugins with dependencies cannot be uninstalled
When I navigate to "Plugins > Plugins overview" in site administration
Then "Uninstall" "link" should not exist in the "Database" "table_row"
And "Required by: filter_data" "text" should exist in the "Database" "table_row"
Scenario: Cancelling plugin uninstall does not uninstall the selected plugin
When I click on "Uninstall" "link" in the "Assignment" "table_row"
Then "Uninstalling Assignment" "text" should exist
And "You are about to uninstall the plugin Assignment. This will completely delete everything in the database associated with this plugin, including its configuration, log records, user files managed by the plugin etc. There is no way back and Moodle itself does not create any recovery backup. Are you SURE you want to continue?" "text" should exist
And I click on "Cancel" "button"
And "Uninstall" "link" should exist in the "Assignment" "table_row"
@@ -1,4 +1,4 @@
@core @core_admin @core_webservice @javascript
@core @core_admin @javascript
Feature: Verify the breadcrumbs in external webservice site administration pages
Whenever I navigate to external webservice page in site administration
As an admin
@@ -1,4 +1,4 @@
@core @core_admin @core_webservice @javascript
@core @core_admin @javascript
Feature: Verify the breadcrumbs in webservice tokens site administration pages
Whenever I navigate to manage tokens page in site administration
As an admin
@@ -18,9 +18,8 @@ Feature: Verify the breadcrumbs in webservice tokens site administration pages
And "Manage tokens" "link" should exist in the ".breadcrumb" "css_element"
And "Web services" "link" should exist in the ".breadcrumb" "css_element"
And I set the field "User" to "John Doe"
And I set the field "Service" to "Moodle mobile web service"
And I press "Save changes"
When I press "Delete" action in the "John Doe" report row
When I click on "Delete" "link"
Then "Delete token" "text" should exist in the ".breadcrumb" "css_element"
And "Manage tokens" "link" should exist in the ".breadcrumb" "css_element"
And "Web services" "link" should exist in the ".breadcrumb" "css_element"
+22
View File
@@ -48,3 +48,25 @@ Feature: View task logs report and use its filters
| operator | shouldornotsee |
| Less than | should not see |
| Greater than | should see |
@javascript
Scenario: Reset task log filters
Given I log in as "admin"
And I change window size to "large"
And I navigate to "Server > Tasks > Task logs" in site administration
When I click on "Filters" "button"
And I set the following fields in the "Result" "core_reportbuilder > Filter" to these values:
| Result operator | Is equal to |
| Result value | Fail |
And I click on "Apply" "button" in the "[data-region='report-filters']" "css_element"
Then I should see "Filters applied"
And I should see "Filters (1)" in the "#dropdownFiltersButton" "css_element"
And I should see "Nothing to display"
And I click on "Reset all" "button" in the "[data-region='report-filters']" "css_element"
And I should see "Filters reset"
And I should not see "Filters (1)" in the "#dropdownFiltersButton" "css_element"
And I should see "Filters" in the "#dropdownFiltersButton" "css_element"
And "[data-region='report-filters']" "css_element" should be visible
And the following fields in the "Result" "core_reportbuilder > Filter" match these values:
| Result operator | Is any value |
And I should not see "Nothing to display"
+2 -2
View File
@@ -31,7 +31,7 @@ require_once($CFG->dirroot . '/webservice/tests/helpers.php');
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class set_block_protection_test extends \externallib_advanced_testcase {
class set_block_protection_test extends \externallib_advanced_testcase {
/**
* Test execute method with no login.
*/
@@ -85,7 +85,7 @@ final class set_block_protection_test extends \externallib_advanced_testcase {
*
* @return array
*/
public static function execute_provider(): array {
public function execute_provider(): array {
return [
[
'block_login',

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