Compare commits

..

201 Commits

Author SHA1 Message Date
Eloy Lafuente (stronk7) 06ede85e1f Moodle 2.0.3 release 2011-05-06 15:09:21 +02:00
Tim Hunt 8ad8b7dba8 MDL-27394 Moodle 2.0 backup and restore may randomise the order of multichoice/numerical/shortanswer answers 2011-05-06 00:18:50 +02:00
Sam Hemelryk 36f0835e72 Merge branch 's9_MDL-27382_dropbox_get_file_fix_20' of github.com:dongsheng/moodle into MOODLE_20_STABLE 2011-05-05 13:26:46 +08:00
Sam Hemelryk 259b9d05f3 Merge branch 's9_MDL-27381_overwrite_dialog_safari_20' of github.com:dongsheng/moodle into MOODLE_20_STABLE 2011-05-05 13:23:22 +08:00
Sam Hemelryk da20cd7b43 Merge branch 's9_MDL-26854_spam_comments_20_2' of git://github.com/dongsheng/moodle into MOODLE_20_STABLE 2011-05-05 11:46:05 +08:00
Dongsheng Cai acb4688d29 MDL-26854 Added comments block lib.php, it contains comments callbacks 2011-05-05 11:25:19 +08:00
Jerome Mouneyrac 471952fe82 MDL-26250 Create a web service function that enrols users to a certain course 2011-05-05 11:00:00 +08:00
Dongsheng Cai 0d738689bd MDL-27381 fixed 'overwrite files' dialog zindex in safari 2011-05-05 10:43:17 +08:00
Sam Hemelryk 7390fdde63 Merge branch 's9_MDL-26854_spam_comments_20_2' of git://github.com/dongsheng/moodle into MOODLE_20_STABLE 2011-05-05 10:42:58 +08:00
Sam Hemelryk 1374c61bb4 Merge branch 'MDL-26838_ratings_bad_data3' of git://github.com/andyjdavis/moodle into MOODLE_20_STABLE 2011-05-05 10:19:54 +08:00
Dongsheng Cai 98d9209a34 MDL-27382 repository dropbox plugin, fixed bug of
file downloading path
2011-05-05 09:57:34 +08:00
Eloy Lafuente (stronk7) 8351ce1bd0 weekly release 2.0.2+ 2011-05-05 00:37:10 +02:00
Eloy Lafuente (stronk7) b911ab236c Merge branch 'MDL-25594-20' of git://github.com/srynot4sale/moodle into MOODLE_20_STABLE 2011-05-04 16:09:44 +02:00
Dongsheng Cai b4730465e6 MDL-26854 COMMENT
1. added pluginname_comment_validate callback to comments api
2. change permission callback defaults to false
3. Tidied up serveal areas of comments and implemented callback to
ignore system permissions for view (credits to Sam Hemelryk)

AMOS BEGIN
    MOV [modulerejectcomment,error],[callbackrejectcomment,error]
AMOS END
2011-05-04 17:59:19 +08:00
Eloy Lafuente (stronk7) 78cc7bee62 Merge branch 'MDL-26788_m20' of git://github.com/rwijaya/moodle into MOODLE_20_STABLE 2011-05-04 11:21:02 +02:00
Dongsheng Cai 5d6eafcf10 MDL-26388 Recent repository plugin should return draft url 2011-05-04 17:03:36 +08:00
Dongsheng Cai 090e19328e MDL-26388 Recent repository plugin should return draft url 2011-05-04 15:48:11 +08:00
Andrew Davis (andyjdavis) 34b5e856b0 MDL-26838 rating: multiple fixes after a review of rating validation code 2011-05-04 15:33:35 +08:00
Sam Hemelryk c44a5d5946 Merge branch 's10_MDL-27330_dropbox_authorization_url_20' of github.com:dongsheng/moodle into MOODLE_20_STABLE 2011-05-04 13:56:59 +08:00
Aaron Barnes b7d7679112 MDL-25594 completion: Remove unused column from query
Which is also a reserved word.
2011-05-04 13:01:27 +12:00
Dongsheng Cai 64a17788aa MDL-27330 repository dropbox plugin
Dropbox changed authorization URL, and added a few error check code
2011-05-03 17:42:57 +08:00
Sam Hemelryk 0baa1eee59 Merged MDL-26388 Fixed whitespace 2011-05-03 16:28:38 +08:00
Sam Hemelryk 9f6eeb4abf Merged MDL-25805 Renamed property and function to be a little more descriptive 2011-05-03 15:43:20 +08:00
Sam Hemelryk 6a48b42ae8 Merge branch 'MDL-25805_MOODLE_20_STABLE' of git://github.com/nebgor/moodle into MOODLE_20_STABLE 2011-05-03 15:42:26 +08:00
Sam Hemelryk 2dd90f136c Merge branch 'MDL-26628' of git://github.com/srynot4sale/moodle into MOODLE_20_STABLE 2011-05-03 11:00:15 +08:00
Sam Hemelryk e6647397b9 Merged MDL-27251 Fixed up whitespace, Fixed broken string, reworded new strings 2011-05-03 10:54:44 +08:00
Aparup Banerjee 07fa10d725 MDL-25840 blog - fixed broken image file image attachment display 2011-05-03 01:14:16 +02:00
Eloy Lafuente (stronk7) 98032d512d Merge branch 'MDL-27293-customlang-timeout_20_STABLE' of git://github.com/mudrd8mz/moodle into MOODLE_20_STABLE 2011-05-02 16:19:40 +02:00
Aparup Banerjee 389235fb00 MDL-25805 Roles propagated ugly hack for parent seeing child activities into navigation lib to display nav node. 2011-05-02 20:21:51 +08:00
Eloy Lafuente (stronk7) 656237dd55 Merge branch 'MDL-26714_20' of git://github.com/timhunt/moodle into MOODLE_20_STABLE 2011-05-02 12:26:22 +02:00
Sam Hemelryk e3cc5b1a7f Merged changes and fixed whitespace issues for MDL-23188 2011-05-02 15:00:54 +08:00
Sam Hemelryk 0742ea86b2 Merge branch 'MDL-26955-themedir-plugindirectory_20_STABLE' of git://github.com/mudrd8mz/moodle into MOODLE_20_STABLE 2011-05-02 14:23:06 +08:00
Sam Hemelryk 41f79b5c07 Merge branch 'MDL-27116-themedir-config_20_STABLE' of git://github.com/mudrd8mz/moodle into MOODLE_20_STABLE 2011-05-02 14:12:45 +08:00
Sam Hemelryk ed85c0acd2 Merge branch 'MDL-27155_20' of git://github.com/timhunt/moodle into MOODLE_20_STABLE 2011-05-02 14:09:26 +08:00
Sam Hemelryk 9c39028179 Merge branch 'MDL-27246-cutomlang-version_20_STABLE' of git://github.com/mudrd8mz/moodle into MOODLE_20_STABLE 2011-05-02 12:14:05 +08:00
Dongsheng Cai 9fb4a6300b MDL-26388, pop up a dialog when attaching a file already being used in draft area. 2011-05-02 10:27:50 +08:00
Tim Hunt 44c9dc6e7b MDL-27340 tablelib.php fix a notice when is false. 2011-05-01 17:37:54 +01:00
Andrew Davis (andyjdavis) 4af7c9d188 MDL-26838 rating: remove logged in users ability to sidestep rating validity checks 2011-05-01 18:44:12 +08:00
Tim Hunt 847dee60ce MDL-27155 qtype_numerical problem with GIFT import. 2011-04-29 14:16:10 +01:00
Rossiani Wijaya e0be8fd6a3 MDL-26788 - grading in lesson containing essays 2011-04-29 18:17:36 +08:00
Tim Hunt 55e03d1233 MDL-26714 qtype_multianswer regression from the previous commit for numerical subquestions. 2011-04-28 23:32:14 +01:00
stabijoh e391f907af MDL-23188 implement custom menu where possible in core themes, 20 stable version 2011-04-27 08:44:53 -04:00
David Mudrak bef867a96c MDL-26955 get_plugin_directory() respects $CFG->themedir
If $CFG->themedir is set (it is not usually) and the given theme is not
found in the standard location, then it is expected to be in the custom
$CFG->themedir directory.

Credit goes to Urs Hunkler for the idea of the patch.
2011-04-27 14:03:53 +02:00
David Mudrak 4207d47742 MDL-27116 theme_config::find_theme_location() respects $CFG->themedir
Credit goes to Jai Gupta for the idea of the patch.
2011-04-27 14:03:10 +02:00
David Mudrak bd7193c7ac MDL-27293 report customlang has extra execution time and displays a progress bar during the checkout
Initial checkout of strings into the translator table may take a long time. The
maximum execution time is increased to one hour and a progress bar is
displayed so the user is informed on what is going on.
2011-04-27 14:01:59 +02:00
David Mudrak 77e4a16c4f MDL-27246 customlang report stores the versions in a char field rather than integer 2011-04-27 14:01:38 +02:00
Petr Skoda 7daba2713e weekly release 2.0.2+ 2011-04-27 12:08:04 +02:00
Aaron Barnes 03b1be3005 MDL-26628 completion report: Fix hard coded strings in Completion report 2011-04-27 15:33:45 +12:00
Aparup Banerjee 5c8511ecf7 MDL-27251 Files API - added timeout re-calculation as an optional argument. added setting for minimum Kbps for large files fetched from internet where the passed in timeout maybe too low.
allowed turning off the http HEAD request timeout calculation with zero (or negative) bitrate

	This was added in to allow servers that have a problem with
	HEAD requests to carry on with the given timeout without re-calculations.
	See PULL-651 for the discussion.

	the optional argument to force recalculation of timeout has been forced within scorm/locallib.php

	timeout re-calculation only increments timeout.
2011-04-27 11:02:21 +08:00
Eloy Lafuente (stronk7) 52435228c5 Merge branch 'w17_MDL-27253_m20_swfsize' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-04-26 11:15:01 +02:00
Eloy Lafuente (stronk7) c9dc3e5564 Merge branch 'w17_MDL-27263_m20_loggedintypo' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-04-26 11:06:31 +02:00
Petr Skoda ffaf18a8c1 MDL-27253 add default swf size and allow #d=100x50 URL resizing in mod/url 2011-04-21 20:36:06 +02:00
Petr Skoda f5fddc6081 MDL-27263 fix already logged in error message typo 2011-04-21 20:35:33 +02:00
stabijoh 9d5a4e2b7c MDL-27252 themes, fix for nonzero current highlight 2011-04-20 13:19:12 -04:00
Eloy Lafuente (stronk7) ba9103b2c6 weekly release 2.0.2+ 2011-04-20 13:36:41 +02:00
Eloy Lafuente (stronk7) 79196c9ed5 Fix permissions 2011-04-20 11:09:11 +02:00
Aparup Banerjee a572cfe5bd MDL-26580 adding precautionary upgrade_set_timeout() calls to some loops which may timeout in very edge cases 2011-04-19 17:58:03 +08:00
Aparup Banerjee 92d7776478 MDL-26580 adding some upgrade_set_timeout() calls to 2010080901 upgrade block, upgrades on questions were timing out 2011-04-19 17:57:56 +08:00
Sam Hemelryk 2715f96932 Merged with small fix branch MDL-26516_m20_calevent_wip4 from git://github.com/rwijaya/moodle.git 2011-04-19 16:29:49 +08:00
John Stabinger 6919d9f790 Fixes for MDL-27138, add correct page headers and footer 2011-04-19 11:03:33 +08:00
Sam Hemelryk 8be0517692 MDL-26835 Fixed up styles being used for suspended users in the grader report. 2011-04-19 10:47:14 +08:00
Sam Hemelryk a4b65c11b9 Merged with conflict resolution branch MDL-26835_MASTER_grader_highlight_suspended_users from git://github.com/andyjdavis/moodle.git 2011-04-19 10:46:54 +08:00
Sam Hemelryk 64e711f04b MDL-25999 Fixed URL bug with admin bookmark changes 2011-04-19 10:26:46 +08:00
Sam Hemelryk e6cf40de78 Merge branch 'MDL-25999_m20' of git://github.com/rwijaya/moodle into MOODLE_20_STABLE 2011-04-19 10:26:30 +08:00
Rossiani Wijaya 174f5a0189 MDL-26516: fix calendar event type (user overides) 2011-04-18 18:12:25 +08:00
Eloy Lafuente (stronk7) dd56fd7c64 MDL-26167 lib/form: minor coding tweaks 2011-04-18 11:45:02 +02:00
Eloy Lafuente (stronk7) 436f83f8ff Merge branch 'MDL-26167-stable' of git://github.com/agwells/moodle into MOODLE_20_STABLE 2011-04-18 11:44:02 +02:00
Rossiani Wijaya d03f904759 MDL-25999: update Admin Bookmarks block to work properly in 2.0 2011-04-18 17:23:36 +08:00
Eloy Lafuente (stronk7) 15a6af91f5 Merge branch 'w16_MDL-17344_m20_uploadprof' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-04-18 11:07:42 +02:00
Andrew Davis (andyjdavis) 0a23dec07a MDL-26835 gradebook: grader report now marks suspended students 2011-04-18 14:25:16 +08:00
Sam Hemelryk 6f8081bef4 MDL-26958 PULL-675 Merged changes and removed border colour style 2011-04-18 10:57:27 +08:00
Andrew Davis (andyjdavis) c643d3ae06 MDL-27153 gradebook - fixed an SQL error in grade export 2011-04-18 02:17:09 +02:00
Eloy Lafuente (stronk7) 507ccf1c4b MDL-27148 whitespace fix 2011-04-18 01:47:11 +02:00
Eloy Lafuente (stronk7) 60bc533ee8 Merge branch 'w16_MDL-27148_m20_stringinc' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-04-18 01:44:10 +02:00
Eloy Lafuente (stronk7) 42f2b8d0dd Merge branch 'w16_MDL-27217_m20_tinyset' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-04-18 01:13:49 +02:00
Eloy Lafuente (stronk7) 84047d1fc9 Merge branch 'w16_MDL-27220_m20_tagprofile' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-04-18 01:08:10 +02:00
Petr Skoda b79ac7e0d6 Merge branch 'm20_MDL-26880_SCORM2004_tracking_fix' of git://github.com/danmarsden/moodle into MOODLE_20_STABLE 2011-04-17 17:16:48 +02:00
Petr Skoda 241ceba38c Merge branch 'm20_MDL-24734_gradefix' of git://github.com/danmarsden/moodle into MOODLE_20_STABLE 2011-04-17 17:09:59 +02:00
Petr Skoda 8e49e0833c Merge branch 'm20_MDL-18392_slash_cleanup' of git://github.com/danmarsden/moodle into MOODLE_20_STABLE 2011-04-17 17:04:09 +02:00
Petr Skoda f6bf9c932d Merge branch 'MDL-27198-m20' of git://github.com/sammarshallou/moodle into MOODLE_20_STABLE 2011-04-17 16:40:10 +02:00
Petr Skoda 1fb2369b09 Merge branch 'wip-MDL-26013-MOODLE_20_STABLE' of git://github.com/samhemelryk/moodle into MOODLE_20_STABLE 2011-04-17 16:22:34 +02:00
Petr Skoda 4de0925820 Merge branch 'wip-MDL-27145-MOODLE_20_STABLE' of git://github.com/samhemelryk/moodle into MOODLE_20_STABLE 2011-04-17 16:13:08 +02:00
Petr Skoda 81f2a1686a MDL-27217 use correct editor name in editor settings page
Credit goes to Franck Bellemain.
2011-04-17 11:18:00 +02:00
Petr Skoda 5f509554a5 MDL-27220 add missing tag lib include 2011-04-17 11:07:57 +02:00
Dan Marsden edd5d0693d SCORM MDL-24734 - fix display of grade information on view.php page - also fixes some XHTML in the same function. 2011-04-16 19:32:11 +12:00
Mary Evans d59a75a841 MDL-26958 Core Themes:adding css patch to base theme so hidden elements are dimmed
This patch also fixes MDL-27160 - the same bug which affects many CORE themes like Binarus and Nonzeo. This patch also a small element of a patch supplied by Thomas Lake in Tracker MDL-26958 (most of which already exists in Base theme).
2011-04-15 00:49:22 +01:00
Aaron Wells 9b482d1cbe MDL-26167 lib/form: Altering Javascript so that "disabledIf" will disable the "Choose" button for a filepicker 2011-04-15 10:02:34 +12:00
sam marshall ca090f3262 MDL-27198 Accessibility: For labels, completion/editing icons must appear after content 2011-04-14 17:28:53 +01:00
Petr Skoda 12db898084 MDL-27148 use new get_in_or_equal() param prefix instead of start 2011-04-14 15:13:28 +02:00
Petr Skoda c79345c0f0 MDL-27148 fix unit tests to accept arbitrary named params format from get_in_or_equal 2011-04-14 15:12:21 +02:00
Petr Skoda d691571218 MDL-27148 use static counter instead of incrementing parameter name 2011-04-14 14:44:50 +02:00
Petr Skoda 4e1bb6e10c MDL-17344 fix case and unicode related profile field issues in user upload
This fixes issue with uppercase and non-ascii profile fields and closes the iterator properly when field not found. Please note that profile fields with upper case letters must be specified exactly in CSV file headers. includes improved docs and parameter typo fix (credit for the parameter typo discovery goes to Aparup Banerjee)
2011-04-14 13:18:42 +02:00
Nadav Kavalerchik a7e920fbfb CSS MDL-26013 Fixed several display bugs when using right to left languages 2011-04-14 15:59:03 +08:00
Matteo Scaramuccia 7a725d6b59 PULL-498 - Fixed potential issues about UTF-8 and multilang filter by adopting pure ASCII chars, the SCO primary key 2011-04-14 10:26:41 +12:00
Matteo Scaramuccia 489c6ef357 MDL-26880 - Fixed missing of activity id
Moved errorCode as global, to allow GetLastError() to work
Applied MDL-17891 mechanism against AJAX failures, "true if true, false otherwise"
Limited what added to log when in debug, to be compliant with mdl_log.action as VARCHAR(40)
2011-04-14 10:26:18 +12:00
Dan Marsden dbb6157aa1 Merge remote branch 'moodle/MOODLE_20_STABLE' into m20_MDL-18392_slash_cleanup 2011-04-14 09:15:17 +12:00
Petr Skoda 50467e50c9 weekly release 2.0.2+ 2011-04-13 22:08:30 +02:00
Petr Skoda 85a91fb98e Merge branch 'MDL-24355_emergency_20' of git://github.com/stronk7/moodle into MOODLE_20_STABLE 2011-04-13 16:49:29 +02:00
Eloy Lafuente (stronk7) 75454e2567 MDL-24355 tags - fix correlation sql to be cross-db 2011-04-13 16:37:37 +02:00
Petr Skoda 06c09f30e3 Revert "MDL-25840 fixed image files link not rendered correctly when attached as file (instead of inline)."
The reason is failed testing. This reverts commit 3d0e3ade75.
2011-04-13 15:07:22 +02:00
Dan Marsden 26daa23d9d SCORM MDL-18392 - remove old call to stripslashes 2011-04-13 20:00:59 +12:00
Matteo Scaramuccia 74a2ee73ed SCORM MDL-18392 MDL-26282 - Fixed calls to add/strip slashes 2011-04-13 19:58:44 +12:00
Petr Skoda fca41d2589 MDL-25718 fix incorrect 'no grade' test and improve coding style 2011-04-12 10:07:05 +02:00
Petr Skoda 6de6a45a10 Merge branch 'MDL-25718_enrol_grades_alternative2' of git://github.com/andyjdavis/moodle into MOODLE_20_STABLE 2011-04-12 09:55:28 +02:00
Sam Hemelryk 0f1fac1f1f form-dateselector MDL-27145 Fixed up error resulting from incorrectly required 'optional' argument on create event 2011-04-12 14:15:51 +08:00
andyjdavis 692134ecd4 enrol MDL-25718 added ability to recover grades when enrolling users 2011-04-12 11:05:02 +08:00
Eloy Lafuente (stronk7) d7706bac2c Merge branch 'MDL-26778-m20' of git://github.com/sammarshallou/moodle into MOODLE_20_STABLE 2011-04-11 17:54:21 +02:00
Eloy Lafuente (stronk7) 391e054c03 Merge branch 'w15_MDL-26577_m20_groupscleanup' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-04-11 16:02:10 +02:00
Eloy Lafuente (stronk7) 71f2316fe6 Merge branch 'w15_MDL-26805_m20_userpol' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-04-11 15:18:31 +02:00
Petr Skoda 16d610019a Merge branch 'wip-MDL-27024-MOODLE_20_STABLE' of git://github.com/samhemelryk/moodle into MOODLE_20_STABLE 2011-04-11 14:13:34 +02:00
Petr Skoda 4f5791da64 Merge branch 'wip-MDL-26599-MOODLE_20_STABLE' of git://github.com/samhemelryk/moodle into MOODLE_20_STABLE 2011-04-11 14:10:38 +02:00
sam marshall c5e3a242b1 MDL-26778 Change to completion should immediately affect nav block
Includes change so that updating completion resets the cache, and a debugging
warning if any module calls the completion viewed thing after it's already
printed navigation (which will mean navigation obviously doesn't update right
away). All existing modules that currently update after printing navigation
were updated.
2011-04-11 12:40:50 +01:00
Petr Skoda 13ad1eaf37 Merge branch 'MDL-25840_m20' of git://github.com/nebgor/moodle into MOODLE_20_STABLE 2011-04-11 13:30:44 +02:00
Petr Skoda 7ca448fa83 MDL-26805 cleanup default role selections
Frontpage role selection now allows only guest and frontpage roles. Default forntpage role is selected duuring install. Unused nodefaultuserrolelists option was removed completely. Coding style improvements.
2011-04-11 13:08:35 +02:00
Eloy Lafuente (stronk7) 37efb4f1ee Merge branch 's9_MDL-27098_filemanager_strings_20' of git://github.com/dongsheng/moodle into MOODLE_20_STABLE 2011-04-11 11:20:31 +02:00
Dongsheng Cai 81bd2292bc MDL-27098
1. Fixed add file language string
2. Fixed hard-coded 'moving' string
3. Fixed hard-coded 'Files' string
2011-04-11 17:10:30 +08:00
Eloy Lafuente (stronk7) 8fa13d155d Merge branch 'w15_MDL-25826_m20_htmlpurifier' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-04-11 11:08:23 +02:00
Sam Hemelryk 465846f082 Merge branch 'MDL-25746_m20' of git://github.com/nebgor/moodle into MOODLE_20_STABLE 2011-04-11 15:38:48 +08:00
Aparup Banerjee f68815ffcf MDL-25746 fixed db tempate page layout to prevent hidden content layers 2011-04-11 15:16:54 +08:00
Sam Hemelryk 67b5d74b2c Merge branch 'MDL-27034_xhprof_table_sql_20' of git://github.com/stronk7/moodle into MOODLE_20_STABLE 2011-04-11 14:34:57 +08:00
Aparup Banerjee 3d0e3ade75 MDL-25840 fixed image files link not rendered correctly when attached as file (instead of inline). 2011-04-11 14:24:22 +08:00
Sam Hemelryk 7d1fb2f6ec Merged branch 'MDL-26185_m20' of git://github.com/andyjdavis/moodle.git and fixed conflicts 2011-04-11 11:54:50 +08:00
Sam Hemelryk 227e54d65a Merge branch 'MDL-27063' of git://github.com/MaryE/moodle into MOODLE_20_STABLE 2011-04-11 11:38:21 +08:00
Sam Hemelryk ee6181b1e7 comment MDL-27024 Removed last check within comment.js for block_comment env 2011-04-11 10:43:25 +08:00
Sam Hemelryk 3962c89bbf comment MDL-27024 Fixed bug that arose when a comment section didnt require pagination but the JS still looked for pageination HTML in the page 2011-04-11 10:43:21 +08:00
Sam Hemelryk 59137c2c5b comment MDL-27024 Removed last reference to the comment block within the comment API and used the options properly 2011-04-11 10:43:17 +08:00
Sam Hemelryk b83e494d6f comment MDL-27024 Fixed up whitespace issues 2011-04-11 10:43:12 +08:00
Sam Hemelryk 3b2164bdca comment MDL-27024 made notoggle and autoload into proper options to remove comment block conditions from comment API 2011-04-11 10:43:06 +08:00
Sam Hemelryk aa54d70440 comment MDL-27024 Cleaned up HTML output in comment class 2011-04-11 10:43:01 +08:00
Sam Hemelryk d2d4bccec1 comment MDL-27024 Cleaned up use of undefined property $this->link 2011-04-11 10:42:56 +08:00
Sam Hemelryk f3a3c59f48 mod-lesson MDL-26599 Fixed echo causing incorrect nesting. 2011-04-11 10:27:58 +08:00
Eloy Lafuente (stronk7) 3d62a46b26 Merge branch 'w15_MDL-27094_m20_assval' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-04-11 01:32:15 +02:00
Eloy Lafuente (stronk7) 9aacfedb22 Merge branch 'w15_MDL-27007_m20_userupload' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-04-11 01:23:04 +02:00
Eloy Lafuente (stronk7) 12b734434b Merge branch 'w15_MDL-26910_m20_httpd' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-04-11 00:17:23 +02:00
Eloy Lafuente (stronk7) f9068e531c Merge branch 'w15_MDL-27121_m20_phpreq' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-04-10 23:55:11 +02:00
Eloy Lafuente (stronk7) 96506c474a Merge branch 'MDL-26319_MOODLE_20_STABLE' of git://github.com/nebgor/moodle into MOODLE_20_STABLE 2011-04-10 23:49:59 +02:00
Tony Levi 0d66e33da5 MDL-27084: mod/feedback fix bad syntax in database query 2011-04-10 23:43:56 +02:00
Petr Skoda 934fa3ed6c MDL-27094 fix incorrect assignment form validation method name
Credit goes to H. Hanna
2011-04-10 21:01:53 +02:00
Petr Skoda 6743d6c520 MDL-26910 add HTTPD server name whitelisting 2011-04-10 13:59:17 +02:00
Petr Skoda cf76e10e4a MDL-26577 fix several groups and groupings issues
The current group in verified on each page which prevents problems when group settings are changed. The default group for course level is now using correct grouping id which solves problems when activity and course are using different grouping. The grouping is now displayed in group selector if user has access to all groups.
2011-04-10 13:28:02 +02:00
Mary Evans c800e31213 MDL-27063 tt styled incorrectly in canvas theme 2011-04-10 00:48:35 +01:00
Petr Skoda 23580ddacb MDL-27121 fix incorrect check_moodle_environment() version parameter
This prevents Moodle 2.0 from using environment info for 2.1 in the installer and CLI upgrade.
2011-04-09 17:42:53 +02:00
Petr Skoda 827fcdb347 MDL-27007 fix incorrectly suspended user enrolment when uploading users
This bug surfaced after the recent change in enrol API, credit goes to Chaim Schendowich and Micky Fokken.
2011-04-09 12:09:53 +02:00
Petr Skoda 60aaf1dc77 MDL-25826 add more HTMLPurifier integration tests 2011-04-09 11:51:55 +02:00
Petr Skoda fd8fa6f9d0 MDL-25826 integrate HTMLPurifier 4.3.0 and improve performance
The new HTMLPurifier finally caches the schema properly eliminating both extra CPU cycles and disk writes. The repeated dir exists tests might cause problems on NFS shares.
2011-04-09 11:51:49 +02:00
Petr Skoda 8adf19eda8 MDL-25826 import HTMLPurifier 4.3.0 2011-04-09 11:51:35 +02:00
Andrew Davis (andyjdavis) a6e29ac6a3 message MDL-26185 fixed the logic around email addy in user profile Vs in messaging preferences 2011-04-07 15:39:03 +08:00
Aparup Banerjee 52e184904f MDL-26319 moved cancel_backup() from backup_ui to cancel_process() in base_ui and renamed callers. fix for method inheritance in restore_ui. 2011-04-07 10:40:45 +08:00
Eloy Lafuente (stronk7) b8d80c062e MDL-27034 profiling - xhprof_table_sql improvement 2011-04-07 00:46:33 +02:00
Petr Skoda eb9d692f8b weekly release 2.0.2+ 2011-04-06 14:55:57 +02:00
Petr Skoda a805d653d1 MDL-26804 fix undefined CFG langcache property during install
Sorry, I have committed fix for this in master incorrectly with MDL-26914.
2011-04-06 10:56:30 +02:00
Sam Hemelryk fe4a0cfe63 PULL-574 MDL-26185 Reverted changes due failed testing + review 2011-04-06 16:35:24 +08:00
Petr Skoda d0e3447e43 revert MDL-26577 added session cleaning to groups_get_activity_group() that fixes an issue with dirty session data. refactored session cleaning into one function, added caching to reduce queries.
(reverse-merged from commit 77335e76e9) This change was causing fatal errors on the course level, I am going to fix it myself.
2011-04-05 18:35:01 +02:00
Sam Hemelryk f046221272 messaging MDL-26185 Last minute fix of bug during integration, empty user preference causing error 2011-04-05 16:53:03 +08:00
Petr Skoda 42a50a0a45 Merge branch 'MDL-26577_groups_api_refactor_take2_m20' of git://github.com/nebgor/moodle into MOODLE_20_STABLE 2011-04-05 10:21:21 +02:00
Aparup Banerjee 77335e76e9 MDL-26577 added session cleaning to groups_get_activity_group() that fixes an issue with dirty session data. refactored session cleaning into one function, added caching to reduce queries. 2011-04-05 12:23:56 +08:00
Eloy Lafuente (stronk7) ddf3e875f3 Merge branch 'wip-MDL-26560-MOODLE_20_STABLE' of git://github.com/samhemelryk/moodle into MOODLE_20_STABLE 2011-04-04 23:16:53 +02:00
Eloy Lafuente (stronk7) 91fa7abf3d Merge branch 'wip-MDL-26858-MOODLE_20_STABLE' of git://github.com/samhemelryk/moodle into MOODLE_20_STABLE 2011-04-04 21:50:22 +02:00
Eloy Lafuente (stronk7) b060e5b0b8 Merge branch 'MDL-26931-blog-post-order_20_STABLE' of git://github.com/mudrd8mz/moodle into MOODLE_20_STABLE 2011-04-04 12:02:17 +02:00
Andrew Davis (andyjdavis) f9c8746704 gradebook MDL-20946 fixed average calculation where users have multiple gradeable roles 2011-04-04 11:27:09 +02:00
Petr Skoda c629aeabe3 MDL-25651 fix incorrect indentation 2011-04-04 10:01:30 +02:00
Petr Skoda 27614a5e5d Merge branch 'wip-MDL-25651_MOODLE_20_STABLE' of git://github.com/jennymgray/moodle into MOODLE_20_STABLE 2011-04-04 09:59:04 +02:00
Sam Hemelryk ba32fc0042 enrolments MDL-26560 Fixed panel.user bug with role assignments dialogue 2011-04-04 14:50:15 +08:00
Sam Hemelryk 87b05ba3e0 enrolments MDL-26858 Added finish user enrolments button to JS dialogue 2011-04-04 14:38:23 +08:00
Sam Hemelryk 728f3c9d08 Merge branch 'MDL-26185_m20' of git://github.com/rwijaya/moodle into MOODLE_20_STABLE 2011-04-04 13:21:24 +08:00
Mary Evans d7dd177eb4 Fix for MDL-27016 added min-width 2011-04-04 13:10:06 +08:00
Rossiani Wijaya 507de745e3 MDL-26185: simplify the logic of email message output and removing email cache 2011-04-01 11:12:54 +08:00
Jenny Gray bf8ff4889b MDL-25651 add capability check to my private files navigation link 2011-03-31 14:54:36 +01:00
Ankit Agarwal f9112bda35 MDL-26931 Order blog entries by the creation date 2011-03-31 11:56:53 +02:00
Tim Hunt ba63612bfa MDL-27035 table_sql forgets to set countparams. 2011-03-30 20:26:34 +01:00
Petr Skoda 408612d111 updating digits to match the .2 release 2011-03-30 18:01:09 +02:00
Petr Skoda 60e837038a weekly release 2011-03-30 14:29:56 +02:00
Petr Skoda 4cb05cea9d Merge branch 'MDL-26964-moodle_url-anchor_20_STABLE' of git://github.com/mudrd8mz/moodle into MOODLE_20_STABLE 2011-03-29 10:00:04 +02:00
Eloy Lafuente (stronk7) 09c1e72a56 Merge branch 'MDL-26744-metasyncstring_20_STABLE' of git://github.com/wildgirl/moodle into MOODLE_20_STABLE 2011-03-28 23:56:08 +02:00
Eloy Lafuente (stronk7) d2455301b7 Merge branch 'w13_MDL-22733_20_youtubeiframe' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-03-28 23:53:40 +02:00
Eloy Lafuente (stronk7) 1be2b904ab Merge branch 'w13_MDL-26909_20_mediatests' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-03-28 23:30:35 +02:00
Eloy Lafuente (stronk7) 337817e669 Merge branch 'w13_MDL-26987_20_sclean' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-03-28 21:59:21 +02:00
Eloy Lafuente (stronk7) b17a3a5591 Merge branch 'w13_MDL-26913_20_sitecreated' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-03-28 20:10:57 +02:00
Eloy Lafuente (stronk7) c44c552efa Merge branch 'w13_MDL-26951_20_archetypes' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-03-28 19:40:35 +02:00
Eloy Lafuente (stronk7) faa297ce6f Merge branch 'w13_MDL-26991_20_legacy' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-03-28 19:26:28 +02:00
Eloy Lafuente (stronk7) b6225c0f2f Merge branch 'w13_MDL-26966_20_urlparsing' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-03-28 19:07:00 +02:00
Eloy Lafuente (stronk7) 50496af78b Merge branch 'w13_MDL-26985_20_branching' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-03-28 18:41:22 +02:00
Tim Hunt 8fc52ccf46 MDL-26425 tablelib ability for table_sql to add a class to the row based on the data. 2011-03-28 17:12:26 +02:00
Tim Hunt 9f9324f408 MDL-26425 tablelib better validation of the sort code, also some cleanup.
You may think that the extra validation is unnecessary, since the sort fields are already validated when the URL parameters are parsed, however that overlooks an important point. There may be other options that affect which columns are in the SQL, for example the quiz show individual question grades setting. These other options can cause a column that was in the table, and being sorted on, to disappear. Therefore, it is necessary to re-validate the sort columns when they are used, to make sure they are still present, otherwise you can get ORDER BY sql that refers to non-existant columns, which then causes DB errors.
2011-03-28 17:10:53 +02:00
Helen Foster d4dc367a5b MDL-26744 enrol meta lang string improvement thanks to Ray Lawrence 2011-03-28 17:07:55 +02:00
Eloy Lafuente (stronk7) 3ede344b5c Merge branch 'w13_MDL-26990_20_missingrebuild' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-03-28 16:35:18 +02:00
Petr Skoda 5c686792c6 Merge branch 'MDL-26335' of github.com:mouneyrac/moodle into MOODLE_20_STABLE 2011-03-28 15:13:47 +02:00
David Mudrak 91a4b3bd3e MDL-26804 core_string_manager::get_list_of_translations() can use a cache again
This patch reimplements the internal cache that was used to store the
list of available translations in Moodle 1.x. By default, the method
get_list_of_translations() still uses the file
moodledata/cache/languages to store the list of available translations.
The location of that file can be redefined in config.php. The internal
format of the cache file is JSON now (used to be a plain text list).

The patch also fixes a usage of the global $CFG in translation_exists()
methods where the internal property should be used instead.
2011-03-28 14:49:17 +02:00
Dongsheng Cai 1a6a1487f4 MOD-WIKI MDL-26213
1. Rewrite the image urls in diff.php and viewdiff.php
2. Fxied image urls in search.php
3. Add modcontext variable to avoid using duplicated
get_context_instance calls
2011-03-28 11:50:37 +02:00
Jenny Gray a5286ae4cc MDL-26932 full url path for toggle completion form POST 2011-03-28 11:04:39 +02:00
Dan Marsden 5b97bff526 Plagiarism MDL-26939 - missing return 2011-03-28 10:53:13 +02:00
Dongsheng Cai 1c4873ba1b MDL-26916, MDL-26917
1. Fix filepicker paging issue MDL-26916
2. Highlight view as icon button by default MDL-26917
2011-03-28 16:13:27 +08:00
Dan Marsden 9a8f8cea49 Forum Overview MDL-26957 add classes to allow better control by theme 2011-03-28 16:11:30 +08:00
stabijoh c37f03543b Revised fix for MDL-26906, magazine and formal white secure browser quiz window 2011-03-28 16:04:21 +08:00
Andrew Davis (andyjdavis) cd4ca97611 message MDL-26077 fixed up a problem with html tags being stripped out 2011-03-28 16:00:36 +08:00
Myles Carrick 77bfc4ce0f MDL-26798 encode file path in dropbox repository plugin, credits to Myles Carrick 2011-03-28 15:57:38 +08:00
Sam Hemelryk 13c53a4a2e Merge branch 'w13_MDL-26992_20_uploadtemplates' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-03-28 14:33:27 +08:00
Sam Hemelryk d3796a2c61 Merge branch 'w13_MDL-26989_20_enrolroles' of git://github.com/skodak/moodle into MOODLE_20_STABLE 2011-03-28 14:17:08 +08:00
Petr Skoda 7bb8ae5911 MDL-26985 new Moodle 2.1 requirements
For now only extra PHP 5.3.2 requirement compared to 2.0.
2011-03-26 22:03:00 +01:00
David Mudrak dbe6d9f821 MDL-26964 URL anchor support for single_button and single_select
If the single_button or single_select use HTTP method 'get' then the
eventual anchor part of the URL is used.
2011-03-24 22:15:47 +01:00
David Mudrak 4927443761 MDL-26964 URL returned by moodle_url::out_omit_querystring() may contain the anchor part 2011-03-24 22:15:47 +01:00
32068 changed files with 1435589 additions and 4692707 deletions
-249
View File
@@ -1,249 +0,0 @@
{
'plugins': [
'@babel',
'promise',
'jsdoc'
],
'extends': [
'eslint:recommended',
'plugin:promise/recommended'
],
'env': {
'browser': true,
// Enable ES6+ features by default.
// See http://eslint.org/docs/user-guide/configuring#specifying-environments
// Note: The YUI override must exactly match this when disabling the ES6+ version because those features are not supported by Shifter.
'es2020': true,
'amd': true
},
'globals': {
'M': true,
'Y': true
},
// Warn about unused eslint-disable statements.
'reportUnusedDisableDirectives': true,
'rules': {
// See http://eslint.org/docs/rules/ for all rules and explanations of all
// rules.
// === Possible Errors ===
'comma-dangle': 'off',
'no-console': 'error',
'no-empty': 'warn',
'no-extra-parens': 'off',
'no-prototype-builtins': 'off',
'no-unreachable': 'warn',
// === Best Practices ===
// (these mostly match our jshint config)
'array-callback-return': 'warn',
'block-scoped-var': 'warn',
'complexity': 'warn',
'consistent-return': 'warn',
'curly': 'error',
'dot-notation': 'warn',
'no-alert': 'warn',
'no-caller': 'error',
'no-div-regex': 'error',
'no-empty-function': 'warn',
'no-eq-null': 'error',
'no-eval': 'error',
'no-extend-native': 'error',
'no-extra-bind': 'warn',
'no-floating-decimal': 'warn',
'no-global-assign': 'warn',
'no-implied-eval': 'error',
'no-invalid-this': 'error',
'no-iterator': 'error',
'no-labels': 'error',
'no-loop-func': 'error',
'no-multi-spaces': 'warn',
'no-multi-str': 'error',
'no-new-func': 'error',
'no-new-wrappers': 'error',
'no-octal-escape': 'error',
'no-proto': 'error',
'no-redeclare': 'warn',
'no-restricted-globals': ['error', { 'name': 'Notification' }],
'no-return-assign': 'error',
'no-script-url': 'error',
'no-self-compare': 'error',
'no-sequences': 'warn',
'no-throw-literal': 'warn',
'no-unmodified-loop-condition': 'error',
'no-unused-expressions': 'error',
'no-useless-call': 'warn',
'no-useless-escape': 'warn',
'wrap-iife': ['error', 'any'],
// === Variables ===
'no-undef-init': 'error',
'no-unused-vars': ['error', { 'caughtErrors': 'none' }],
// === Stylistic Issues ===
'array-bracket-spacing': 'warn',
'block-spacing': 'warn',
'brace-style': ['warn', '1tbs'],
'camelcase': 'warn',
'capitalized-comments': ['warn', 'always', { 'ignoreConsecutiveComments': true }],
'comma-spacing': ['warn', { 'before': false, 'after': true }],
'comma-style': ['warn', 'last'],
'computed-property-spacing': 'error',
'consistent-this': 'off',
'eol-last': 'off',
'func-call-spacing': ['warn', 'never'],
'func-names': 'off',
'func-style': 'off',
// indent currently not doing well with our wrapping style, so disabled.
'indent': ['off', 4, { 'SwitchCase': 1 }],
'key-spacing': ['warn', { 'beforeColon': false, 'afterColon': true, 'mode': minimum }],
'keyword-spacing': 'warn',
'linebreak-style': ['error', 'unix'],
'lines-around-comment': 'off',
'max-len': ['error', 132],
'max-lines': 'off',
'max-depth': 'warn',
'max-nested-callbacks': ['warn', 5],
'max-params': 'off',
'max-statements': 'off',
'max-statements-per-line': ['warn', { max: 2 }],
'new-cap': ['warn', { 'properties': false }],
'new-parens': 'warn',
'newline-per-chained-call': 'off',
'no-array-constructor': 'off',
'no-bitwise': 'error',
'no-continue': 'off',
'no-inline-comments': 'off',
'no-lonely-if': 'off',
'no-mixed-operators': 'off',
'no-multiple-empty-lines': 'warn',
'no-negated-condition': 'off',
'no-nested-ternary': 'warn',
'no-new-object': 'off',
'no-plusplus': 'off',
'no-tabs': 'error',
'no-ternary': 'off',
'no-trailing-spaces': 'error',
'no-underscore-dangle': 'off',
'no-unneeded-ternary': 'off',
'no-whitespace-before-property': 'warn',
'object-curly-newline': 'off',
'object-curly-spacing': 'warn',
'object-property-newline': 'off',
'one-var': 'off',
'one-var-declaration-per-line': ['warn', 'initializations'],
'operator-assignment': 'off',
'operator-linebreak': 'off',
'padded-blocks': 'off',
'quote-props': ['warn', 'as-needed', {'unnecessary': false, 'keywords': true, 'numbers': true}],
'quotes': 'off',
'semi': 'error',
'semi-spacing': ['warn', {'before': false, 'after': true}],
'sort-vars': 'off',
'space-before-blocks': 'warn',
'space-before-function-paren': ['warn', 'never'],
'space-in-parens': 'warn',
'space-infix-ops': 'warn',
'space-unary-ops': 'warn',
'spaced-comment': 'warn',
'unicode-bom': 'error',
'wrap-regex': 'off',
// === Promises (override plugin:promise/recommended) ===
'promise/always-return': 'warn',
'promise/no-return-wrap': 'warn',
'promise/param-names': 'warn',
'promise/catch-or-return': ['warn', {terminationMethod: ['catch', 'fail', 'always'], allowFinally: true}],
'promise/no-native': 'warn',
'promise/avoid-new': 'warn',
// === Deprecations ===
"no-restricted-properties": ['warn', {
'object': 'M',
'property': 'str',
'message': 'Use "core/str" module or M.util.get_string()'
}],
},
overrides: [
{
files: ["**/yui/src/**/*.js"],
'env': {
// Disable ES6+ for YUI files.
'es2020': false,
},
// Disable some rules which we can't safely define for YUI rollups.
rules: {
'no-undef': 'off',
'no-unused-vars': 'off',
'no-unused-expressions': 'off',
// === JSDocs ===
"jsdoc/check-access": 'off',
"jsdoc/check-alignment": 'off',
"jsdoc/check-param-names": 'off',
"jsdoc/check-property-names": 'off',
"jsdoc/empty-tags": 'off',
"jsdoc/implements-on-classes": 'off',
"jsdoc/multiline-blocks": 'off',
"jsdoc/require-jsdoc": 'off',
"jsdoc/require-param": 'off',
"jsdoc/require-param-name": 'off',
"jsdoc/require-param-type": 'off',
"jsdoc/require-property": 'off',
"jsdoc/require-property-name": 'off',
"jsdoc/require-property-type": 'off',
}
},
{
files: ["**/amd/src/*.js", "**/amd/src/**/*.js", "Gruntfile.js", ".grunt/*.js", ".grunt/tasks/*.js", "jsdoc.conf.js"],
// We're using babel transpiling so use their parser
// for linting.
parser: '@babel/eslint-parser',
// Check AMD with some slightly stricter rules.
rules: {
'no-implicit-globals': 'error',
// Disable all of the rules that have babel versions.
'new-cap': 'off',
// Not using this rule for the time being because it isn't
// compatible with jQuery and ES6.
'no-invalid-this': 'off',
'object-curly-spacing': 'off',
'quotes': 'off',
'semi': 'off',
'no-unused-expressions': 'off',
// Enable all of the babel version of these rules.
'@babel/new-cap': ['warn', { 'properties': false }],
// Not using this rule for the time being because it isn't
// compatible with jQuery and ES6.
'@babel/no-invalid-this': 'off',
'@babel/object-curly-spacing': 'warn',
'@babel/semi': 'error',
'@babel/no-unused-expressions': 'error',
// === Promises ===
// We have Promise now that we're using ES6.
'promise/no-native': 'off',
'promise/avoid-new': 'off',
// === JSDocs ===
'jsdoc/check-access': 'error',
'jsdoc/check-alignment': 1, // Recommended.
'jsdoc/check-param-names': 'error',
'jsdoc/check-property-names': 'error',
'jsdoc/empty-tags': 'error',
'jsdoc/implements-on-classes': 'error',
'jsdoc/multiline-blocks': 'error',
'jsdoc/require-jsdoc': 'error',
'jsdoc/require-param': 'error',
'jsdoc/require-param-name': 'error',
'jsdoc/require-param-type': 'error',
'jsdoc/require-property': 'error',
'jsdoc/require-property-name': 'error',
'jsdoc/require-property-type': 'error'
},
parserOptions: {
'sourceType': 'module',
'requireConfigFile': false
}
}
]
}
-24
View File
@@ -1,24 +0,0 @@
{
"indentation": ["on",{
"Feature": 0,
"Background": 2,
"Scenario": 2,
"Examples": 4,
"example": 6,
"Step": 4,
"given": 4,
"and": 4
}],
"no-dupe-feature-names": "on",
"no-dupe-scenario-names": "off",
"no-empty-file": "on",
"no-files-without-scenarios": "on",
"no-multiple-empty-lines": "on",
"no-partially-commented-tag-lines": "on",
"no-trailing-spaces": "on",
"no-unnamed-features": "on",
"no-unnamed-scenarios": "on",
"no-scenario-outlines-without-examples": "on",
"no-examples-in-scenarios": "on",
"new-line-at-eof": ["on", "yes"]
}
-3
View File
@@ -1,3 +0,0 @@
**/yui/build/** -diff
**/amd/build/** -diff
lib/dml/oci_native_moodle_package.sql text eol=lf
-2
View File
@@ -1,2 +0,0 @@
# Primary donations pages.
custom: ["https://moodle.com/donations/", moodle.org]
-7
View File
@@ -1,7 +0,0 @@
*** PLEASE DO NOT OPEN PULL REQUESTS VIA GITHUB ***
The moodle.git repository at Github is just a mirror of the official repository. We do not accept pull requests at Github.
See CONTRIBUTING.md guidelines for how to contribute patches for Moodle. Thank you.
--
-6
View File
@@ -1,6 +0,0 @@
> [!IMPORTANT]
> PLEASE DO NOT PUBLISH SECURITY FINDINGS PUBLICLY.
If you discover a potential security issue, please report it via our [Security Submission Form](https://moodle.org/security/report).
For more information about our security processes and responsible disclosure policy, see the [Security Procedures documentation](https://moodledev.io/general/development/process/security).
-1
View File
@@ -1 +0,0 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1231.24 314.44" preserveAspectRatio="xMinYMid meet"><defs><style>.cls-1{fill:#f48020;}.cls-2,.cls-3{fill:#383738;stroke:#4a4a4c;}.cls-2{stroke-width:2.37px;}.cls-3{stroke-width:2.38px;}.cls-4{fill:#585858;}</style></defs><title>Moodle_Logo_UseonBlack_RGB</title><path class="cls-1" d="M1086.3,235.34a53.42,53.42,0,0,0,16.71,29q13.26,12.27,29.48,12.27,11.29,0,24.56-5.89,16.23-6.88,16.22-16.22l58-.49q-12.29,29-41.76,45.2-27.52,15.23-60.43,15.24-42.25,0-70-26.29t-27.75-68.54q0-43.73,27.75-69.51t72-25.8q48.15,0,75.17,31.94,25.07,29.47,25.06,79.1Zm89.91-33.91q-3.43-17.69-14.73-29-12.8-12.28-29.49-12.29-17.21,0-28.74,11.79t-15.47,29.48Z"/><path class="cls-1" d="M965.45,308.54V0h55V308.54Z"/><path class="cls-1" d="M892.69,308.54V290.36q-7.38,9.83-25.06,15.72a92.31,92.31,0,0,1-30,5.41q-39.31,0-63.13-27T750.7,217.65q0-39.3,23.34-65.84t62.15-26.53q43.71,0,56.5,23.58V0h53.55V308.54Zm0-102.68q0-14.75-14-28.26t-28.74-13.51q-21.12,0-33.41,17.2-10.8,15.24-10.8,37.33,0,21.63,10.8,36.85,12.29,17.71,33.41,17.69,12.78,0,27.76-12t15-24.81Z"/><path class="cls-1" d="M647,313.45q-41.77,0-69-26.53t-27.27-68.3q0-41.73,27.27-68.28t69-26.53q41.75,0,69.27,26.53t27.52,68.28q0,41.79-27.52,68.3T647,313.45ZM647,168q-19.89,0-30.58,15.07t-10.68,35.79q0,20.73,9.94,35.05,11.43,16.31,31.32,16.28t31.32-16.28q10.44-14.31,10.44-35.05t-9.94-35Q667.42,168,647,168Z"/><path class="cls-1" d="M448.07,313.45q-41.76,0-69-26.53t-27.27-68.3q0-41.73,27.27-68.28t69-26.53q41.74,0,69.28,26.53t27.5,68.28q0,41.79-27.5,68.3T448.07,313.45Zm0-145.43q-19.89,0-30.59,15.07t-10.69,35.79q0,20.73,9.94,35.05,11.43,16.31,31.34,16.28t31.32-16.28q10.42-14.31,10.44-35.05t-9.94-35Q468.45,168,448.07,168Z"/><path class="cls-1" d="M288.89,309.11V201q0-33.9-28-33.9t-28,33.9V309.11h-55V201q0-33.9-27.52-33.9-28,0-28,33.9V309.11h-55V194.65q0-35.37,24.56-53.54,21.61-16.22,58.46-16.22,37.35,0,55,19.16,15.23-19.16,55.51-19.16,36.86,0,58.45,16.22,24.57,18.16,24.57,53.54V309.11Z"/><path class="cls-2" d="M.76,145.76"/><path class="cls-3" d="M1.73,145.32"/><path class="cls-4" d="M173.86,116.85l55.08-40.1-.7-2.44C135,83.92,80.42,96.44,0,144.13l.73,2.38,21-.67c15.17.52,30.85,1.26,46,1.32-1,24.09.83,32.65.83,32.65,51.92,17.58,101.22,1.53,127.14-43.9A248.72,248.72,0,0,0,173.86,116.85Z"/><path class="cls-4" d="M113.78,110c-.3-1.08-2.23-1.48-4.28-.9a4.73,4.73,0,0,0-3.14,2.19c-18.53,5-49.16,13.72-70.73,21l-1.33.45c-16.68,5.61-24.73,8.49-25.22,12.55-2.29,18.44-.87,42.18-.86,42.48l.19,6.4c-9.74,27.56.06,49.6,8.38,69.77.06-24.92,1.07-45.22-6-71-.2-3.82-1.49-30.36.61-47.34.34-2.75,11.65-6.56,23.63-10.59l1.33-.44c22-7.42,53.57-16.39,71.9-21.29a6.75,6.75,0,0,0,2.29-.25C112.64,112.43,114.08,111.09,113.78,110Z"/></svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

-31
View File
@@ -1,31 +0,0 @@
name: 'Close Pull Requests'
on:
pull_request_target:
types: opened
workflow_dispatch:
jobs:
close:
# Avoid forks to run this job
if: github.repository_owner == 'moodle'
name: Close Pull Requests
runs-on: ubuntu-latest
steps:
- uses: dessant/repo-lockdown@v4
with:
process-only: prs
close-pr: true
skip-closed-pr-comment: true
pr-comment: >
**Please, don't open pull requests via GitHub.**
This repository (moodle.git) at GitHub is just a mirror of the official Moodle repository.
For how to contribute patches for Moodle see
[CONTRIBUTING.md](https://github.com/moodle/moodle/blob/main/CONTRIBUTING.md).
Closing this pull request.
-71
View File
@@ -1,71 +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/>.
/**
* Template configuraton file for github actions CI/CD.
*
* @package core
* @copyright 2020 onwards Eloy Lafuente (stronk7) {@link https://stronk7.com}
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// This cannot be used out from a github actions workflow, so just exit.
getenv('GITHUB_WORKFLOW') || die; // phpcs:ignore moodle.Files.MoodleInternal.MoodleInternalGlobalState
unset($CFG);
global $CFG;
$CFG = new stdClass();
$CFG->dbtype = getenv('dbtype');
$CFG->dblibrary = 'native';
$CFG->dbhost = '127.0.0.1';
$CFG->dbname = 'test';
$CFG->dbuser = 'test';
$CFG->dbpass = 'test';
$CFG->prefix = 'm_';
$CFG->dboptions = ['dbcollation' => 'utf8mb4_bin'];
$host = 'localhost';
$CFG->wwwroot = "http://{$host}";
$CFG->dataroot = realpath(dirname(__DIR__)) . '/moodledata';
$CFG->admin = 'admin';
$CFG->directorypermissions = 0777;
// Debug options - possible to be controlled by flag in future.
$CFG->debug = (E_ALL | E_STRICT); // DEBUG_DEVELOPER.
$CFG->debugdisplay = 1;
$CFG->debugstringids = 1; // Add strings=1 to url to get string ids.
$CFG->perfdebug = 15;
$CFG->debugpageinfo = 1;
$CFG->allowthemechangeonurl = 1;
$CFG->passwordpolicy = 0;
$CFG->cronclionly = 0;
$CFG->pathtophp = getenv('pathtophp');
$CFG->phpunit_dataroot = realpath(dirname(__DIR__)) . '/phpunitdata';
$CFG->phpunit_prefix = 't_';
define('TEST_EXTERNAL_FILES_HTTP_URL', 'http://localhost:8080');
define('TEST_EXTERNAL_FILES_HTTPS_URL', 'http://localhost:8080');
define('TEST_SESSION_REDIS_HOST', 'localhost');
define('TEST_CACHESTORE_REDIS_TESTSERVERS', 'localhost');
// TODO: add others (solr, mongodb, memcached, ldap...).
// Too much for now: define('PHPUNIT_LONGTEST', true); // Only leaves a few tests out and they are run later by CI.
require_once(__DIR__ . '/lib/setup.php');
-125
View File
@@ -1,125 +0,0 @@
name: Core
on:
push:
branches-ignore:
- main
- master
- MOODLE_[0-9]+_STABLE
tags-ignore:
- v[0-9]+.[0-9]+.[0-9]+*
workflow_dispatch:
inputs:
phpunit_extra_options:
description: Additional options to apply to PHPUnit
required: false
default: ''
env:
php: 8.3
jobs:
Grunt:
runs-on: ubuntu-22.04
steps:
- name: Checking out code
uses: actions/checkout@v4
- name: Configuring node & npm
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- name: Installing node stuff
run: npm install
- name: Running grunt
run: npx grunt
- name: Looking for uncommitted changes
# Add all files to the git index and then run diff --cached to see all changes.
# This ensures that we get the status of all files, including new files.
# We ignore npm-shrinkwrap.json to make the tasks immune to npm changes.
run: |
git add .
git reset -- npm-shrinkwrap.json
git diff --cached --exit-code
PHPUnit:
runs-on: ${{ matrix.os }}
services:
exttests:
image: moodlehq/moodle-exttests
ports:
- 8080:80
redis:
image: redis
ports:
- 6379:6379
strategy:
fail-fast: false
matrix:
include:
# MySQL builds always run with the lowest PHP supported version.
- os: ubuntu-22.04
php: 8.1
extensions:
db: mysqli
# PostgreSQL builds always run with the highest PHP supported version.
- os: ubuntu-22.04
php: 8.3
db: pgsql
steps:
- name: Setting up DB mysql
if: ${{ matrix.db == 'mysqli' }}
uses: moodlehq/mysql-action@v1
with:
collation server: utf8mb4_bin
mysql version: 8.0
mysql database: test
mysql user: test
mysql password: test
use tmpfs: true
tmpfs size: '1024M'
extra conf: --skip-log-bin
- name: Setting up DB pgsql
if: ${{ matrix.db == 'pgsql' }}
uses: m4nu56/postgresql-action@v1
with:
postgresql version: 13
postgresql db: test
postgresql user: test
postgresql password: test
- name: Configuring git vars
uses: rlespinasse/github-slug-action@v4
- name: Setting up PHP ${{ matrix.php }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: ${{ matrix.extensions }}
ini-values: max_input_vars=5000
coverage: none
- name: Checking out code from ${{ env.GITHUB_REF_SLUG }}
uses: actions/checkout@v4
- name: Setting up PHPUnit
env:
dbtype: ${{ matrix.db }}
run: |
echo "pathtophp=$(which php)" >> $GITHUB_ENV # Inject installed pathtophp to env. The template config needs it.
cp .github/workflows/config-template.php config.php
mkdir ../moodledata
sudo locale-gen en_AU.UTF-8
php admin/tool/phpunit/cli/init.php --no-composer-self-update
- name: Running PHPUnit tests
env:
dbtype: ${{ matrix.db }}
phpunit_options: ${{ secrets.phpunit_options }}
run: vendor/bin/phpunit $phpunit_options ${{ inputs.phpunit_extra_options }}
-149
View File
@@ -1,149 +0,0 @@
name: Windows Testing
on:
workflow_dispatch:
inputs:
phpunit_extra_options:
description: Additional options to apply to PHPUnit
required: false
default: ''
env:
php: 8.3
jobs:
Grunt:
runs-on: windows-latest
steps:
- name: Set git to use LF
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- name: Checking out code
uses: actions/checkout@v4
- name: Configuring node & npm
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- name: Installing node stuff
run: npm ci
- name: Running grunt
run: npx grunt
- name: Looking for uncommitted changes
# Add all files to the git index and then run diff --cached to see all changes.
# This ensures that we get the status of all files, including new files.
# We ignore npm-shrinkwrap.json to make the tasks immune to npm changes.
run: |
git add .
git reset -- npm-shrinkwrap.json
git diff --cached --exit-code
PHPUnit:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
php: 8.3
# Ideally we should use mysql/mariadb, but they are 4x slower without tweaks and configuration
# so let's run only postgres (1.5h vs 6h) only, If some day we want to improve the mysql runs,
# this is the place to enable them.
# db: mysqli
db: pgsql
extensions: exif, fileinfo, gd, intl, pgsql, mysql, redis, soap, sodium, zip
- os: windows-latest
php: 8.1
db: pgsql
extensions: exif, fileinfo, gd, intl, pgsql, mysql, redis, soap, sodium
steps:
- name: Setting up DB mysql
if: ${{ matrix.db == 'mysqli' }}
uses: shogo82148/actions-setup-mysql@v1
with:
mysql-version: 8.0
user: test
password: test
- name: Creating DB mysql
if: ${{ matrix.db == 'mysqli' }}
run: mysql --host 127.0.0.1 -utest -ptest -e 'CREATE DATABASE IF NOT EXISTS test COLLATE = utf8mb4_bin;';
- name: Setting up DB pgsql
if: ${{ matrix.db == 'pgsql' }}
run: |
# TODO: Remove these conf. modifications when php74 or php80 are lowest.
# Change to old md5 auth, because php73 does not support it.
# #password_encryption = scram-sha-256
(Get-Content "$env:PGDATA\postgresql.conf"). `
replace('#password_encryption = scram-sha-256', 'password_encryption = md5') | `
Set-Content "$env:PGDATA\postgresql.conf"
(Get-Content "$env:PGDATA\pg_hba.conf"). `
replace('scram-sha-256', 'md5') | `
Set-Content "$env:PGDATA\pg_hba.conf"
$pgService = Get-Service -Name postgresql*
Set-Service -InputObject $pgService -Status running -StartupType automatic
Start-Process -FilePath "$env:PGBIN\pg_isready" -Wait -PassThru
& $env:PGBIN\psql --command="CREATE USER test PASSWORD 'test'" --command="\du"
- name: Creating DB pgsql
if: ${{ matrix.db == 'pgsql' }}
run: |
& $env:PGBIN\createdb --owner=test test
$env:PGPASSWORD = 'test'
& $env:PGBIN\psql --username=test --host=localhost --list test
- name: Configuring git vars
uses: rlespinasse/github-slug-action@v4
- name: Setting up PHP ${{ matrix.php }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: ${{ matrix.extensions }}
ini-values: max_input_vars=5000
coverage: none
- name: Set git to use LF
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- name: Checking out code from ${{ env.GITHUB_REF_SLUG }}
uses: actions/checkout@v4
# Needs to be done after php is available, git configured and Moodle checkout has happened.
- name: Setting up moodle-exttests service
run: |
git clone https://github.com/moodlehq/moodle-exttests.git
nssm install php-built-in C:\tools\php\php.exe -S localhost:8080 -t D:\a\moodle\moodle\moodle-exttests
nssm start php-built-in
- name: Setting up redis service
run: |
choco install redis-64 --version 3.0.503 --no-progress
nssm install redis redis-server
nssm start redis
- name: Setting up PHPUnit
env:
dbtype: ${{ matrix.db }}
shell: bash
run: |
echo "pathtophp=$(which php)" >> $GITHUB_ENV # Inject installed pathtophp to env. The template config needs it.
cp .github/workflows/config-template.php config.php
mkdir ../moodledata
php admin/tool/phpunit/cli/init.php --no-composer-self-update
- name: Running PHPUnit tests
env:
dbtype: ${{ matrix.db }}
phpunit_options: ${{ secrets.phpunit_options }}
run: vendor/bin/phpunit $phpunit_options ${{ inputs.phpunit_extra_options }}
+3 -34
View File
@@ -9,24 +9,15 @@
#
# See gitignore(5) man page for more details
#
# Swap files (vim)
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Temporary files including undo
*~
#
/config.php
/lib/editor/tinymce/extra/tools/temp/
*~
*.swp
/tags
/TAGS
/cscope.*
/.patches/
/.idea/
.phpstorm.*
!.phpstorm.meta.php
/nbproject/
CVS
.DS_Store
@@ -34,25 +25,3 @@ CVS
/.project
/.buildpath
/.cache
.phpunit.result.cache
phpunit.xml
# Composer support. Do not ignore composer.json, or composer.lock. These should be shipped by us.
composer.phar
/vendor/
/behat.yml
*/yui/build/*/*-coverage.js
/lib/yuilib/*/build/*/*-coverage.js
# lib/yuilib/version/module/module-coverage.js
/lib/yuilib/*/*/*-coverage.js
atlassian-ide-plugin.xml
/node_modules/
/.vscode/
moodle-plugin-ci.phar
.eslintignore
.stylelintignore
/jsdoc
/admin/tool/componentlibrary/docs
/admin/tool/componentlibrary/hugo/site/data/my-index.json
.hugo_build.lock
phpcs.xml
jsconfig.json
-169
View File
@@ -1,169 +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/>.
/**
* This is a babel plugin to add the Moodle module names to the AMD modules
* as part of the transpiling process.
*
* In addition it will also add a return statement for the default export if the
* module is using default exports. This is a highly specific Moodle thing because
* we're transpiling to AMD and none of the existing Babel 7 plugins work correctly.
*
* This will fix the issue where an ES6 module using "export default Foo" will be
* transpiled into an AMD module that returns {default: Foo}; Instead it will now
* just simply return Foo.
*
* Note: This means all other named exports in that module are ignored and won't be
* exported.
*
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
"use strict";
/* eslint-env node */
module.exports = ({template, types}) => {
const fs = require('fs');
const path = require('path');
const cwd = process.cwd();
const ComponentList = require(path.join(process.cwd(), '.grunt', 'components.js'));
/**
* Search the list of components that match the given file name
* and return the Moodle component for that file, if found.
*
* Throw an exception if no matching component is found.
*
* @throws {Error}
* @param {string} searchFileName The file name to look for.
* @return {string} Moodle component
*/
function getModuleNameFromFileName(searchFileName) {
searchFileName = fs.realpathSync(searchFileName);
const relativeFileName = searchFileName.replace(`${cwd}${path.sep}`, '').replace(/\\/g, '/');
const [componentPath, file] = relativeFileName.split('/amd/src/');
const fileName = file.replace('.js', '');
// Check subsystems first which require an exact match.
const componentName = ComponentList.getComponentFromPath(componentPath);
if (componentName) {
return `${componentName}/${fileName}`;
}
// This matches the previous PHP behaviour that would throw an exception
// if it couldn't parse an AMD file.
throw new Error(`Unable to find module name for ${searchFileName} (${componentPath}::${file}}`);
}
/**
* This is heavily inspired by the babel-plugin-add-module-exports plugin.
* See: https://github.com/59naga/babel-plugin-add-module-exports
*
* This is used when we detect a module using "export default Foo;" to make
* sure the transpiled code just returns Foo directly rather than an object
* with the default property (i.e. {default: Foo}).
*
* Note: This means that we can't support modules that combine named exports
* with a default export.
*
* @param {String} path
* @param {String} exportObjectName
*/
function addModuleExportsDefaults(path, exportObjectName) {
const rootPath = path.findParent(path => {
return path.key === 'body' || !path.parentPath;
});
// HACK: `path.node.body.push` instead of path.pushContainer(due doesn't work in Plugin.post).
// This is hardcoded to work specifically with AMD.
rootPath.node.body.push(template(`return ${exportObjectName}.default`)());
}
return {
pre() {
this.seenDefine = false;
this.addedReturnForDefaultExport = false;
},
visitor: {
// Plugin ordering is only respected if we visit the "Program" node.
// See: https://babeljs.io/docs/en/plugins.html#plugin-preset-ordering
//
// We require this to run after the other AMD module transformation so
// let's visit the "Program" node.
Program: {
exit(path) {
path.traverse({
CallExpression(path) {
// If we find a "define" function call.
if (!this.seenDefine && path.get('callee').isIdentifier({name: 'define'})) {
// We only want to modify the first instance of define that we find.
this.seenDefine = true;
// Get the Moodle component for the file being processed.
var moduleName = getModuleNameFromFileName(this.file.opts.filename);
// The function signature of `define()` is:
// define = function (name, deps, callback) {...}
// Ensure that if the moduel supplied its own name that it is replaced.
if (path.node.arguments.length > 0) {
// Ensure that there is only one name.
if (path.node.arguments[0].type === 'StringLiteral') {
// eslint-disable-next-line
console.log(`Replacing module name '${path.node.arguments[0].extra.rawValue}' with ${moduleName}`);
path.node.arguments.shift();
}
}
// Add the module name as the first argument to the define function.
path.node.arguments.unshift(types.stringLiteral(moduleName));
// Add a space after the define function in the built file so that previous versions
// of Moodle will not try to add the module name to the file when it's being served
// by PHP. This forces the regex in PHP to not match for this file.
path.node.callee.name = 'define ';
}
// Check for any Object.defineProperty('exports', 'default') calls.
if (!this.addedReturnForDefaultExport && path.get('callee').matchesPattern('Object.defineProperty')) {
const [identifier, prop] = path.get('arguments');
const objectName = identifier.get('name').node;
const propertyName = prop.get('value').node;
if ((objectName === 'exports' || objectName === '_exports') && propertyName === 'default') {
addModuleExportsDefaults(path, objectName);
this.addedReturnForDefaultExport = true;
}
}
},
AssignmentExpression(path) {
// Check for an exports.default assignments.
if (
!this.addedReturnForDefaultExport &&
(
path.get('left').matchesPattern('exports.default') ||
path.get('left').matchesPattern('_exports.default')
)
) {
const objectName = path.get('left.object.name').node;
addModuleExportsDefaults(path, objectName);
this.addedReturnForDefaultExport = true;
}
}
}, this);
}
}
}
};
};
-394
View File
@@ -1,394 +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/>.
/**
* Helper functions for working with Moodle component names, directories, and sources.
*
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
"use strict";
/* eslint-env node */
/** @var {Object} A list of subsystems in Moodle */
const componentData = {};
/**
* Load details of all moodle modules.
*
* @returns {object}
*/
const fetchComponentData = () => {
const fs = require('fs');
const path = require('path');
const glob = require('glob');
const gruntFilePath = process.cwd();
if (!Object.entries(componentData).length) {
componentData.subsystems = {};
componentData.pathList = [];
// Fetch the component definiitions from the distributed JSON file.
const components = JSON.parse(fs.readFileSync(`${gruntFilePath}/lib/components.json`));
// Build the list of moodle subsystems.
componentData.subsystems.lib = 'core';
componentData.pathList.push(process.cwd() + path.sep + 'lib');
for (const [component, thisPath] of Object.entries(components.subsystems)) {
if (thisPath) {
// Prefix "core_" to the front of the subsystems.
componentData.subsystems[thisPath] = `core_${component}`;
componentData.pathList.push(process.cwd() + path.sep + thisPath);
}
}
// 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]) => {
// We don't allow any code in this place..?
glob.sync(`${pluginTypePath}/*/version.php`).forEach(versionPath => {
const componentPath = fs.realpathSync(path.dirname(versionPath));
const componentName = path.basename(componentPath);
const frankenstyleName = `${pluginType}_${componentName}`;
componentData.components[`${pluginTypePath}/${componentName}`] = frankenstyleName;
componentData.pathList.push(componentPath);
// Look for any subplugins.
const subPluginConfigurationFile = `${componentPath}/db/subplugins.json`;
if (fs.existsSync(subPluginConfigurationFile)) {
const subpluginList = JSON.parse(fs.readFileSync(fs.realpathSync(subPluginConfigurationFile)));
Object.entries(subpluginList.plugintypes).forEach(([subpluginType, subpluginTypePath]) => {
glob.sync(`${subpluginTypePath}/*/version.php`).forEach(versionPath => {
const componentPath = fs.realpathSync(path.dirname(versionPath));
const componentName = path.basename(componentPath);
const frankenstyleName = `${subpluginType}_${componentName}`;
componentData.components[`${subpluginTypePath}/${componentName}`] = frankenstyleName;
componentData.pathList.push(componentPath);
});
});
}
});
});
}
return componentData;
};
/**
* Get the list of component paths.
*
* @param {string} relativeTo
* @returns {array}
*/
const getComponentPaths = (relativeTo = '') => fetchComponentData().pathList.map(componentPath => {
return componentPath.replace(relativeTo, '');
});
/**
* Get the list of paths to build AMD sources.
*
* @returns {Array}
*/
const getAmdSrcGlobList = () => {
const globList = [];
fetchComponentData().pathList.forEach(componentPath => {
globList.push(`${componentPath}/amd/src/*.js`);
globList.push(`${componentPath}/amd/src/**/*.js`);
});
return globList;
};
/**
* Get the list of paths to build YUI sources.
*
* @param {String} relativeTo
* @returns {Array}
*/
const getYuiSrcGlobList = relativeTo => {
const globList = [];
fetchComponentData().pathList.forEach(componentPath => {
const relativeComponentPath = componentPath.replace(relativeTo, '');
globList.push(`${relativeComponentPath}/yui/src/**/*.js`);
});
return globList;
};
/**
* Get the list of paths to thirdpartylibs.xml.
*
* @param {String} relativeTo
* @returns {Array}
*/
const getThirdPartyLibsList = relativeTo => {
const fs = require('fs');
const path = require('path');
return fetchComponentData().pathList
.map(componentPath => path.relative(relativeTo, componentPath) + '/thirdpartylibs.xml')
.map(componentPath => componentPath.replace(/\\/g, '/'))
.filter(path => fs.existsSync(path))
.sort();
};
/**
* Get the list of thirdparty library paths.
*
* @returns {array}
*/
const getThirdPartyPaths = () => {
const DOMParser = require('@xmldom/xmldom').DOMParser;
const fs = require('fs');
const path = require('path');
const xpath = require('xpath');
const thirdpartyfiles = getThirdPartyLibsList(fs.realpathSync('./'));
const libs = ['node_modules/', 'vendor/'];
const addLibToList = lib => {
if (!lib.match('\\*') && fs.statSync(lib).isDirectory()) {
// Ensure trailing slash on dirs.
lib = lib.replace(/\/?$/, '/');
}
// Look for duplicate paths before adding to array.
if (libs.indexOf(lib) === -1) {
libs.push(lib);
}
};
thirdpartyfiles.forEach(function(file) {
const dirname = path.dirname(file);
const xmlContent = fs.readFileSync(file, 'utf8');
const doc = new DOMParser().parseFromString(xmlContent);
const nodes = xpath.select("/libraries/library/location/text()", doc);
nodes.forEach(function(node) {
let lib = path.posix.join(dirname, node.toString());
addLibToList(lib);
});
});
return libs;
};
/**
* Find the name of the component matching the specified path.
*
* @param {String} path
* @returns {String|null} Name of matching component.
*/
const getComponentFromPath = path => {
const componentList = fetchComponentData().components;
if (componentList.hasOwnProperty(path)) {
return componentList[path];
}
return null;
};
/**
* Check whether the supplied path, relative to the Gruntfile.js, is in a known component.
*
* @param {String} checkPath The path to check. This can be with either Windows, or Linux directory separators.
* @returns {String|null}
*/
const getOwningComponentDirectory = checkPath => {
const path = require('path');
// Fetch all components into a reverse sorted array.
// This ensures that components which are within the directory of another component match first.
const pathList = Object.keys(fetchComponentData().components).sort().reverse();
for (const componentPath of pathList) {
// If the componentPath is the directory being checked, it will be empty.
// If the componentPath is a parent of the directory being checked, the relative directory will not start with ..
if (!path.relative(componentPath, checkPath).startsWith('..')) {
return componentPath;
}
}
return null;
};
/**
* Get the latest tag in a remote GitHub repository.
*
* @param {string} url The remote repository.
* @returns {Array}
*/
const getRepositoryTags = async(url) => {
const gtr = require('git-tags-remote');
try {
const tags = await gtr.get(url);
if (tags !== undefined) {
return tags;
}
} catch (error) {
return [];
}
return [];
};
/**
* Get the list of thirdparty libraries that could be upgraded.
*
* @returns {Array}
*/
const getThirdPartyLibsUpgradable = async() => {
const libraries = getThirdPartyLibsData().filter((library) => !!library.repository);
const upgradableLibraries = [];
const versionCompare = (a, b) => {
if (a === b) {
return 0;
}
const aParts = a.split('.');
const bParts = b.split('.');
for (let i = 0; i < Math.min(aParts.length, bParts.length); i++) {
const aPart = parseInt(aParts[i], 10);
const bPart = parseInt(bParts[i], 10);
if (aPart > bPart) {
// 1.1.0 > 1.0.9
return 1;
} else if (aPart < bPart) {
// 1.0.9 < 1.1.0
return -1;
} else {
// Same version.
continue;
}
}
if (aParts.length > bParts.length) {
// 1.0.1 > 1.0
return 1;
}
// 1.0 < 1.0.1
return -1;
};
for (let library of libraries) {
upgradableLibraries.push(
getRepositoryTags(library.repository).then((tagMap) => {
library.version = library.version.replace(/^v/, '');
const currentVersion = library.version.replace(/moodle-/, '');
const currentMajorVersion = library.version.split('.')[0];
const tags = [...tagMap]
.map((tagData) => tagData[0])
.filter((tag) => !tag.match(/(alpha|beta|rc)/))
.map((tag) => tag.replace(/^v/, ''))
.sort((a, b) => versionCompare(b, a));
if (!tags.length) {
library.warning = "Unable to find any comparable tags.";
return library;
}
library.latestVersion = tags[0];
tags.some((tag) => {
if (!tag) {
return false;
}
// See if the version part matches.
const majorVersion = tag.split('.')[0];
if (majorVersion === currentMajorVersion) {
library.latestSameMajorVersion = tag;
return true;
}
return false;
});
if (versionCompare(currentVersion, library.latestVersion) > 0) {
// Moodle somehow has a newer version than the latest version.
library.warning = `Newer version found: ${currentVersion} > ${library.latestVersion} for ${library.name}`;
return library;
}
if (library.version !== library.latestVersion) {
// Delete version and add it again at the end of the array. That way, current and new will stay closer.
delete library.version;
library.version = currentVersion;
return library;
}
return null;
})
);
}
return (await Promise.all(upgradableLibraries)).filter((library) => !!library);
};
/**
* Get the list of thirdparty libraries.
*
* @returns {Array}
*/
const getThirdPartyLibsData = () => {
const DOMParser = require('@xmldom/xmldom').DOMParser;
const fs = require('fs');
const xpath = require('xpath');
const path = require('path');
const libraryList = [];
const libraryFields = [
'location',
'name',
'version',
'repository',
];
const thirdpartyfiles = getThirdPartyLibsList(fs.realpathSync('./'));
thirdpartyfiles.forEach(function(libraryPath) {
const xmlContent = fs.readFileSync(libraryPath, 'utf8');
const doc = new DOMParser().parseFromString(xmlContent);
const libraries = xpath.select("/libraries/library", doc);
for (const library of libraries) {
const libraryData = [];
for (const field of libraryFields) {
libraryData[field] = xpath.select(`${field}/text()`, library)?.toString();
}
libraryData.location = path.join(path.dirname(libraryPath), libraryData.location);
libraryList.push(libraryData);
}
});
return libraryList.sort((a, b) => a.location.localeCompare(b.location));
};
module.exports = {
fetchComponentData,
getAmdSrcGlobList,
getComponentFromPath,
getComponentPaths,
getOwningComponentDirectory,
getYuiSrcGlobList,
getThirdPartyLibsList,
getThirdPartyPaths,
getThirdPartyLibsUpgradable,
};
-19
View File
@@ -1,19 +0,0 @@
# Moodle JavaScript Documentation
```
.-..-.
_____ | || |
/____/-.---_ .---. .---. .-.| || | .---.
| | _ _ |/ _ \/ _ \/ _ || |/ __ \
* | | | | | || |_| || |_| || |_| || || |___/
|_| |_| |_|\_____/\_____/\_____||_|\_____)
Moodle - the world's open source learning platform
```
## About
This generated documentation includes API documentation for JavaScript written in the AMD and ES2015 module formats within Moodle.
## Related information
See [https://moodledev.io](https://moodledev.io) for other related Developer Documentation.
-131
View File
@@ -1,131 +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/>.
/**
* Helper functions for working with Moodle component names, directories, and sources.
*
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
"use strict";
/* eslint-env node */
// Do not include any plugins as stanard.
const plugins = [];
plugins.push('plugins/markdown');
/**
* Get the source configuration.
*
* @return {Object}
*/
const getSource = () => {
const glob = require('glob');
const path = require('path');
const ComponentList = require(path.resolve('.grunt/components.js'));
const thirdPartyPaths = ComponentList.getThirdPartyPaths();
const source = {
include: [],
includePattern: ".+\\.js$",
};
let includeList = [];
ComponentList.getAmdSrcGlobList().forEach(async pattern => {
includeList.push(...glob.sync(pattern));
});
const cwdLength = process.cwd().length + 1;
includeList.forEach(path => {
if (source.include.indexOf(path) !== -1) {
// Ensure no duplicates.
return;
}
const relPath = path.substring(cwdLength);
if (thirdPartyPaths.indexOf(relPath) !== -1) {
return;
}
source.include.push(path);
});
source.include.push('.grunt/jsdoc/README.md');
return source;
};
const tags = {
// Allow the use of unknown tags.
// We have a lot of legacy uses of these.
allowUnknownTags: true,
// We make use of jsdoc and closure dictionaries as standard.
dictionaries: [
'jsdoc',
'closure',
],
};
// Template configuraiton.
const templates = {
cleverLinks: false,
monospaceLinks: false,
};
module.exports = {
opts: {
destination: "./jsdoc/",
template: "node_modules/docdash",
},
plugins,
recurseDepth: 10,
source: getSource(),
sourceType: 'module',
tags,
templates,
docdash: {
collapse: true,
search: true,
sort: true,
sectionOrder: [
"Namespaces",
"Modules",
"Events",
"Classes",
"Externals",
"Mixins",
"Tutorials",
"Interfaces"
],
"menu": {
"Developer Docs": {
href: "https://moodledev.io",
target: "_blank",
"class": "menu-item",
id: "devdocs"
},
"MDN Docs": {
href: "https://developer.mozilla.org/en-US/docs/Web/JavaScript",
target: "_blank",
"class": "menu-item",
id: "mdndocs",
},
},
typedefs: true,
},
};
-142
View File
@@ -1,142 +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/>.
/* jshint node: true, browser: false */
/* eslint-env node */
/**
* Component Library build tasks.
*
* @copyright 2021 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
module.exports = grunt => {
/**
* Get a child path of the component library.
*
* @param {string} path
* @returns {string}
*/
const getCLPath = path => `admin/tool/componentlibrary/${path}`;
/**
* Get a spawn handler.
*
* This is a generic function to write the spawn output, and then to exit if required and mark the async task as
* complete.
*
* @param {Promise} done
* @returns {function}
*/
const getSpawnHandler = done => (error, result, code) => {
grunt.log.write(result);
if (error) {
grunt.log.error(result.stdout);
process.exit(code);
}
done();
};
/**
* Spawn a function against Node with the provided args.
*
* @param {array} args
* @returns {object}
*/
const spawnNodeCall = (args) => grunt.util.spawn({
cmd: 'node',
args,
}, getSpawnHandler(grunt.task.current.async()));
/**
* Build the docs using Hugo.
*
* @returns {Object} Reference to the spawned task
*/
const docsBuild = () => spawnNodeCall([
'node_modules/hugo-bin/cli.js',
'--config', getCLPath('config.yml'),
'--cleanDestinationDir',
]);
/**
* Build the docs index using the hugo-lunr-indexer.
*
* @returns {Object} Reference to the spawned task
*/
const indexBuild = () => spawnNodeCall([
'node_modules/hugo-lunr-indexer/bin/hli.js',
'-i', getCLPath('content/**'),
'-o', getCLPath('hugo/site/data/my-index.json'),
'-l', 'yaml',
'-d', '---',
]);
/**
* Build the hugo CSS.
*
* @returns {Object} Reference to the spawned task
*/
const cssBuild = () => spawnNodeCall([
'node_modules/sass/sass.js',
'--style', 'expanded',
'--source-map',
'--embed-sources',
'--precision', 6,
'--load-path', process.cwd(),
getCLPath('hugo/scss/docs.scss'),
getCLPath('hugo/dist/css/docs.css'),
]);
// Register the various component library tasks.
grunt.registerTask('componentlibrary:docsBuild', 'Build the component library', docsBuild);
grunt.registerTask('componentlibrary:cssBuild', 'Build the component library', cssBuild);
grunt.registerTask('componentlibrary:indexBuild', 'Build the component library', indexBuild);
grunt.registerTask('componentlibrary', 'Build the component library', [
'componentlibrary:docsBuild',
'componentlibrary:cssBuild',
'componentlibrary:indexBuild',
]);
grunt.config.merge({
watch: {
componentLibraryDocs: {
files: [
getCLPath('content/**/*.md'),
getCLPath('hugo'),
],
tasks: ['componentlibrary:docsBuild', 'componentlibrary:indexBuild'],
},
componentLibraryCSS: {
files: [
getCLPath('hugo/scss/**/*.scss'),
'hugo',
],
tasks: ['componentlibrary:cssBuild'],
},
},
});
// Add the 'componentlibrary' task as a startup task.
grunt.moodleEnv.startupTasks.push('componentlibrary');
return {
docsBuild,
cssBuild,
indexBuild,
};
};
-64
View File
@@ -1,64 +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/>.
/* jshint node: true, browser: false */
/* eslint-env node */
/**
* @copyright 2021 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
module.exports = grunt => {
const files = grunt.moodleEnv.files;
// Project configuration.
grunt.config.merge({
eslint: {
// Even though warnings dont stop the build we don't display warnings by default because
// at this moment we've got too many core warnings.
// To display warnings call: grunt eslint --show-lint-warnings
// To fail on warnings call: grunt eslint --max-lint-warnings=0
// Also --max-lint-warnings=-1 can be used to display warnings but not fail.
options: {
quiet: (!grunt.option('show-lint-warnings')) && (typeof grunt.option('max-lint-warnings') === 'undefined'),
maxWarnings: ((typeof grunt.option('max-lint-warnings') !== 'undefined') ? grunt.option('max-lint-warnings') : -1)
},
// Check AMD src files.
amd: {src: files ? files : grunt.moodleEnv.amdSrc},
// Check YUI module source files.
yui: {src: files ? files : grunt.moodleEnv.yuiSrc},
},
});
grunt.loadNpmTasks('grunt-eslint');
// On watch, we dynamically modify config to build only affected files. This
// method is slightly complicated to deal with multiple changed files at once (copied
// from the grunt-contrib-watch readme).
let changedFiles = Object.create(null);
const onChange = grunt.util._.debounce(function() {
const files = Object.keys(changedFiles);
grunt.config('eslint.amd.src', files);
grunt.config('eslint.yui.src', files);
changedFiles = Object.create(null);
}, 200);
grunt.event.on('watch', (action, filepath) => {
changedFiles[filepath] = action;
onChange();
});
};
-92
View File
@@ -1,92 +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/>.
/* jshint node: true, browser: false */
/* eslint-env node */
/**
* @copyright 2021 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
module.exports = grunt => {
/**
* Get the list of feature files to pass to the gherkin linter.
*
* @returns {Array}
*/
const getGherkinLintTargets = () => {
if (grunt.moodleEnv.files) {
// Specific files were requested. Only check these.
return grunt.moodleEnv.files;
}
if (grunt.moodleEnv.inComponent) {
return [`${grunt.moodleEnv.runDir}/tests/behat/*.feature`];
}
return ['**/tests/behat/*.feature'];
};
const handler = function() {
const done = this.async();
const options = grunt.config('gherkinlint.options');
// Grab the gherkin-lint linter and required scaffolding.
const linter = require('gherkin-lint/dist/linter.js');
const featureFinder = require('gherkin-lint/dist/feature-finder.js');
const configParser = require('gherkin-lint/dist/config-parser.js');
const formatter = require('gherkin-lint/dist/formatters/stylish.js');
// Run the linter.
return linter.lint(
featureFinder.getFeatureFiles(grunt.file.expand(options.files)),
configParser.getConfiguration(configParser.defaultConfigFileName)
)
.then(results => {
// Print the results out uncondtionally.
formatter.printResults(results);
return results;
})
.then(results => {
// Report on the results.
// The done function takes a bool whereby a falsey statement causes the task to fail.
return results.every(result => result.errors.length === 0);
})
.then(done); // eslint-disable-line promise/no-callback-in-promise
};
grunt.registerTask('gherkinlint', 'Run gherkinlint against the current directory', handler);
grunt.config.set('gherkinlint', {
options: {
files: getGherkinLintTargets(),
}
});
grunt.config.merge({
watch: {
gherkinlint: {
files: [grunt.moodleEnv.inComponent ? 'tests/behat/*.feature' : '**/tests/behat/*.feature'],
tasks: ['gherkinlint'],
},
},
});
// Add the 'gherkinlint' task as a startup task.
grunt.moodleEnv.startupTasks.push('gherkinlint');
return handler;
};
-100
View File
@@ -1,100 +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/>.
/* jshint node: true, browser: false */
/* eslint-env node */
/**
* @copyright 2021 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
module.exports = grunt => {
/**
* Generate the PHPCS configuration.
*
* @param {Object} thirdPartyPaths
*/
const phpcsIgnore = (thirdPartyPaths) => {
const {toXML} = require('jstoxml');
const config = {
_name: 'ruleset',
_attrs: {
name: "MoodleCore",
},
_content: [
{
rule: {
_attrs: {
ref: './phpcs.xml.dist',
},
},
},
],
};
thirdPartyPaths.forEach(library => {
config._content.push({
'exclude-pattern': library,
});
});
grunt.file.write('phpcs.xml', toXML(config, {
header: true,
indent: ' ',
}) + "\n");
};
/**
* Generate ignore files (utilising thirdpartylibs.xml data)
*/
const handler = function() {
const path = require('path');
const ComponentList = require(path.join(process.cwd(), '.grunt', 'components.js'));
// An array of paths to third party directories.
const thirdPartyPaths = ComponentList.getThirdPartyPaths();
// Generate .eslintignore.
const eslintIgnores = [
'# Generated by "grunt ignorefiles"',
// Do not ignore the .grunt directory.
'!/.grunt',
// Ignore all yui/src meta directories and build directories.
'*/**/yui/src/*/meta/',
'*/**/build/',
].concat(thirdPartyPaths);
grunt.file.write('.eslintignore', eslintIgnores.join('\n') + '\n');
// Generate .stylelintignore.
const stylelintIgnores = [
'# Generated by "grunt ignorefiles"',
'**/yui/build/*',
'theme/boost/style/moodle.css',
'theme/classic/style/moodle.css',
'jsdoc/styles/*.css',
'admin/tool/componentlibrary/hugo/dist/css/docs.css',
].concat(thirdPartyPaths);
grunt.file.write('.stylelintignore', stylelintIgnores.join('\n') + '\n');
phpcsIgnore(thirdPartyPaths);
};
grunt.registerTask('ignorefiles', 'Generate ignore files for linters', handler);
return handler;
};
-215
View File
@@ -1,215 +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/>.
/* jshint node: true, browser: false */
/* eslint-env node */
/**
* @copyright 2021 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Function to generate the destination for the minification task
* (e.g. build/file.min.js). This function will be passed to
* the rename property of files array when building dynamically:
* http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically
*
* @param {String} destPath the current destination
* @param {String} srcPath the matched src path
* @return {String} The rewritten destination path.
*/
const babelRename = function(destPath, srcPath) {
destPath = srcPath.replace(`amd/src`, `amd/build`);
destPath = destPath.replace(/\.js$/, '.min.js');
return destPath;
};
module.exports = grunt => {
// Load the Ignorefiles tasks.
require('./ignorefiles')(grunt);
// Load the Shifter tasks.
require('./shifter')(grunt);
// Load ESLint.
require('./eslint')(grunt);
// Load jsconfig.
require('./jsconfig')(grunt);
// Load JSDoc.
require('./jsdoc')(grunt);
const path = require('path');
// Register JS tasks.
grunt.registerTask('yui', ['eslint:yui', 'shifter']);
grunt.registerTask('amd', ['ignorefiles', 'eslint:amd', 'rollup']);
grunt.registerTask('js', ['amd', 'yui']);
// Register NPM tasks.
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-rollup');
const babelTransform = require('@babel/core').transform;
const babel = (options = {}) => {
return {
name: 'babel',
transform: (code, id) => {
grunt.log.debug(`Transforming ${id}`);
options.filename = id;
const transformed = babelTransform(code, options);
return {
code: transformed.code,
map: transformed.map
};
}
};
};
// Note: We have to use a rate limit plugin here because rollup runs all tasks asynchronously and in parallel.
// When we kick off a full run, if we kick off a rollup of every file this will fork-bomb the machine.
// To work around this we use a concurrent Promise queue based on the number of available processors.
const rateLimit = () => {
const queue = [];
let queueRunner;
const startQueue = () => {
if (queueRunner) {
return;
}
queueRunner = setTimeout(() => {
const limit = Math.max(1, require('os').cpus().length / 2);
grunt.log.debug(`Starting rollup with queue size of ${limit}`);
runQueue(limit);
}, 100);
};
// The queue runner will run the next `size` items in the queue.
const runQueue = (size = 1) => {
queue.splice(0, size).forEach(resolve => {
grunt.log.debug(`Item resolved. Kicking off next one.`);
resolve();
});
};
return {
name: 'ratelimit',
// The options hook is run in parallel.
// We can return an unresolved Promise which is queued for later resolution.
options: async(options) => {
return new Promise(resolve => {
queue.push(resolve);
startQueue();
return options;
});
},
// When an item in the queue completes, start the next item in the queue.
generateBundle: (options, bundle) => {
grunt.log.debug(`Finished output phase for ${Object.keys(bundle).join(', ')}`);
runQueue();
},
};
};
const terser = require('rollup-plugin-terser').terser;
grunt.config.merge({
rollup: {
options: {
format: 'esm',
dir: 'output',
sourcemap: true,
treeshake: false,
context: 'window',
plugins: [
rateLimit(),
babel({
sourceMaps: true,
comments: false,
compact: false,
plugins: [
'transform-es2015-modules-amd-lazy',
'system-import-transformer',
// This plugin modifies the Babel transpiling for "export default"
// so that if it's used then only the exported value is returned
// by the generated AMD module.
//
// It also adds the Moodle plugin name to the AMD module definition
// so that it can be imported as expected in other modules.
path.resolve('.grunt/babel-plugin-add-module-to-define.js')
],
presets: [
['@babel/preset-env', {
modules: false,
useBuiltIns: false
}]
]
}),
terser({
// Do not mangle variables.
// Makes debugging easier.
mangle: false,
}),
],
},
dist: {
files: [{
expand: true,
src: grunt.moodleEnv.files ? grunt.moodleEnv.files : grunt.moodleEnv.amdSrc,
rename: babelRename
}],
},
},
});
grunt.config.merge({
watch: {
amd: {
files: grunt.moodleEnv.inComponent
? ['amd/src/*.js', 'amd/src/**/*.js']
: ['**/amd/src/**/*.js'],
tasks: ['amd']
},
},
});
// Add the 'js' task as a startup task.
grunt.moodleEnv.startupTasks.push('js');
// On watch, we dynamically modify config to build only affected files. This
// method is slightly complicated to deal with multiple changed files at once (copied
// from the grunt-contrib-watch readme).
let changedFiles = Object.create(null);
const onChange = grunt.util._.debounce(function() {
const files = Object.keys(changedFiles);
grunt.config('rollup.dist.files', [{expand: true, src: files, rename: babelRename}]);
changedFiles = Object.create(null);
}, 200);
grunt.event.on('watch', function(action, filepath) {
changedFiles[filepath] = action;
onChange();
});
return {
babelRename,
};
};
-53
View File
@@ -1,53 +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/>.
/* jshint node: true, browser: false */
/* eslint-env node */
/**
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
const configuration = {
compilerOptions: {
baseUrl: ".",
paths: {
},
target: "es2015",
allowSyntheticDefaultImports: false,
},
exclude: [
"node_modules",
],
include: [],
};
module.exports = (grunt) => {
const handler = () => {
const jsconfigData = Object.assign({}, configuration);
const path = require('path');
const {fetchComponentData} = require(path.join(process.cwd(), '.grunt', 'components.js'));
const componentData = fetchComponentData().components;
for (const [thisPath, component] of Object.entries(componentData)) {
jsconfigData.compilerOptions.paths[`${component}/*`] = [`${thisPath}/amd/src/*`];
jsconfigData.include.push(`${thisPath}/amd/src/**/*`);
}
grunt.file.write('jsconfig.json', JSON.stringify(jsconfigData, null, " ") + "\n");
};
grunt.registerTask('jsconfig', 'Generate a jsconfig configuration compatible with the LSP', handler);
};
-52
View File
@@ -1,52 +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/>.
/* jshint node: true, browser: false */
/* eslint-env node */
/**
* @copyright 2021 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
module.exports = (grunt) => {
const path = require('path');
grunt.registerTask('jsdoc', 'Generate JavaScript documentation using jsdoc', function() {
const done = this.async();
const configuration = path.resolve('.grunt/jsdoc/jsdoc.conf.js');
grunt.util.spawn({
cmd: 'jsdoc',
args: [
'--configure',
configuration,
]
}, function(error, result, code) {
if (result.stdout) {
grunt.log.write(result.stdout);
}
if (result.stderr) {
grunt.log.error(result.stderr);
}
if (error) {
grunt.fail.fatal(`JSDoc failed with error code ${code}`);
} else {
grunt.log.write('JSDoc completed successfully'.green);
}
done();
});
});
};
-40
View File
@@ -1,40 +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/>.
/* jshint node: true, browser: false */
/* eslint-env node */
/**
* @copyright 2021 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
module.exports = grunt => {
grunt.loadNpmTasks('grunt-sass');
grunt.config.merge({
sass: {
dist: {
files: {
"theme/boost/style/moodle.css": "theme/boost/scss/preset/default.scss",
"theme/classic/style/moodle.css": "theme/classic/scss/classicgrunt.scss"
}
},
options: {
implementation: require('sass'),
includePaths: ["theme/boost/scss/", "theme/classic/scss/"]
}
},
});
};
-155
View File
@@ -1,155 +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/>.
/* jshint node: true, browser: false */
/* eslint-env node */
/**
* @copyright 2021 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/* eslint-env node */
module.exports = grunt => {
/**
* Shifter task. Is configured with a path to a specific file or a directory,
* in the case of a specific file it will work out the right module to be built.
*
* Note that this task runs the invidiaul shifter jobs async (becase it spawns
* so be careful to to call done().
*/
const handler = function() {
const done = this.async();
const options = grunt.config('shifter.options');
const async = require('async');
const path = require('path');
// Run the shifter processes one at a time to avoid confusing output.
async.eachSeries(options.paths, function(src, filedone) {
var args = [];
args.push(path.normalize(process.cwd() + '/node_modules/shifter/bin/shifter'));
// Always ignore the node_modules directory.
args.push('--excludes', 'node_modules');
// Determine the most appropriate options to run with based upon the current location.
if (grunt.file.isMatch('**/yui/**/*.js', src)) {
// When passed a JS file, build our containing module (this happen with
// watch).
grunt.log.debug('Shifter passed a specific JS file');
src = path.dirname(path.dirname(src));
options.recursive = false;
} else if (grunt.file.isMatch('**/yui/src', src)) {
// When in a src directory --walk all modules.
grunt.log.debug('In a src directory');
args.push('--walk');
options.recursive = false;
} else if (grunt.file.isMatch('**/yui/src/*', src)) {
// When in module, only build our module.
grunt.log.debug('In a module directory');
options.recursive = false;
} else if (grunt.file.isMatch('**/yui/src/*/js', src)) {
// When in module src, only build our module.
grunt.log.debug('In a source directory');
src = path.dirname(src);
options.recursive = false;
}
if (grunt.option('watch')) {
grunt.fail.fatal('The --watch option has been removed, please use `grunt watch` instead');
}
// Add the stderr option if appropriate
if (grunt.option('verbose')) {
args.push('--lint-stderr');
}
if (grunt.option('no-color')) {
args.push('--color=false');
}
var execShifter = function() {
grunt.log.ok("Running shifter on " + src);
grunt.util.spawn({
cmd: "node",
args: args,
opts: {cwd: src, stdio: 'inherit', env: process.env}
}, function(error, result, code) {
if (code) {
grunt.fail.fatal('Shifter failed with code: ' + code);
} else {
grunt.log.ok('Shifter build complete.');
filedone();
}
});
};
// Actually run shifter.
if (!options.recursive) {
execShifter();
} else {
// Check that there are yui modules otherwise shifter ends with exit code 1.
if (grunt.file.expand({cwd: src}, '**/yui/src/**/*.js').length > 0) {
args.push('--recursive');
execShifter();
} else {
grunt.log.ok('No YUI modules to build.');
filedone();
}
}
}, done);
};
// Register the shifter task.
grunt.registerTask('shifter', 'Run Shifter against the current directory', handler);
// Configure it.
grunt.config.set('shifter', {
options: {
recursive: true,
// Shifter takes a relative path.
paths: grunt.moodleEnv.files ? grunt.moodleEnv.files : [grunt.moodleEnv.runDir]
}
});
grunt.config.merge({
watch: {
yui: {
files: grunt.moodleEnv.inComponent
? ['yui/src/*.json', 'yui/src/**/*.js']
: ['**/yui/src/**/*.js'],
tasks: ['yui']
},
},
});
// On watch, we dynamically modify config to build only affected files. This
// method is slightly complicated to deal with multiple changed files at once (copied
// from the grunt-contrib-watch readme).
let changedFiles = Object.create(null);
const onChange = grunt.util._.debounce(function() {
const files = Object.keys(changedFiles);
grunt.config('shifter.options.paths', files);
changedFiles = Object.create(null);
}, 200);
grunt.event.on('watch', (action, filepath) => {
changedFiles[filepath] = action;
onChange();
});
return handler;
};
-46
View File
@@ -1,46 +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/>.
/* jshint node: true, browser: false */
/* eslint-env node */
/**
* @copyright 2021 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
module.exports = grunt => {
/**
* Generate ignore files (utilising thirdpartylibs.xml data)
*/
const handler = function() {
const path = require('path');
// Are we in a YUI directory?
if (path.basename(path.resolve(grunt.moodleEnv.cwd, '../../')) == 'yui') {
grunt.task.run('yui');
// Are we in an AMD directory?
} else if (grunt.moodleEnv.inAMD) {
grunt.task.run('amd');
} else {
// Run all of the requested startup tasks.
grunt.moodleEnv.startupTasks.forEach(taskName => grunt.task.run(taskName));
}
};
// Register the startup task.
grunt.registerTask('startup', 'Run the correct tasks for the current directory', handler);
return handler;
};
-35
View File
@@ -1,35 +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/>.
/* jshint node: true, browser: false */
/* eslint-env node */
/**
* @copyright 2021 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
module.exports = grunt => {
// Load the ignorefiles tasks.
require('./ignorefiles')(grunt);
// Load the Style Lint tasks.
require('./stylelint')(grunt);
// Load the SASS tasks.
require('./sass')(grunt);
// Add the 'css' task as a startup task.
grunt.moodleEnv.startupTasks.push('css');
};
-187
View File
@@ -1,187 +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/>.
/* jshint node: true, browser: false */
/* eslint-env node */
/**
* @copyright 2021 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
module.exports = grunt => {
const getCssConfigForFiles = files => {
return {
stylelint: {
css: {
// Use a fully-qualified path.
src: files,
options: {
quietDeprecationWarnings: true,
configOverrides: {
rules: {
// These rules have to be disabled in .stylelintrc for scss compat.
"at-rule-no-unknown": true,
}
}
}
},
},
};
};
const getScssConfigForFiles = files => {
return {
stylelint: {
scss: {
options: {
quietDeprecationWarnings: true,
customSyntax: 'postcss-scss',
},
src: files,
},
},
};
};
/**
* Register any stylelint tasks.
*
* @param {Object} grunt
* @param {Array} files
* @param {String} fullRunDir
*/
const registerStyleLintTasks = () => {
const glob = require('glob');
// The stylelinters do not handle the case where a configuration was provided but no files were included.
// Keep track of whether any files were found.
let hasCss = false;
let hasScss = false;
// The stylelint processors do not take a path argument. They always check all provided values.
// As a result we must check through each glob and determine if any files match the current directory.
const scssFiles = [];
const cssFiles = [];
const requestedFiles = grunt.moodleEnv.files;
if (requestedFiles) {
// Grunt was called with a files argument.
// Check whether each of the requested files matches either the CSS or SCSS source file list.
requestedFiles.forEach(changedFilePath => {
let matchesGlob;
// Check whether this watched path matches any watched SCSS file.
matchesGlob = grunt.moodleEnv.scssSrc.some(watchedPathGlob => {
return glob.sync(watchedPathGlob).indexOf(changedFilePath) !== -1;
});
if (matchesGlob) {
scssFiles.push(changedFilePath);
hasScss = true;
}
// Check whether this watched path matches any watched CSS file.
matchesGlob = grunt.moodleEnv.cssSrc.some(watchedPathGlob => {
return glob.sync(watchedPathGlob).indexOf(changedFilePath) !== -1;
});
if (matchesGlob) {
cssFiles.push(changedFilePath);
hasCss = true;
}
});
} else {
// Grunt was called without a list of files.
// The start directory (runDir) may be a child dir of the project.
// Check each scssSrc file to see if it's in the start directory.
// This means that we can lint just mod/*/styles.css if started in the mod directory.
grunt.moodleEnv.scssSrc.forEach(path => {
if (path.startsWith(grunt.moodleEnv.runDir)) {
scssFiles.push(path);
hasScss = true;
}
});
grunt.moodleEnv.cssSrc.forEach(path => {
if (path.startsWith(grunt.moodleEnv.runDir)) {
cssFiles.push(path);
hasCss = true;
}
});
}
// Register the tasks.
const scssTasks = ['sass'];
if (hasScss) {
grunt.config.merge(getScssConfigForFiles(scssFiles));
scssTasks.unshift('stylelint:scss');
}
scssTasks.unshift('ignorefiles');
const cssTasks = ['ignorefiles'];
if (hasCss) {
grunt.config.merge(getCssConfigForFiles(cssFiles));
cssTasks.push('stylelint:css');
}
// The tasks must be registered, even if empty to ensure a consistent command list.
// They jsut won't run anything.
grunt.registerTask('scss', scssTasks);
grunt.registerTask('rawcss', cssTasks);
};
// Register CSS tasks.
grunt.loadNpmTasks('grunt-stylelint');
// Register the style lint tasks.
registerStyleLintTasks();
grunt.registerTask('css', ['scss', 'rawcss']);
const getCoreThemeMatches = () => {
const scssMatch = 'scss/**/*.scss';
if (grunt.moodleEnv.inTheme) {
return [scssMatch];
}
if (grunt.moodleEnv.runDir.startsWith('theme')) {
return [`*/${scssMatch}`];
}
return [`theme/*/${scssMatch}`];
};
// Add the watch configuration for rawcss, and scss.
grunt.config.merge({
watch: {
rawcss: {
files: [
'**/*.css',
],
excludes: [
'**/moodle.css',
'**/editor.css',
'jsdoc/styles/*.css',
],
tasks: ['rawcss']
},
scss: {
files: getCoreThemeMatches(),
tasks: ['scss']
},
},
});
};
-42
View File
@@ -1,42 +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/>.
/* jshint node: true, browser: false */
/* eslint-env node */
/**
* @copyright 2023 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
module.exports = grunt => {
/**
* Generate upgradable third-party libraries (utilising thirdpartylibs.xml data)
*/
grunt.registerTask('upgradablelibs', 'Generate upgradable third-party libraries', async function() {
const done = this.async();
const path = require('path');
const ComponentList = require(path.join(process.cwd(), '.grunt', 'components.js'));
// An array of third party libraries that have a newer version in their repositories.
const thirdPartyLibs = await ComponentList.getThirdPartyLibsUpgradable({progress: true});
for (let library of thirdPartyLibs) {
grunt.log.ok(JSON.stringify(Object.assign({}, library), null, 4));
}
done();
});
};
-272
View File
@@ -1,272 +0,0 @@
/**
* This is a wrapper task to handle the grunt watch command. It attempts to use
* Watchman to monitor for file changes, if it's installed, because it's much faster.
*
* If Watchman isn't installed then it falls back to the grunt-contrib-watch file
* watcher for backwards compatibility.
*/
/* eslint-env node */
module.exports = grunt => {
/**
* This is a wrapper task to handle the grunt watch command. It attempts to use
* Watchman to monitor for file changes, if it's installed, because it's much faster.
*
* If Watchman isn't installed then it falls back to the grunt-contrib-watch file
* watcher for backwards compatibility.
*/
const watchHandler = function() {
const async = require('async');
const watchTaskDone = this.async();
let watchInitialised = false;
let watchTaskQueue = {};
let processingQueue = false;
const watchman = require('fb-watchman');
const watchmanClient = new watchman.Client();
// Grab the tasks and files that have been queued up and execute them.
var processWatchTaskQueue = function() {
if (!Object.keys(watchTaskQueue).length || processingQueue) {
// If there is nothing in the queue or we're already processing then wait.
return;
}
processingQueue = true;
// Grab all tasks currently in the queue.
var queueToProcess = watchTaskQueue;
// Reset the queue.
watchTaskQueue = {};
async.forEachSeries(
Object.keys(queueToProcess),
function(task, next) {
var files = queueToProcess[task];
var filesOption = '--files=' + files.join(',');
grunt.log.ok('Running task ' + task + ' for files ' + filesOption);
// Spawn the task in a child process so that it doesn't kill this one
// if it failed.
grunt.util.spawn(
{
// Spawn with the grunt bin.
grunt: true,
// Run from current working dir and inherit stdio from process.
opts: {
cwd: grunt.moodleEnv.fullRunDir,
stdio: 'inherit'
},
args: [task, filesOption]
},
function(err, res, code) {
if (code !== 0) {
// The grunt task failed.
grunt.log.error(err);
}
// Move on to the next task.
next();
}
);
},
function() {
// No longer processing.
processingQueue = false;
// Once all of the tasks are done then recurse just in case more tasks
// were queued while we were processing.
processWatchTaskQueue();
}
);
};
const originalWatchConfig = grunt.config.get(['watch']);
const watchConfig = Object.keys(originalWatchConfig).reduce(function(carry, key) {
if (key == 'options') {
return carry;
}
const value = originalWatchConfig[key];
const taskNames = value.tasks;
const files = value.files;
let excludes = [];
if (value.excludes) {
excludes = value.excludes;
}
taskNames.forEach(function(taskName) {
carry[taskName] = {
files,
excludes,
};
});
return carry;
}, {});
watchmanClient.on('error', function(error) {
// We have to add an error handler here and parse the error string because the
// example way from the docs to check if Watchman is installed doesn't actually work!!
// See: https://github.com/facebook/watchman/issues/509
if (error.message.match('Watchman was not found')) {
// If watchman isn't installed then we should fallback to the other watch task.
grunt.log.ok('It is recommended that you install Watchman for better performance using the "watch" command.');
// Fallback to the old grunt-contrib-watch task.
grunt.renameTask('watch-grunt', 'watch');
grunt.task.run(['watch']);
// This task is finished.
watchTaskDone(0);
} else {
grunt.log.error(error);
// Fatal error.
watchTaskDone(1);
}
});
watchmanClient.on('subscription', function(resp) {
if (resp.subscription !== 'grunt-watch') {
return;
}
resp.files.forEach(function(file) {
grunt.log.ok('File changed: ' + file.name);
var fullPath = grunt.moodleEnv.fullRunDir + '/' + file.name;
Object.keys(watchConfig).forEach(function(task) {
const fileGlobs = watchConfig[task].files;
var match = fileGlobs.some(function(fileGlob) {
return grunt.file.isMatch(`**/${fileGlob}`, fullPath);
});
if (match) {
// If we are watching a subdirectory then the file.name will be relative
// to that directory. However the grunt tasks expect the file paths to be
// relative to the Gruntfile.js location so let's normalise them before
// adding them to the queue.
var relativePath = fullPath.replace(grunt.moodleEnv.gruntFilePath + '/', '');
if (task in watchTaskQueue) {
if (!watchTaskQueue[task].includes(relativePath)) {
watchTaskQueue[task] = watchTaskQueue[task].concat(relativePath);
}
} else {
watchTaskQueue[task] = [relativePath];
}
}
});
});
processWatchTaskQueue();
});
process.on('SIGINT', function() {
// Let the user know that they may need to manually stop the Watchman daemon if they
// no longer want it running.
if (watchInitialised) {
grunt.log.ok('The Watchman daemon may still be running and may need to be stopped manually.');
}
process.exit();
});
// Initiate the watch on the current directory.
watchmanClient.command(['watch-project', grunt.moodleEnv.fullRunDir], function(watchError, watchResponse) {
if (watchError) {
grunt.log.error('Error initiating watch:', watchError);
watchTaskDone(1);
return;
}
if ('warning' in watchResponse) {
grunt.log.error('warning: ', watchResponse.warning);
}
var watch = watchResponse.watch;
var relativePath = watchResponse.relative_path;
watchInitialised = true;
watchmanClient.command(['clock', watch], function(clockError, clockResponse) {
if (clockError) {
grunt.log.error('Failed to query clock:', clockError);
watchTaskDone(1);
return;
}
// Generate the expression query used by watchman.
// Documentation is limited, but see https://facebook.github.io/watchman/docs/expr/allof.html for examples.
// We generate an expression to match any value in the files list of all of our tasks, but excluding
// all value in the excludes list of that task.
//
// [anyof, [
// [allof, [
// [anyof, [
// ['match', validPath, 'wholename'],
// ['match', validPath, 'wholename'],
// ],
// [not,
// [anyof, [
// ['match', invalidPath, 'wholename'],
// ['match', invalidPath, 'wholename'],
// ],
// ],
// ],
var matchWholeName = fileGlob => ['match', fileGlob, 'wholename'];
var matches = Object.keys(watchConfig).map(function(task) {
const matchAll = [];
matchAll.push(['anyof'].concat(watchConfig[task].files.map(matchWholeName)));
if (watchConfig[task].excludes.length) {
matchAll.push(['not', ['anyof'].concat(watchConfig[task].excludes.map(matchWholeName))]);
}
return ['allof'].concat(matchAll);
});
matches = ['anyof'].concat(matches);
var sub = {
expression: matches,
// Which fields we're interested in.
fields: ["name", "size", "type"],
// Add our time constraint.
since: clockResponse.clock
};
if (relativePath) {
/* eslint-disable camelcase */
sub.relative_root = relativePath;
}
watchmanClient.command(['subscribe', watch, 'grunt-watch', sub], function(subscribeError) {
if (subscribeError) {
// Probably an error in the subscription criteria.
grunt.log.error('failed to subscribe: ', subscribeError);
watchTaskDone(1);
return;
}
grunt.log.ok('Listening for changes to files in ' + grunt.moodleEnv.fullRunDir);
});
});
});
};
// Rename the grunt-contrib-watch "watch" task because we're going to wrap it.
grunt.renameTask('watch', 'watch-grunt');
// Register the new watch handler.
grunt.registerTask('watch', 'Run tasks on file changes', watchHandler);
grunt.config.merge({
watch: {
options: {
nospawn: true // We need not to spawn so config can be changed dynamically.
},
},
});
return watchHandler;
};
-2
View File
@@ -1,2 +0,0 @@
**/amd/**
/*.js
-58
View File
@@ -1,58 +0,0 @@
// NOTE: We use eslint now. This file is used only by shifter. We keep the configuration
// here because shifter uses jshint after modules have been concating. Eslint can't
// currently do this.
{
"asi": false,
"bitwise": true,
"boss": false,
"browser": true,
"curly": true,
"debug": false,
"eqeqeq": false,
"eqnull": false,
"es5": false,
"esnext": false,
"evil": false,
"expr": false,
"forin": false,
"funcscope": false,
"globalstrict": false,
"immed": true,
"indent": 4,
"iterator": false,
"lastsemic": false,
"latedef": true,
"laxbreak": true,
"laxcomma": false,
"loopfunc": false,
"maxerr": 500,
"maxlen": 132,
"multistr": false,
"newcap": true,
"noarg": true,
"noempty": true,
"nomen": false,
"onecase": false,
"onevar": false,
"passfail": false,
"plusplus": false,
"predef": [
"M",
"define",
"require"
],
"proto": false,
"regexdash": false,
"regexp": false,
"scripturl": false,
"shadow": false,
"smarttabs": false,
"strict": false,
"sub": false,
"supernew": false,
"trailing": true,
"undef": true,
"unused": true,
"white": false,
"yui": true
}
-1
View File
@@ -1 +0,0 @@
lts/iron
-40
View File
@@ -1,40 +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/>.
/**
* Helper file for PhpStorm, and other IDEs, to provide better code completion.
*
* @package core
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace PHPSTORM_META;
// Assume that anything returned by \core\di::get('...') is an instance of the first argument.
override(\core\di::get(0), map([
'' => '@',
]));
// Assume that anything returned by \Psr\Container\ContainerInterface::get('...') is an instance of the first argument.
override(\Psr\Container\ContainerInterface::get(0), map([
'' => '@',
]));
// Assume that anything returned by \DI\Container::get('...') is an instance of the first argument.
override(\DI\Container::get(0), map([
'' => '@',
]));
-5
View File
@@ -1,5 +0,0 @@
{
"coverage": false,
"lint": "config",
"clean": true
}
-107
View File
@@ -1,107 +0,0 @@
{
"customSyntax": "postcss-scss",
"plugins": [
"stylelint-csstree-validator"
],
"rules": {
"csstree/validator": {
"syntaxExtensions": [
"sass"
]
},
"at-rule-empty-line-before": [ "always",
{"except": [ "blockless-after-blockless"], ignore: ["after-comment", "inside-block"]}
],
"at-rule-name-case": "lower",
"at-rule-name-space-after": "always-single-line",
"at-rule-no-unknown": null, # Enabled for non-scss in grunt.
"at-rule-semicolon-newline-after": "always",
"at-rule-semicolon-space-before": "never",
"block-closing-brace-newline-after": "always",
"block-closing-brace-newline-before": "always",
"block-closing-brace-space-before": "always-single-line",
"block-no-empty": true,
"block-opening-brace-newline-after": "always",
"block-opening-brace-space-after": "always-single-line",
"block-opening-brace-space-before": "always",
"color-hex-case": ["lower", { "severity": "warning" }],
"color-hex-length": ["short", { "severity": "warning" }],
"color-no-invalid-hex": true,
"declaration-bang-space-after": "never",
"declaration-bang-space-before": "always",
"declaration-block-no-duplicate-properties": true,
"declaration-block-no-shorthand-property-overrides": true,
"declaration-block-semicolon-newline-after": "always-multi-line",
"declaration-block-semicolon-space-after": "always-single-line",
"declaration-block-semicolon-space-before": "never",
"declaration-block-single-line-max-declarations": 1,
"declaration-block-trailing-semicolon": "always",
"declaration-colon-newline-after": "always-multi-line",
"declaration-colon-space-after": "always-single-line",
"declaration-colon-space-before": "never",
"declaration-no-important": true,
"font-family-no-duplicate-names": true,
"function-calc-no-unspaced-operator": true,
"function-comma-newline-after": "always-multi-line",
"function-comma-space-after": "always-single-line",
"function-comma-space-before": "never",
"function-linear-gradient-no-nonstandard-direction": true,
"function-max-empty-lines": 0,
"function-name-case": "lower",
"function-parentheses-newline-inside": "always-multi-line",
"function-parentheses-space-inside": "never-single-line",
"function-url-scheme-disallowed-list": ["data"],
"function-whitespace-after": "always",
"indentation": 4,
"keyframe-declaration-no-important": true,
"length-zero-no-unit": [true, { "severity": "warning" }],
"max-empty-lines": 2,
"max-line-length": [132, { "severity": "warning" }],
"media-feature-colon-space-after": "always",
"media-feature-colon-space-before": "never",
"media-feature-parentheses-space-inside": "never",
"media-feature-range-operator-space-after": "always",
"media-feature-range-operator-space-before": "always",
"media-query-list-comma-newline-after": "always-multi-line",
"media-query-list-comma-space-after": "always-single-line",
"media-query-list-comma-space-before": "never",
"no-empty-source": true,
"no-eol-whitespace": true,
"no-extra-semicolons": [true, { "severity": "warning" }],
"no-invalid-double-slash-comments": true,
"no-unknown-animations": true,
"property-case": "lower",
"property-no-unknown": true,
"selector-attribute-brackets-space-inside": "never",
"selector-attribute-operator-space-after": "never",
"selector-attribute-operator-space-before": "never",
"selector-combinator-space-after": "always",
"selector-combinator-space-before": "always",
"selector-list-comma-newline-after": "always",
"selector-list-comma-space-before": "never",
"selector-max-empty-lines": 0,
"selector-pseudo-class-case": "lower",
"selector-pseudo-class-no-unknown": true,
"selector-pseudo-class-parentheses-space-inside": "never",
"selector-pseudo-element-case": "lower",
"selector-pseudo-element-no-unknown": true,
"selector-type-case": "lower",
"selector-type-no-unknown": true,
"string-no-newline": true,
"time-min-milliseconds": 100,
"unit-disallowed-list": ["pt"],
"unit-case": "lower",
"unit-no-unknown": true,
"value-keyword-case": ["lower", {"ignoreKeywords": ["/(@|$)/"]}],
"value-list-comma-newline-after": "always-multi-line",
"value-list-comma-space-after": "always-single-line",
"value-list-comma-space-before": "never",
},
"overrides": [
{
"files": ["**/yui/**/*.css"],
"rules": {
}
}
]
}
-52
View File
@@ -1,52 +0,0 @@
# Contributing to Moodle
[Moodle][1] is made by people like you. We are members of a big worldwide community of developers, designers, teachers, testers, translators and many more. We work in universities, schools, companies and other places. You are very welcome to join us and contribute to the project.
There are many ways that you can contribute to Moodle, not just through development. See our [community contribution guide][2] for some of the many ways that you can help.
## Github
> [!NOTE]
> Please do not open pull requests via Github.
All issues should be reported via, and patched provided to the [Moodle Tracker][3].
The Moodle [Github][4] repository is a clone of the official Moodle repository, whcih can be found at https://git.moodle.org.
## Moodle core bug fixes and new features
Over the years, Moodle has developed a mature process for reporting, reviewing, and accepting patches. This is fully documented in our [documentation on development processes][6], but in summary:
- Every bug fix or new feature must have a tracker issue.
- You publish the branch implementing the fix or new feature in your public clone of the moodle.git repository (typically on Github).
- Your patch is peer-reviewed, discussed, integrated, tested and then released as a part of one of our weekly releases.
- New features are developed on the `main` branch. Bug fixes are also backported to currently supported maintenance (stable) branches.
> [!IMPORTANT]
> Please do not publish security issues, or patches releating to them publicly.
> See our [Responsible Disclosure Policy][5] for more information.
## Moodle plugins
Moodle has a framework for additional plugins to extend its functionality. We
have a Moodle plugins directory <https://moodle.org/plugins/> where you can
register and maintain your plugin. Plugins hosted in the plugins directory can
be easily installed and updated via the Moodle administration interface.
* You are expected to have a public source code repository with your plugin
code.
* After registering your plugin in the plugins directory it is reviewed before
being published.
* You are expected to continuously release updated versions of the plugin via
the plugins directory. We do not pull from your code repository; you must do
it explicitly.
For further details, see <https://moodledev.io/general/community/plugincontribution>.
[1]: https://moodle.org
[2]: https://moodledev.io/general/community/contribute
[3]: https://tracker.moodle.org
[4]: https://github.com/moodle/moodle
[5]: https://moodledev.io/general/development/process/security
[6]: https://moodledev.io/general/development/process
-267
View File
@@ -1,267 +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/>.
/* jshint node: true, browser: false */
/* eslint-env node */
/**
* Grunt configuration for Moodle.
*
* @copyright 2014 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Setup the Grunt Moodle environment.
*
* @param {Grunt} grunt
* @returns {Object}
*/
const setupMoodleEnvironment = grunt => {
const fs = require('fs');
const path = require('path');
const ComponentList = require(path.join(process.cwd(), '.grunt', 'components.js'));
const getAmdConfiguration = () => {
// If the cwd is the amd directory in the current component then it will be empty.
// If the cwd is a child of the component's AMD directory, the relative directory will not start with ..
let inAMD = !path.relative(`${componentDirectory}/amd`, cwd).startsWith('..');
// Globbing pattern for matching all AMD JS source files.
let amdSrc = [];
if (inComponent) {
amdSrc.push(
componentDirectory + "/amd/src/*.js",
componentDirectory + "/amd/src/**/*.js"
);
} else {
amdSrc = ComponentList.getAmdSrcGlobList();
}
return {
inAMD,
amdSrc,
};
};
const getYuiConfiguration = () => {
let yuiSrc = [];
if (inComponent) {
yuiSrc.push(componentDirectory + "/yui/src/**/*.js");
} else {
yuiSrc = ComponentList.getYuiSrcGlobList(gruntFilePath + '/');
}
return {
yuiSrc,
};
};
const getStyleConfiguration = () => {
const ComponentList = require(path.join(process.cwd(), '.grunt', 'components.js'));
// Build the cssSrc and scssSrc.
// Valid paths are:
// [component]/styles.css; and either
// [theme/[themename]]/scss/**/*.scss; or
// [theme/[themename]]/style/*.css.
//
// If a theme has scss, then it is assumed that the style directory contains generated content.
let cssSrc = [];
let scssSrc = [];
const checkComponentDirectory = componentDirectory => {
const isTheme = componentDirectory.startsWith('theme/');
if (isTheme) {
const scssDirectory = `${componentDirectory}/scss`;
if (fs.existsSync(scssDirectory)) {
// This theme has an SCSS directory.
// Include all scss files within it recursively, but do not check for css files.
scssSrc.push(`${scssDirectory}/*.scss`);
scssSrc.push(`${scssDirectory}/**/*.scss`);
} else {
// This theme has no SCSS directory.
// Only hte CSS files in the top-level directory are checked.
cssSrc.push(`${componentDirectory}/style/*.css`);
}
} else {
// This is not a theme.
// All other plugin types are restricted to a single styles.css in their top level.
cssSrc.push(`${componentDirectory}/styles.css`);
}
};
if (inComponent) {
checkComponentDirectory(componentDirectory);
} else {
ComponentList.getComponentPaths(`${gruntFilePath}/`).forEach(componentPath => {
checkComponentDirectory(componentPath);
});
}
return {
cssSrc,
scssSrc,
};
};
/**
* Calculate the cwd, taking into consideration the `root` option (for Windows).
*
* @param {Object} grunt
* @returns {String} The current directory as best we can determine
*/
const getCwd = grunt => {
let cwd = fs.realpathSync(process.env.PWD || process.cwd());
// Windows users can't run grunt in a subdirectory, so allow them to set
// the root by passing --root=path/to/dir.
if (grunt.option('root')) {
const root = grunt.option('root');
if (grunt.file.exists(__dirname, root)) {
cwd = fs.realpathSync(path.join(__dirname, root));
grunt.log.ok('Setting root to ' + cwd);
} else {
grunt.fail.fatal('Setting root to ' + root + ' failed - path does not exist');
}
}
return cwd;
};
// Detect directories:
// * gruntFilePath The real path on disk to this Gruntfile.js
// * cwd The current working directory, which can be overridden by the `root` option
// * relativeCwd The cwd, relative to the Gruntfile.js
// * componentDirectory The root directory of the component if the cwd is in a valid component
// * inComponent Whether the cwd is in a valid component
// * runDir The componentDirectory or cwd if not in a component, relative to Gruntfile.js
// * fullRunDir The full path to the runDir
const gruntFilePath = fs.realpathSync(process.cwd());
const cwd = getCwd(grunt);
const relativeCwd = path.relative(gruntFilePath, cwd);
const componentDirectory = ComponentList.getOwningComponentDirectory(relativeCwd);
const inComponent = !!componentDirectory;
const inTheme = !!componentDirectory && componentDirectory.startsWith('theme/');
const runDir = inComponent ? componentDirectory : relativeCwd;
const fullRunDir = fs.realpathSync(gruntFilePath + path.sep + runDir);
const {inAMD, amdSrc} = getAmdConfiguration();
const {yuiSrc} = getYuiConfiguration();
const {cssSrc, scssSrc} = getStyleConfiguration();
let files = null;
if (grunt.option('files')) {
// Accept a comma separated list of files to process.
files = grunt.option('files').split(',');
}
grunt.log.debug('============================================================================');
grunt.log.debug(`= Node version: ${process.versions.node}`);
grunt.log.debug(`= grunt version: ${grunt.package.version}`);
grunt.log.debug(`= process.cwd: '` + process.cwd() + `'`);
grunt.log.debug(`= process.env.PWD: '${process.env.PWD}'`);
grunt.log.debug(`= path.sep '${path.sep}'`);
grunt.log.debug('============================================================================');
grunt.log.debug(`= gruntFilePath: '${gruntFilePath}'`);
grunt.log.debug(`= relativeCwd: '${relativeCwd}'`);
grunt.log.debug(`= componentDirectory: '${componentDirectory}'`);
grunt.log.debug(`= inComponent: '${inComponent}'`);
grunt.log.debug(`= runDir: '${runDir}'`);
grunt.log.debug(`= fullRunDir: '${fullRunDir}'`);
grunt.log.debug('============================================================================');
if (inComponent) {
grunt.log.ok(`Running tasks for component directory ${componentDirectory}`);
}
return {
amdSrc,
componentDirectory,
cwd,
cssSrc,
files,
fullRunDir,
gruntFilePath,
inAMD,
inComponent,
inTheme,
relativeCwd,
runDir,
scssSrc,
yuiSrc,
};
};
/**
* Verify tha tthe current NodeJS version matches the required version in package.json.
*
* @param {Grunt} grunt
*/
const verifyNodeVersion = grunt => {
const semver = require('semver');
// Verify the node version is new enough.
var expected = semver.validRange(grunt.file.readJSON('package.json').engines.node);
var actual = semver.valid(process.version);
if (!semver.satisfies(actual, expected)) {
grunt.fail.fatal('Node version not satisfied. Require ' + expected + ', version installed: ' + actual);
}
};
/**
* Grunt configuration.
*
* @param {Grunt} grunt
*/
module.exports = function(grunt) {
// Verify that the Node version meets our requirements.
verifyNodeVersion(grunt);
// Setup the Moodle environemnt within the Grunt object.
grunt.moodleEnv = setupMoodleEnvironment(grunt);
/**
* Add the named task.
*
* @param {string} name
* @param {Grunt} grunt
*/
const addTask = (name, grunt) => {
const path = require('path');
const taskPath = path.resolve(`./.grunt/tasks/${name}.js`);
grunt.log.debug(`Including tasks for ${name} from ${taskPath}`);
require(path.resolve(`./.grunt/tasks/${name}.js`))(grunt);
};
// Startup tasks.
grunt.moodleEnv.startupTasks = [];
// Add Moodle task configuration.
addTask('gherkinlint', grunt);
addTask('ignorefiles', grunt);
addTask('javascript', grunt);
addTask('style', grunt);
addTask('componentlibrary', grunt);
addTask('watch', grunt);
addTask('startup', grunt);
addTask('upgradablelibs', grunt);
// Register the default task.
grunt.registerTask('default', ['startup']);
};
-20
View File
@@ -1,20 +0,0 @@
MOODLE INSTALLATION
===================
Here is a short summary of the installation process (which can take just a few
minutes):
1. Move the Moodle files into your web directory.
2. Create a single database for Moodle to store all its tables in (or choose an
existing database).
3. Visit your Moodle site with a browser. You should be taken to the
install.php script, which will lead you through creating a config.php file
and then setting up Moodle, creating an admin account etc.
4. Set up a cron task to call the file admin/cron.php every minute.
For more information, see <https://docs.moodle.org/en/Installing_Moodle>.
Good luck and have fun!
-48
View File
@@ -1,48 +0,0 @@
# Moodle
<p align="center"><a href="https://moodle.org" target="_blank" title="Moodle Website">
<img src="https://raw.githubusercontent.com/moodle/moodle/main/.github/moodlelogo.svg" alt="The Moodle Logo">
</a></p>
[Moodle][1] is the World's Open Source Learning Platform, widely used around the world by countless universities, schools, companies, and all manner of organisations and individuals.
Moodle is designed to allow educators, administrators and learners to create personalised learning environments with a single robust, secure and integrated system.
## Documentation
- Read our [User documentation][3]
- Discover our [developer documentation][5]
- Take a look at our [demo site][4]
## Community
[moodle.org][1] is the central hub for the Moodle Community, with spaces for educators, administrators and developers to meet and work together.
You may also be interested in:
- attending a [Moodle Moot][6]
- our regular series of [developer meetings][7]
- the [Moodle User Association][8]
## Installation and hosting
Moodle is Free, and Open Source software. You can easily [download Moodle][9] and run it on your own web server, however you may prefer to work with one of our experienced [Moodle Partners][10].
Moodle also offers hosting through both [MoodleCloud][11], and our [partner network][10].
## License
Moodle is provided freely as open source software, under version 3 of the GNU General Public License. For more information on our license see
[1]: https://moodle.org
[2]: https://moodle.com
[3]: https://docs.moodle.org/
[4]: https://sandbox.moodledemo.net/
[5]: https://moodledev.io
[6]: https://moodle.com/events/mootglobal/
[7]: https://moodledev.io/general/community/meetings
[8]: https://moodleassociation.org/
[9]: https://download.moodle.org
[10]: https://moodle.com/partners
[11]: https://moodle.com/cloud
[12]: https://moodledev.io/general/license
+29
View File
@@ -0,0 +1,29 @@
QUICK INSTALL
=============
For the impatient, here is a basic outline of the
installation process, which normally takes me only
a few minutes:
1) Move the Moodle files into your web directory.
2) Create a single database for Moodle to store all
it's tables in (or choose an existing database).
3) Visit your Moodle site with a browser, you should
be taken to the install.php script, which will lead
you through creating a config.php file and then
setting up Moodle, creating an admin account etc.
4) Set up a cron task to call the file admin/cron.php
every five minutes or so.
For more information, see the INSTALL DOCUMENTATION:
http://docs.moodle.org/en/Installing_Moodle
Good luck and have fun!
Martin Dougiamas, Lead Developer
+28 -60
View File
@@ -1,66 +1,34 @@
MOODLE TRADEMARKS POLICY
========================
The Moodle Trademark
--------------------
The brand and word "Moodle" has trade mark protection in many countries around
the world. Various Moodle™ trade marks are owned by either Moodle Pty Ltd or
one of its associated entities. The law obligates trade mark owners to police
their trade marks and prevent the use of confusingly similar names by
third parties.
We have created a more concise and thorough set of Trademark Guidelines
(https://moodle.com/wp-content/uploads/2023/02/Moodle-Trademark-Guidelines-2023-4.pdf)
to help you understand how to comply with Moodle's trade mark requirements.
The Trademark Guidelines will also assist our Moodle Certified Partners and the
community to understand some of the principles that inform Moodle's stance
whenever potential infringement has been detected by us or our community.
We need to be clear about use of the word "Moodle" and how we restrict its use
when promoting commercial Moodle™ branded services. The proper use of
trade marks reliably assures consumers about the quality of the associated
products or services and where they have been derived from. Because the
Moodle LMS is made available under the open source GNU General Public License'
that permits you to modify the copyrighted software, the distribution of such
modified software in combination with Moodle trade marks can potentially
mislead others in the community. To be clear, the GNU GPL does not include an
implied right or licence to use Moodle's trade marks.
We do all this to protect the very business model that allows us to continue
developing various Moodle software solutions for you.
Allowed uses of "Moodle"
------------------------
Moodle Trademark License
------------------------
The following uses don't require any permission at all:
The name Moodle™ is a registered trademark of the Moodle Trust.
* Referring to the software or the Moodle project as the "Moodle™ software"
or the "Moodle™ project"
* Describing your own implementation of the Moodle™ software platform
(including within corporate settings).
* Describing a Moodle-based community hub.
* Describing certain software you've made that integrates with Moodle™
branded software (e.g. a Moodle™ integration feature on another system).
A key part of the business model that allows us to produce and
distribute Moodle as completely Free open source software is that
we restrict the commercial use of the Moodle trademark to those
who have contracted to support Moodle development (Moodle Partners).
Restricted uses of "Moodle"
---------------------------
If you are intending to use the name (and/or logo) to advertise
generic Moodle™ services (eg Moodle Hosting, Moodle Support,
Moodle Certification, Moodle Training, Moodle Consulting,
Moodle Customisation, Moodle Courseware Development, Moodle
Theme design, Moodle Integrations, Moodle Installations, etc)
or as the name of a software package, then you must seek
direct permission in writing from the Moodle Trust via the
moodle.com helpdesk, in accordance with normal trademark
restrictions.
The following uses are generally prohibited without explicit and direct
permission being granted to you by Moodle Pty Ltd. We do this to protect the
Moodle™ project from software and sites which could confuse people. If in
doubt, reach out for clarification at <trademarks@moodle.com>
There are no restrictions on how you use the name in other
contexts (for example, if you use Moodle just to provide
courses then you can use the name freely to refer to it.)
* You can't use the word "Moodle" or any Moodle logos for commercial purposes
without prior written permission from Moodle HQ.
* You can't use "Moodle" in the name of your software (including Mobile apps).
* You can't use "Moodle" in your company name.
* You can't use "Moodle" in your domain name.
* You can't use "Moodle" in advertising-related keywords (such as Adsense).
* You can't use "Moodle" to describe services around Moodle
(such as hosting, training, support, consulting, course creation services,
theme development, customisation, installation, integration, analytics
and certification) that cause consumers to be confused that you are
associated with Moodle HQ if you are not.
This applies even if you do not charge for the services.
Note that usually only Moodle Partners have this permission.
If you aren't sure of a particular case, please ask us via
http://moodle.com/helpdesk: we'll be happy to either provide
you with official permission in writing or help you fix
your wording.
Martin Dougiamas
Executive Director
Moodle Trust
http://moodle.com
-3
View File
@@ -1,3 +0,0 @@
define("core_admin/block_management_table",["exports","./plugin_management_table","core_table/dynamic","core/ajax","core/pending","core/notification"],(function(_exports,_plugin_management_table,_dynamic,_ajax,_pending,_notification){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_plugin_management_table=_interopRequireDefault(_plugin_management_table),_pending=_interopRequireDefault(_pending);class _default extends _plugin_management_table.default{constructor(){super(),this.addClickHandler(this.handleBlockProtectToggle)}setBlockProtectState(plugin,state){return(0,_ajax.call)([{methodname:"core_admin_set_block_protection",args:{plugin:plugin,state:state}}])[0]}async handleBlockProtectToggle(tableRoot,e){const stateToggle=e.target.closest('[data-action="toggleprotectstate"]');if(stateToggle){e.preventDefault();const pendingPromise=new _pending.default("core_table/dynamic:processAction");await this.setBlockProtectState(stateToggle.dataset.plugin,"1"===stateToggle.dataset.targetState?1:0);const[updatedRoot]=await Promise.all([(0,_dynamic.refreshTableContent)(tableRoot),(0,_notification.fetchNotifications)()]);updatedRoot.querySelector('[data-action="toggleprotectstate"][data-plugin="'.concat(stateToggle.dataset.plugin,'"]')).focus(),pendingPromise.resolve()}}}return _exports.default=_default,_exports.default}));
//# sourceMappingURL=block_management_table.min.js.map
@@ -1 +0,0 @@
{"version":3,"file":"block_management_table.min.js","sources":["../src/block_management_table.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\nimport PluginManagementTable from './plugin_management_table';\nimport {refreshTableContent} from 'core_table/dynamic';\nimport {call as fetchMany} from 'core/ajax';\nimport Pending from 'core/pending';\nimport {fetchNotifications} from 'core/notification';\n\nexport default class extends PluginManagementTable {\n constructor() {\n super();\n this.addClickHandler(this.handleBlockProtectToggle);\n }\n\n /**\n * Set the block protection state.\n *\n * @param {string} plugin\n * @param {number} state\n * @returns {Promise}\n */\n setBlockProtectState(plugin, state) {\n return fetchMany([{\n methodname: 'core_admin_set_block_protection',\n args: {\n plugin,\n state,\n },\n }])[0];\n }\n\n /**\n * Handle toggling of block protection.\n *\n * @param {HTMLElement} tableRoot\n * @param {Event} e\n */\n async handleBlockProtectToggle(tableRoot, e) {\n const stateToggle = e.target.closest('[data-action=\"toggleprotectstate\"]');\n if (stateToggle) {\n e.preventDefault();\n const pendingPromise = new Pending('core_table/dynamic:processAction');\n\n await this.setBlockProtectState(\n stateToggle.dataset.plugin,\n stateToggle.dataset.targetState === '1' ? 1 : 0\n );\n\n const [updatedRoot] = await Promise.all([\n refreshTableContent(tableRoot),\n fetchNotifications(),\n ]);\n\n // Refocus on the link that as pressed in the first place.\n updatedRoot.querySelector(`[data-action=\"toggleprotectstate\"][data-plugin=\"${stateToggle.dataset.plugin}\"]`).focus();\n pendingPromise.resolve();\n }\n }\n}\n"],"names":["PluginManagementTable","constructor","addClickHandler","this","handleBlockProtectToggle","setBlockProtectState","plugin","state","methodname","args","tableRoot","e","stateToggle","target","closest","preventDefault","pendingPromise","Pending","dataset","targetState","updatedRoot","Promise","all","querySelector","focus","resolve"],"mappings":"uhBAqB6BA,iCACzBC,2BAESC,gBAAgBC,KAAKC,0BAU9BC,qBAAqBC,OAAQC,cAClB,cAAU,CAAC,CACdC,WAAY,kCACZC,KAAM,CACFH,OAAAA,OACAC,MAAAA,UAEJ,kCASuBG,UAAWC,SAChCC,YAAcD,EAAEE,OAAOC,QAAQ,yCACjCF,YAAa,CACbD,EAAEI,uBACIC,eAAiB,IAAIC,iBAAQ,0CAE7Bd,KAAKE,qBACPO,YAAYM,QAAQZ,OACgB,MAApCM,YAAYM,QAAQC,YAAsB,EAAI,SAG3CC,mBAAqBC,QAAQC,IAAI,EACpC,gCAAoBZ,YACpB,wCAIJU,YAAYG,wEAAiEX,YAAYM,QAAQZ,cAAYkB,QAC7GR,eAAeS"}
-10
View File
@@ -1,10 +0,0 @@
define("core_admin/bulk_user_actions",["exports","core_reportbuilder/local/selectors","core_table/local/dynamic/events","core_form/changechecker","core/custom_interaction_events","jquery"],(function(_exports,reportSelectors,tableEvents,FormChangeChecker,CustomEvents,_jquery){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)}function _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}
/**
* Add bulk actions to the users list report
*
* @module core_admin/bulk_user_actions
* @copyright 2024 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,reportSelectors=_interopRequireWildcard(reportSelectors),tableEvents=_interopRequireWildcard(tableEvents),FormChangeChecker=_interopRequireWildcard(FormChangeChecker),CustomEvents=_interopRequireWildcard(CustomEvents),_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj};const Selectors_bulkActionsForm="form#user-bulk-action-form",Selectors_userReportWrapper='[data-region="report-user-list-wrapper"]',Selectors_checkbox='input[type="checkbox"][data-togglegroup="report-select-all"][data-toggle="slave"]',Selectors_masterCheckbox='input[type="checkbox"][data-togglegroup="report-select-all"][data-toggle="master"]',Selectors_checkedRows='[data-togglegroup="report-select-all"][data-toggle="slave"]:checked';_exports.init=()=>{var _userBulkForm$closest;const userBulkForm=document.querySelector(Selectors_bulkActionsForm),userReport=null==userBulkForm||null===(_userBulkForm$closest=userBulkForm.closest(Selectors_userReportWrapper))||void 0===_userBulkForm$closest?void 0:_userBulkForm$closest.querySelector(reportSelectors.regions.report);if(!userBulkForm||!userReport)return;const actionSelect=userBulkForm.querySelector("select");CustomEvents.define(actionSelect,[CustomEvents.events.accessibleChange]),(0,_jquery.default)(actionSelect).on(CustomEvents.events.accessibleChange,(event=>{if(event.target.value&&"0"!=="".concat(event.target.value)){const e=new Event("submit",{cancelable:!0});userBulkForm.dispatchEvent(e),e.defaultPrevented||(FormChangeChecker.markFormSubmitted(userBulkForm),userBulkForm.submit())}}));const updateUserIds=()=>{const selectedUsers=[...userReport.querySelectorAll(Selectors_checkedRows)],selectedUserIds=selectedUsers.map((check=>parseInt(check.value)));userBulkForm.querySelector('[name="userids"]').value=selectedUserIds.join(","),actionSelect.disabled=0===selectedUsers.length;const selectedUsersNames=selectedUsers.map((check=>document.querySelector('label[for="'.concat(check.id,'"]')).textContent));userBulkForm.data={userids:selectedUserIds,usernames:selectedUsersNames}};updateUserIds(),document.addEventListener("change",(event=>{(event.target.matches(Selectors_checkbox)||event.target.matches(Selectors_masterCheckbox))&&userReport.contains(event.target)&&updateUserIds()})),document.addEventListener(tableEvents.tableContentRefreshed,(event=>{userReport.contains(event.target)&&updateUserIds()}))}}));
//# sourceMappingURL=bulk_user_actions.min.js.map
File diff suppressed because one or more lines are too long
-3
View File
@@ -1,3 +0,0 @@
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
-10
View File
@@ -1,10 +0,0 @@
define("core_admin/themeselector/preview_modal",["exports","core/modal_events","core/modal_cancel","core/modal_save_cancel","core/notification","core/templates","core/str"],(function(_exports,_modal_events,_modal_cancel,_modal_save_cancel,_notification,_templates,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Modal for theme previews.
*
* @module core_admin/themeselector/preview_modal
* @copyright 2023 David Woloszyn <david.woloszyn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modal_events=_interopRequireDefault(_modal_events),_modal_cancel=_interopRequireDefault(_modal_cancel),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_notification=_interopRequireDefault(_notification),_templates=_interopRequireDefault(_templates);const SELECTORS_THEMES_CONTAINER="themelist",SELECTORS_PREVIEW='[data-action="preview"]';_exports.init=()=>{registerListenerEvents()};const registerListenerEvents=()=>{document.addEventListener("click",(e=>{const preview=e.target.closest(SELECTORS_PREVIEW);preview&&buildModal(preview).catch(_notification.default.exception)}))},buildModal=async element=>{let description=await(0,_str.getString)("choosereadme","theme_"+element.getAttribute("data-choose"));const themesContainer=document.getElementById(SELECTORS_THEMES_CONTAINER),definedInConfig=parseInt(themesContainer.dataset.definedinconfig),data={name:element.getAttribute("data-name"),image:element.getAttribute("data-image"),description:description.replace(/<[^>]+>/g," "),current:element.getAttribute("data-current"),actionurl:element.getAttribute("data-actionurl"),choose:element.getAttribute("data-choose"),sesskey:element.getAttribute("data-sesskey"),definedinconfig:definedInConfig};let modalTemplate=_modal_save_cancel.default;(data.current||data.definedinconfig)&&(modalTemplate=_modal_cancel.default);const modal=await modalTemplate.create({title:data.name,body:_templates.default.render("core_admin/themeselector/theme_preview_modal",data),large:!0,buttons:{save:(0,_str.getString)("selecttheme","moodle"),cancel:(0,_str.getString)("closebuttontitle","moodle")},show:!0});modal.getRoot().on(_modal_events.default.save,(()=>{modal.getRoot().find("form").submit()}))}}));
//# sourceMappingURL=preview_modal.min.js.map
@@ -1 +0,0 @@
{"version":3,"file":"preview_modal.min.js","sources":["../../src/themeselector/preview_modal.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 * Modal for theme previews.\n *\n * @module core_admin/themeselector/preview_modal\n * @copyright 2023 David Woloszyn <david.woloszyn@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalEvents from 'core/modal_events';\nimport ModalCancel from 'core/modal_cancel';\nimport ModalSaveCancel from 'core/modal_save_cancel';\nimport Notification from 'core/notification';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\n\nconst SELECTORS = {\n THEMES_CONTAINER: 'themelist',\n PREVIEW: '[data-action=\"preview\"]',\n};\n\n/**\n * Entrypoint of the js.\n *\n * @method init\n */\nexport const init = () => {\n registerListenerEvents();\n};\n\n/**\n * Register theme related event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n document.addEventListener('click', (e) => {\n const preview = e.target.closest(SELECTORS.PREVIEW);\n if (preview) {\n buildModal(preview).catch(Notification.exception);\n }\n });\n};\n\n/**\n * Build the modal with the provided data.\n *\n * @method buildModal\n * @param {object} element\n */\nconst buildModal = async(element) => {\n\n // This string can be long. We will fetch it with JS as opposed to passing it as an attribute.\n let description = await getString('choosereadme', 'theme_' + element.getAttribute('data-choose'));\n\n const themesContainer = document.getElementById(SELECTORS.THEMES_CONTAINER);\n const definedInConfig = parseInt(themesContainer.dataset.definedinconfig);\n // Prepare data for modal.\n const data = {\n name: element.getAttribute('data-name'),\n image: element.getAttribute('data-image'),\n description: description.replace(/<[^>]+>/g, ' '), // Strip out HTML tags.\n current: element.getAttribute('data-current'),\n actionurl: element.getAttribute('data-actionurl'),\n choose: element.getAttribute('data-choose'),\n sesskey: element.getAttribute('data-sesskey'),\n definedinconfig: definedInConfig,\n };\n\n // Determine which modal template we should use.\n let modalTemplate = ModalSaveCancel;\n if (data.current || data.definedinconfig) {\n modalTemplate = ModalCancel;\n }\n\n const modal = await modalTemplate.create({\n title: data.name,\n body: Templates.render('core_admin/themeselector/theme_preview_modal', data),\n large: true,\n buttons: {\n 'save': getString('selecttheme', 'moodle'),\n 'cancel': getString('closebuttontitle', 'moodle'),\n },\n show: true,\n });\n\n modal.getRoot().on(ModalEvents.save, () => {\n modal.getRoot().find('form').submit();\n });\n};\n"],"names":["SELECTORS","registerListenerEvents","document","addEventListener","e","preview","target","closest","buildModal","catch","Notification","exception","async","description","element","getAttribute","themesContainer","getElementById","definedInConfig","parseInt","dataset","definedinconfig","data","name","image","replace","current","actionurl","choose","sesskey","modalTemplate","ModalSaveCancel","ModalCancel","modal","create","title","body","Templates","render","large","buttons","show","getRoot","on","ModalEvents","save","find","submit"],"mappings":";;;;;;;gWA8BMA,2BACgB,YADhBA,kBAEO,wCAQO,KAChBC,gCAQEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,UAC1BC,QAAUD,EAAEE,OAAOC,QAAQP,mBAC7BK,SACAG,WAAWH,SAASI,MAAMC,sBAAaC,eAW7CH,WAAaI,MAAAA,cAGXC,kBAAoB,kBAAU,eAAgB,SAAWC,QAAQC,aAAa,sBAE5EC,gBAAkBd,SAASe,eAAejB,4BAC1CkB,gBAAkBC,SAASH,gBAAgBI,QAAQC,iBAEnDC,KAAO,CACTC,KAAMT,QAAQC,aAAa,aAC3BS,MAAOV,QAAQC,aAAa,cAC5BF,YAAaA,YAAYY,QAAQ,WAAY,KAC7CC,QAASZ,QAAQC,aAAa,gBAC9BY,UAAWb,QAAQC,aAAa,kBAChCa,OAAQd,QAAQC,aAAa,eAC7Bc,QAASf,QAAQC,aAAa,gBAC9BM,gBAAiBH,qBAIjBY,cAAgBC,4BAChBT,KAAKI,SAAWJ,KAAKD,mBACrBS,cAAgBE,6BAGdC,YAAcH,cAAcI,OAAO,CACrCC,MAAOb,KAAKC,KACZa,KAAMC,mBAAUC,OAAO,+CAAgDhB,MACvEiB,OAAO,EACPC,QAAS,OACG,kBAAU,cAAe,kBACvB,kBAAU,mBAAoB,WAE5CC,MAAM,IAGVR,MAAMS,UAAUC,GAAGC,sBAAYC,MAAM,KACjCZ,MAAMS,UAAUI,KAAK,QAAQC"}
-72
View File
@@ -1,72 +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 PluginManagementTable from './plugin_management_table';
import {refreshTableContent} from 'core_table/dynamic';
import {call as fetchMany} from 'core/ajax';
import Pending from 'core/pending';
import {fetchNotifications} from 'core/notification';
export default class extends PluginManagementTable {
constructor() {
super();
this.addClickHandler(this.handleBlockProtectToggle);
}
/**
* Set the block protection state.
*
* @param {string} plugin
* @param {number} state
* @returns {Promise}
*/
setBlockProtectState(plugin, state) {
return fetchMany([{
methodname: 'core_admin_set_block_protection',
args: {
plugin,
state,
},
}])[0];
}
/**
* Handle toggling of block protection.
*
* @param {HTMLElement} tableRoot
* @param {Event} e
*/
async handleBlockProtectToggle(tableRoot, e) {
const stateToggle = e.target.closest('[data-action="toggleprotectstate"]');
if (stateToggle) {
e.preventDefault();
const pendingPromise = new Pending('core_table/dynamic:processAction');
await this.setBlockProtectState(
stateToggle.dataset.plugin,
stateToggle.dataset.targetState === '1' ? 1 : 0
);
const [updatedRoot] = await Promise.all([
refreshTableContent(tableRoot),
fetchNotifications(),
]);
// Refocus on the link that as pressed in the first place.
updatedRoot.querySelector(`[data-action="toggleprotectstate"][data-plugin="${stateToggle.dataset.plugin}"]`).focus();
pendingPromise.resolve();
}
}
}
-91
View File
@@ -1,91 +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/>.
/**
* Add bulk actions to the users list report
*
* @module core_admin/bulk_user_actions
* @copyright 2024 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import * as reportSelectors from 'core_reportbuilder/local/selectors';
import * as tableEvents from 'core_table/local/dynamic/events';
import * as FormChangeChecker from 'core_form/changechecker';
import * as CustomEvents from 'core/custom_interaction_events';
import jQuery from 'jquery';
const Selectors = {
bulkActionsForm: 'form#user-bulk-action-form',
userReportWrapper: '[data-region="report-user-list-wrapper"]',
checkbox: 'input[type="checkbox"][data-togglegroup="report-select-all"][data-toggle="slave"]',
masterCheckbox: 'input[type="checkbox"][data-togglegroup="report-select-all"][data-toggle="master"]',
checkedRows: '[data-togglegroup="report-select-all"][data-toggle="slave"]:checked',
};
/**
* Initialise module
*/
export const init = () => {
const userBulkForm = document.querySelector(Selectors.bulkActionsForm);
const userReport = userBulkForm?.closest(Selectors.userReportWrapper)?.querySelector(reportSelectors.regions.report);
if (!userBulkForm || !userReport) {
return;
}
const actionSelect = userBulkForm.querySelector('select');
CustomEvents.define(actionSelect, [CustomEvents.events.accessibleChange]);
jQuery(actionSelect).on(CustomEvents.events.accessibleChange, event => {
if (event.target.value && `${event.target.value}` !== "0") {
const e = new Event('submit', {cancelable: true});
userBulkForm.dispatchEvent(e);
if (!e.defaultPrevented) {
FormChangeChecker.markFormSubmitted(userBulkForm);
userBulkForm.submit();
}
}
});
// Every time the checkboxes in the report are changed, update the list of users in the form values
// and enable/disable the action select.
const updateUserIds = () => {
const selectedUsers = [...userReport.querySelectorAll(Selectors.checkedRows)];
const selectedUserIds = selectedUsers.map(check => parseInt(check.value));
userBulkForm.querySelector('[name="userids"]').value = selectedUserIds.join(',');
actionSelect.disabled = selectedUsers.length === 0;
const selectedUsersNames = selectedUsers.map(check => document.querySelector(`label[for="${check.id}"]`).textContent);
// Add the user ids and names to the form data attributes so they can be available from the
// other JS modules that listen to the form submit event.
userBulkForm.data = {userids: selectedUserIds, usernames: selectedUsersNames};
};
updateUserIds();
document.addEventListener('change', event => {
// When checkboxes are checked next to individual users or the master toggle (Select all/none).
if ((event.target.matches(Selectors.checkbox) || event.target.matches(Selectors.masterCheckbox))
&& userReport.contains(event.target)) {
updateUserIds();
}
});
document.addEventListener(tableEvents.tableContentRefreshed, event => {
// When the report contents is updated (i.e. page is changed, filters applied, etc).
if (userReport.contains(event.target)) {
updateUserIds();
}
});
};
-179
View File
@@ -1,179 +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 {refreshTableContent} from 'core_table/dynamic';
import * as Selectors from 'core_table/local/dynamic/selectors';
import {call as fetchMany} from 'core/ajax';
import Pending from 'core/pending';
import {fetchNotifications} from 'core/notification';
let watching = false;
export default class {
/**
* @property {function[]} clickHandlers a list of handlers to call on click.
*/
clickHandlers = [];
constructor() {
this.addClickHandler(this.handleStateToggle);
this.addClickHandler(this.handleMoveUpDown);
this.registerEventListeners();
}
/**
* Initialise an instance of the class.
*
* This is just a way of making it easier to initialise an instance of the class from PHP.
*/
static init() {
if (watching) {
return;
}
watching = true;
new this();
}
/**
* Add a click handler to the list of handlers.
*
* @param {Function} handler A handler to call on a click event
*/
addClickHandler(handler) {
this.clickHandlers.push(handler.bind(this));
}
/**
* Register the event listeners for this instance.
*/
registerEventListeners() {
document.addEventListener('click', function(e) {
const tableRoot = this.getTableRoot(e);
if (!tableRoot) {
return;
}
this.clickHandlers.forEach((handler) => handler(tableRoot, e));
}.bind(this));
}
/**
* Get the table root from an event.
*
* @param {Event} e
* @returns {HTMLElement|bool}
*/
getTableRoot(e) {
const tableRoot = e.target.closest(Selectors.main.region);
if (!tableRoot) {
return false;
}
return tableRoot;
}
/**
* Set the plugin state (enabled or disabled)
*
* @param {string} methodname The web service to call
* @param {string} plugin The name of the plugin to set the state for
* @param {number} state The state to set
* @returns {Promise}
*/
setPluginState(methodname, plugin, state) {
return fetchMany([{
methodname,
args: {
plugin,
state,
},
}])[0];
}
setPluginOrder(methodname, plugin, direction) {
return fetchMany([{
methodname,
args: {
plugin,
direction,
},
}])[0];
}
/**
* Handle state toggling.
*
* @param {HTMLElement} tableRoot
* @param {Event} e
*/
async handleStateToggle(tableRoot, e) {
const stateToggle = e.target.closest('[data-action="togglestate"][data-toggle-method]');
if (stateToggle) {
e.preventDefault();
const pendingPromise = new Pending('core_table/dynamic:togglestate');
await this.setPluginState(
stateToggle.dataset.toggleMethod,
stateToggle.dataset.plugin,
stateToggle.dataset.state === '1' ? 0 : 1
);
const [updatedRoot] = await Promise.all([
refreshTableContent(tableRoot),
fetchNotifications(),
]);
// Refocus on the link that as pressed in the first place.
updatedRoot.querySelector(`[data-action="togglestate"][data-plugin="${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('core_table/dynamic:processAction');
await this.setPluginOrder(
actionLink.dataset.method,
actionLink.dataset.plugin,
actionLink.dataset.direction === 'up' ? -1 : 1,
);
const [updatedRoot] = await Promise.all([
refreshTableContent(tableRoot),
fetchNotifications(),
]);
// Refocus on the link that as pressed in the first place.
const exactMatch = updatedRoot.querySelector(
`[data-action="move"][data-plugin="${actionLink.dataset.plugin}"][data-direction="${actionLink.dataset.direction}"]`
);
if (exactMatch) {
exactMatch.focus();
} else {
// The move link is not present anymore, so we need to focus on the other one.
updatedRoot.querySelector(`[data-action="move"][data-plugin="${actionLink.dataset.plugin}"]`)?.focus();
}
pendingPromise.resolve();
}
}
@@ -1,104 +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/>.
/**
* Modal for theme previews.
*
* @module core_admin/themeselector/preview_modal
* @copyright 2023 David Woloszyn <david.woloszyn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import ModalEvents from 'core/modal_events';
import ModalCancel from 'core/modal_cancel';
import ModalSaveCancel from 'core/modal_save_cancel';
import Notification from 'core/notification';
import Templates from 'core/templates';
import {getString} from 'core/str';
const SELECTORS = {
THEMES_CONTAINER: 'themelist',
PREVIEW: '[data-action="preview"]',
};
/**
* Entrypoint of the js.
*
* @method init
*/
export const init = () => {
registerListenerEvents();
};
/**
* Register theme related event listeners.
*
* @method registerListenerEvents
*/
const registerListenerEvents = () => {
document.addEventListener('click', (e) => {
const preview = e.target.closest(SELECTORS.PREVIEW);
if (preview) {
buildModal(preview).catch(Notification.exception);
}
});
};
/**
* Build the modal with the provided data.
*
* @method buildModal
* @param {object} element
*/
const buildModal = async(element) => {
// This string can be long. We will fetch it with JS as opposed to passing it as an attribute.
let description = await getString('choosereadme', 'theme_' + element.getAttribute('data-choose'));
const themesContainer = document.getElementById(SELECTORS.THEMES_CONTAINER);
const definedInConfig = parseInt(themesContainer.dataset.definedinconfig);
// Prepare data for modal.
const data = {
name: element.getAttribute('data-name'),
image: element.getAttribute('data-image'),
description: description.replace(/<[^>]+>/g, ' '), // Strip out HTML tags.
current: element.getAttribute('data-current'),
actionurl: element.getAttribute('data-actionurl'),
choose: element.getAttribute('data-choose'),
sesskey: element.getAttribute('data-sesskey'),
definedinconfig: definedInConfig,
};
// Determine which modal template we should use.
let modalTemplate = ModalSaveCancel;
if (data.current || data.definedinconfig) {
modalTemplate = ModalCancel;
}
const modal = await modalTemplate.create({
title: data.name,
body: Templates.render('core_admin/themeselector/theme_preview_modal', data),
large: true,
buttons: {
'save': getString('selecttheme', 'moodle'),
'cancel': getString('closebuttontitle', 'moodle'),
},
show: true,
});
modal.getRoot().on(ModalEvents.save, () => {
modal.getRoot().find('form').submit();
});
};
-113
View File
@@ -1,113 +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/>.
/**
* Allows admin to configure antiviruses.
*
* @package core_antivirus
* @copyright 2015 Ruslan Kabalin, Lancaster University.
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once('../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/tablelib.php');
$action = required_param('action', PARAM_ALPHANUMEXT);
$antivirus = required_param('antivirus', PARAM_PLUGIN);
$confirm = optional_param('confirm', 0, PARAM_BOOL);
$PAGE->set_url('/admin/antiviruses.php', array('action' => $action, 'antivirus' => $antivirus));
$PAGE->set_context(context_system::instance());
require_admin();
$returnurl = "$CFG->wwwroot/$CFG->admin/settings.php?section=manageantiviruses";
// Get currently installed and enabled antivirus plugins.
$availableantiviruses = \core\antivirus\manager::get_available();
if (!empty($antivirus) and empty($availableantiviruses[$antivirus])) {
redirect ($returnurl);
}
$activeantiviruses = explode(',', $CFG->antiviruses);
foreach ($activeantiviruses as $key => $active) {
if (empty($availableantiviruses[$active])) {
unset($activeantiviruses[$key]);
}
}
if (!confirm_sesskey()) {
redirect($returnurl);
}
$needsupdate = false;
switch ($action) {
case 'disable':
// Remove from enabled list.
$class = \core_plugin_manager::resolve_plugininfo_class('antivirus');
$class::enable_plugin($antivirus, false);
break;
case 'enable':
// Add to enabled list.
if (!in_array($antivirus, $activeantiviruses)) {
$class = \core_plugin_manager::resolve_plugininfo_class('antivirus');
$class::enable_plugin($antivirus, true);
}
break;
case 'down':
$key = array_search($antivirus, $activeantiviruses);
// Check auth plugin is valid.
if ($key !== false) {
// Move down the list.
if ($key < (count($activeantiviruses) - 1)) {
$fsave = $activeantiviruses[$key];
$activeantiviruses[$key] = $activeantiviruses[$key + 1];
$activeantiviruses[$key + 1] = $fsave;
$needsupdate = true;
}
}
break;
case 'up':
$key = array_search($antivirus, $activeantiviruses);
// Check auth is valid.
if ($key !== false) {
// Move up the list.
if ($key >= 1) {
$fsave = $activeantiviruses[$key];
$activeantiviruses[$key] = $activeantiviruses[$key - 1];
$activeantiviruses[$key - 1] = $fsave;
$needsupdate = true;
}
}
break;
default:
break;
}
if ($needsupdate) {
$new = implode(',', $activeantiviruses);
add_to_config_log('antiviruses', $CFG->antiviruses, $new, 'core');
set_config('antiviruses', $new);
core_plugin_manager::reset_caches();
}
redirect ($returnurl);
+29 -18
View File
@@ -11,14 +11,15 @@ require_once('../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/tablelib.php');
require_admin();
require_login();
require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM));
$returnurl = new moodle_url('/admin/settings.php', array('section'=>'manageauths'));
$PAGE->set_url($returnurl);
$action = optional_param('action', '', PARAM_ALPHANUMEXT);
$auth = optional_param('auth', '', PARAM_PLUGIN);
$action = optional_param('action', '', PARAM_ACTION);
$auth = optional_param('auth', '', PARAM_SAFEDIR);
get_enabled_auth_plugins(true); // fix the list of enabled auths
if (empty($CFG->auth)) {
@@ -28,7 +29,7 @@ if (empty($CFG->auth)) {
}
if (!empty($auth) and !exists_auth_plugin($auth)) {
throw new \moodle_exception('pluginnotinstalled', 'auth', $returnurl, $auth);
print_error('pluginnotinstalled', 'auth', $returnurl, $auth);
}
////////////////////////////////////////////////////////////////////////////////
@@ -40,31 +41,41 @@ if (!confirm_sesskey()) {
switch ($action) {
case 'disable':
// Remove from enabled list.
$class = \core_plugin_manager::resolve_plugininfo_class('auth');
$class::enable_plugin($auth, false);
// remove from enabled list
$key = array_search($auth, $authsenabled);
if ($key !== false) {
unset($authsenabled[$key]);
set_config('auth', implode(',', $authsenabled));
}
if ($auth == $CFG->registerauth) {
set_config('registerauth', '');
}
session_gc(); // remove stale sessions
break;
case 'enable':
// Add to enabled list.
$class = \core_plugin_manager::resolve_plugininfo_class('auth');
$class::enable_plugin($auth, true);
// add to enabled list
if (!in_array($auth, $authsenabled)) {
$authsenabled[] = $auth;
$authsenabled = array_unique($authsenabled);
set_config('auth', implode(',', $authsenabled));
}
session_gc(); // remove stale sessions
break;
case 'down':
$key = array_search($auth, $authsenabled);
// check auth plugin is valid
if ($key === false) {
throw new \moodle_exception('pluginnotenabled', 'auth', $returnurl, $auth);
print_error('pluginnotenabled', 'auth', $returnurl, $auth);
}
// move down the list
if ($key < (count($authsenabled) - 1)) {
$fsave = $authsenabled[$key];
$authsenabled[$key] = $authsenabled[$key + 1];
$authsenabled[$key + 1] = $fsave;
$value = implode(',', $authsenabled);
add_to_config_log('auth', $CFG->auth, $value, 'core');
set_config('auth', $value);
set_config('auth', implode(',', $authsenabled));
}
break;
@@ -72,16 +83,14 @@ switch ($action) {
$key = array_search($auth, $authsenabled);
// check auth is valid
if ($key === false) {
throw new \moodle_exception('pluginnotenabled', 'auth', $returnurl, $auth);
print_error('pluginnotenabled', 'auth', $returnurl, $auth);
}
// move up the list
if ($key >= 1) {
$fsave = $authsenabled[$key];
$authsenabled[$key] = $authsenabled[$key - 1];
$authsenabled[$key - 1] = $fsave;
$value = implode(',', $authsenabled);
add_to_config_log('auth', $CFG->auth, $value, 'core');
set_config('auth', $value);
set_config('auth', implode(',', $authsenabled));
}
break;
@@ -90,3 +99,5 @@ switch ($action) {
}
redirect($returnurl);
+179
View File
@@ -0,0 +1,179 @@
<?php
/**
* Edit configuration for an individual auth plugin
*/
require_once '../config.php';
require_once $CFG->libdir.'/adminlib.php';
$auth = required_param('auth', PARAM_SAFEDIR);
$PAGE->set_pagetype('admin-auth-' . $auth);
admin_externalpage_setup('authsetting'.$auth);
$authplugin = get_auth_plugin($auth);
$err = array();
$returnurl = "$CFG->wwwroot/$CFG->admin/settings.php?section=manageauths";
// save configuration changes
if ($frm = data_submitted() and confirm_sesskey()) {
$authplugin->validate_form($frm, $err);
if (count($err) == 0) {
// save plugin config
if ($authplugin->process_config($frm)) {
// save field lock configuration
foreach ($frm as $name => $value) {
if (preg_match('/^lockconfig_(.+?)$/', $name, $matches)) {
$plugin = "auth/$auth";
$name = $matches[1];
set_config($name, $value, $plugin);
}
}
redirect($returnurl);
exit;
}
} else {
foreach ($err as $key => $value) {
$focus = "form.$key";
}
}
} else {
$frmlegacystyle = get_config('auth/'.$auth);
$frmnewstyle = get_config('auth_'.$auth);
$frm = (object)array_merge((array)$frmlegacystyle, (array)$frmnewstyle);
}
$user_fields = $authplugin->userfields;
//$user_fields = array("firstname", "lastname", "email", "phone1", "phone2", "institution", "department", "address", "city", "country", "description", "idnumber", "lang");
/// Get the auth title (from core or own auth lang files)
$authtitle = $authplugin->get_title();
/// Get the auth descriptions (from core or own auth lang files)
$authdescription = $authplugin->get_description();
// output configuration form
echo $OUTPUT->header();
// choose an authentication method
echo "<form id=\"authmenu\" method=\"post\" action=\"auth_config.php\">\n";
echo "<div>\n";
echo "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />\n";
echo "<input type=\"hidden\" name=\"auth\" value=\"".$auth."\" />\n";
// auth plugin description
echo $OUTPUT->box_start();
echo $OUTPUT->heading($authtitle);
echo $OUTPUT->box_start('informationbox');
echo $authdescription;
echo $OUTPUT->box_end();
echo "<hr />\n";
$authplugin->config_form($frm, $err, $user_fields);
echo $OUTPUT->box_end();
echo '<p style="text-align: center"><input type="submit" value="' . get_string("savechanges") . "\" /></p>\n";
echo "</div>\n";
echo "</form>\n";
echo $OUTPUT->footer();
exit;
/// Functions /////////////////////////////////////////////////////////////////
// Good enough for most auth plugins
// but some may want a custom one if they are offering
// other options
// Note: lockconfig_ fields have special handling.
function print_auth_lock_options ($auth, $user_fields, $helptext, $retrieveopts, $updateopts) {
global $OUTPUT;
echo '<tr><td colspan="3">';
if ($retrieveopts) {
echo $OUTPUT->heading(get_string('auth_data_mapping', 'auth'));
} else {
echo $OUTPUT->heading(get_string('auth_fieldlocks', 'auth'));
}
echo '</td></tr>';
$lockoptions = array ('unlocked' => get_string('unlocked', 'auth'),
'unlockedifempty' => get_string('unlockedifempty', 'auth'),
'locked' => get_string('locked', 'auth'));
$updatelocaloptions = array('oncreate' => get_string('update_oncreate', 'auth'),
'onlogin' => get_string('update_onlogin', 'auth'));
$updateextoptions = array('0' => get_string('update_never', 'auth'),
'1' => get_string('update_onupdate', 'auth'));
$pluginconfig = get_config("auth/$auth");
// helptext is on a field with rowspan
if (empty($helptext)) {
$helptext = '&nbsp;';
}
foreach ($user_fields as $field) {
// Define some vars we'll work with
if (!isset($pluginconfig->{"field_map_$field"})) {
$pluginconfig->{"field_map_$field"} = '';
}
if (!isset($pluginconfig->{"field_updatelocal_$field"})) {
$pluginconfig->{"field_updatelocal_$field"} = '';
}
if (!isset($pluginconfig->{"field_updateremote_$field"})) {
$pluginconfig->{"field_updateremote_$field"} = '';
}
if (!isset($pluginconfig->{"field_lock_$field"})) {
$pluginconfig->{"field_lock_$field"} = '';
}
// define the fieldname we display to the user
$fieldname = $field;
if ($fieldname === 'lang') {
$fieldname = get_string('language');
} elseif (preg_match('/^(.+?)(\d+)$/', $fieldname, $matches)) {
$fieldname = get_string($matches[1]) . ' ' . $matches[2];
} elseif ($fieldname == 'url') {
$fieldname = get_string('webpage');
} else {
$fieldname = get_string($fieldname);
}
if ($retrieveopts) {
$varname = 'field_map_' . $field;
echo '<tr valign="top"><td align="right">';
echo '<label for="lockconfig_'.$varname.'">'.$fieldname.'</label>';
echo '</td><td>';
echo "<input id=\"lockconfig_{$varname}\" name=\"lockconfig_{$varname}\" type=\"text\" size=\"30\" value=\"{$pluginconfig->$varname}\" />";
echo '<div style="text-align: right">';
echo '<label for="menulockconfig_field_updatelocal_'.$field.'">'.get_string('auth_updatelocal', 'auth') . '</label>&nbsp;';
echo html_writer::select($updatelocaloptions, "lockconfig_field_updatelocal_{$field}", $pluginconfig->{"field_updatelocal_$field"}, false);
echo '<br />';
if ($updateopts) {
echo '<label for="menulockconfig_field_updateremote_'.$field.'">'.get_string('auth_updateremote', 'auth') . '</label>&nbsp;';
echo html_writer::select($updateextoptions, "lockconfig_field_updateremote_{$field}", $pluginconfig->{"field_updateremote_$field"}, false);
echo '<br />';
}
echo '<label for="menulockconfig_field_lock_'.$field.'">'.get_string('auth_fieldlock', 'auth') . '</label>&nbsp;';
echo html_writer::select($lockoptions, "lockconfig_field_lock_{$field}", $pluginconfig->{"field_lock_$field"}, false);
echo '</div>';
} else {
echo '<tr valign="top"><td align="right">';
echo '<label for="menulockconfig_field_lock_'.$field.'">'.$fieldname.'</label>';
echo '</td><td>';
echo html_writer::select($lockoptions, "lockconfig_field_lock_{$field}", $pluginconfig->{"field_lock_$field"}, false);
}
echo '</td>';
if (!empty($helptext)) {
echo '<td rowspan="' . count($user_fields) . '">' . $helptext . '</td>';
$helptext = '';
}
echo '</tr>';
}
}
+76
View File
@@ -0,0 +1,76 @@
<?php
// block.php - allows admin to edit all local configuration variables for a block
require_once('../config.php');
require_once($CFG->libdir.'/adminlib.php');
$blockid = required_param('block', PARAM_INT);
if(!$blockrecord = blocks_get_record($blockid)) {
print_error('blockdoesnotexist', 'error');
}
admin_externalpage_setup('blocksetting'.$blockrecord->name);
$block = block_instance($blockrecord->name);
if($block === false) {
print_error('blockcannotinistantiate', 'error');
}
// Define the data we're going to silently include in the instance config form here,
// so we can strip them from the submitted data BEFORE handling it.
$hiddendata = array(
'block' => $blockid,
'sesskey' => sesskey()
);
/// If data submitted, then process and store.
if ($config = data_submitted()) {
if (!confirm_sesskey()) {
print_error('confirmsesskeybad', 'error');
}
if(!$block->has_config()) {
print_error('blockcannotconfig', 'error');
}
$remove = array_keys($hiddendata);
foreach($remove as $item) {
unset($config->$item);
}
$block->config_save($config);
redirect("$CFG->wwwroot/$CFG->admin/blocks.php", get_string("changessaved"), 1);
exit;
}
/// Otherwise print the form.
$strmanageblocks = get_string('manageblocks');
$strblockname = $block->get_title();
echo $OUTPUT->header();
echo $OUTPUT->heading($strblockname);
echo $OUTPUT->notification('This block still uses an old-style config_global.html file. ' .
'It must be updated by a developer to use a settings.php file.');
echo $OUTPUT->box(get_string('configwarning', 'admin'), 'generalbox boxwidthnormal boxaligncenter');
echo '<br />';
echo '<form method="post" action="block.php">';
echo '<p>';
foreach($hiddendata as $name => $val) {
echo '<input type="hidden" name="'. $name .'" value="'. $val .'" />';
}
echo '</p>';
echo $OUTPUT->box_start();
include($CFG->dirroot.'/blocks/'. $block->name() .'/config_global.html');
echo $OUTPUT->box_end();
echo '</form>';
echo $OUTPUT->footer();
+216 -79
View File
@@ -1,94 +1,231 @@
<?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/>.
/**
* Allows the admin to configure blocks (hide/show, uninstall and configure)
*
* @package core_admin
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// Allows the admin to configure blocks (hide/show, delete and configure)
require_once('../config.php');
require_once("{$CFG->libdir}/adminlib.php");
require_once("{$CFG->libdir}/blocklib.php");
require_once("{$CFG->libdir}/tablelib.php");
require_once('../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/tablelib.php');
admin_externalpage_setup('manageblocks');
admin_externalpage_setup('manageblocks');
$plugin = optional_param('plugin', '', PARAM_PLUGIN);
$action = optional_param('action', '', PARAM_ALPHA);
$unprotect = optional_param('unprotect', 0, PARAM_PLUGIN);
$protect = optional_param('protect', 0, PARAM_PLUGIN);
$confirm = optional_param('confirm', 0, PARAM_BOOL);
$hide = optional_param('hide', 0, PARAM_INT);
$show = optional_param('show', 0, PARAM_INT);
$delete = optional_param('delete', 0, PARAM_INT);
$strmanageblocks = get_string('manageblocks');
/// Print headings
// If data submitted, then process and store.
if (!empty($plugin) && !empty($action) && confirm_sesskey()) {
$manager = \core_plugin_manager::resolve_plugininfo_class('block');
$pluginname = get_string('pluginname', "block_{$plugin}");
$strmanageblocks = get_string('manageblocks');
$strdelete = get_string('delete');
$strversion = get_string('version');
$strhide = get_string('hide');
$strshow = get_string('show');
$strsettings = get_string('settings');
$strcourses = get_string('blockinstances', 'admin');
$strname = get_string('name');
$strshowblockcourse = get_string('showblockcourse');
if ($action === 'disable' && $manager::enable_plugin($plugin, 0)) {
\core\notification::add(
get_string('plugin_disabled', 'core_admin', $pluginname),
\core\notification::SUCCESS
);
// Settings not required - only pages.
admin_get_root(true, false);
} else if ($action === 'enable' && $manager::enable_plugin($plugin, 1)) {
\core\notification::add(
get_string('plugin_enabled', 'core_admin', $pluginname),
\core\notification::SUCCESS
);
/// If data submitted, then process and store.
// Settings not required - only pages.
admin_get_root(true, false);
if (!empty($hide) && confirm_sesskey()) {
if (!$block = $DB->get_record('block', array('id'=>$hide))) {
print_error('blockdoesnotexist', 'error');
}
$DB->set_field('block', 'visible', '0', array('id'=>$block->id)); // Hide block
admin_get_root(true, false); // settings not required - only pages
}
// Redirect back to the page with out any params.
redirect(new moodle_url('/admin/blocks.php'));
}
if (!empty($show) && confirm_sesskey() ) {
if (!$block = $DB->get_record('block', array('id'=>$show))) {
print_error('blockdoesnotexist', 'error');
}
$DB->set_field('block', 'visible', '1', array('id'=>$block->id)); // Show block
admin_get_root(true, false); // settings not required - only pages
}
if (!empty($protect) && confirm_sesskey()) {
block_manager::protect_block($protect);
$pluginname = get_string('pluginname', "block_{$protect}");
\core\notification::add(
get_string('blockprotected', 'core_admin', $pluginname),
\core\notification::SUCCESS
);
// Settings not required - only pages.
admin_get_root(true, false);
}
if (!empty($delete) && confirm_sesskey()) {
echo $OUTPUT->header();
echo $OUTPUT->heading($strmanageblocks);
if (!empty($unprotect) && confirm_sesskey()) {
block_manager::unprotect_block($unprotect);
$pluginname = get_string('pluginname', "block_{$unprotect}");
\core\notification::add(
get_string('blockunprotected', 'core_admin', $pluginname),
\core\notification::SUCCESS
);
// Settings not required - only pages.
admin_get_root(true, false);
}
if (!$block = blocks_get_record($delete)) {
print_error('blockdoesnotexist', 'error');
}
if (get_string_manager()->string_exists('pluginname', "block_$block->name")) {
$strblockname = get_string('pluginname', "block_$block->name");
} else {
$strblockname = $block->name;
}
if (!$confirm) {
echo $OUTPUT->confirm(get_string('blockdeleteconfirm', '', $strblockname), 'blocks.php?delete='.$block->id.'&confirm=1', 'blocks.php');
echo $OUTPUT->footer();
exit;
} else {
// Inform block it's about to be deleted
if (file_exists("$CFG->dirroot/blocks/$block->name/block_$block->name.php")) {
$blockobject = block_instance($block->name);
if ($blockobject) {
$blockobject->before_delete(); //only if we can create instance, block might have been already removed
}
}
// First delete instances and then block
$instances = $DB->get_records('block_instances', array('blockname' => $block->name));
if(!empty($instances)) {
foreach($instances as $instance) {
blocks_delete_instance($instance);
}
}
// Delete block
$DB->delete_records('block', array('id'=>$block->id));
drop_plugin_tables($block->name, "$CFG->dirroot/blocks/$block->name/db/install.xml", false); // old obsoleted table names
drop_plugin_tables('block_'.$block->name, "$CFG->dirroot/blocks/$block->name/db/install.xml", false);
// Delete the capabilities that were defined by this block
capabilities_cleanup('block/'.$block->name);
// Remove event handlers and dequeue pending events
events_uninstall('block/'.$block->name);
$a->block = $strblockname;
$a->directory = $CFG->dirroot.'/blocks/'.$block->name;
notice(get_string('blockdeletefiles', '', $a), 'blocks.php');
}
}
echo $OUTPUT->header();
echo $OUTPUT->heading($strmanageblocks);
/// Main display starts here
/// Get and sort the existing blocks
if (!$blocks = $DB->get_records('block', array(), 'name ASC')) {
print_error('noblocks', 'error'); // Should never happen
}
$incompatible = array();
/// Print the table of all blocks
$table = new flexible_table('admin-blocks-compatible');
$table->define_columns(array('name', 'instances', 'version', 'hideshow', 'delete', 'settings'));
$table->define_headers(array($strname, $strcourses, $strversion, $strhide.'/'.$strshow, $strdelete, $strsettings));
$table->define_baseurl($CFG->wwwroot.'/'.$CFG->admin.'/blocks.php');
$table->set_attribute('class', 'compatibleblockstable blockstable generaltable');
$table->setup();
foreach ($blocks as $blockid=>$block) {
$blockname = $block->name;
if (!file_exists("$CFG->dirroot/blocks/$blockname/block_$blockname.php")) {
$blockobject = false;
$strblockname = '<span class="notifyproblem">'.$blockname.' ('.get_string('missingfromdisk').')</span>';
$plugin = new stdClass();
$plugin->version = $block->version;
} else {
$plugin = new stdClass();
$plugin->version = '???';
if (file_exists("$CFG->dirroot/blocks/$blockname/version.php")) {
include("$CFG->dirroot/blocks/$blockname/version.php");
}
if (!$blockobject = block_instance($block->name)) {
$incompatible[] = $block;
continue;
}
$strblockname = get_string('pluginname', 'block_'.$blockname);
}
$delete = '<a href="blocks.php?delete='.$blockid.'&amp;sesskey='.sesskey().'">'.$strdelete.'</a>';
$settings = ''; // By default, no configuration
if ($blockobject and $blockobject->has_config()) {
$blocksettings = admin_get_root()->locate('blocksetting' . $block->name);
if ($blocksettings instanceof admin_externalpage) {
$settings = '<a href="' . $blocksettings->url . '">' . get_string('settings') . '</a>';
} else if ($blocksettings instanceof admin_settingpage) {
$settings = '<a href="'.$CFG->wwwroot.'/'.$CFG->admin.'/settings.php?section=blocksetting'.$block->name.'">'.$strsettings.'</a>';
} else {
$settings = '<a href="block.php?block='.$blockid.'">'.$strsettings.'</a>';
}
}
// MDL-11167, blocks can be placed on mymoodle, or the blogs page
// and it should not show up on course search page
$totalcount = $DB->count_records('block_instances', array('blockname'=>$blockname));
$count = $DB->count_records('block_instances', array('blockname'=>$blockname, 'pagetypepattern'=>'course-view-*'));
if ($count>0) {
$blocklist = "<a href=\"{$CFG->wwwroot}/course/search.php?blocklist=$blockid&amp;sesskey=".sesskey()."\" ";
$blocklist .= "title=\"$strshowblockcourse\" >$totalcount</a>";
}
else {
$blocklist = "$totalcount";
}
$class = ''; // Nothing fancy, by default
if (!$blockobject) {
// ignore
$visible = '';
} else if ($blocks[$blockid]->visible) {
$visible = '<a href="blocks.php?hide='.$blockid.'&amp;sesskey='.sesskey().'" title="'.$strhide.'">'.
'<img src="'.$OUTPUT->pix_url('i/hide') . '" class="icon" alt="'.$strhide.'" /></a>';
} else {
$visible = '<a href="blocks.php?show='.$blockid.'&amp;sesskey='.sesskey().'" title="'.$strshow.'">'.
'<img src="'.$OUTPUT->pix_url('i/show') . '" class="icon" alt="'.$strshow.'" /></a>';
$class = ' class="dimmed_text"'; // Leading space required!
}
if ($block->version == $plugin->version) {
$version = $block->version;
} else {
$version = "$block->version ($plugin->version)";
}
$table->add_data(array(
'<span'.$class.'>'.$strblockname.'</span>',
$blocklist,
'<span'.$class.'>'.$version.'</span>',
$visible,
$delete,
$settings
));
}
$table->print_html();
if (!empty($incompatible)) {
echo $OUTPUT->heading(get_string('incompatibleblocks', 'blockstable', 'admin'));
$table = new flexible_table('admin-blocks-incompatible');
$table->define_columns(array('block', 'delete'));
$table->define_headers(array($strname, $strdelete));
$table->define_baseurl($CFG->wwwroot.'/'.$CFG->admin.'/blocks.php');
$table->set_attribute('class', 'incompatibleblockstable generaltable');
$table->setup();
foreach ($incompatible as $block) {
$table->add_data(array(
$block->name,
'<a href="blocks.php?delete='.$block->id.'&amp;sesskey='.sesskey().'">'.$strdelete.'</a>',
));
}
$table->print_html();
}
echo $OUTPUT->footer();
echo $OUTPUT->header();
echo $OUTPUT->heading($strmanageblocks);
echo $OUTPUT->notification(get_string('noteunneededblocks', 'admin'), 'info', false);
// Print the table of all blocks.
$table = new \core_admin\table\block_management_table();
$table->out();
echo $OUTPUT->footer();
+152
View File
@@ -0,0 +1,152 @@
<?php
/// Create "blog" forums in each course and copy blog entries from these courses' participants in these forums
define('NO_OUTPUT_BUFFERING', true);
require_once('../config.php');
require_once($CFG->dirroot.'/course/lib.php');
require_once($CFG->dirroot.'/blog/lib.php');
require_once($CFG->dirroot.'/mod/forum/lib.php');
require_once($CFG->libdir.'/adminlib.php');
admin_externalpage_setup('bloglevelupgrade');
$PAGE->set_pagelayout('maintenance');
$go = optional_param('go', 0, PARAM_BOOL);
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('bloglevelupgrade', 'admin'));
$strbloglevelupgrade = get_string('bloglevelupgradeinfo', 'admin');
if (!$go or !data_submitted() or !confirm_sesskey()) { /// Print a form
$optionsyes = array('go'=>1, 'sesskey'=>sesskey());
echo $OUTPUT->confirm($strbloglevelupgrade, new moodle_url('bloglevelupgrade.php', $optionsyes), 'index.php');
echo $OUTPUT->footer();
die;
}
echo $OUTPUT->box_start();
/// Turn off time limits, sometimes upgrades can be slow.
set_time_limit(0);
$i = 0;
// If $CFG->bloglevel is set to BLOG_GROUP_LEVEL or BLOG_COURSE_LEVEL, create a new "blog" forum in each course
// whose enrolled students have written blog entries, copy these entries in that forum and switch off blogs at site level
if ($CFG->bloglevel == BLOG_COURSE_LEVEL || $CFG->bloglevel == BLOG_GROUP_LEVEL) {
$pbar = new progress_bar('bloglevelupgrade', 500, true);
$bloggers = $DB->get_records_sql("SELECT userid FROM {post} WHERE module = 'blog' GROUP BY userid");
require_once($CFG->dirroot.'/mod/forum/lib.php');
$a = new stdClass();
$a->userscount = 0;
$a->blogcount = 0;
foreach ($bloggers as $blogger) {
$courses = enrol_get_users_courses($blogger->userid, true, 'groupmode,groupmodeforce');
$blogentries = $DB->get_records('post', array('module' => 'blog', 'userid' => $blogger->userid));
foreach ($courses as $course) {
$forum = forum_get_course_forum($course->id, 'blog');
$cm = get_coursemodule_from_instance('forum', $forum->id);
if ($CFG->bloglevel == BLOG_GROUP_LEVEL && $course->groupmode != NOGROUPS) {
// Unless the course is set to separate groups forced, force the forum to Separate groups
if (!($course->groupmode == SEPARATEGROUPS && $course->groupmodeforce)) {
$cm->groupmode = SEPARATEGROUPS;
$DB->update_record('course_modules', $cm);
}
$groups = groups_get_user_groups($course->id, $blogger->userid);
foreach ($groups[0] as $groupid) { // [0] is for all groupings combined
$a->blogcount += bloglevelupgrade_entries($blogentries, $forum, $cm, $groupid);
}
} else {
$a->blogcount += bloglevelupgrade_entries($blogentries, $forum, $cm);
}
}
$a->userscount = $i . '/' . count($bloggers);
$pbar->update($i, count($bloggers), get_string('bloglevelupgradeprogress', 'admin', $a));
$i++;
}
}
function bloglevelupgrade_entries($blogentries, $forum, $cm, $groupid=-1) {
$count = 0;
$forumcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
$sitecontext = get_context_instance(CONTEXT_SYSTEM);
foreach ($blogentries as $blogentry) {
$discussion = new stdClass();
$discussion->course = $forum->course;
$discussion->forum = $forum->id;
$discussion->name = $blogentry->subject;
$discussion->assessed = $forum->assessed;
$discussion->message = $blogentry->summary;
$discussion->messageformat = $blogentry->summaryformat;
$discussion->messagetrust = 0;
$discussion->attachments = 0;
$discussion->mailnow = false;
$discussion->timemodified = $blogentry->created;
$discussion->itemid = null;
$discussion->groupid = $groupid;
$message = '';
$discussionid = forum_add_discussion($discussion, null, $message);
// Copy file attachment records
$fs = get_file_storage();
$files = $fs->get_area_files($sitecontext->id, 'blog', 'attachment', $blogentry->id);
if (!empty($files)) {
foreach ($files as $storedfile) {
$newfile = new stdClass();
$newfile->component = 'mod_forum';
$newfile->filearea = 'attachment';
$newfile->itemid = $discussion->firstpost;
$newfile->contextid = $forumcontext->id;
$fs->create_file_from_storedfile($newfile, $storedfile->get_id());
}
}
$files = $fs->get_area_files($sitecontext->id, 'blog', 'post', $blogentry->id);
if (!empty($files)) {
foreach ($files as $storedfile) {
$newfile = new stdClass();
$newfile->component = 'mod_forum';
$newfile->filearea = 'post';
$newfile->itemid = $discussion->firstpost;
$newfile->contextid = $forumcontext->id;
$fs->create_file_from_storedfile($newfile, $storedfile->get_id());
}
}
$count++;
}
return $count;
}
// END OF LOOP
// set conversion flag - switches to new plugin automatically
set_config('bloglevel_upgrade_complete', 1);
// Finally switch bloglevel to 0 (disabled)
set_config('bloglevel', 0);
echo $OUTPUT->box_end();
/// Rebuild course cache which might be incorrect now
echo $OUTPUT->notification('Rebuilding course cache...', 'notifysuccess');
rebuild_course_cache();
echo $OUTPUT->notification('...finished', 'notifysuccess');
echo $OUTPUT->continue_button('index.php');
echo $OUTPUT->footer();
die;
-163
View File
@@ -1,163 +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/>.
/**
* This file is used to display a categories sub categories, external pages, and settings.
*
* @since Moodle 2.3
* @package admin
* @copyright 2011 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once('../config.php');
require_once($CFG->libdir.'/adminlib.php');
$category = required_param('category', PARAM_SAFEDIR);
$return = optional_param('return','', PARAM_ALPHA);
$adminediting = optional_param('adminedit', -1, PARAM_BOOL);
require_login(0, false);
$PAGE->set_context(context_system::instance());
$PAGE->set_url('/admin/category.php', array('category' => $category));
$PAGE->set_pagetype('admin-setting-' . $category);
$PAGE->set_pagelayout('admin');
$PAGE->navigation->clear_cache();
$adminroot = admin_get_root(); // need all settings
$settingspage = $adminroot->locate($category, true);
if (empty($settingspage) or !($settingspage instanceof admin_category)) {
throw new \moodle_exception('categoryerror', 'error', "$CFG->wwwroot/$CFG->admin/");
}
if (!($settingspage->check_access())) {
throw new \moodle_exception('accessdenied', 'admin');
}
$hassiteconfig = has_capability('moodle/site:config', $PAGE->context);
if ($hassiteconfig) {
$PAGE->add_header_action($OUTPUT->render_from_template('core_admin/header_search_input', [
'action' => new moodle_url('/admin/search.php'),
]));
}
$statusmsg = '';
$errormsg = '';
if ($data = data_submitted() and confirm_sesskey()) {
$count = admin_write_settings($data);
if (empty($adminroot->errors)) {
// No errors. Did we change any setting? If so, then indicate success.
if ($count) {
$statusmsg = get_string('changessaved');
} else {
switch ($return) {
case 'site': redirect("$CFG->wwwroot/");
case 'admin': redirect("$CFG->wwwroot/$CFG->admin/");
}
}
} else {
$errormsg = get_string('errorwithsettings', 'admin');
$firsterror = reset($adminroot->errors);
}
$settingspage = $adminroot->locate($category, true);
}
if ($PAGE->user_allowed_editing() && $adminediting != -1) {
$USER->editing = $adminediting;
}
$buttons = null;
if ($PAGE->user_allowed_editing() && !$PAGE->theme->haseditswitch) {
$url = clone($PAGE->url);
if ($PAGE->user_is_editing()) {
$caption = get_string('blockseditoff');
$url->param('adminedit', 'off');
} else {
$caption = get_string('blocksediton');
$url->param('adminedit', 'on');
}
$buttons = $OUTPUT->single_button($url, $caption, 'get');
}
$savebutton = false;
$outputhtml = '';
foreach ($settingspage->children as $childpage) {
if ($childpage->is_hidden() || !$childpage->check_access()) {
continue;
}
if ($childpage instanceof admin_externalpage) {
$outputhtml .= $OUTPUT->heading(html_writer::link($childpage->url, $childpage->visiblename), 3);
} else if ($childpage instanceof admin_settingpage) {
$outputhtml .= $OUTPUT->heading(html_writer::link(new moodle_url('/'.$CFG->admin.'/settings.php', array('section' => $childpage->name)), $childpage->visiblename), 3);
// If its a settings page and has settings lets display them.
if (!empty($childpage->settings)) {
$outputhtml .= html_writer::start_tag('fieldset', array('class' => 'adminsettings'));
foreach ($childpage->settings as $setting) {
if (empty($setting->nosave)) {
$savebutton = true;
}
$fullname = $setting->get_full_name();
if (array_key_exists($fullname, $adminroot->errors)) {
$data = $adminroot->errors[$fullname]->data;
} else {
$data = $setting->get_setting();
}
$outputhtml .= html_writer::tag('div', '<!-- -->', array('class' => 'clearer'));
$outputhtml .= $setting->output_html($data);
}
$outputhtml .= html_writer::end_tag('fieldset');
}
} else if ($childpage instanceof admin_category) {
$outputhtml .= $OUTPUT->heading(html_writer::link(new moodle_url('/'.$CFG->admin.'/category.php', array('category' => $childpage->name)), get_string('admincategory', 'admin', $childpage->visiblename)), 3);
}
}
if ($savebutton) {
$outputhtml .= html_writer::start_tag('div', array('class' => 'form-buttons'));
$outputhtml .= html_writer::empty_tag('input', array('class' => 'btn btn-primary form-submit', 'type' => 'submit', 'value' => get_string('savechanges','admin')));
$outputhtml .= html_writer::end_tag('div');
}
$PAGE->set_title(implode(moodle_page::TITLE_SEPARATOR, $settingspage->visiblepath));
$PAGE->set_heading($SITE->fullname);
if ($buttons) {
$PAGE->set_button($buttons);
}
echo $OUTPUT->header();
if ($errormsg !== '') {
echo $OUTPUT->notification($errormsg);
} else if ($statusmsg !== '') {
echo $OUTPUT->notification($statusmsg, 'notifysuccess');
}
echo $OUTPUT->heading(get_string('admincategory', 'admin', $settingspage->visiblename), 2);
echo html_writer::start_tag('form', array('action' => '', 'method' => 'post', 'id' => 'adminsettings'));
echo html_writer::start_tag('div');
echo html_writer::input_hidden_params(new moodle_url($PAGE->url, array('sesskey' => sesskey(), 'return' => $return)));
echo html_writer::end_tag('div');
echo html_writer::start_tag('fieldset');
echo html_writer::tag('div', '<!-- -->', array('class' => 'clearer'));
echo $outputhtml;
echo html_writer::end_tag('fieldset');
echo html_writer::end_tag('form');
// Add the form change checker.
$PAGE->requires->js_call_amd('core_form/changechecker', 'watchFormById', ['adminsettings']);
echo $OUTPUT->footer();
@@ -1,127 +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\admin;
use admin_setting;
use core_plugin_manager;
use core_text;
/**
* Admin setting plugin manager.
*
* @package core_admin
* @subpackage admin
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class admin_setting_plugin_manager extends admin_setting {
/** @var core_plugin_manager The plugin manager instance */
protected core_plugin_manager $pluginmanager;
/** @var string The plugintype that this manager covers */
protected string $plugintype;
/** @var string The class of the management table to use */
protected string $tableclass;
public function __construct(
string $plugintype,
string $tableclass,
string $name,
string $visiblename,
string $description = '',
string $defaultsetting = '',
) {
$this->nosave = true;
$this->pluginmanager = core_plugin_manager::instance();
$this->plugintype = $plugintype;
$this->tableclass = $tableclass;
parent::__construct($name, $visiblename, $description, $defaultsetting);
}
/**
* Always returns true, does nothing
*
* @return true
*/
public function get_setting(): bool {
return true;
}
/**
* Always returns true, does nothing
*
* @return true
*/
public function get_defaultsetting(): bool {
return true;
}
/**
* Always returns '', does not write anything
*
* @return string Always returns ''
*/
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
public function write_setting($data): string {
// Do not write any setting.
return '';
}
/**
* Checks if $query is one of the available editors
*
* @param string $query The string to search for
* @return bool Returns true if found, false if not
*/
public function is_related($query) {
if (parent::is_related($query)) {
return true;
}
$plugins = $this->pluginmanager->get_installed_plugins($this->plugintype);
foreach (array_keys($plugins) as $plugin) {
$plugin = "{$this->plugintype}_{$plugin}";
if (str_contains($plugin, $query)) {
return true;
}
$pluginname = get_string('pluginname', $plugin);
if (strpos(core_text::strtolower($pluginname), $query) !== false) {
return true;
}
}
return false;
}
/**
* Builds the XHTML to display the control
*
* @param string $data Unused
* @param string $query
* @return string
*/
public function output_html($data, $query = ''): string {
$table = new $this->tableclass();
if (!($table instanceof \core_admin\table\plugin_management_table)) {
throw new \coding_exception("{$this->tableclass} must be an instance of \\core_admin\\table\\plugin_management_table");
}
return highlight($query, $table->get_content());
}
}
-97
View File
@@ -1,97 +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\external;
use block_manager;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
/**
* Web Service to control the state of a plugin.
*
* @package core_admin
* @category external
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class set_block_protection extends external_api {
/**
* Returns description of method parameters
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'plugin' => new external_value(PARAM_PLUGIN, 'The name of the plugin', VALUE_REQUIRED),
'state' => new external_value(PARAM_INT, 'The target state', VALUE_REQUIRED),
]);
}
/**
* Set the block protection state.
*
* @param string $plugin The name of the plugin
* @param int $state The target state
* @return null
*/
public static function execute(
string $plugin,
int $state,
): array {
[
'plugin' => $plugin,
'state' => $state,
] = self::validate_parameters(self::execute_parameters(), [
'plugin' => $plugin,
'state' => $state,
]);
$context = \context_system::instance();
self::validate_context($context);
require_capability('moodle/site:config', $context);
[, $pluginname] = explode('_', \core_component::normalize_componentname($plugin), 2);
$displayname = get_string('pluginname', $plugin);
if ($state) {
block_manager::protect_block($pluginname);
\core\notification::add(
get_string('blockprotected', 'core_admin', $displayname),
\core\notification::SUCCESS
);
} else {
block_manager::unprotect_block($pluginname);
\core\notification::add(
get_string('blockunprotected', 'core_admin', $displayname),
\core\notification::SUCCESS
);
}
return [];
}
/**
* Describe the return structure of the external service.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([]);
}
}
-84
View File
@@ -1,84 +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\external;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
/**
* Web Service to control the order of a plugin.
*
* @package core_admin
* @category external
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class set_plugin_order extends external_api {
/**
* Returns description of method parameters
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'plugin' => new external_value(PARAM_PLUGIN, 'The name of the plugin', VALUE_REQUIRED),
'direction' => new external_value(PARAM_INT, 'The direction to move', VALUE_REQUIRED),
]);
}
/**
* Set the plugin state.
*
* @param string $plugin The name of the plugin
* @param int $direction The direction to move the plugin
* @return array
*/
public static function execute(
string $plugin,
int $direction,
): array {
[
'plugin' => $plugin,
'direction' => $direction,
] = self::validate_parameters(self::execute_parameters(), [
'plugin' => $plugin,
'direction' => $direction,
]);
$context = \context_system::instance();
self::validate_context($context);
require_capability('moodle/site:config', $context);
[$plugintype, $pluginname] = explode('_', \core_component::normalize_componentname($plugin), 2);
$manager = \core_plugin_manager::resolve_plugininfo_class($plugintype);
$manager::change_plugin_order($pluginname, $direction);
return [];
}
/**
* Describe the return structure of the external service.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([]);
}
}
-98
View File
@@ -1,98 +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\external;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
/**
* Web Service to control the state of a plugin.
*
* @package core_admin
* @category external
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class set_plugin_state extends external_api {
/**
* Returns description of method parameters
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'plugin' => new external_value(PARAM_PLUGIN, 'The name of the plugin', VALUE_REQUIRED),
'state' => new external_value(PARAM_INT, 'The target state', VALUE_REQUIRED),
]);
}
/**
* Set the plugin state.
*
* @param string $plugin The name of the plugin
* @param int $state The target state
* @return null
*/
public static function execute(
string $plugin,
int $state,
): array {
[
'plugin' => $plugin,
'state' => $state,
] = self::validate_parameters(self::execute_parameters(), [
'plugin' => $plugin,
'state' => $state,
]);
$context = \context_system::instance();
self::validate_context($context);
require_capability('moodle/site:config', $context);
[$plugintype, $pluginname] = explode('_', \core_component::normalize_componentname($plugin), 2);
$manager = \core_plugin_manager::resolve_plugininfo_class($plugintype);
$displayname = get_string('pluginname', $plugin);
if ($manager::enable_plugin($pluginname, $state)) {
if (!empty($state)) {
\core\notification::add(
get_string('plugin_enabled', 'core_admin', $displayname),
\core\notification::SUCCESS
);
} else {
\core\notification::add(
get_string('plugin_disabled', 'core_admin', $displayname),
\core\notification::SUCCESS
);
}
}
return [];
}
/**
* Describe the return structure of the external service.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([]);
}
}
-76
View File
@@ -1,76 +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/>.
/**
* Form for selective purging of caches.
*
* @package core
* @copyright 2018 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_admin\form;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
/**
* Form for selecting which caches to purge on admin/purgecaches.php
*
* @package core
* @copyright 2018 The Open University
* @author Mark Johnson <mark.johnson@open.ac.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class purge_caches extends \moodleform {
/**
* Define a "Purge all caches" button, and a fieldset with checkboxes for selectively purging separate caches.
*/
public function definition() {
$mform = $this->_form;
$mform->addElement('hidden', 'returnurl', $this->_customdata['returnurl']);
$mform->setType('returnurl', PARAM_LOCALURL);
$mform->addElement('submit', 'all', get_string('purgecaches', 'admin'));
$mform->addElement('header', 'purgecacheheader', get_string('purgeselectedcaches', 'admin'));
$checkboxes = [
$mform->createElement('advcheckbox', 'theme', '', get_string('purgethemecache', 'admin')),
$mform->createElement('advcheckbox', 'lang', '', get_string('purgelangcache', 'admin')),
$mform->createElement('advcheckbox', 'js', '', get_string('purgejscache', 'admin')),
$mform->createElement('advcheckbox', 'template', '', get_string('purgetemplates', 'admin')),
$mform->createElement('advcheckbox', 'filter', '', get_string('purgefiltercache', 'admin')),
$mform->createElement('advcheckbox', 'muc', '', get_string('purgemuc', 'admin')),
$mform->createElement('advcheckbox', 'other', '', get_string('purgeothercaches', 'admin'))
];
$mform->addGroup($checkboxes, 'purgeselectedoptions');
$mform->addElement('submit', 'purgeselectedcaches', get_string('purgeselectedcaches', 'admin'));
}
/**
* If the "Purge selected caches" button was pressed, ensure at least one cache was selected.
*
* @param array $data
* @param array $files
* @return array Error messages
*/
public function validation($data, $files) {
$errors = [];
if (isset($data['purgeselectedcaches']) && empty(array_filter($data['purgeselectedoptions']))) {
$errors['purgeselectedoptions'] = get_string('purgecachesnoneselected', 'admin');
}
return $errors;
}
}
@@ -1,92 +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/>.
/**
* Testing outgoing mail configuration form
*
* @package core
* @copyright 2019 Victor Deniz <victor@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_admin\form;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
/**
* Test mail form
*
* @package core
* @copyright 2019 Victor Deniz <victor@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class testoutgoingmailconf_form extends \moodleform {
/**
* Add elements to form
*/
public function definition() {
$mform = $this->_form;
// Recipient.
$options = ['maxlength' => '100', 'size' => '25', 'autocomplete' => 'email'];
$mform->addElement('text', 'recipient', get_string('testoutgoingmailconf_toemail', 'admin'), $options);
$mform->setType('recipient', PARAM_EMAIL);
$mform->addRule('recipient', get_string('required'), 'required');
// From user.
$options = ['maxlength' => '100', 'size' => '25'];
$mform->addElement('text', 'from', get_string('testoutgoingmailconf_fromemail', 'admin'), $options);
$mform->setType('from', PARAM_TEXT);
$mform->addHelpButton('from', 'testoutgoingmailconf_fromemail', 'admin');
// Additional subject text.
$options = ['size' => '25'];
$mform->addElement('text', 'additionalsubject', get_string('testoutgoingmailconf_subjectadditional', 'admin'), $options);
$mform->setType('additionalsubject', PARAM_TEXT);
$buttonarray = array();
$buttonarray[] = $mform->createElement('submit', 'send', get_string('testoutgoingmailconf_sendtest', 'admin'));
$buttonarray[] = $mform->createElement('cancel');
$mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
$mform->closeHeaderBefore('buttonar');
}
/**
* Validate Form field, should be a valid email format or a username that matches with a Moodle user.
*
* @param array $data
* @param array $files
* @return array
* @throws \dml_exception|\coding_exception
*/
public function validation($data, $files): array {
$errors = parent::validation($data, $files);
if (isset($data['from']) && $data['from']) {
$userfrom = \core_user::get_user_by_username($data['from']);
if (!$userfrom && !validate_email($data['from'])) {
$errors['from'] = get_string('testoutgoingmailconf_fromemail_invalid', 'admin');
}
}
return $errors;
}
}
@@ -1,71 +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/>.
/**
* External admin page class that allows a callback to be provided to determine whether page can be accessed
*
* @package core_admin
* @copyright 2019 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_admin\local\externalpage;
use admin_externalpage;
defined('MOODLE_INTERNAL') || die();
require_once("{$CFG->libdir}/adminlib.php");
/**
* Admin externalpage class
*
* @package core_admin
* @copyright 2019 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class accesscallback extends admin_externalpage {
/** @var callable $accesscheckcallback */
protected $accesscheckcallback;
/**
* Class constructor
*
* @param string $name
* @param string $visiblename
* @param string $url
* @param callable $accesscheckcallback The callback method that will be executed to check whether user has access to
* this page. The setting instance ($this) is passed as an argument to the callback. Should return boolean value
* @param bool $hidden
*/
public function __construct(string $name, string $visiblename, string $url, callable $accesscheckcallback,
bool $hidden = false) {
$this->accesscheckcallback = $accesscheckcallback;
parent::__construct($name, $visiblename, $url, [], $hidden);
}
/**
* Determines if the current user has access to this external page based on access callback
*
* @return bool
*/
public function check_access() {
return ($this->accesscheckcallback)($this);
}
}
@@ -1,206 +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/>.
/**
* Auto complete admin setting.
*
* @package core_admin
* @copyright 2020 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_admin\local\settings;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/adminlib.php');
/**
* Auto complete setting class.
*
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @copyright 2020 The Open University
*/
class autocomplete extends \admin_setting_configmultiselect {
/** @var boolean Should we allow typing new entries to the field? */
protected $tags = false;
/** @var string Name of an AMD module to send/process ajax requests. */
protected $ajax = '';
/** @var string Placeholder text for an empty list. */
protected $placeholder = '';
/** @var bool Whether the search has to be case-sensitive. */
protected $casesensitive = false;
/** @var bool Show suggestions by default - but this can be turned off. */
protected $showsuggestions = true;
/** @var string String that is shown when there are no selections. */
protected $noselectionstring = '';
/** @var string Delimiter to store values in database. */
protected $delimiter = ',';
/** @var string Should be multiple choices? */
protected $multiple = true;
/** @var string The link to manage choices. */
protected $manageurl = true;
/** @var string The text to display in manage link. */
protected $managetext = true;
/**
* Constructor
*
* @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
* for ones in config_plugins.
* @param string $visiblename localised
* @param string $description long localised info
* @param array $defaultsetting array of selected items
* @param array $choices options for autocomplete field
* @param array $attributes settings for autocomplete field
*/
public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $attributes = null) {
if ($attributes === null) {
$attributes = [];
}
$this->placeholder = get_string('search');
$this->noselectionstring = get_string('noselection', 'form');
$defaultattributes = [
'tags',
'showsuggestions',
'placeholder',
'noselectionstring',
'ajax',
'casesensitive',
'delimiter',
'multiple',
'manageurl',
'managetext'
];
foreach ($defaultattributes as $attributename) {
if (isset($attributes[$attributename])) {
$this->$attributename = $attributes[$attributename];
}
}
parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
}
/**
* Returns the select setting(s)
*
* @return mixed null or array. Null if no settings else array of setting(s)
*/
public function get_setting() {
$result = $this->config_read($this->name);
if (is_null($result)) {
return null;
}
if ($result === '') {
return [];
}
return explode($this->delimiter, $result);
}
/**
* Saves setting(s) provided through $data
*
* @param array $data
*/
public function write_setting($data) {
if (!is_array($data)) {
return ''; // Ignore it.
}
if (!$this->load_choices() || empty($this->choices)) {
return '';
}
unset($data['xxxxx']);
$save = [];
foreach ($data as $value) {
if (!array_key_exists($value, $this->choices)) {
continue; // Ignore it.
}
$save[] = $value;
}
return ($this->config_write($this->name, implode($this->delimiter, $save)) ? '' : get_string('errorsetting', 'admin'));
}
/**
* Returns XHTML autocomplete field
*
* @param array $data Array of values to select by default
* @param string $query
* @return string XHTML autocomplete field
*/
public function output_html($data, $query = '') {
global $OUTPUT;
if (!$this->load_choices() or empty($this->choices)) {
return '';
}
$default = $this->get_defaultsetting();
if (empty($default)) {
$default = [];
}
if (is_null($data)) {
$data = [];
}
$context = [
'id' => $this->get_id(),
'name' => $this->get_full_name()
];
$defaults = [];
$options = [];
$template = 'core_admin/local/settings/autocomplete';
foreach ($this->choices as $value => $name) {
if (in_array($value, $default)) {
$defaults[] = $name;
}
$options[] = [
'value' => $value,
'text' => $name,
'selected' => in_array($value, $data),
'disabled' => false
];
}
$context['options'] = $options;
$context['tags'] = $this->tags;
$context['placeholder'] = $this->placeholder;
$context['casesensitive'] = $this->casesensitive;
$context['multiple'] = $this->multiple;
$context['showsuggestions'] = $this->showsuggestions;
$context['manageurl'] = $this->manageurl;
$context['managetext'] = $this->managetext;
if (is_null($default)) {
$defaultinfo = null;
} if (!empty($defaults)) {
$defaultinfo = implode(', ', $defaults);
} else {
$defaultinfo = get_string('none');
}
$element = $OUTPUT->render_from_template($template, $context);
return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
}
}
-197
View File
@@ -1,197 +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/>.
/**
* File size admin setting.
*
* @package core_admin
* @copyright 2019 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_admin\local\settings;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/adminlib.php');
/**
* An admin setting to support entering and displaying of file sizes in Bytes, KB, MB or GB.
*
* @copyright 2019 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class filesize extends \admin_setting {
/** @var int The byte unit. Number of bytes in a byte */
const UNIT_B = 1;
/** @var int The kilobyte unit (number of bytes in a kilobyte) */
const UNIT_KB = 1024;
/** @var int The megabyte unit (number of bytes in a megabyte) */
const UNIT_MB = 1048576;
/** @var int The gigabyte unit (number of bytes in a gigabyte) */
const UNIT_GB = 1073741824;
/** @var int default size unit */
protected $defaultunit;
/**
* Constructor
*
* @param string $name unique ascii name, either 'mysetting' for settings that in config,
* or 'myplugin/mysetting' for ones in config_plugins.
* @param string $visiblename localised name
* @param string $description localised long description
* @param int|null $defaultvalue Value of the settings in bytes
* @param int|null $defaultunit GB, MB, etc. (in bytes)
*/
public function __construct(string $name, string $visiblename, string $description,
int $defaultvalue = null, int $defaultunit = null) {
$defaultsetting = self::parse_bytes($defaultvalue);
if ($defaultunit && array_key_exists($defaultunit, self::get_units())) {
$this->defaultunit = $defaultunit;
} else {
$this->defaultunit = self::UNIT_MB;
}
parent::__construct($name, $visiblename, $description, $defaultsetting);
}
/**
* Returns selectable units.
*
* @return array
*/
protected static function get_units(): array {
return [
self::UNIT_GB => get_string('sizegb'),
self::UNIT_MB => get_string('sizemb'),
self::UNIT_KB => get_string('sizekb'),
self::UNIT_B => get_string('sizeb'),
];
}
/**
* Converts bytes to some more user friendly string.
*
* @param int $bytes The number of bytes we want to convert from
* @return string
*/
protected static function get_size_text(int $bytes): string {
if (empty($bytes)) {
return get_string('none');
}
return display_size($bytes, 0);
}
/**
* Finds suitable units for given file size.
*
* @param int $bytes The number of bytes
* @return array Parsed file size in the format of ['v' => value, 'u' => unit]
*/
protected static function parse_bytes(int $bytes): array {
foreach (self::get_units() as $unit => $unused) {
if ($bytes % $unit === 0) {
return ['v' => (int)($bytes / $unit), 'u' => $unit];
}
}
return ['v' => (int)$bytes, 'u' => self::UNIT_B];
}
/**
* Get the selected file size as array.
*
* @return array|null An array containing 'v' => xx, 'u' => xx, or null if not set
*/
public function get_setting(): ?array {
$bytes = $this->config_read($this->name);
if (is_null($bytes)) {
return null;
}
$bytes = intval($bytes);
return self::parse_bytes($bytes);
}
/**
* Store the file size as bytes.
*
* @param array $data Must be form 'h' => xx, 'm' => xx
* @return string The error string if any
*/
public function write_setting($data): string {
if (!is_array($data)) {
return '';
}
if (!is_numeric($data['v']) || $data['v'] < 0) {
return get_string('errorsetting', 'admin');
}
// Calculate size in bytes, ensuring we don't overflow PHP_INT_MAX.
$bytes = $data['v'] * $data['u'];
$result = (is_int($bytes) && $this->config_write($this->name, $bytes));
return ($result ? '' : get_string('errorsetting', 'admin'));
}
/**
* Returns file size text+select fields.
*
* @param array $data The current setting value. Must be form 'v' => xx, 'u' => xx.
* @param string $query Admin search query to be highlighted.
* @return string File size text+select fields and wrapping div(s).
*/
public function output_html($data, $query = ''): string {
global $OUTPUT;
$default = $this->get_defaultsetting();
if (is_number($default)) {
$defaultinfo = self::get_size_text($default);
} else if (is_array($default)) {
$defaultinfo = self::get_size_text($default['v'] * $default['u']);
} else {
$defaultinfo = null;
}
$inputid = $this->get_id() . 'v';
$units = self::get_units();
$defaultunit = $this->defaultunit;
$context = (object) [
'id' => $this->get_id(),
'name' => $this->get_full_name(),
'value' => $data['v'],
'readonly' => $this->is_readonly(),
'options' => array_map(function($unit, $title) use ($data, $defaultunit) {
return [
'value' => $unit,
'name' => $title,
'selected' => ($data['v'] == 0 && $unit == $defaultunit) || $unit == $data['u']
];
}, array_keys($units), $units)
];
$element = $OUTPUT->render_from_template('core_admin/setting_configfilesize', $context);
return format_admin_setting($this, $this->visiblename, $element, $this->description, $inputid, '', $defaultinfo, $query);
}
}
@@ -1,37 +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/>.
/**
* A settings page which can be viewed with a link directly.
*
* @package core_admin
* @copyright 2021 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_admin\local\settings;
use moodle_url;
interface linkable_settings_page {
/**
* Get the URL to view this settings page.
*
* @return moodle_url
*/
public function get_settings_page_url(): moodle_url;
}
@@ -1,74 +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/>.
/**
* Payment gateway admin setting.
*
* @package core_admin
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_admin\local\settings;
/**
* Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
*/
class manage_payment_gateway_plugins extends \admin_setting_manage_plugins {
/**
* Get the admin settings section title (use get_string).
*
* @return string
*/
public function get_section_title() {
return get_string('type_paygw_plural', 'plugin');
}
/**
* Get the type of plugin to manage.
*
* @return string
*/
public function get_plugin_type() {
return 'paygw';
}
/**
* Get the name of the second column.
*
* @return string
*/
public function get_info_column_name() {
return get_string('supportedcurrencies', 'core_payment');
}
/**
* Get the type of plugin to manage.
*
* @param plugininfo The plugin info class.
* @return string
*/
public function get_info_column($plugininfo) {
$codes = $plugininfo->get_supported_currencies();
$currencies = [];
foreach ($codes as $c) {
$currencies[$c] = new \lang_string($c, 'core_currencies');
}
return implode(get_string('listsep', 'langconfig') . ' ', $currencies);
}
}
@@ -1,121 +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/>.
/**
* Admin setting to show current scheduled task's status.
*
* @package core
* @copyright 2021 Universitat Rovira i Virgili
* @author Jordi Pujol-Ahulló <jpahullo@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_admin\local\settings;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/adminlib.php');
require_once($CFG->libdir . '/moodlelib.php');
use admin_setting_description;
use core\task\manager;
use core\task\scheduled_task;
use html_writer;
use lang_string;
use moodle_url;
use stdClass;
/**
* This admin setting tells whether a given scheduled task is enabled, providing a link to its configuration page.
*
* The goal of this setting is to help contextualizing the configuration settings with related scheduled task status,
* providing the big picture of that part of the system.
*
* @package core
* @copyright 2021 Universitat Rovira i Virgili
* @author Jordi Pujol-Ahulló <jpahullo@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class setting_scheduled_task_status extends admin_setting_description {
/**
* @var string fully qualified class name of a scheduled task.
*/
private $classname;
/**
* @var string additional text to append to the description.
*/
private $extradescription;
/**
* setting_scheduled_task_status constructor.
* @param string $name unique setting name.
* @param string $scheduledtaskclassname full classpath class name of the scheduled task.
* @param string $extradescription extra detail to append to the scheduled task status to add context in the setting
* page.
*/
public function __construct(string $name, string $scheduledtaskclassname, string $extradescription = '') {
$visiblename = new lang_string('task_status', 'admin');
$this->classname = $scheduledtaskclassname;
$this->extradescription = $extradescription;
parent::__construct($name, $visiblename, '');
}
/**
* Calculates lazily the content of the description.
* @param mixed $data nothing expected in this case.
* @param string $query nothing expected in this case.
* @return string the HTML content to print for this setting.
*/
public function output_html($data, $query = ''): string {
if (empty($this->description)) {
$this->description = $this->get_task_description();
}
return parent::output_html($data, $query);
}
/**
* Returns the HTML to print as the description.
* @return string description to be printed.
*/
private function get_task_description(): string {
$task = manager::get_scheduled_task($this->classname);
if ($task->is_enabled()) {
$taskenabled = get_string('enabled', 'admin');
} else {
$taskenabled = get_string('disabled', 'admin');
}
$taskenabled = strtolower($taskenabled);
$gotourl = new moodle_url(
'/admin/tool/task/scheduledtasks.php',
[],
scheduled_task::get_html_id($this->classname)
);
if (!empty($this->extradescription)) {
$this->extradescription = '<br />' . $this->extradescription;
}
$taskdetail = new stdClass();
$taskdetail->class = $this->classname;
$taskdetail->name = $task->get_name();
$taskdetail->status = $taskenabled;
$taskdetail->gotourl = $gotourl->out(false);
$taskdetail->extradescription = $this->extradescription;
return html_writer::tag('p', get_string('task_status_desc', 'admin', $taskdetail));
}
}
-71
View File
@@ -1,71 +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\output;
use moodle_url;
use renderable;
use renderer_base;
use stdClass;
use templatable;
/**
* Theme selector renderable.
*
* @package core_admin
* @copyright 2023 David Woloszyn <david.woloszyn@moodle.com>
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class theme_selector implements renderable, templatable {
/** @var array $themedata Theme data to pass to the template. */
private $themedata = null;
/** @var bool Whether $CFG->theme is defined in config.php. */
private $definedinconfig;
/**
* Constructor.
*
* @param array $themedata Theme data used for template.
* @param bool $definedinconfig Whether $CFG->theme is defined in config.php.
*/
public function __construct(array $themedata, bool $definedinconfig = false) {
$this->themedata = $themedata;
$this->definedinconfig = $definedinconfig;
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param renderer_base $output Renderer base.
* @return stdClass
*/
public function export_for_template(renderer_base $output): stdClass {
$data = new stdClass();
// Theme data used to populate cards and modal.
$data->themes = $this->themedata;
// Reset theme caches button.
$reseturl = new moodle_url('/admin/themeselector.php', ['sesskey' => sesskey(), 'reset' => 1]);
$resetbutton = new \single_button($reseturl, get_string('themeresetcaches', 'admin'), 'post',
\single_button::BUTTON_SECONDARY);
$data->resetbutton = $resetbutton->export_for_template($output);
$data->definedinconfig = $this->definedinconfig;
return $data;
}
}
-41
View File
@@ -1,41 +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/>.
/**
* Privacy Subsystem implementation for core_admin.
*
* @package core_admin
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_admin\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for core_admin implementing null_provider.
*
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -1,128 +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/>.
declare(strict_types=1);
namespace core_admin\reportbuilder\datasource;
use core_admin\reportbuilder\local\entities\task_log;
use core_reportbuilder\datasource;
use core_reportbuilder\local\entities\user;
use core_reportbuilder\local\filters\select;
/**
* Task logs datasource
*
* @package core_admin
* @copyright 2022 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class task_logs extends datasource {
/**
* Return user friendly name of the report source
*
* @return string
*/
public static function get_name(): string {
return get_string('tasklogs', 'core_admin');
}
/**
* Initialise report
*/
protected function initialise(): void {
$tasklogentity = new task_log();
$tasklogalias = $tasklogentity->get_table_alias('task_log');
$this->set_main_table('task_log', $tasklogalias);
$this->add_entity($tasklogentity);
// Join the user entity to represent the associated user.
$userentity = new user();
$useralias = $userentity->get_table_alias('user');
$this->add_entity($userentity->add_join("
LEFT JOIN {user} {$useralias}
ON {$useralias}.id = {$tasklogalias}.userid")
);
// Add report elements from each of the entities we added to the report.
$this->add_all_from_entities();
}
/**
* Return the columns that will be added to the report upon creation
*
* @return string[]
*/
public function get_default_columns(): array {
return [
'task_log:name',
'task_log:starttime',
'task_log:duration',
'task_log:result',
];
}
/**
* Return the column sorting that will be added to the report upon creation
*
* @return int[]
*/
public function get_default_column_sorting(): array {
return [
'task_log:starttime' => SORT_DESC,
];
}
/**
* Return the filters that will be added to the report upon creation
*
* @return string[]
*/
public function get_default_filters(): array {
return [
'task_log:timestart',
'task_log:result',
];
}
/**
* Return the conditions that will be added to the report upon creation
*
* @return string[]
*/
public function get_default_conditions(): array {
return [
'task_log:type',
'task_log:timestart',
'task_log:result',
];
}
/**
* Return the condition values that will be set for the report upon creation
*
* @return array
*/
public function get_default_condition_values(): array {
return [
'task_log:type_operator' => select::EQUAL_TO,
'task_log:type_value' => \core\task\database_logger::TYPE_SCHEDULED,
];
}
}
@@ -1,429 +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\reportbuilder\local\entities;
use core_reportbuilder\local\filters\date;
use core_reportbuilder\local\filters\duration;
use core_reportbuilder\local\filters\number;
use core_reportbuilder\local\filters\select;
use core_reportbuilder\local\filters\text;
use core_reportbuilder\local\filters\autocomplete;
use core_reportbuilder\local\helpers\format;
use lang_string;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\report\column;
use core_reportbuilder\local\report\filter;
use stdClass;
use core_collator;
/**
* Task log entity class implementation
*
* @package core_admin
* @copyright 2021 David Matamoros <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class task_log extends base {
/** @var int Result success */
protected const SUCCESS = 0;
/** @var int Result failed */
protected const FAILED = 1;
/**
* Database tables that this entity uses
*
* @return string[]
*/
protected function get_default_tables(): array {
return [
'task_log',
];
}
/**
* The default title for this entity in the list of columns/conditions/filters in the report builder
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('entitytasklog', 'admin');
}
/**
* Initialise the entity
*
* @return base
*/
public function initialise(): base {
$columns = $this->get_all_columns();
foreach ($columns as $column) {
$this->add_column($column);
}
// All the filters defined by the entity can also be used as conditions.
$filters = $this->get_all_filters();
foreach ($filters as $filter) {
$this
->add_filter($filter)
->add_condition($filter);
}
return $this;
}
/**
* Returns list of all available columns
*
* @return column[]
*/
protected function get_all_columns(): array {
global $DB;
$tablealias = $this->get_table_alias('task_log');
// Name column.
$columns[] = (new column(
'name',
new lang_string('name'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("$tablealias.classname")
->set_is_sortable(true)
->add_callback(static function(string $classname): string {
$output = '';
if (class_exists($classname)) {
$task = new $classname;
if ($task instanceof \core\task\task_base) {
$output = $task->get_name();
}
}
$output .= \html_writer::tag('div', "\\{$classname}", [
'class' => 'small text-muted',
]);
return $output;
});
// Component column.
$columns[] = (new column(
'component',
new lang_string('plugin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$tablealias}.component")
->set_is_sortable(true);
// Type column.
$columns[] = (new column(
'type',
new lang_string('tasktype', 'admin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$tablealias}.type")
->set_is_sortable(true)
->add_callback(static function($value): string {
if (\core\task\database_logger::TYPE_SCHEDULED === (int) $value) {
return get_string('task_type:scheduled', 'admin');
}
return get_string('task_type:adhoc', 'admin');
});
// Start time column.
$columns[] = (new column(
'starttime',
new lang_string('task_starttime', 'admin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_field("{$tablealias}.timestart")
->set_is_sortable(true)
->add_callback([format::class, 'userdate'], get_string('strftimedatetimeshortaccurate', 'core_langconfig'));
// End time column.
$columns[] = (new column(
'endtime',
new lang_string('task_endtime', 'admin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_field("{$tablealias}.timeend")
->set_is_sortable(true)
->add_callback([format::class, 'userdate'], get_string('strftimedatetimeshortaccurate', 'core_langconfig'));
// Duration column.
$columns[] = (new column(
'duration',
new lang_string('task_duration', 'admin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_FLOAT)
->add_field("{$tablealias}.timeend - {$tablealias}.timestart", 'duration')
->set_is_sortable(true)
->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(
'hostname',
new lang_string('hostname', 'admin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("$tablealias.hostname")
->set_is_sortable(true);
// PID column.
$columns[] = (new column(
'pid',
new lang_string('pid', 'admin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_INTEGER)
->add_field("{$tablealias}.pid")
->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(
'database',
new lang_string('task_dbstats', 'admin'),
$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(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(
'dbreads',
new lang_string('task_dbreads', 'admin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_INTEGER)
->add_fields("{$tablealias}.dbreads")
->set_is_sortable(true);
// Database writes column.
$columns[] = (new column(
'dbwrites',
new lang_string('task_dbwrites', 'admin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_INTEGER)
->add_fields("{$tablealias}.dbwrites")
->set_is_sortable(true);
// Result column.
$columns[] = (new column(
'result',
new lang_string('task_result', 'admin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_BOOLEAN)
// For accurate aggregation, we need to return boolean success = true by xor'ing the field value.
->add_field($DB->sql_bitxor("{$tablealias}.result", 1), 'success')
->set_is_sortable(true)
->add_callback(static function(bool $success): string {
if (!$success) {
return get_string('task_result:failed', 'admin');
}
return get_string('success');
});
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
global $DB;
$tablealias = $this->get_table_alias('task_log');
// Name filter (Filter by classname).
$filters[] = (new filter(
autocomplete::class,
'name',
new lang_string('classname', 'tool_task'),
$this->get_entity_name(),
"{$tablealias}.classname"
))
->add_joins($this->get_joins())
->set_options_callback(static function(): array {
global $DB;
$classnames = $DB->get_fieldset_sql('SELECT DISTINCT classname FROM {task_log} ORDER BY classname ASC');
$options = [];
foreach ($classnames as $classname) {
if (class_exists($classname)) {
$task = new $classname;
$options[$classname] = $task->get_name();
}
}
core_collator::asort($options);
return $options;
});
// Component filter.
$filters[] = (new filter(
text::class,
'component',
new lang_string('plugin'),
$this->get_entity_name(),
"{$tablealias}.component"
))
->add_joins($this->get_joins());
// Type filter.
$filters[] = (new filter(
select::class,
'type',
new lang_string('tasktype', 'admin'),
$this->get_entity_name(),
"{$tablealias}.type"
))
->add_joins($this->get_joins())
->set_options([
\core\task\database_logger::TYPE_ADHOC => new lang_string('task_type:adhoc', 'admin'),
\core\task\database_logger::TYPE_SCHEDULED => new lang_string('task_type:scheduled', 'admin'),
]);
// Output filter (Filter by task output).
$filters[] = (new filter(
text::class,
'output',
new lang_string('task_logoutput', 'admin'),
$this->get_entity_name(),
$DB->sql_cast_to_char("{$tablealias}.output")
))
->add_joins($this->get_joins());
// Start time filter.
$filters[] = (new filter(
date::class,
'timestart',
new lang_string('task_starttime', 'admin'),
$this->get_entity_name(),
"{$tablealias}.timestart"
))
->add_joins($this->get_joins())
->set_limited_operators([
date::DATE_ANY,
date::DATE_RANGE,
date::DATE_PREVIOUS,
date::DATE_CURRENT,
]);
// End time.
$filters[] = (new filter(
date::class,
'timeend',
new lang_string('task_endtime', 'admin'),
$this->get_entity_name(),
"{$tablealias}.timeend"
))
->add_joins($this->get_joins())
->set_limited_operators([
date::DATE_ANY,
date::DATE_RANGE,
date::DATE_PREVIOUS,
date::DATE_CURRENT,
]);
// Duration filter.
$filters[] = (new filter(
duration::class,
'duration',
new lang_string('task_duration', 'admin'),
$this->get_entity_name(),
"{$tablealias}.timeend - {$tablealias}.timestart"
))
->add_joins($this->get_joins());
// Database reads.
$filters[] = (new filter(
number::class,
'dbreads',
new lang_string('task_dbreads', 'admin'),
$this->get_entity_name(),
"{$tablealias}.dbreads"
))
->add_joins($this->get_joins());
// Database writes.
$filters[] = (new filter(
number::class,
'dbwrites',
new lang_string('task_dbwrites', 'admin'),
$this->get_entity_name(),
"{$tablealias}.dbwrites"
))
->add_joins($this->get_joins());
// Result filter.
$filters[] = (new filter(
select::class,
'result',
new lang_string('task_result', 'admin'),
$this->get_entity_name(),
"{$tablealias}.result"
))
->add_joins($this->get_joins())
->set_options([
self::SUCCESS => get_string('success'),
self::FAILED => get_string('task_result:failed', 'admin'),
]);
return $filters;
}
}
@@ -1,108 +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/>.
declare(strict_types=1);
namespace core_admin\reportbuilder\local\filters;
use core\context\system;
use core_course_category;
use MoodleQuickForm;
use core_reportbuilder\local\filters\base;
use core_reportbuilder\local\helpers\database;
/**
* Course role report filter (by role, category, course)
*
* The provided filter field SQL must refer/return an expression for the user ID (e.g. "{$user}.id")
*
* @package core_admin
* @copyright 2023 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class courserole extends base {
/**
* Setup form
*
* @param MoodleQuickForm $mform
*/
public function setup_form(MoodleQuickForm $mform): void {
$elements = [];
// Role.
$elements['role'] = $mform->createElement('select', "{$this->name}_role", get_string('rolefullname', 'core_role'),
[0 => get_string('anyrole', 'core_filters')] + get_default_enrol_roles(system::instance()));
// Category.
$elements['category'] = $mform->createElement('select', "{$this->name}_category", get_string('category'),
[0 => get_string('anycategory', 'core_filters')] + core_course_category::make_categories_list());
// Course.
$elements['course'] = $mform->createElement('text', "{$this->name}_course", get_string('shortnamecourse'));
$mform->setType("{$this->name}_course", PARAM_RAW_TRIMMED);
$mform->addGroup($elements, "{$this->name}_group", $this->get_header(), '', false)
->setHiddenLabel(true);
}
/**
* Return filter SQL
*
* @param array $values
* @return array
*/
public function get_sql_filter(array $values): array {
[$fieldsql, $params] = $this->filter->get_field_sql_and_params();
[$contextalias, $rolealias, $coursealias] = database::generate_aliases(3);
[$roleparam, $categoryparam, $courseparam] = database::generate_param_names(3);
// Role.
$role = (int) ($values["{$this->name}_role"] ?? 0);
if ($role > 0) {
$selects[] = "{$rolealias}.roleid = :{$roleparam}";
$params[$roleparam] = $role;
}
// Category.
$category = (int) ($values["{$this->name}_category"] ?? 0);
if ($category > 0) {
$selects[] = "{$coursealias}.category = :{$categoryparam}";
$params[$categoryparam] = $category;
}
// Course.
$course = trim($values["{$this->name}_course"] ?? '');
if ($course !== '') {
$selects[] = "{$coursealias}.shortname = :{$courseparam}";
$params[$courseparam] = $course;
}
// Filter values are not set.
if (empty($selects)) {
return ['', []];
}
return ["{$fieldsql} IN (
SELECT {$rolealias}.userid
FROM {role_assignments} {$rolealias}
JOIN {context} {$contextalias} ON {$contextalias}.id = {$rolealias}.contextid AND {$contextalias}.contextlevel = 50
JOIN {course} {$coursealias} ON {$coursealias}.id = {$contextalias}.instanceid
WHERE " . implode(' AND ', $selects) . "
)", $params];
}
}
@@ -1,159 +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\reportbuilder\local\systemreports;
use context_system;
use core_admin\reportbuilder\local\entities\task_log;
use core_reportbuilder\local\entities\user;
use core_reportbuilder\local\report\action;
use lang_string;
use moodle_url;
use pix_icon;
use core_reportbuilder\system_report;
/**
* Task logs system report class implementation
*
* @package core_admin
* @copyright 2021 David Matamoros <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class task_logs extends system_report {
/**
* Initialise report, we need to set the main table, load our entities and set columns/filters
*/
protected function initialise(): void {
// Our main entity, it contains all of the column definitions that we need.
$entitymain = new task_log();
$entitymainalias = $entitymain->get_table_alias('task_log');
$this->set_main_table('task_log', $entitymainalias);
$this->add_entity($entitymain);
// Any columns required by actions should be defined here to ensure they're always available.
$this->add_base_fields("{$entitymainalias}.id");
// We can join the "user" entity to our "main" entity and use the fullname column from the user entity.
$entityuser = new user();
$entituseralias = $entityuser->get_table_alias('user');
$this->add_entity($entityuser->add_join(
"LEFT JOIN {user} {$entituseralias} ON {$entituseralias}.id = {$entitymainalias}.userid"
));
// Now we can call our helper methods to add the content we want to include in the report.
$this->add_columns();
$this->add_filters();
$this->add_actions();
// Set if report can be downloaded.
$this->set_downloadable(true, get_string('tasklogs', 'admin'));
}
/**
* Validates access to view this report
*
* @return bool
*/
protected function can_view(): bool {
return has_capability('moodle/site:config', context_system::instance());
}
/**
* Get the visible name of the report
*
* @return string
*/
public static function get_name(): string {
return get_string('entitytasklog', 'admin');
}
/**
* Adds the columns we want to display in the report
*
* They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their
* unique identifier
*/
public function add_columns(): void {
$columns = [
'task_log:name',
'task_log:type',
'user:fullname',
'task_log:starttime',
'task_log:duration',
'task_log:hostname',
'task_log:pid',
'task_log:database',
'task_log:result',
];
$this->add_columns_from_entities($columns);
// 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);
}
/**
* Adds the filters we want to display in the report
*
* They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their
* unique identifier
*/
protected function add_filters(): void {
$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);
}
/**
* Add the system report actions. An extra column will be appended to each row, containing all actions added here
*
* Note the use of ":id" placeholder which will be substituted according to actual values in the row
*/
protected function add_actions(): void {
// Action to view individual task log on a popup window.
$this->add_action((new action(
new moodle_url('/admin/tasklogs.php', ['logid' => ':id']),
new pix_icon('e/search', ''),
[],
true,
new lang_string('view'),
)));
// Action to download individual task log.
$this->add_action((new action(
new moodle_url('/admin/tasklogs.php', ['logid' => ':id', 'download' => true]),
new pix_icon('t/download', ''),
[],
false,
new lang_string('download'),
)));
}
}
@@ -1,416 +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\reportbuilder\local\systemreports;
use core_admin\reportbuilder\local\filters\courserole;
use core\context\system;
use core_cohort\reportbuilder\local\entities\cohort;
use core_cohort\reportbuilder\local\entities\cohort_member;
use core_reportbuilder\local\entities\user;
use core_reportbuilder\local\filters\boolean_select;
use core_reportbuilder\local\helpers\database;
use core_reportbuilder\local\helpers\user_profile_fields;
use core_reportbuilder\local\report\action;
use core_reportbuilder\local\report\filter;
use core_reportbuilder\system_report;
use core_role\reportbuilder\local\entities\role;
use core_user\fields;
use lang_string;
use moodle_url;
use pix_icon;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/authlib.php');
require_once($CFG->libdir.'/enrollib.php');
require_once($CFG->dirroot.'/user/lib.php');
/**
* Browse users system report class implementation
*
* @package core_admin
* @copyright 2023 David Carrillo <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class users extends system_report {
/**
* Initialise report, we need to set the main table, load our entities and set columns/filters
*/
protected function initialise(): void {
global $CFG;
// Our main entity, it contains all of the column definitions that we need.
$entityuser = new user();
$entityuseralias = $entityuser->get_table_alias('user');
$this->set_main_table('user', $entityuseralias);
$this->add_entity($entityuser);
// Any columns required by actions should be defined here to ensure they're always available.
$fullnamefields = array_map(fn($field) => "{$entityuseralias}.{$field}", fields::get_name_fields());
$this->add_base_fields("{$entityuseralias}.id, {$entityuseralias}.confirmed, {$entityuseralias}.mnethostid,
{$entityuseralias}.suspended, {$entityuseralias}.username, " . implode(', ', $fullnamefields));
if ($this->get_parameter('withcheckboxes', false, PARAM_BOOL)) {
$canviewfullnames = has_capability('moodle/site:viewfullnames', \context_system::instance());
$this->set_checkbox_toggleall(static function(\stdClass $row) use ($canviewfullnames): array {
return [$row->id, fullname($row, $canviewfullnames)];
});
}
$paramguest = database::generate_param_name();
$this->add_base_condition_sql("{$entityuseralias}.deleted <> 1 AND {$entityuseralias}.id <> :{$paramguest}",
[$paramguest => $CFG->siteguest]);
$entitycohortmember = new cohort_member();
$entitycohortmemberalias = $entitycohortmember->get_table_alias('cohort_members');
$this->add_entity($entitycohortmember
->add_joins($entitycohortmember->get_joins())
->add_join("LEFT JOIN {cohort_members} {$entitycohortmemberalias}
ON {$entityuseralias}.id = {$entitycohortmemberalias}.userid")
);
$entitycohort = new cohort();
$entitycohortalias = $entitycohort->get_table_alias('cohort');
$this->add_entity($entitycohort
->add_joins($entitycohort->get_joins())
->add_joins($entitycohortmember->get_joins())
->add_join("LEFT JOIN {cohort} {$entitycohortalias}
ON {$entitycohortalias}.id = {$entitycohortmemberalias}.cohortid")
);
// Join the role entity (Needed for the system role filter).
$roleentity = new role();
$role = $roleentity->get_table_alias('role');
$this->add_entity($roleentity
->add_join("LEFT JOIN (
SELECT DISTINCT r0.id, ras.userid
FROM {role} r0
JOIN {role_assignments} ras ON ras.roleid = r0.id
WHERE ras.contextid = ".SYSCONTEXTID."
) {$role} ON {$role}.userid = {$entityuseralias}.id")
);
// Now we can call our helper methods to add the content we want to include in the report.
$this->add_columns();
$this->add_filters();
$this->add_actions();
// Set if report can be downloaded.
$this->set_downloadable(true);
}
/**
* Validates access to view this report
*
* @return bool
*/
protected function can_view(): bool {
return has_any_capability(['moodle/user:update', 'moodle/user:delete'], system::instance());
}
/**
* Adds the columns we want to display in the report
*
* They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their
* unique identifier
*/
public function add_columns(): void {
$entityuser = $this->get_entity('user');
$entityuseralias = $entityuser->get_table_alias('user');
$this->add_column($entityuser->get_column('fullnamewithpicturelink'));
// Include identity field columns.
$identitycolumns = $entityuser->get_identity_columns($this->get_context());
foreach ($identitycolumns as $identitycolumn) {
$this->add_column($identitycolumn);
}
// Add "Last access" column.
$this->add_column(($entityuser->get_column('lastaccess'))
->set_callback(static function ($value, \stdClass $row): string {
if ($row->lastaccess) {
return format_time(time() - $row->lastaccess);
}
return get_string('never');
})
);
if ($column = $this->get_column('user:fullnamewithpicturelink')) {
$column
->add_fields("{$entityuseralias}.suspended, {$entityuseralias}.confirmed")
->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 ml-1']);
}
if (!$row->confirmed) {
$fullname .= ' ' . \html_writer::tag('span', get_string('confirmationpending', 'admin'),
['class' => 'badge badge-danger ml-1']);
}
return $fullname;
});
}
$this->set_initial_sort_column('user:fullnamewithpicturelink', SORT_ASC);
$this->set_default_no_results_notice(new lang_string('nousersfound', 'moodle'));
}
/**
* Adds the filters we want to display in the report
*
* They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their
* unique identifier
*/
protected function add_filters(): void {
$entityuser = $this->get_entity('user');
$entityuseralias = $entityuser->get_table_alias('user');
$filters = [
'user:firstname',
'user:lastname',
'user:username',
'user:idnumber',
'user:email',
'user:department',
'user:institution',
'user:city',
'user:country',
'user:confirmed',
'user:suspended',
'user:timecreated',
'user:lastaccess',
'user:timemodified',
'user:auth',
'user:lastip',
'cohort:idnumber',
'role:name',
];
$this->add_filters_from_entities($filters);
// Enrolled in any course filter.
$ue = database::generate_alias();
[$now1, $now2] = database::generate_param_names(2);
$now = time();
$sql = "CASE WHEN ({$entityuseralias}.id IN (
SELECT userid FROM {user_enrolments} {$ue}
WHERE {$ue}.status = " . ENROL_USER_ACTIVE . "
AND ({$ue}.timestart = 0 OR {$ue}.timestart < :{$now1})
AND ({$ue}.timeend = 0 OR {$ue}.timeend > :{$now2})
)) THEN 1 ELSE 0 END";
$this->add_filter((new filter(
boolean_select::class,
'enrolledinanycourse',
new lang_string('anycourses', 'filters'),
$this->get_entity('user')->get_entity_name(),
))
->set_field_sql($sql, [
$now1 => $now,
$now2 => $now,
])
);
// Course role filter.
$this->add_filter((new filter(
courserole::class,
'courserole',
new lang_string('courserole', 'filters'),
$this->get_entity('user')->get_entity_name(),
))
->set_field_sql("{$entityuseralias}.id")
);
// Add user profile fields filters.
$userprofilefields = new user_profile_fields($entityuseralias . '.id', $entityuser->get_entity_name());
foreach ($userprofilefields->get_filters() as $filter) {
$this->add_filter($filter);
}
// Set options for system role filter.
if ($filter = $this->get_filter('role:name')) {
$filter
->set_header(new lang_string('globalrole', 'role'))
->set_options(get_assignable_roles(system::instance()));
}
}
/**
* Add the system report actions. An extra column will be appended to each row, containing all actions added here
*
* Note the use of ":id" placeholder which will be substituted according to actual values in the row
*/
protected function add_actions(): void {
global $DB, $USER;
$contextsystem = system::instance();
// Action to edit users.
$this->add_action((new action(
new moodle_url('/user/editadvanced.php', ['id' => ':id', 'course' => get_site()->id]),
new pix_icon('t/edit', ''),
[],
false,
new lang_string('edit', 'moodle'),
))->add_callback(static function(\stdclass $row) use ($USER, $contextsystem): bool {
return has_capability('moodle/user:update', $contextsystem) && (is_siteadmin($USER) || !is_siteadmin($row));
}));
// Action to suspend users (non mnet remote users).
$this->add_action((new action(
new moodle_url('/admin/user.php', ['suspend' => ':id', 'sesskey' => sesskey()]),
new pix_icon('t/show', ''),
[],
false,
new lang_string('suspenduser', 'admin'),
))->add_callback(static function(\stdclass $row) use ($USER, $contextsystem): bool {
return has_capability('moodle/user:update', $contextsystem) && !$row->suspended && !is_mnet_remote_user($row) &&
!($row->id == $USER->id || is_siteadmin($row));
}));
// Action to unsuspend users (non mnet remote users).
$this->add_action((new action(
new moodle_url('/admin/user.php', ['unsuspend' => ':id', 'sesskey' => sesskey()]),
new pix_icon('t/hide', ''),
[],
false,
new lang_string('unsuspenduser', 'admin'),
))->add_callback(static function(\stdclass $row) use ($USER, $contextsystem): bool {
return has_capability('moodle/user:update', $contextsystem) && $row->suspended && !is_mnet_remote_user($row) &&
!($row->id == $USER->id || is_siteadmin($row));
}));
// Action to unlock users (non mnet remote users).
$this->add_action((new action(
new moodle_url('/admin/user.php', ['unlock' => ':id', 'sesskey' => sesskey()]),
new pix_icon('t/unlock', ''),
[],
false,
new lang_string('unlockaccount', 'admin'),
))->add_callback(static function(\stdclass $row) use ($contextsystem): bool {
return has_capability('moodle/user:update', $contextsystem) && !is_mnet_remote_user($row) &&
login_is_lockedout($row);
}));
// Action to suspend users (mnet remote users).
$this->add_action((new action(
new moodle_url('/admin/user.php', ['acl' => ':id', 'sesskey' => sesskey(), 'accessctrl' => 'deny']),
new pix_icon('t/show', ''),
[],
false,
new lang_string('denyaccess', 'mnet'),
))->add_callback(static function(\stdclass $row) use ($DB, $contextsystem): bool {
if (!$accessctrl = $DB->get_field(table: 'mnet_sso_access_control', return: 'accessctrl',
conditions: ['username' => $row->username, 'mnet_host_id' => $row->mnethostid]
)) {
$accessctrl = 'allow';
}
return has_capability('moodle/user:update', $contextsystem) && !$row->suspended &&
is_mnet_remote_user($row) && $accessctrl == 'allow';
}));
// Action to unsuspend users (mnet remote users).
$this->add_action((new action(
new moodle_url('/admin/user.php', ['acl' => ':id', 'sesskey' => sesskey(), 'accessctrl' => 'allow']),
new pix_icon('t/hide', ''),
[],
false,
new lang_string('allowaccess', 'mnet'),
))->add_callback(static function(\stdclass $row) use ($DB, $contextsystem): bool {
if (!$accessctrl = $DB->get_field(table: 'mnet_sso_access_control', return: 'accessctrl',
conditions: ['username' => $row->username, 'mnet_host_id' => $row->mnethostid]
)) {
$accessctrl = 'allow';
}
return has_capability('moodle/user:update', $contextsystem) && !$row->suspended &&
is_mnet_remote_user($row) && $accessctrl == 'deny';
}));
// Action to delete users.
$this->add_action((new action(
new moodle_url('/admin/user.php', ['delete' => ':id', 'sesskey' => sesskey()]),
new pix_icon('t/delete', ''),
[
'class' => 'text-danger',
'data-modal' => 'confirmation',
'data-modal-title-str' => json_encode(['deleteuser', 'admin']),
'data-modal-content-str' => ':deletestr',
'data-modal-yes-button-str' => json_encode(['delete', 'core']),
'data-modal-destination' => ':deleteurl',
],
false,
new lang_string('delete', 'moodle'),
))->add_callback(static function(\stdclass $row) use ($USER, $contextsystem): bool {
// Populate deletion modal attributes.
$row->deletestr = json_encode([
'deletecheckfull',
'moodle',
fullname($row, true),
]);
$row->deleteurl = (new moodle_url('/admin/user.php', [
'delete' => $row->id,
'confirm' => md5($row->id),
'sesskey' => sesskey(),
]))->out(false);
return has_capability('moodle/user:delete', $contextsystem) &&
!is_mnet_remote_user($row) && $row->id != $USER->id && !is_siteadmin($row);
}));
$this->add_action_divider();
// Action to confirm users.
$this->add_action((new action(
new moodle_url('/admin/user.php', ['confirmuser' => ':id', 'sesskey' => sesskey()]),
new pix_icon('t/check', ''),
[],
false,
new lang_string('confirmaccount', 'moodle'),
))->add_callback(static function(\stdclass $row) use ($contextsystem): bool {
return has_capability('moodle/user:update', $contextsystem) && !$row->confirmed;
}));
// Action to resend email.
$this->add_action((new action(
new moodle_url('/admin/user.php', ['resendemail' => ':id', 'sesskey' => sesskey()]),
new pix_icon('t/email', ''),
[],
false,
new lang_string('resendemail', 'moodle'),
))->add_callback(static function(\stdclass $row): bool {
return !$row->confirmed && !is_mnet_remote_user($row);
}));
}
/**
* Row class
*
* @param \stdClass $row
* @return string
*/
public function get_row_class(\stdClass $row): string {
return $row->suspended ? 'text-muted' : '';
}
}
@@ -1,118 +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 core_plugin_manager;
use dml_exception;
use html_writer;
use moodle_url;
use stdClass;
/**
* Activity Module admin settings.
*
* @package core_admin
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class activity_management_table extends plugin_management_table {
public function setup() {
$this->set_attribute('id', 'modules');
$this->set_attribute('class', 'admintable generaltable');
parent::setup();
}
protected function get_table_id(): string {
return 'module-administration-table';
}
protected function get_plugintype(): string {
return 'mod';
}
public function guess_base_url(): void {
$this->define_baseurl(
new moodle_url('/admin/modules.php')
);
}
protected function get_action_url(array $params = []): moodle_url {
return new moodle_url('/admin/modules.php', $params);
}
protected function get_column_list(): array {
$columns = parent::get_column_list();
return array_merge(
array_slice($columns, 0, 1, true),
['activities' => get_string('activities')],
array_slice($columns, 1, null, true),
);
}
protected function col_name(stdClass $row): string {
global $OUTPUT;
$status = $row->plugininfo->get_status();
if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
return html_writer::span(
get_string('pluginmissingfromdisk', 'core', $row->plugininfo),
'notifyproblem'
);
}
return html_writer::span(
html_writer::img(
$OUTPUT->image_url('monologo', $row->plugininfo->name),
'',
[
'class' => 'icon',
],
) . get_string('modulename', $row->plugininfo->name)
);
}
/**
* Show the number of activities present, with a link to courses containing activity if relevant.
*
* @param mixed $row
* @return string
*/
protected function col_activities(stdClass $row): string {
global $DB, $OUTPUT;
try {
$count = $DB->count_records_select($row->plugininfo->name, "course <> 0");
} catch (dml_exception $e) {
$count = -1;
}
if ($count > 0) {
return $OUTPUT->action_link(
new moodle_url('/course/search.php', [
'modulelist' => $row->plugininfo->name,
]),
$count,
null,
['title' => get_string('showmodulecourse')]
);
} else if ($count < 0) {
return get_string('error');
} else {
return $count;
}
}
}
@@ -1,153 +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 html_writer;
use moodle_url;
use stdClass;
/**
* Tiny admin settings.
*
* @package core_admin
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class block_management_table extends \core_admin\table\plugin_management_table {
/** @var plugininfo[] A list of blocks which cannot be deleted */
protected array $undeletableblocktypes;
/** @var stdClass[] A list of basic block data */
protected array $blockdata;
/** @var array<string,int> A list of course counts */
protected array $courseblocks;
public function __construct() {
global $DB;
parent::__construct();
$this->undeletableblocktypes = \block_manager::get_undeletable_block_types();
$sql = 'SELECT b.name,
b.id,
COUNT(DISTINCT binst.id) as totalcount
FROM {block} b
LEFT JOIN {block_instances} binst ON binst.blockname = b.name
GROUP BY b.id,
b.name
ORDER BY b.name ASC';
$this->blockdata = $DB->get_records_sql($sql);
$sql = "SELECT blockname
FROM {block_instances}
WHERE pagetypepattern = 'course-view-*'
GROUP BY blockname";
$this->courseblocks = $DB->get_records_sql($sql);
}
protected function get_plugintype(): string {
return 'block';
}
public function guess_base_url(): void {
$this->define_baseurl(
new moodle_url('/admin/blocks.php')
);
}
protected function get_action_url(array $params = []): moodle_url {
return new moodle_url('/admin/blocks.php', $params);
}
protected function get_table_js_module(): string {
return 'core_admin/block_management_table';
}
protected function get_column_list(): array {
$columns = parent::get_column_list();
return array_merge(
array_slice($columns, 0, 1, true),
['instances' => get_string('blockinstances', 'admin')],
array_slice($columns, 1, 2, true),
['protect' => get_string('blockprotect', 'admin')],
array_slice($columns, 3, null, true),
);
}
protected function get_columns_with_help(): array {
return [
'protect' => new \help_icon('blockprotect', 'admin'),
];
}
/**
* Render the instances column
* @param stdClass $row
* @return string
*/
protected function col_instances(stdClass $row): string {
$blockdata = $this->blockdata[$row->plugininfo->name];
if (array_key_exists($blockdata->name, $this->courseblocks)) {
return html_writer::link(
new moodle_url('/course/search.php', [
'blocklist' => $blockdata->id,
]),
$blockdata->totalcount,
);
}
return $blockdata->totalcount;
}
/**
* Render the protect column.
*
* @param stdClass $row
* @return string
*/
protected function col_protect(stdClass $row): string {
global $OUTPUT;
$params = [
'sesskey' => sesskey(),
];
$protected = in_array($row->plugininfo->name, $this->undeletableblocktypes);
$pluginname = $row->plugininfo->displayname;
if ($protected) {
$params['unprotect'] = $row->plugininfo->name;
$icon = $OUTPUT->pix_icon('t/unlock', get_string('blockunprotectblock', 'admin', $pluginname));
} else {
$params['protect'] = $row->plugininfo->name;
$icon = $OUTPUT->pix_icon('t/lock', get_string('blockprotectblock', 'admin', $pluginname));
}
return html_writer::link(
$this->get_action_url($params),
$icon,
[
'data-action' => 'toggleprotectstate',
'data-plugin' => $row->plugin,
'data-target-state' => $protected ? 0 : 1,
],
);
return '';
}
}
@@ -1,63 +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;
/**
* Tiny admin settings.
*
* @package core_admin
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class editor_management_table extends \core_admin\table\plugin_management_table {
protected function get_plugintype(): string {
return 'editor';
}
public function guess_base_url(): void {
$this->define_baseurl(
new moodle_url('/admin/settings.php', ['section' => 'manageeditors'])
);
}
protected function get_action_url(array $params = []): moodle_url {
return new moodle_url('/admin/editors.php', $params);
}
protected function order_plugins(array $plugins): array {
global $CFG;
// The Editor list is stored in an ordered string.
$activeeditors = explode(',', $CFG->texteditors);
$sortedplugins = [];
foreach ($activeeditors as $editor) {
if (isset($plugins[$editor])) {
$sortedplugins[$editor] = $plugins[$editor];
unset($plugins[$editor]);
}
}
$otherplugins = parent::order_plugins($plugins);
return array_merge(
$sortedplugins,
$otherplugins
);
}
}
-281
View File
@@ -1,281 +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 core_plugin_manager;
use flexible_table;
use html_writer;
use stdClass;
defined('MOODLE_INTERNAL') || die();
require_once("{$CFG->libdir}/tablelib.php");
/**
* Plugin Management table.
*
* @package core_admin
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class hook_list_table extends flexible_table {
/** @var \core\plugininfo\base[] The plugin list */
protected array $plugins = [];
/** @var int The number of enabled plugins of this type */
protected int $enabledplugincount = 0;
/** @var core_plugin_manager */
protected core_plugin_manager $pluginmanager;
/** @var string The plugininfo class for this plugintype */
protected string $plugininfoclass;
/** @var stdClass[] The list of emitted hooks with metadata */
protected array $emitters;
public function __construct() {
global $CFG;
$this->define_baseurl('/admin/hooks.php');
parent::__construct('core_admin-hook_list_table');
// Add emitted hooks.
$this->emitters = \core\hook\manager::discover_known_hooks();
$this->setup_column_configuration();
$this->setup();
}
/**
* Set up the column configuration for this table.
*/
protected function setup_column_configuration(): void {
$columnlist = [
'details' => get_string('hookname', 'core_admin'),
'callbacks' => get_string('hookcallbacks', 'core_admin'),
'deprecates' => get_string('hookdeprecates', 'core_admin'),
];
$this->define_columns(array_keys($columnlist));
$this->define_headers(array_values($columnlist));
$columnswithhelp = [
'callbacks' => new \help_icon('hookcallbacks', 'admin'),
];
$columnhelp = array_map(function (string $column) use ($columnswithhelp): ?\renderable {
if (array_key_exists($column, $columnswithhelp)) {
return $columnswithhelp[$column];
}
return null;
}, array_keys($columnlist));
$this->define_help_for_headers($columnhelp);
}
/**
* Print the table.
*/
public function out(): void {
// All hook consumers referenced from the db/hooks.php files.
$hookmanager = \core\di::get(\core\hook\manager::class);
$allhooks = (array)$hookmanager->get_all_callbacks();
// Add any unused hooks.
foreach (array_keys($this->emitters) as $classname) {
if (isset($allhooks[$classname])) {
continue;
}
$allhooks[$classname] = [];
}
// Order rows by hook name, putting core first.
\core_collator::ksort($allhooks);
$corehooks = [];
foreach ($allhooks as $classname => $consumers) {
if (str_starts_with($classname, 'core\\')) {
$corehooks[$classname] = $consumers;
unset($allhooks[$classname]);
}
}
$allhooks = array_merge($corehooks, $allhooks);
foreach ($allhooks as $classname => $consumers) {
$this->add_data_keyed(
$this->format_row((object) [
'classname' => $classname,
'callbacks' => $consumers,
]),
$this->get_row_class($classname),
);
}
$this->finish_output(false);
}
protected function col_details(stdClass $row): string {
return $row->classname .
$this->get_description($row) .
html_writer::div($this->get_tags_for_row($row));
}
/**
* Show the name column content.
*
* @param stdClass $row
* @return string
*/
protected function get_description(stdClass $row): string {
if (!array_key_exists($row->classname, $this->emitters)) {
return '';
}
return html_writer::tag(
'small',
clean_text(markdown_to_html($this->emitters[$row->classname]['description']), FORMAT_HTML),
);
}
protected function col_deprecates(stdClass $row): string {
if (!class_exists($row->classname)) {
return '';
}
$deprecates = \core\hook\manager::get_replaced_callbacks($row->classname);
if (count($deprecates) === 0) {
return '';
}
$content = html_writer::start_tag('ul');
foreach ($deprecates as $deprecatedmethod) {
$content .= html_writer::tag('li', $deprecatedmethod);
}
$content .= html_writer::end_tag('ul');
return $content;
}
protected function col_callbacks(stdClass $row): string {
global $CFG;
$hookclass = $row->classname;
$cbinfo = [];
foreach ($row->callbacks as $definition) {
$iscallable = is_callable($definition['callback'], false, $callbackname);
$isoverridden = isset($CFG->hooks_callback_overrides[$hookclass][$definition['callback']]);
$info = "{$callbackname}&nbsp;({$definition['priority']})";
if (!$iscallable) {
$info .= '&nbsp;';
$info .= $this->get_tag(
get_string('error'),
'danger',
get_string('hookcallbacknotcallable', 'core_admin', $callbackname),
);
}
if ($isoverridden) {
// The lang string meaning should be close enough here.
$info .= $this->get_tag(
get_string('hookconfigoverride', 'core_admin'),
'warning',
get_string('hookconfigoverride_help', 'core_admin'),
);
}
$cbinfo[] = $info;
}
if ($cbinfo) {
$output = html_writer::start_tag('ol');
foreach ($cbinfo as $callback) {
$class = '';
if ($definition['disabled']) {
$class = 'dimmed_text';
}
$output .= html_writer::tag('li', $callback, ['class' => $class]);
}
$output .= html_writer::end_tag('ol');
return $output;
} else {
return '';
}
}
/**
* Get the HTML to display the badge with tooltip.
*
* @param string $tag The main text to display
* @param null|string $type The pill type
* @param null|string $tooltip The content of the tooltip
* @return string
*/
protected function get_tag(
string $tag,
?string $type = null,
?string $tooltip = null,
): string {
$attributes = [];
if ($type === null) {
$type = 'info';
}
if ($tooltip) {
$attributes['data-toggle'] = 'tooltip';
$attributes['title'] = $tooltip;
}
return html_writer::span($tag, "badge badge-{$type}", $attributes);
}
/**
* Get the code to display a set of tags for this table row.
*
* @param stdClass $row
* @return string
*/
protected function get_tags_for_row(stdClass $row): string {
if (!array_key_exists($row->classname, $this->emitters)) {
// This hook has been defined in the db/hooks.php file
// but does not refer to a hook in this version of Moodle.
return $this->get_tag(
get_string('hookunknown', 'core_admin'),
'warning',
get_string('hookunknown_desc', 'core_admin'),
);
}
if (!class_exists($row->classname)) {
// This hook has been defined in a hook discovery agent, but the class it refers to could not be found.
return $this->get_tag(
get_string('hookclassmissing', 'core_admin'),
'warning',
get_string('hookclassmissing_desc', 'core_admin'),
);
}
$tags = $this->emitters[$row->classname]['tags'] ?? [];
$taglist = array_map(function($tag): string {
if (is_array($tag)) {
return $this->get_tag(...$tag);
}
return $this->get_tag($tag, 'badge bg-info text-white');
}, $tags);
return implode("\n", $taglist);
}
protected function get_row_class(string $classname): string {
return '';
}
}
@@ -1,71 +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;
/**
* Media plugin admin settings.
*
* @package core_admin
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class media_management_table extends \core_admin\table\plugin_management_table {
/** @var array The list of used extensions */
protected array $usedextensions = [];
protected function get_plugintype(): string {
return 'media';
}
protected function get_action_url(array $params = []): moodle_url {
return new moodle_url('/admin/media.php', $params);
}
protected function get_column_list(): array {
$columns = parent::get_column_list();
return array_merge(
array_slice($columns, 0, 1, true),
['supports' => get_string('supports', 'core_media')],
array_slice($columns, 1, null, true),
);
}
protected function col_name(stdClass $row): string {
global $OUTPUT, $PAGE;
$name = $row->plugininfo->name;
if ($PAGE->theme->resolve_image_location('icon', 'media_' . $name, false)) {
$icon = $OUTPUT->pix_icon('icon', '', "media_{$name}", ['class' => 'icon pluginicon']);
} else {
$icon = $OUTPUT->pix_icon('spacer', '', 'moodle', ['class' => 'icon pluginicon noicon']);
}
$help = '';
if (get_string_manager()->string_exists('pluginname_help', 'media_' . $name)) {
$help = '&nbsp;' . $OUTPUT->help_icon('pluginname', 'media_' . $name);
}
return $icon . $row->plugininfo->displayname . $help;
}
protected function col_supports(stdClass $row): string {
return $row->plugininfo->supports($this->usedextensions);
}
}
@@ -1,503 +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 context_system;
use core_plugin_manager;
use core_table\dynamic as dynamic_table;
use flexible_table;
use html_writer;
use moodle_url;
use stdClass;
defined('MOODLE_INTERNAL') || die();
require_once("{$CFG->libdir}/tablelib.php");
/**
* Plugin Management table.
*
* @package core_admin
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class plugin_management_table extends flexible_table implements dynamic_table {
/** @var \core\plugininfo\base[] The plugin list */
protected array $plugins = [];
/** @var int The number of enabled plugins of this type */
protected int $enabledplugincount = 0;
/** @var core_plugin_manager */
protected core_plugin_manager $pluginmanager;
/** @var string The plugininfo class for this plugintype */
protected string $plugininfoclass;
public function __construct() {
global $CFG;
parent::__construct($this->get_table_id());
require_once($CFG->libdir . '/adminlib.php');
// Fetch the plugininfo class.
$this->pluginmanager = core_plugin_manager::instance();
$this->plugininfoclass = $this->pluginmanager::resolve_plugininfo_class($this->get_plugintype());
$this->guess_base_url();
$this->plugins = $this->get_sorted_plugins();
$this->enabledplugincount = count(array_filter($this->plugins, function ($plugin) {
return $plugin->is_enabled();
}));
$this->setup_column_configuration();
$this->set_filterset(new plugin_management_table_filterset());
$this->setup();
}
/**
* Get the list of sorted plugins.
*
* @return \core\plugininfo\base[]
*/
protected function get_sorted_plugins(): array {
if ($this->plugininfoclass::plugintype_supports_ordering()) {
return $this->plugininfoclass::get_sorted_plugins();
} else {
$plugins = $this->pluginmanager->get_plugins_of_type($this->get_plugintype());
return self::sort_plugins($plugins);
}
}
/**
* Sort the plugins list.
*
* Note: This only applies to plugins which do not support ordering.
*
* @param \core\plugininfo\base[] $plugins
* @return \core\plugininfo\base[]
*/
protected function sort_plugins(array $plugins): array {
// The asort functions work by reference.
\core_collator::asort_objects_by_property($plugins, 'displayname');
return $plugins;
}
/**
* Set up the column configuration for this table.
*/
protected function setup_column_configuration(): void {
$columnlist = $this->get_column_list();
$this->define_columns(array_keys($columnlist));
$this->define_headers(array_values($columnlist));
$columnswithhelp = $this->get_columns_with_help();
$columnhelp = array_map(function (string $column) use ($columnswithhelp): ?\renderable {
if (array_key_exists($column, $columnswithhelp)) {
return $columnswithhelp[$column];
}
return null;
}, array_keys($columnlist));
$this->define_help_for_headers($columnhelp);
}
/**
* Set the standard order of the plugins.
*
* @param array $plugins
* @return array
*/
protected function order_plugins(array $plugins): array {
uasort($plugins, function ($a, $b) {
if ($a->is_enabled() && !$b->is_enabled()) {
return -1;
} else if (!$a->is_enabled() && $b->is_enabled()) {
return 1;
}
return strnatcasecmp($a->name, $b->name);
});
return $plugins;
}
/**
* Get the plugintype for this table.
*
* @return string
*/
abstract protected function get_plugintype(): string;
/**
* Get the action URL for this table.
*
* The action URL is used to perform all actions when JS is not available.
*
* @param array $params
* @return moodle_url
*/
abstract protected function get_action_url(array $params = []): moodle_url;
/**
* Provide a default implementation for guessing the base URL from the action URL.
*/
public function guess_base_url(): void {
$this->define_baseurl($this->get_action_url());
}
/**
* Get the web service method used to toggle state.
*
* @return null|string
*/
protected function get_toggle_service(): ?string {
return 'core_admin_set_plugin_state';
}
/**
* Get the web service method used to order plugins.
*
* @return null|string
*/
protected function get_sortorder_service(): ?string {
return 'core_admin_set_plugin_order';
}
/**
* Get the ID of the table.
*
* @return string
*/
protected function get_table_id(): string {
return 'plugin_management_table-' . $this->get_plugintype();
}
/**
* Get a list of the column titles
* @return string[]
*/
protected function get_column_list(): array {
$columns = [
'name' => get_string('name', 'core'),
'version' => get_string('version', 'core'),
];
if ($this->supports_disabling()) {
$columns['enabled'] = get_string('pluginenabled', 'core_plugin');
}
if ($this->supports_ordering()) {
$columns['order'] = get_string('order', 'core');
}
$columns['settings'] = get_string('settings', 'core');
$columns['uninstall'] = get_string('uninstallplugin', 'core_admin');
return $columns;
}
protected function get_columns_with_help(): array {
return [];
}
/**
* Get the context for this table.
*
* @return context_system
*/
public function get_context(): context_system {
return context_system::instance();
}
/**
* Get the table content.
*/
public function get_content(): string {
ob_start();
$this->out();
$content = ob_get_contents();
ob_end_clean();
return $content;
}
/**
* Print the table.
*/
public function out(): void {
$plugintype = $this->get_plugintype();
foreach ($this->plugins as $plugininfo) {
$plugin = "{$plugintype}_{$plugininfo->name}";
$rowdata = (object) [
'plugin' => $plugin,
'plugininfo' => $plugininfo,
'name' => $plugininfo->displayname,
'version' => $plugininfo->versiondb,
];
$this->add_data_keyed(
$this->format_row($rowdata),
$this->get_row_class($rowdata)
);
}
$this->finish_output(false);
}
/**
* This table is not downloadable.
* @param bool $downloadable
* @return bool
*/
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
public function is_downloadable($downloadable = null): bool {
return false;
}
/**
* Show the name column content.
*
* @param stdClass $row
* @return string
*/
protected function col_name(stdClass $row): string {
$status = $row->plugininfo->get_status();
if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
return html_writer::span(
get_string('pluginmissingfromdisk', 'core', $row->plugininfo),
'notifyproblem'
);
}
if ($row->plugininfo->is_installed_and_upgraded()) {
return $row->plugininfo->displayname;
}
return html_writer::span(
$row->plugininfo->displayname,
'notifyproblem'
);
}
/**
* Show the enable/disable column content.
*
* @param stdClass $row
* @return string
*/
protected function col_enabled(stdClass $row): string {
global $OUTPUT;
$enabled = $row->plugininfo->is_enabled();
$params = [
'sesskey' => sesskey(),
'plugin' => $row->plugininfo->name,
'action' => $enabled ? 'disable' : 'enable',
];
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 {
global $OUTPUT;
if (!$this->supports_ordering()) {
return '';
}
if (!$row->plugininfo->is_enabled()) {
return '';
}
if ($this->enabledplugincount <= 1) {
// There is only one row.
return '';
}
$hasup = true;
$hasdown = true;
if (empty($this->currentrow)) {
// This is the top row.
$hasup = false;
}
if ($this->currentrow === ($this->enabledplugincount - 1)) {
// This is the last row.
$hasdown = false;
}
if ($this->supports_ordering()) {
$dataattributes = [
'data-method' => $this->get_sortorder_service(),
'data-action' => 'move',
'data-plugin' => $row->plugin,
];
} else {
$dataattributes = [];
}
if ($hasup) {
$upicon = html_writer::link(
$this->get_action_url([
'sesskey' => sesskey(),
'action' => 'up',
'plugin' => $row->plugininfo->name,
]),
$OUTPUT->pix_icon('t/up', get_string('moveup')),
array_merge($dataattributes, ['data-direction' => 'up']),
);
} else {
$upicon = $OUTPUT->spacer();
}
if ($hasdown) {
$downicon = html_writer::link(
$this->get_action_url([
'sesskey' => sesskey(),
'action' => 'down',
'plugin' => $row->plugininfo->name,
]),
$OUTPUT->pix_icon('t/down', get_string('movedown')),
array_merge($dataattributes, ['data-direction' => 'down']),
);
} else {
$downicon = $OUTPUT->spacer();
}
// For now just add the up/down icons.
return html_writer::span($upicon . $downicon);
}
/**
* Show the settings column content.
*
* @param stdClass $row
* @return string
*/
protected function col_settings(stdClass $row): string {
if ($settingsurl = $row->plugininfo->get_settings_url()) {
return html_writer::link($settingsurl, get_string('settings'));
}
return '';
}
/**
* Show the Uninstall column content.
*
* @param stdClass $row
* @return string
*/
protected function col_uninstall(stdClass $row): string {
$status = $row->plugininfo->get_status();
if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
return get_string('status_new', 'core_plugin');
}
if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
$uninstall = get_string('status_missing', 'core_plugin') . '<br/>';
} else {
$uninstall = '';
}
if ($uninstallurl = $this->pluginmanager->get_uninstall_url($row->plugin)) {
$uninstall .= html_writer::link($uninstallurl, get_string('uninstallplugin', 'core_admin'));
}
return $uninstall;
}
/**
* Get the JS module used to manage this table.
*
* This should be a class which extends 'core_admin/plugin_management_table'.
*
* @return string
*/
protected function get_table_js_module(): string {
return 'core_admin/plugin_management_table';
}
/**
* Add JS specific to this implementation.
*
* @return string
*/
protected function get_dynamic_table_html_end(): string {
global $PAGE;
$PAGE->requires->js_call_amd($this->get_table_js_module(), 'init');
return parent::get_dynamic_table_html_end();
}
/**
* Get any class to add to the row.
*
* @param mixed $row
* @return string
*/
protected function get_row_class($row): string {
$plugininfo = $row->plugininfo;
if ($plugininfo->get_status() === core_plugin_manager::PLUGIN_STATUS_MISSING) {
return '';
}
if (!$plugininfo->is_enabled()) {
return 'dimmed_text';
}
return '';
}
public static function get_filterset_class(): string {
return self::class . '_filterset';
}
/**
* Whether this plugin type supports the disabling of plugins.
*
* @return bool
*/
protected function supports_disabling(): bool {
return $this->plugininfoclass::plugintype_supports_disabling();
}
/**
* Whether this table should show ordering fields.
*
* @return bool
*/
protected function supports_ordering(): bool {
return $this->plugininfoclass::plugintype_supports_ordering();
}
}
@@ -1,27 +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;
/**
* This file contains the dynamic interface.
*
* @package core_admin
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class plugin_management_table_filterset extends \core_table\local\filter\filterset {
}
@@ -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/>.
namespace core_admin\table;
use moodle_url;
/**
* Admin tool settings.
*
* @package core_admin
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_plugin_management_table extends \core_admin\table\plugin_management_table {
protected function get_plugintype(): string {
return 'tool';
}
protected function get_column_list(): array {
$columns = parent::get_column_list();
unset($columns['settings']);
return $columns;
}
protected function get_action_url(array $params = []): moodle_url {
return new moodle_url('/admin/settings.php', array_merge(['section' => 'toolsmanagement'], $params));
}
}
-174
View File
@@ -1,174 +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/>.
/**
* Task executor for adhoc tasks.
*
* @package core
* @subpackage cli
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__ . '/../../config.php');
require_once("{$CFG->libdir}/clilib.php");
list($options, $unrecognized) = cli_get_params(
[
'help' => false,
'showsql' => false,
'showdebugging' => false,
'execute' => false,
'keep-alive' => 0,
'ignorelimits' => false,
'force' => false,
'id' => null,
'classname' => null,
'taskslimit' => null,
'failed' => false,
], [
'h' => 'help',
'e' => 'execute',
'k' => 'keep-alive',
'i' => 'ignorelimits',
'f' => 'force',
'c' => 'classname',
'l' => 'taskslimit',
]
);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
$help = <<<EOT
Ad hoc cron tasks.
Options:
-h, --help Print out this help
--showsql Show sql queries before they are executed
--showdebugging Show developer level debugging information
-e, --execute Run all queued adhoc tasks
-k, --keep-alive=N Keep this script alive for N seconds and poll for new adhoc tasks
-i --ignorelimits Ignore task_adhoc_concurrency_limit and task_adhoc_max_runtime limits
-f, --force Run even if cron is disabled
--id Run (failed) task with id
-c, --classname Run tasks with a certain classname (FQN)
-l, --taskslimit=N Run at most N tasks
--failed Run only tasks that failed, ie those with a fail delay
Run all queued tasks:
\$sudo -u www-data /usr/bin/php admin/cli/adhoc_task.php --execute
Run all queued tasks of specific class:
\$sudo -u www-data /usr/bin/php admin/cli/adhoc_task.php --classname=\\\\core_course\\\\task\\\\course_delete_modules
Double backslash for the shell escape reasons.
Run a specific task:
\$sudo -u www-data /usr/bin/php admin/cli/adhoc_task.php --id=123456
Run a specific task with debugging:
\$sudo -u www-data /usr/bin/php admin/cli/adhoc_task.php --id=123456 --showsql --showdebugging
To profile a long running task:
\$sudo -u www-data /usr/bin/php admin/cli/adhoc_task.php --taskslimit=1 --classname='\\some\\class\\name' --ignorelimits
EOT;
if ($options['help']) {
echo $help;
exit(0);
}
if (CLI_MAINTENANCE) {
echo "CLI maintenance mode active, cron execution suspended.\n";
exit(1);
}
if (moodle_needs_upgrading()) {
echo "Moodle upgrade pending, cron execution suspended.\n";
exit(1);
}
if (!get_config('core', 'cron_enabled') && !$options['force']) {
mtrace('Cron is disabled. Use --force to override.');
exit(1);
}
// Common debugging options.
if ($options['showdebugging']) {
set_debugging(DEBUG_DEVELOPER, true);
}
if ($options['showsql']) {
$DB->set_debug(true);
}
if (!empty($CFG->showcronsql)) {
$DB->set_debug(true);
}
if (!empty($CFG->showcrondebugging)) {
set_debugging(DEBUG_DEVELOPER, true);
}
// Process params.
core_php_time_limit::raise();
// Increase memory limit.
raise_memory_limit(MEMORY_EXTRA);
// Emulate normal session - we use admin account by default.
\core\cron::setup_user();
\core\local\cli\shutdown::script_supports_graceful_exit();
$humantimenow = date('r', time());
mtrace("Server Time: {$humantimenow}\n");
$classname = $options['classname'];
// Run a single adhoc task only, if requested.
if (!empty($options['id'])) {
$taskid = (int) $options['id'];
\core\cron::run_adhoc_task($taskid);
exit(0);
}
// Run all failed tasks.
if (!empty($options['failed'])) {
\core\cron::run_failed_adhoc_tasks($classname);
exit(0);
}
// Examine params and determine if we should run.
$execute = (bool) $options['execute'];
$keepalive = empty($options['keep-alive']) ? 0 : (int) $options['keep-alive'];
$taskslimit = empty($options['taskslimit']) ? null : (int) $options['taskslimit'];
$checklimits = empty($options['ignorelimits']);
if ($classname || $keepalive || $taskslimit) {
$execute = true;
}
// Output the help text if no criteria for running the adhoc tasks are given.
if (!$execute) {
echo $help;
exit(0);
}
\core\cron::run_adhoc_tasks(time(), $keepalive, $checklimits, null, $taskslimit, $classname);
-111
View File
@@ -1,111 +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/>.
/**
* This hack is intended for clustered sites that do not want
* to use shared cachedir for component cache.
*
* This file needs to be called after any change in PHP files in dataroot,
* that is before upgrade and install.
*
* @package core
* @copyright 2013 Petr Skoda (skodak) {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
define('ABORT_AFTER_CONFIG', true); // We need just the values from config.php.
define('CACHE_DISABLE_ALL', true); // This prevents reading of existing caches.
define('IGNORE_COMPONENT_CACHE', true);
require(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/clilib.php');
// Now get cli options.
list($options, $unrecognized) = cli_get_params(
array(
'file' => false,
'rebuild' => false,
'print' => false,
'help' => false
),
array(
'h' => 'help'
)
);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized), 2);
}
if (!$options['rebuild'] and !$options['file'] and !$options['print']) {
$help =
"Create alternative component cache file
Options:
-h, --help Print out this help
--rebuild Rebuild \$CFG->alternative_component_cache file
--file=filepath Save component cache to file
--print Print component cache file content
Example:
\$ php admin/cli/rebuild_alternative_component_cache.php --rebuild
";
echo $help;
exit(0);
}
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);
$content = core_component::get_cache_content();
if ($options['print']) {
echo $content;
exit(0);
}
if ($options['rebuild']) {
if (empty($CFG->alternative_component_cache)) {
fwrite(STDERR, 'config.php does not contain $CFG->alternative_component_cache setting');
fwrite(STDERR, "\n");
exit(2);
}
$target = $CFG->alternative_component_cache;
} else {
$target = $options['file'];
}
if (!$target) {
fwrite(STDERR, "Invalid target file $target");
fwrite(STDERR, "\n");
exit(1);
}
$bytes = file_put_contents($target, $content);
if (!$bytes) {
fwrite(STDERR, "Error writing to $target");
fwrite(STDERR, "\n");
exit(1);
}
// Success.
echo "File $target was updated\n";
exit(0);
+8 -6
View File
@@ -28,8 +28,9 @@
define('CLI_SCRIPT', true);
require(__DIR__.'/../../config.php');
require(dirname(dirname(dirname(__FILE__))).'/config.php');
require_once($CFG->libdir.'/clilib.php'); // cli only functions
require_once($CFG->libdir.'/cronlib.php');
// now get cli options
list($options, $unrecognized) = cli_get_params(array('help'=>false),
@@ -74,15 +75,16 @@ if (!empty($CFG->showcronsql)) {
$DB->set_debug(true);
}
if (!empty($CFG->showcrondebugging)) {
set_debugging(DEBUG_DEVELOPER, true);
$CFG->debug = DEBUG_DEVELOPER;
$CFG->debugdisplay = true;
}
$starttime = microtime();
// Emulate normal session.
\core\cron::setup_user();
/// emulate normal session
cron_setup_user();
// Start output log.
/// Start output log
$timenow = time();
mtrace("Server Time: ".date('r',$timenow)."\n\n");
@@ -95,4 +97,4 @@ backup_cron_automated_helper::run_automated_backup(backup_cron_automated_helper:
mtrace("Automated cron backups completed correctly");
$difftime = microtime_diff($starttime, microtime());
mtrace("Execution took ".$difftime." seconds");
mtrace("Execution took ".$difftime." seconds");
-135
View File
@@ -1,135 +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/>.
/**
* This script allows to do backup.
*
* @package core
* @subpackage cli
* @copyright 2013 Lancaster University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', 1);
require(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/clilib.php');
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
// Now get cli options.
list($options, $unrecognized) = cli_get_params(array(
'courseid' => false,
'courseshortname' => '',
'cmid' => false,
'destination' => '',
'help' => false,
), array('h' => 'help'));
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
if ($options['help'] || !($options['courseid'] || $options['courseshortname'] || $options['cmid'])) {
$help = <<<EOL
Perform backup of the given course or course module.
Options:
--courseid=INTEGER Course ID for backup.
--courseshortname=STRING Course shortname for backup. This option is ignored if courseid is set.
--cmid=INTEGER Course module ID for backup. This option is ignored if courseid or courseshortname is set.
--destination=STRING Path where to store backup file. If not set the backup
will be stored within the course backup file area.
-h, --help Print out this help.
Example:
\$sudo -u www-data /usr/bin/php admin/cli/backup.php --courseid=2 --destination=/moodle/backup/\n
EOL;
echo $help;
die;
}
$admin = get_admin();
if (!$admin) {
mtrace("Error: No admin account was found");
die;
}
// Do we need to store backup somewhere else?
$dir = rtrim($options['destination'], '/');
if (!empty($dir)) {
if (!file_exists($dir) || !is_dir($dir) || !is_writable($dir)) {
mtrace("Destination directory does not exists or not writable.");
die;
}
}
// Check that the course or course module exists.
if ($options['courseid']) {
$course = $DB->get_record('course', array('id' => $options['courseid']), '*', MUST_EXIST);
} else if ($options['courseshortname']) {
$course = $DB->get_record('course', array('shortname' => $options['courseshortname']), '*', MUST_EXIST);
} else if ($options['cmid']) {
$cm = $DB->get_record('course_modules', ['id' => $options['cmid']], '*', MUST_EXIST);
}
cli_heading('Performing backup...');
if (!empty($course)) {
$bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
backup::INTERACTIVE_YES, backup::MODE_GENERAL, $admin->id);
} else if (!empty($cm)) {
$bc = new backup_controller(backup::TYPE_1ACTIVITY, $cm->id, backup::FORMAT_MOODLE,
backup::INTERACTIVE_YES, backup::MODE_GENERAL, $admin->id);
} else {
throw new \moodle_exception('invalidoption');
}
// Set the default filename.
$format = $bc->get_format();
$type = $bc->get_type();
$id = $bc->get_id();
$users = $bc->get_plan()->get_setting('users')->get_value();
$anonymised = $bc->get_plan()->get_setting('anonymize')->get_value();
$filename = backup_plan_dbops::get_default_backup_filename($format, $type, $id, $users, $anonymised);
$bc->get_plan()->get_setting('filename')->set_value($filename);
// Execution.
$bc->finish_ui();
$bc->execute_plan();
$results = $bc->get_results();
$file = $results['backup_destination']; // May be empty if file already moved to target location.
// Do we need to store backup somewhere else?
if (!empty($dir)) {
if ($file) {
mtrace("Writing " . $dir.'/'.$filename);
if ($file->copy_content_to($dir.'/'.$filename)) {
$file->delete();
mtrace("Backup completed.");
} else {
mtrace("Destination directory does not exist or is not writable. Leaving the backup in the course backup file area.");
}
}
} else {
if (!empty($course)) {
mtrace("Backup completed, the new file is listed in the backup area of the given course");
} else {
mtrace("Backup completed, the new file is listed in the backup area of the given module");
}
}
$bc->destroy();
exit(0);
-119
View File
@@ -1,119 +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/>.
/**
* Build and store theme CSS.
*
* @package core
* @subpackage cli
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__.'/../../config.php');
require_once("$CFG->libdir/clilib.php");
require_once("$CFG->libdir/csslib.php");
require_once("$CFG->libdir/outputlib.php");
$longparams = [
'themes' => null,
'direction' => null,
'help' => false,
'verbose' => false
];
$shortmappings = [
't' => 'themes',
'd' => 'direction',
'h' => 'help',
'v' => 'verbose'
];
// Get CLI params.
list($options, $unrecognized) = cli_get_params($longparams, $shortmappings);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
if ($options['help']) {
echo
"Compile the CSS for one or more installed themes.
Existing CSS caches will replaced.
By default all themes will be recompiled unless otherwise specified.
Options:
-t, --themes A comma separated list of themes to be compiled
-d, --direction Only compile a single direction (either ltr or rtl)
-v, --verbose Print info comments to stdout
-h, --help Print out this help
Example:
\$ sudo -u www-data /usr/bin/php admin/cli/build_theme_css.php --themes=boost --direction=ltr
";
die;
}
if (empty($options['verbose'])) {
$trace = new null_progress_trace();
} else {
$trace = new text_progress_trace();
}
cli_heading('Build theme css');
// Determine which themes we need to build.
$themenames = [];
if (is_null($options['themes'])) {
$trace->output('No themes specified. Finding all installed themes.');
$themenames = array_keys(core_component::get_plugin_list('theme'));
} else {
if (is_string($options['themes'])) {
$themenames = explode(',', $options['themes']);
} else {
cli_error('--themes must be a comma separated list of theme names');
}
}
$trace->output('Checking that each theme is correctly installed...');
$themeconfigs = [];
foreach ($themenames as $themename) {
if (is_null(theme_get_config_file_path($themename))) {
cli_error("Unable to find theme config for {$themename}");
}
// Load the config for the theme.
$themeconfigs[] = theme_config::load($themename);
}
$directions = ['ltr', 'rtl'];
if (!is_null($options['direction'])) {
if (!in_array($options['direction'], $directions)) {
cli_error("--direction must be either ltr or rtl");
}
$directions = [$options['direction']];
}
$trace->output('Building CSS for themes: ' . implode(', ', $themenames));
theme_build_css_for_themes($themeconfigs, $directions);
exit(0);
-185
View File
@@ -1,185 +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/>.
/**
* CLI script allowing to get and set config values.
*
* This is technically just a thin wrapper for {@link get_config()} and
* {@link set_config()} functions.
*
* @package core
* @subpackage cli
* @copyright 2017 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/clilib.php');
$usage = "Displays the current value of the given site setting. Allows to set it to the given value, too.
Usage:
# php cfg.php [--component=<componentname>] [--json] [--shell-arg]
# php cfg.php --name=<configname> [--component=<componentname>] [--shell-arg] [--no-eol]
# php cfg.php --name=<configname> [--component=<componentname>] --set=<value>
# php cfg.php --name=<configname> [--component=<componentname>] --unset
# php cfg.php [--help|-h]
Options:
-h --help Print this help.
--component=<frankenstyle> Name of the component the variable is part of. Defaults to core.
--name=<configname> Name of the configuration variable to get/set. If missing, print all
configuration variables of the given component.
--set=<value> Set the given variable to this value.
--unset Unset the given variable.
--shell-arg Escape output values so that they can be directly used as shell script arguments.
--json Encode output list of values using JSON notation.
--no-eol Do not include the trailing new line character when printing the value.
The list of all variables of the given component can be printed as
tab-separated list (default) or JSON object (--json). Particular values are
printed as raw text values, optionally escaped so that they can be directly
used as shell script arguments (--shell-arg). Single values are displayed with
trailing new line by default, unless explicitly disabled (--no-eol).
In the read mode, the script exits with success status 0 if the requested value
is found. If the requested variable is not set, the script exits with status 3.
When listing all variables of the component, the exit status is always 0 even
if no variables for the given component are found. When setting/unsetting a
value, the exit status is 0. When attempting to set/unset a value that has
already been hard-set in config.php, the script exits with error status 4. In
case of unexpected error, the script exits with error status 1.
Examples:
# php cfg.php
Prints tab-separated list of all core configuration variables and their values.
# php cfg.php --json
Prints list of all core configuration variables and their values as a JSON object.
# php cfg.php --name=release
Prints the given configuration variable - e.g. \$CFG->release in this case.
# php cfg.php --component=tool_recyclebin
# Prints tab-separated list of the plugin's configuration variables.
# export DATAROOT=\$(php cfg.php --name=dataroot --shell-arg --no-eol)
Stores the given configuration variable in the shell variable, escaped
so that it can be safely used as a shell argument.
# php cfg.php --name=theme --set=classic
Sets the given configuration variable to the given value.
# php cfg.php --name=noemailever --unset
Unsets the previously configured variable.
";
list($options, $unrecognised) = cli_get_params([
'help' => false,
'component' => null,
'name' => null,
'set' => null,
'unset' => false,
'shell-arg' => false,
'json' => false,
'no-eol' => false,
], [
'h' => 'help'
]);
if ($unrecognised) {
$unrecognised = implode(PHP_EOL.' ', $unrecognised);
cli_error(get_string('cliunknowoption', 'core_admin', $unrecognised));
}
if ($options['help']) {
cli_writeln($usage);
exit(2);
}
if ($options['unset'] || $options['set'] !== null) {
// Unset the variable or set it to the given value.
if (empty($options['name'])) {
cli_error('Missing configuration variable name', 2);
}
// Check that the variable is not hard-set in the main config.php already.
$component = $options['component'];
if (!empty($component)) {
$componentsettings = $CFG->forced_plugin_settings[$component] ?? [];
$settinginconfig = array_key_exists($options['name'], $componentsettings);
} else {
$settinginconfig = array_key_exists($options['name'], $CFG->config_php_settings);
}
if ($settinginconfig) {
cli_error('The configuration variable is hard-set in the config.php, unable to change.', 4);
}
$new = $options['set'];
$old = get_config($options['component'], $options['name']);
if ($new !== $old) {
set_config($options['name'], $options['set'], $options['component']);
add_to_config_log($options['name'], $old, $new, $options['component']);
}
exit(0);
}
if ($options['name'] === null) {
// List all variables provided by the component (defaults to core).
$got = get_config($options['component']);
if ($options['json']) {
cli_writeln(json_encode($got));
} else {
foreach ($got as $name => $value) {
if ($options['shell-arg']) {
$value = escapeshellarg($value);
}
cli_writeln($name."\t".$value);
}
}
exit(0);
} else {
// Display the value of a single variable.
$got = get_config($options['component'], $options['name']);
if ($got === false) {
cli_error('No such configuration variable found.', 3);
}
if ($options['shell-arg']) {
$got = escapeshellarg($got);
}
if ($options['json']) {
$got = json_encode($got);
}
if ($options['no-eol']) {
cli_write($got);
} else {
cli_writeln($got);
}
exit(0);
}
-76
View File
@@ -1,76 +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/>.
/**
* Validate that the current db structure matches the install.xml files.
*
* @package core
* @copyright 2014 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Petr Skoda <petr.skoda@totaralms.com>
*/
define('CLI_SCRIPT', true);
require(__DIR__ . '/../../config.php');
require_once($CFG->libdir.'/clilib.php');
$help = "Validate database structure
Options:
-h, --help Print out this help.
Example:
\$ sudo -u www-data /usr/bin/php admin/cli/check_database_schema.php
";
list($options, $unrecognized) = cli_get_params(
array(
'help' => false,
),
array(
'h' => 'help',
)
);
if ($options['help']) {
echo $help;
exit(0);
}
if (empty($CFG->version)) {
echo "Database is not yet installed.\n";
exit(2);
}
$dbmanager = $DB->get_manager();
$schema = $dbmanager->get_install_xml_schema();
if (!$errors = $dbmanager->check_database_schema($schema)) {
echo "Database structure is ok.\n";
exit(0);
}
foreach ($errors as $table => $items) {
cli_separator();
echo "$table\n";
foreach ($items as $item) {
echo " * $item\n";
}
}
cli_separator();
exit(1);
-171
View File
@@ -1,171 +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/>.
/**
* CLI tool for system checks
*
* @package core
* @category check
* @copyright 2020 Brendan Heywood (brendan@catalyst-au.net)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__ . '/../../config.php');
require_once($CFG->libdir.'/clilib.php');
use core\check\result;
list($options, $unrecognized) = cli_get_params([
'help' => false,
'filter' => '',
'type' => 'status',
'verbose' => false,
], [
'h' => 'help',
'f' => 'filter',
'v' => 'verbose',
't' => 'type',
]);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
$checks = \core\check\manager::get_checks($options['type']);
$types = join(', ', \core\check\manager::TYPES);
$help = "Run Moodle system checks
Options:
-h, --help Print out this help
-f, --filter Filter to a subset of checks
-t, --type Which set of checks? Defaults to 'status'
One of $types
-v, --verbose Show details of all checks, not just failed checks
Example:
sudo -u www-data php admin/cli/checks.php
sudo -u www-data php admin/cli/checks.php -v
sudo -u www-data php admin/cli/checks.php -v --filter=environment
";
if ($options['help']) {
echo $help;
die();
}
$filter = $options['filter'];
if ($filter) {
$checks = array_filter($checks, function($check, $key) use ($filter) {
$ref = $check->get_ref();
return (strpos($ref, $filter) !== false);
}, 1);
}
// These shell exit codes and labels align with the NRPE standard.
$exitcodes = [
result::NA => 0,
result::OK => 0,
result::INFO => 0,
result::UNKNOWN => 3,
result::WARNING => 1,
result::ERROR => 2,
result::CRITICAL => 2,
];
$exitlabel = [
result::NA => 'OK',
result::OK => 'OK',
result::INFO => 'OK',
result::UNKNOWN => 'UNKNOWN',
result::WARNING => 'WARNING',
result::ERROR => 'CRITICAL',
result::CRITICAL => 'CRITICAL',
];
$format = "% 10s| % -60s\n";
$spacer = "----------+--------------------------------------------------------------------\n";
$prefix = ' |';
$output = '';
$header = $exitlabel[result::OK] . ': ' . get_string('checksok', '', $options['type']) . "\n";
$exitcode = $exitcodes[result::OK];
foreach ($checks as $check) {
$ref = $check->get_ref();
$result = $check->get_result();
$status = $result->get_status();
$checkexitcode = $exitcodes[$status];
// Summary is treated as html.
$summary = $result->get_summary();
$summary = html_to_text($summary, 60, false);
if ($checkexitcode > $exitcode) {
$exitcode = $checkexitcode;
$header = $exitlabel[$status] . ': ' . $check->get_name() . " (" . $check->get_ref() . ")\n";
}
if (empty($messages[$status])) {
$messages[$status] = $result;
}
$len = strlen(get_string('status' . $status));
if ($options['verbose'] ||
$status == result::WARNING ||
$status == result::CRITICAL ||
$status == result::ERROR) {
$output .= sprintf(
$format,
$OUTPUT->check_result($result),
sprintf('%s (%s)', $check->get_name(), $ref)
);
$summary = str_replace("\n", "\n" . $prefix . ' ', $summary);
$output .= sprintf( $format, '', ' ' . $summary);
if ($options['verbose']) {
$actionlink = $check->get_action_link();
if ($actionlink) {
$output .= sprintf( $format, '', ' ' . $actionlink->url);
}
$output .= sprintf( $format, '', '');
}
}
}
// Print NRPE header.
print $header;
// Only show the table header if there is anything to show.
if ($output) {
print sprintf($format,
get_string('status'). ' ',
get_string('check')
) . $spacer;
print $output;
}
// NRPE shell exit code.
exit($exitcode);
+10 -135
View File
@@ -1,4 +1,5 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -29,32 +30,13 @@
define('CLI_SCRIPT', true);
require(__DIR__ . '/../../config.php');
require_once($CFG->libdir . '/clilib.php');
require(dirname(dirname(dirname(__FILE__))).'/config.php');
require_once($CFG->libdir.'/clilib.php'); // cli only functions
require_once($CFG->libdir.'/cronlib.php');
// Now get cli option.
[$options, $unrecognized] = cli_get_params(
[
'help' => false,
'stop' => false,
'list' => false,
'force' => false,
'enable' => false,
'disable' => false,
'disable-wait' => false,
'keep-alive' => null,
],
[
'h' => 'help',
's' => 'stop',
'l' => 'list',
'f' => 'force',
'e' => 'enable',
'd' => 'disable',
'w' => 'disable-wait',
'k' => 'keep-alive',
]
);
// now get cli options
list($options, $unrecognized) = cli_get_params(array('help'=>false),
array('h'=>'help'));
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
@@ -63,19 +45,10 @@ if ($unrecognized) {
if ($options['help']) {
$help =
"Execute periodic cron actions.
"Execute periodic cron actions.
Options:
-h, --help Print out this help
-s, --stop Notify all other running cron processes to stop after the current task
-l, --list Show the list of currently running tasks and how long they have been running
-f, --force Execute task even if cron is disabled
-e, --enable Enable cron
-d, --disable Disable cron
-w, --disable-wait=600 Disable cron and wait until all tasks finished or fail after N seconds (optional param)
-k, --keep-alive=N Keep this script alive for N seconds and poll for new tasks
The default value can be set by administrators in:
Site administration > Server > Tasks > Task processing > Keep alive time
-h, --help Print out this help
Example:
\$sudo -u www-data /usr/bin/php admin/cli/cron.php
@@ -85,102 +58,4 @@ Example:
die;
}
if ($options['stop']) {
// By clearing the caches this signals to other running processes
// to exit after finishing the current task.
\core\task\manager::clear_static_caches();
die;
}
if ($options['enable']) {
set_config('cron_enabled', 1);
mtrace('Cron has been enabled for the site.');
exit(0);
}
if ($options['disable']) {
set_config('cron_enabled', 0);
\core\task\manager::clear_static_caches();
mtrace('Cron has been disabled for the site.');
exit(0);
}
if ($options['list']) {
$tasks = \core\task\manager::get_running_tasks();
mtrace('The list of currently running tasks:');
$format = "%7s %-12s %-9s %-20s %-52s\n";
printf(
$format,
'PID',
'HOST',
'TYPE',
'TIME',
'CLASSNAME'
);
foreach ($tasks as $task) {
printf(
$format,
$task->pid,
substr($task->hostname, 0, 12),
$task->type,
format_time(time() - $task->timestarted),
substr($task->classname, 0, 52)
);
}
exit(0);
}
if ($wait = $options['disable-wait']) {
$started = time();
if (true === $wait) {
// Default waiting time.
$waitsec = 600;
} else {
$waitsec = $wait;
$wait = true;
}
set_config('cron_enabled', 0);
\core\task\manager::clear_static_caches();
mtrace('Cron has been disabled for the site.');
mtrace('Allocating ' . format_time($waitsec) . ' for the tasks to finish.');
$lastcount = 0;
while ($wait) {
$tasks = \core\task\manager::get_running_tasks();
if (count($tasks) == 0) {
mtrace('');
mtrace('All scheduled and adhoc tasks finished.');
exit(0);
}
if (time() - $started >= $waitsec) {
mtrace('');
mtrace('Wait time (' . format_time($waitsec) . ') elapsed, but ' . count($tasks) . ' task(s) still running.');
mtrace('Exiting with code 1.');
exit(1);
}
if (count($tasks) !== $lastcount) {
mtrace('');
mtrace(count($tasks) . " tasks currently running.", '');
$lastcount = count($tasks);
} else {
mtrace('.', '');
}
sleep(1);
}
}
if (!get_config('core', 'cron_enabled') && !$options['force']) {
mtrace('Cron is disabled. Use --force to override.');
exit(1);
}
\core\local\cli\shutdown::script_supports_graceful_exit();
$keepalive = $options['keep-alive'];
\core\cron::run_main_process($keepalive);
cron_run();
-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/>.
/**
* CLI script to reset dashboards.
*
* @package core
* @subpackage cli
* @copyright 2021 Brendan Heywood (brendan@catalyst-au.net)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/clilib.php');
require_once($CFG->dirroot.'/my/lib.php');
list($options, $unrecognized) = cli_get_params([
'help' => false,
'execute' => false,
], [
'h' => 'help',
'e' => 'execute',
]);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized), 2);
}
if (!$options['execute']) {
$help = <<<EOF
Resets Moodle dashboards for all users
Options:
-h, --help Print out this help
-e, --execute Actually run the reset
Example:
\$ sudo -u www-data /usr/bin/php admin/cli/dashboard_reset.php -e
EOF;
echo $help;
exit(0);
}
$progressbar = new progress_bar();
$progressbar->create();
my_reset_page_for_all_users(MY_PAGE_PRIVATE, 'my-index', $progressbar);
-123
View File
@@ -1,123 +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/>.
/**
* CLI script to delete a course.
*
* @package core
* @subpackage cli
* @author Mikhail Golenkov <mikhailgolenkov@catalyst-au.net>
* @copyright 2022 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__ . '/../../config.php');
require_once($CFG->libdir . '/clilib.php');
list($options, $unrecognized) = cli_get_params(
[
'courseid' => false,
'help' => false,
'showsql' => false,
'showdebugging' => false,
'disablerecyclebin' => false,
'non-interactive' => false,
], [
'c' => 'courseid',
'h' => 'help',
]
);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
if ($options['help'] || empty($options['courseid'])) {
$help = <<<EOT
CLI script to delete a course.
Options:
-h, --help Print out this help
--showsql Show sql queries before they are executed
--showdebugging Show developer level debugging information
--disablerecyclebin Skip backing up the course
--non-interactive No interactive questions or confirmations
-c, --courseid Course id to be deleted
Example:
\$sudo -u www-data /usr/bin/php admin/cli/delete_course.php --courseid=123456
\$sudo -u www-data /usr/bin/php admin/cli/delete_course.php --courseid=123456 --showdebugging
\$sudo -u www-data /usr/bin/php admin/cli/delete_course.php --courseid=123456 --disablerecyclebin
EOT;
echo $help;
die;
}
$interactive = empty($options['non-interactive']);
if ($options['showdebugging']) {
mtrace('Enabling debugging...');
set_debugging(DEBUG_DEVELOPER, true);
}
if ($options['showsql']) {
mtrace('Enabling SQL debugging...');
$DB->set_debug(true);
}
if (CLI_MAINTENANCE) {
cli_error('CLI maintenance mode active, CLI execution suspended');
}
if (moodle_needs_upgrading()) {
cli_error('Moodle upgrade pending, CLI execution suspended');
}
$course = $DB->get_record('course', array('id' => $options['courseid']));
if (empty($course)) {
cli_error('Course not found');
}
mtrace('Deleting course id ' . $course->id);
mtrace('Course name: ' . $course->fullname);
mtrace('Short name: ' . $course->shortname);
if ($interactive) {
mtrace('');
$input = cli_input('Are you sure you wish to delete this course? (y/N)', 'N', ['y', 'Y', 'n', 'N']);
if (strtolower($input) != 'y') {
exit(0);
}
}
if ($options['disablerecyclebin']) {
mtrace('Disabling recycle bin...');
$overrideconfig = ['tool_recyclebin' => ['coursebinenable' => false, 'categorybinenable' => false]];
$CFG->forced_plugin_settings = array_merge($CFG->forced_plugin_settings, $overrideconfig);
}
core_php_time_limit::raise();
delete_course($course);
mtrace('Updating course count in categories...');
fix_course_sortorder();
mtrace('Done!');
-209
View File
@@ -1,209 +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/>.
/**
* This script allows you to view and change the emailstop flag of any user.
*
* @package core
* @subpackage cli
* @copyright 2023 Stephan Robotta (stephan.robotta@bfh.ch)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/clilib.php');
// Define the input options.
$longparams = [
'email' => '',
'help' => false,
'id' => '',
'quiet' => false,
'stop' => '',
'username' => '',
];
$shortparams = [
'e' => 'email',
'h' => 'help',
'i' => 'id',
'q' => 'quiet',
's' => 'stop',
'u' => 'username',
];
// Define exit codes.
$exitsuccess = 0;
$exitunknownoption = 1;
$exitmissinguserarg = 2;
$exittoomanyuserarg = 3;
$exitnosearchargs = 4;
$exitnousersfound = 5;
$exitinvalidstopflag = 6;
$exiterrordb = 7;
// Now get cli options that are set by the caller.
list($options, $unrecognized) = cli_get_params($longparams, $shortparams);
$verbose = empty($options['quiet']);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
if ($verbose) {
cli_error(get_string('cliunknowoption', 'admin', $unrecognized), $exitunknownoption);
}
exit($exitunknownoption);
}
if ($options['help']) {
$help =
"Set/unset or show status of emailstop flag for a user, identified by username or email.
There are no security checks here because anybody who is able to
execute this file may execute any PHP too.
Options:
-h, --help Print out this help
-e, --email=email Specify user by email, separate many users by comma
-i, --id=id Specify user by id, separate many users by comma
-q, --quiet No output to stdout
-s, --stop=0|1|off|on Set new value for emailstop flag
-u, --username=username Specify user by username, separate many users by comma
Example:
\$sudo -u www-data /usr/bin/php admin/cli/emailstop.php --email=student1@example.com --stop=1
\$sudo -u www-data /usr/bin/php admin/cli/emailstop.php --email=student1@example.com,student2@example.com
\$sudo -u www-data /usr/bin/php admin/cli/emailstop.php --u=student1,student2 -s=on
";
echo $help;
exit($exitsuccess);
}
$cntempty = 0;
$cntfilled = 0;
$searchargs = [];
// Try to find out which option is used to fetch the users from. Also do sanitize etc.
foreach (['email', 'username', 'id'] as $option) {
if (empty($options[$option])) {
$cntempty++;
} else {
$cntfilled++;
$argname = $option;
// The search args must be: split by the comma, trimmed, and empty elements filtered out.
$searchargs = array_flip(array_filter(
array_map(
function ($item) {
return trim($item);
},
explode(',', $options[$option])
),
function ($item) {
return $item !== '';
}
));
}
}
if ($cntempty === 3) {
if ($verbose) {
cli_error('One of username, email, or id must be set.', $exitmissinguserarg);
}
exit($exitmissinguserarg);
}
if ($cntfilled > 1) {
if ($verbose) {
cli_error('Only one of email, username, or id can be set to identify a user.', $exittoomanyuserarg);
}
exit($exittoomanyuserarg);
}
if (empty($searchargs)) {
if ($verbose) {
cli_error('No values are provided for users.', $exitnosearchargs);
}
exit($exitnosearchargs);
}
try {
$users = $DB->get_records_list('user', $argname, array_keys($searchargs));
} catch (Exception $e) {
if ($verbose) {
cli_error("Could not fetch data from db by {$argname}: '{$options[$argname]}'.", $exiterrordb);
}
exit($exiterrordb);
}
if (empty($users)) {
if ($verbose) {
cli_error("Can not find any user by {$argname}: '{$options[$argname]}'.", $exitnousersfound);
}
exit($exitnousersfound);
}
// No stop flag set, then just print the user and the current emailstop flag state.
if ($options['stop'] === '') {
foreach ($users as $user) {
if ($verbose) {
echo 'user=' . $user->{$argname} . ' - emailstop=' . (int)$user->emailstop . PHP_EOL;
unset($searchargs[$user->{$argname}]);
}
}
if ($verbose) {
foreach (array_keys($searchargs) as $arg) {
echo 'user=' . $arg . ' - not found' . PHP_EOL;
}
}
exit($exitsuccess);
}
// Allowed values for the stop flag enabled are 1 and on, for disabled are 0 and off.
$validvalues = ['0', '1', 'off', 'on'];
$stopflag = strtolower($options['stop']);
if (!in_array($stopflag, $validvalues)) {
if ($verbose) {
cli_error('Value for the emailstop flag must be one of: ' . implode(', ', $validvalues) . '.', $exitinvalidstopflag);
}
exit($exitinvalidstopflag);
}
foreach ($validvalues as $value) {
if ($value === $stopflag) {
$stopflag = ($value === '1' || $value === 'on') ? 1 : 0;
break;
}
}
// Update each user with the stop flag to be set if it is necessary.
foreach ($users as $user) {
$line = 'Update user ' . $user->{$argname} . ($argname !== 'id' ? ' (' . $user->id . ')' : '') . ' - ';
if ((int)$user->emailstop !== $stopflag) {
$DB->set_field('user', 'emailstop', $stopflag, ['id' => $user->id]);
$line .= 'ok';
} else {
$line .= 'already done';
}
if ($verbose) {
echo $line . PHP_EOL;
unset($searchargs[$user->{$argname}]);
}
}
if ($verbose) {
foreach (array_keys($searchargs) as $arg) {
echo 'user=' . $arg . ' - not found' . PHP_EOL;
}
}
exit($exitsuccess);

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