Compare commits

..

424 Commits

Author SHA1 Message Date
Eloy Lafuente (stronk7) fb41a38153 Moodle release 2.5.1 2013-07-06 17:59:23 +02:00
Damyon Wiese 4c6f1ec52e weekly release 2.5+ 2013-07-04 15:34:24 +08:00
Damyon Wiese ef572665e9 MDL-40308: Fix javascript for lti mod_form
It was toggling the state of the hidden input - not the checkbox.
2013-07-04 13:47:23 +08:00
Eloy Lafuente (stronk7) 0383404678 MDL-40376 Fix copy/paste typo 2013-07-03 17:54:14 +02:00
Yuliya Bozhko a7c2742900 MDL-39986 badges: Show badge navigation to users with awardbadge capability
Also fixes an arror when a user with this capability tries to award a badge,
but their role is not among the criteria set up for a badge
2013-07-03 13:05:50 +08:00
Damyon Wiese fc83888891 MDL-39558 badges: Prevent backup of badges without user information 2013-07-03 10:54:31 +08:00
Yuliya Bozhko 18811be378 MDL-39558 badges: Add backup/restore of manually awarded badges 2013-07-03 10:13:05 +08:00
Yuliya Bozhko 56d54e1eed MDL-39558 badges: Add backup/restore to course badges 2013-07-03 10:12:46 +08:00
Michael Aherne 3a4a5356eb MDL-40275 core Improve documentation for block_base::has_config() 2013-07-02 21:52:59 +02:00
Eloy Lafuente (stronk7) 1a5986dab9 Merge branch 'MDL-40376-m25' of git://github.com/andrewnicols/moodle into MOODLE_25_STABLE 2013-07-02 21:12:10 +02:00
Eloy Lafuente (stronk7) c10fc915ad MDL-37516 Translate fieldnames on output only.
This reverts a change performed by the previous commit where user
fieldnames were being translated (and cached) when fetched,
causing the information to remain static later, not responding
to language switches (and perhaps leading to other problems if those
structures are used lated for DB / $USER comparisons.

With the commit, the translation happens on output, so no
potential interferences will happen and, also, the information
changes dynamically on lang switching.
2013-07-02 21:01:56 +02:00
sam marshall a6683d327f MDL-37516 Conditional activities: user field condition display fixes 2013-07-02 20:59:07 +02:00
Eloy Lafuente (stronk7) a9155c2875 Merge branch 'MDL-40246_25' of git://github.com/dmonllao/moodle into MOODLE_25_STABLE 2013-07-02 14:27:24 +02:00
Eloy Lafuente (stronk7) 4d52fbbc14 Merge branch 'MDL-40439-en_fix_25' of git://github.com/mudrd8mz/moodle into MOODLE_25_STABLE 2013-07-02 13:47:37 +02:00
Helen Foster bb07f7ecce MDL-40439 Merge English strings from the en_fix language pack 2013-07-02 11:47:49 +02:00
Dan Poltawski dee6316364 Merge branch 'MDL-40389_25' of git://github.com/stronk7/moodle into MOODLE_25_STABLE 2013-07-02 15:53:06 +08:00
Dan Poltawski f6b1260812 Merge branch 'MDL-40282.25' of github.com:colchambers/moodle into MOODLE_25_STABLE 2013-07-02 15:15:32 +08:00
Damyon Wiese 1e6919f6c8 Merge branch 'wip-MDL-40353-m25' of git://github.com/marinaglancy/moodle into MOODLE_25_STABLE 2013-07-02 14:29:08 +08:00
Dan Poltawski a852df6f11 Merge branch 'MDL-40297_25' of git://github.com/stronk7/moodle into MOODLE_25_STABLE 2013-07-02 13:43:45 +08:00
Andreas Grabs e235c97f09 MDL-39570 mod_feedback - Missing privilege check 2013-07-02 13:36:51 +08:00
Dan Poltawski d7e626156e Merge branch 'w27_MDL-40399_m25_msarrayshift' of https://github.com/skodak/moodle into MOODLE_25_STABLE 2013-07-02 13:33:59 +08:00
Dan Marsden 0bde9de161 MDL-39628 Chat: check mod/chat:chat capability when using daemon.
Thanks to Francois Gauthier for report/fix.
2013-07-02 12:18:24 +08:00
Jason Fowler dafe65e438 MDL-37818 - RSS - Prevent access to blocks while RSS context errors are displayed 2013-07-02 10:03:57 +08:00
Dan Poltawski 204383e50d Merge branch 'w27_MDL-40266_m25_arrayshift' of https://github.com/skodak/moodle into MOODLE_25_STABLE 2013-07-02 09:55:24 +08:00
Eloy Lafuente (stronk7) f1293083a2 Merge branch 'get_users_by_capability_fix_m25' of http://github.com/timgus/moodle into MOODLE_25_STABLE 2013-07-02 00:46:38 +02:00
Sam Hemelryk f0d974fb28 Merge branch 'MDL-39733_25' of git://github.com/stronk7/moodle into MOODLE_25_STABLE 2013-07-02 10:31:28 +12:00
Eloy Lafuente (stronk7) 16a2860ad2 MDL-39733 xhprof: implement import of runs 2013-07-02 00:27:30 +02:00
Rossiani Wijaya bb9024d9d4 MDL-39546 Lesson module: removed duplicate to decode special characters 2013-07-02 09:36:46 +12:00
Rossiani Wijaya a77f19bc47 MDL-39546 Lesson module: fixed matching question issues. 2013-07-02 09:36:32 +12:00
Eloy Lafuente (stronk7) 2bc8f21600 Merge branch 'w27_MDL-39754_m25_evn26' of https://github.com/skodak/moodle into MOODLE_25_STABLE 2013-07-01 23:14:05 +02:00
Eloy Lafuente (stronk7) 75b8d4a146 Merge branch 'MDL-40370_25' of git://github.com/timhunt/moodle into MOODLE_25_STABLE 2013-07-01 18:02:51 +02:00
Tim Hunt 33b4331eaf MDL-40370 qtype_shortanswer: unique index upgrade problem.
Eliminate duplicate rows before we try to add a unique index. We
arbitrarily keep the duplicate row with the lowest id.
2013-07-01 11:23:55 +01:00
Petr Škoda 00a2921469 MDL-39754 copy 2.5 requirements to Moodle 2.6
Optional features and warnings may be added later,
but we need at least required environment for 2.5.1

This includes database family cleanup and more realistic memory requirement.
2013-07-01 10:03:14 +02:00
Petr Škoda fc4523f8f3 MDL-40266 improve emulate_bound_params() for mssql driver
Looping over large numbers of items with array_shift() is expensive.
Reverse the array and fetch items from the top of the pile.

Credit goes to Martin Langhoff for original mysqli fix.
2013-07-01 08:52:51 +02:00
Petr Škoda 39714cb255 MDL-40266 improve emulate_bound_params() for sqlsrv driver
Looping over large numbers of items with array_shift() is expensive.
Reverse the array and fetch items from the top of the pile.

Credit goes to Martin Langhoff for original mysqli fix.
2013-07-01 08:52:51 +02:00
Mark Nielsen c11621b415 MDL-40308: Privacy settings do not change
Update LTI mod form so checkboxes always submit their value.
2013-07-01 11:27:43 +08:00
Dan Poltawski 7698803c95 Merge branch 'wip-MDL-40344-m25' of git://github.com/samhemelryk/moodle into MOODLE_25_STABLE 2013-07-01 11:22:05 +08:00
Sam Hemelryk ebc9b6d158 Merge branch 'MDL-40069_25' of git://github.com/timhunt/moodle into MOODLE_25_STABLE 2013-07-01 13:47:50 +12:00
Sam Hemelryk ae754bba07 NOBUG: fixed whitespace 2013-07-01 13:24:04 +12:00
Sam Hemelryk d20391916c Merge branch 'wip-MDL-39741-m25' of git://github.com/marinaglancy/moodle into MOODLE_25_STABLE
Conflicts:
	theme/bootstrapbase/style/moodle.css
2013-07-01 13:17:48 +12:00
Sam Hemelryk 9a561bc5a1 Merge branch 'MDL-35206-email-links_25' of git://github.com/mudrd8mz/moodle into MOODLE_25_STABLE 2013-07-01 13:08:15 +12:00
Eloy Lafuente (stronk7) 7af092c2d6 MDL-39733 profiling: UI for importing runs 2013-07-01 01:39:55 +02:00
Eloy Lafuente (stronk7) 706db16531 MDL-39733 profiling: UI for exporting runs 2013-07-01 01:39:50 +02:00
Eloy Lafuente (stronk7) e3c2cb4656 MDL-39733 xhprof: implement export of runs 2013-07-01 01:39:45 +02:00
Andrew Nicols 2738bb2c2f MDL-40376 Themes: Remove padding from course/manage page 2013-06-30 19:28:41 +01:00
Martin Langhoff c17768973e MDL-40266 improve emulate_bound_params() for mysqli
Looping over large numbers of items with array_shift() is expensive.
Reverse the array and fetch items from the top of the pile.
2013-06-30 18:39:53 +02:00
Eloy Lafuente (stronk7) 11ffc395f4 MDL-40389 Test "parent" course/category generators
Verify that the category->parent switchers for
course & category behat generators are happening
as expected. Use the manage courses page for that.
2013-06-30 18:10:25 +02:00
Eloy Lafuente (stronk7) 1608fbe7c0 MDL-40297 whitespace, comments & var names cleanup 2013-06-30 16:48:44 +02:00
Dmytro Vorona 0927af1e8d MDL-40297 Hide empty categories in my courses
This is a followup of MDL-38631 were was detected that
whith categories being displayed in my courses, some of
them, not having courses for the user, were still shown.

With this patch, any category in the tree, not having courses
is automatically hidden, any depth.
2013-06-30 16:48:39 +02:00
Tim Hunt 77504426de MDL-40069 question/type/upgrade.txt editing changes
Try to more clearly document what question type authors need to do to
upgrade their question types.
2013-06-28 18:21:04 +01:00
Colin Chambers 2fe9be7a9e MDL-40069 Document editing form changes 2013-06-28 18:21:02 +01:00
David Mudrák 52d485c435 MDL-35206 Fix the links list enumeration in the html2text library 2013-06-28 13:13:29 +02:00
Colin Chambers 93a2a0e3d7 MDL-40282 Collapsible editor loses border in IE8-9 2013-06-28 11:23:47 +01:00
Mary Evans dc59a1b1f9 MDL-40131 theme_bootstrapbase: added missing grader table css to less/moodle/grade.less 2013-06-28 02:45:29 +01:00
Sam Hemelryk 21d9cb723b MDL-40344 navigation: expandable branch id is now properly unique 2013-06-28 09:59:09 +12:00
Eloy Lafuente (stronk7) 32e550a6eb weekly release 2.5+ 2013-06-27 20:12:40 +02:00
Eloy Lafuente (stronk7) 97a7b6a7aa Merge branch 'install_25_STABLE' of git://git.moodle.cz/moodle-install into MOODLE_25_STABLE 2013-06-27 20:05:10 +02:00
Marina Glancy 3c4c23876c MDL-39741 removed unused css classes 2013-06-27 22:18:59 +10:00
David Monllao ff46f98998 MDL-40246 mod_forum: Changing scenario to non-JS
Also replacing hardcoded waits for a
wait to be redirected.
2013-06-27 15:13:12 +08:00
Marina Glancy 00b4836197 MDL-40353 fixed wrong redirection on course group page 2013-06-27 13:45:16 +10:00
Marina Glancy 4af378d743 MDL-39741 Use CSS class dimmed_text instead of hidden in grader report 2013-06-27 12:36:11 +10:00
AMOS bot fab0986a5b Automatically generated installer lang files 2013-06-27 00:40:24 +00:00
Eloy Lafuente (stronk7) 866ab6e1ce Merge branch 'w26_MDL-39701_m25_filterupgrade' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-26 12:30:51 +02:00
Petr Škoda 0b675e0ca2 MDL-39701 prevent string filtering before finishing 2.5 upgrade 2013-06-26 09:51:03 +02:00
Eloy Lafuente (stronk7) 3166c21471 Merge branch 'MDL-40033_25' of git://github.com/dmonllao/moodle into MOODLE_25_STABLE 2013-06-25 12:08:25 +02:00
Gareth J Barnard 2ab1b3de30 MDL-40204 theme_bootstrapbase & theme_clean: Fix RTL 3 column lyt with 1 blk. 2013-06-25 11:38:27 +12:00
Sam Hemelryk 6e07abb32d Merge branch 'wip-MDL-40065-m25_4' of git://github.com/gjb2048/moodle into MOODLE_25_STABLE 2013-06-25 09:58:44 +12:00
Sam Hemelryk bc0227d3e5 Merge branch 'w26_MDL-40289_m25_badgescaps' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-25 09:13:05 +12:00
Eloy Lafuente (stronk7) e9d80e664b Merge branch 'w26_MDL-40233_m25_frontset' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-24 21:29:13 +02:00
Eloy Lafuente (stronk7) ffc480afbd Merge branch 'MDL-40264-m25-essay-zero' of git://github.com/ktemkin/moodle into MOODLE_25_STABLE 2013-06-24 21:23:43 +02:00
Eloy Lafuente (stronk7) 3d4a0f2887 Merge branch 'w26_MDL-40254_m25_flatfile' of https://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-24 21:13:06 +02:00
Eloy Lafuente (stronk7) 9067595bf3 Merge branch 'MDL-34610_25' of git://github.com/timhunt/moodle into MOODLE_25_STABLE 2013-06-24 20:21:30 +02:00
Eloy Lafuente (stronk7) 94a541f7ff Merge branch 'MDL-40255_M25' of git://github.com/lazydaisy/moodle into MOODLE_25_STABLE 2013-06-24 19:26:22 +02:00
Eloy Lafuente (stronk7) 3e6f11b240 Merge branch 'm25_MDL-40200_Notices_When_Viewing_Profile_Invalid_UserId' of https://github.com/scara/moodle into MOODLE_25_STABLE 2013-06-24 19:00:43 +02:00
Sam Chaffee 7991637057 MDL-40258: Fixed invalid json string error in file-picker 2013-06-24 18:56:54 +02:00
Ankit Agarwal 4f61afa0b7 MDL-40250 phpunit: Add unit tests for core_files_external::upload() 2013-06-24 18:06:26 +02:00
Gareth J Barnard fb42a8c761 MDL-40065 theme_bootstrapbase theme_clean: Simplify column lyts / fix blocks. 2013-06-24 16:59:11 +01:00
Sam Hemelryk 4418496864 Merge branch 'wip-MDL-39044-stable25' of git://github.com/phalacee/moodle into MOODLE_25_STABLE 2013-06-24 15:41:24 +12:00
Sam Hemelryk 82125f7a78 Merge branch 'w26_MDL-39701_m25_filterupgrade' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-24 15:07:14 +12:00
Sam Hemelryk 9e5adb9305 Merge branch 'MDL-39779_m25' of git://github.com/rwijaya/moodle into MOODLE_25_STABLE 2013-06-24 14:33:04 +12:00
Sam Hemelryk 23dd6b7ebf Merge branch 'MDL-39760-MOODLE_25_STABLE' of https://github.com/lucisgit/moodle into MOODLE_25_STABLE 2013-06-24 14:29:56 +12:00
Frederic Massart af4bdb8c8d MDL-39280 form: Multiple select support disabledIf with multiple values 2013-06-24 14:20:33 +12:00
Sam Hemelryk 12a9b4ec3a Merge branch 'mdl25-MDL-40130-let-course-category-caching-be-used' of git://github.com/brki/moodle into MOODLE_25_STABLE 2013-06-24 11:43:54 +12:00
Sam Hemelryk 084b971bc8 MDL-38347 fixed whitespace 2013-06-24 11:29:26 +12:00
Jason Fowler df7662560b MDL-38347 - Calendar - Fixing arrow output on Android devices 2013-06-24 11:22:51 +12:00
Sam Hemelryk 405a60712b Merge branch 'MDL-28124_M25' of git://github.com/lazydaisy/moodle into MOODLE_25_STABLE 2013-06-24 10:30:08 +12:00
Amanda Doughty bce7b6d40e MDL-38376 mod_assign: Feedback displayed when Grade set to hidden 2013-06-24 10:22:34 +12:00
Petr Škoda 55384d2016 MDL-40254 ignore flatfile records with no user idnumber 2013-06-23 21:38:39 +02:00
Petr Škoda f8bb8999e8 MDL-40289 fix block_badges capability defaults 2013-06-23 12:34:02 +02:00
Petr Škoda 2fbf721f27 MDL-40289 fix all badges capabilities
1/ contextlevel must be the lowest level where capability can be overriden
2/ coursecreator is for course creation only!!!
3/ do not mix user with other archetypes
4/ config risk is for admin actions only in standard install

No upgrade cleanup, sorry, blame integrators...
2013-06-23 12:23:50 +02:00
Mary Evans 233edd4dde MDL-40255 theme_boxxie/theme_formfactor: Added block-region class to layout files to enable dnd file uploader to work. 2013-06-22 21:24:23 +01:00
Petr Škoda 75f1afeda0 MDL-39701 prevent filters before finishing 2.5 upgrade
This prevents problems with unsigned integers in MySQL 5.5
2013-06-22 22:23:25 +02:00
Petr Škoda 808043d356 MDL-40233 add proper cache invalidation on frontpage setting save 2013-06-22 22:19:27 +02:00
Matteo Scaramuccia da0c3ea381 MDL-40200 Administration: Return the invalid user message and stop there
To avoid adding duplicate code, 'invalid user' and 'user deleted' checks
have been merged together.
2013-06-22 10:00:59 +02:00
Marina Glancy a028ac4aaf MDL-38631 link categories in My courses only when needed 2013-06-22 16:25:43 +10:00
Dmytro Vorona 02e1c8ad20 MDL-38631 Fixed faulty add_category call, introduced in 758103 2013-06-22 16:25:42 +10:00
Tim Hunt 3174f09839 MDL-34610 remove legacy IE CSS. It breaks form layout 2013-06-21 21:57:37 +01:00
Brian King 1a703d311e MDL-40130 Caching: let make_categories_list use the cache it makes 2013-06-21 11:14:08 +02:00
David Monllao 922a5cf370 MDL-40033 behat: Waiting until file manager is available 2013-06-21 15:36:41 +08:00
Dan Poltawski e978b66009 weekly release 2.5+ 2013-06-21 13:30:52 +08:00
Dan Poltawski 8e29fe2d72 Merge branch 'install_25_STABLE' of git://git.moodle.cz/moodle-install into MOODLE_25_STABLE 2013-06-21 13:29:46 +08:00
Kyle Temkin 11f6680dee MDL-40264 fix essay handling of zero-string response
Fixes an inappropriate behavior of the is_complete_response() function
for the Essay question type, which caused the string "0" to be
inappropriately marked as an incomplete response.

Also adds a set of unit tests for the Essay question type's
is_complete_response.
2013-06-20 19:12:21 -04:00
Tim Gus 0bad576425 MDL-40259 - For SCORM reports limit fields returned from
get_users_by_capability to user ID to decrease memory utilization
2013-06-20 15:56:07 -04:00
Jason Fowler 1620d20165 MDL-39044 - Course Completion - Replaceing strtolower with multibyte safe textlib::strtolower 2013-06-20 08:49:38 +08:00
Ruslan Kabalin e2d5b6cbc8 MDL-39760: mod_assign: Add few more callback methods.
Adds lock, unlock and release_to_draft. All of them accept single parameter -
an assign_submission record.
2013-06-19 15:34:02 +01:00
Yuliya Bozhko e787333622 MDL-40022 badges: Fix missing calls to setType in course criteria settings 2013-06-19 13:41:49 +08:00
David Monllao 4ab623b9b6 MDL-40109 behat: Fixing backup feature 2013-06-19 09:14:05 +08:00
Marina Glancy 8526be5612 Merge branch 'MDL-40207-25' of git://github.com/danpoltawski/moodle into MOODLE_25_STABLE 2013-06-18 15:38:20 +10:00
Ruslan Kabalin eede0dbd7b MDL-33546: course: Fix current week hightlighting after dragging the section 2013-06-18 13:02:27 +10:00
Marina Glancy 4d7c173514 Merge branch 'MDL-40165-m25' of git://github.com/andrewnicols/moodle into MOODLE_25_STABLE 2013-06-18 12:57:06 +10:00
Sam Hemelryk 70d4b3182d Merge branch 'MDL-39634_25' of git://github.com/dmonllao/moodle into MOODLE_25_STABLE 2013-06-18 14:50:47 +12:00
Nadav Kavalerchik 32a3dc0ec0 MDL-40180 - RTL/Clean theme: Misalignment of student name and profile image in grader (fixed by Mary Evans)
Conflicts:
	theme/bootstrapbase/style/moodle.css
2013-06-18 10:43:48 +08:00
Sam Hemelryk 663c69e974 Merge branch 'MDL-40036-25' of git://github.com/FMCorz/moodle into MOODLE_25_STABLE 2013-06-18 13:59:30 +12:00
Sam Hemelryk eaf4854359 Merge branch 'MDL-39865-25' of git://github.com/FMCorz/moodle into MOODLE_25_STABLE 2013-06-18 13:54:56 +12:00
Marina Glancy 04597742cf Merge branch 'MDL-38079_25' of git://github.com/mr-russ/moodle into MOODLE_25_STABLE 2013-06-18 11:13:26 +10:00
Eloy Lafuente (stronk7) f68a2c50e6 MDL-40137 Whitespace fix 2013-06-18 01:47:44 +02:00
Eloy Lafuente (stronk7) 5e069cbd96 Merge branch 'wip-MDL-40137-m25' of git://github.com/samhemelryk/moodle into MOODLE_25_STABLE 2013-06-18 01:45:27 +02:00
Sam Hemelryk 3d19b183c6 Merge branch 'wip-mdl-39863-m25' of git://github.com/rajeshtaneja/moodle into MOODLE_25_STABLE 2013-06-18 11:19:30 +12:00
Sam Hemelryk d81ebc8f0d Merge branch 'MDL-40109_25' of git://github.com/dmonllao/moodle into MOODLE_25_STABLE 2013-06-18 11:07:57 +12:00
Eloy Lafuente (stronk7) 5cc26839e7 Merge branch 'w25_MDL-40210_m25_badgescaps' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-18 00:56:11 +02:00
Eloy Lafuente (stronk7) a831f154ae Merge branch 'MDL-33562-25' of git://github.com/danpoltawski/moodle into MOODLE_25_STABLE
Conflicts:
	lib/db/upgrade.php
	version.php
2013-06-18 00:44:17 +02:00
Sam Hemelryk 54c7d518f3 Merge branch 'MDL-30132_M25' of git://github.com/lazydaisy/moodle into MOODLE_25_STABLE 2013-06-18 10:26:53 +12:00
Petr Škoda ec18ffb86c MDL-40210 do not use CAP_PREVENT for role defaults! 2013-06-17 12:13:20 +02:00
Russell Smith 842aac5ecb MDL-38079 mod_feedback - Hide "add question to activity" button 2013-06-17 19:59:37 +10:00
David Monllao dc24efe971 MDL-40110 behat: AM/PM strings are OS-dependant, removing this part from tests 2013-06-17 17:06:38 +08:00
Dan Poltawski 160e432aee Merge branch 'MDL-39825_master' of https://github.com/totara/openbadges into MOODLE_25_STABLE 2013-06-17 16:41:15 +08:00
Dan Poltawski e38c37939a Merge branch 'wip-MDL-40136-m25' of git://github.com/samhemelryk/moodle into MOODLE_25_STABLE 2013-06-17 13:37:23 +08:00
Sam Hemelryk e15bf9b1e5 Merge branch 'm25_MDL-40095_Completion_locked_Error_on_save' of https://github.com/scara/moodle into MOODLE_25_STABLE 2013-06-17 16:10:38 +12:00
Sam Hemelryk 1d1b52eb0c Merge branch 'MDL-21933_25' of https://github.com/mr-russ/moodle into MOODLE_25_STABLE 2013-06-17 15:45:33 +12:00
Sam Hemelryk 209658eab2 Merge branch 'wip-MDL-40091-MOODLE_25_STABLE' of git://github.com/satrun77/moodle into MOODLE_25_STABLE 2013-06-17 15:43:18 +12:00
Rossiani Wijaya 49a60b6c65 MDL-39779 wiki module: fix the alignment for required label to display inline on mobile devices 2013-06-17 11:42:47 +08:00
Sam Hemelryk 8c155823cc Merge branch 'MDL-39369_m25' of git://github.com/rwijaya/moodle into MOODLE_25_STABLE 2013-06-17 15:10:21 +12:00
Dan Poltawski 3c4fcce769 MDL-40207 simplepie: reduce false failures in unit tests
The simplepie tests time out too quickly due to a low connect timeout,
to fix this, I converted the unit tests to use a high timeout value.
2013-06-17 10:46:53 +08:00
Sam Hemelryk 443563a82e Merge branch 'MDL-39761-MOODLE_25_STABLE' of https://github.com/lucisgit/moodle into MOODLE_25_STABLE 2013-06-17 14:26:37 +12:00
David Monllao 346765035a MDL-39634 behat: Adding debugging() messages to deprecated methods 2013-06-17 09:02:39 +08:00
David Monllao 944fd1ca83 MDL-39634 behat: Adding deprecated methods
To keep compatibility if someone is
already using these methods.
2013-06-17 09:01:38 +08:00
David Monllao ded65ca8e0 MDL-39634 behat: Removing unused arguments 2013-06-17 08:59:59 +08:00
AMOS bot 2bf9edf84f Automatically generated installer lang files 2013-06-17 00:43:36 +00:00
Marina Glancy 29a4508930 Merge branch 'wip-MDL-40142-m25' of git://github.com/samhemelryk/moodle into MOODLE_25_STABLE 2013-06-17 10:40:39 +10:00
Sam Hemelryk bda2a7bdf5 MDL-39949 dock: fixed whitespace 2013-06-17 10:24:53 +12:00
Jason Fowler 64ddb28dfd MDL-39949 - Blocks - preventing blocks with empty titles from blocking the execution of javascript 2013-06-17 10:01:44 +12:00
Sam Hemelryk 6fa5e8658a MDL-40137 theme_clean: corrected lib.php function names 2013-06-17 09:39:20 +12:00
Sam Hemelryk 43dc7aa976 MDL-40136 cache: fixed dirroot change bugs with plugin caches 2013-06-17 09:23:47 +12:00
Sam Hemelryk 5d9349b61a MDL-40142 navigation: fixed chainable issue with navigation JS 2013-06-17 09:12:43 +12:00
Mary Evans ff24397ff3 MDL-28124 admin/tool/customlang: Changed page layout from 'standard' to 'report'. 2013-06-14 23:27:34 +01:00
Mohamed Alsharaf de5568b18e MDL-40091 book: revision must be incremented after editing a chapter 2013-06-14 21:28:31 +12:00
Marina Glancy d3cf194050 weekly release 2.5+ 2013-06-14 13:53:28 +10:00
Marina Glancy 37d38a3125 Merge branch 'install_25_STABLE' of git://git.moodle.cz/moodle-install into MOODLE_25_STABLE 2013-06-14 13:50:50 +10:00
Andrew Nicols 2804eb29c6 MDL-40165 Backup: Split out test classes into multiple files 2013-06-13 08:57:24 +01:00
Rossiani Wijaya e85e3bff3e MDL-39369: Lesson module: change settype PARAM_TEXT to PARAM_NOTAGS 2013-06-13 15:37:45 +08:00
Rossiani Wijaya 51c72ecaa5 MDL-39369: Lesson module: fix setType() properly 2013-06-13 15:36:16 +08:00
Dan Poltawski 30703dc8f2 MDL-39824: theme_bootstrap: initalise array before use
This causes warnings when no navigation items are returned.

Also fixed some bad whitespace.
2013-06-13 14:23:55 +08:00
Dan Poltawski 33beb96847 Revert "MDL-39832 Files: Revert MDL-39688 commit, 161be9206571787557eb5c11ee440c9e6ae93ca8"
This reverts commit d618e3c07d.
2013-06-13 13:57:02 +08:00
Dan Poltawski 7f742cece1 Revert "MDL-39832 Files: Fixed ETag format according with RFC2616."
This reverts commit 52286ac358.
2013-06-13 13:57:00 +08:00
Russell Smith ab0e64524d MDL-21933 course: Allow users to search participants course permission
Capability checks are performed at the top of the page and do not need
to be completed again.

Search is an expected feature for users and it was confusion to only
appear after 3 pages.  This has been changed to enable search when
there are more records than are visible on the screen.  If users
are unable to use browser search, they will be able to use Moodle's
search.
2013-06-13 12:43:29 +10:00
AMOS bot 2a93e8d72c Automatically generated installer lang files 2013-06-13 00:41:25 +00:00
Mary Evans 6780a2301e MDL-30312 User: Changed page layout from mydashboard to mypublic in user/profile.php 2013-06-12 21:33:28 +01:00
sam marshall 3a2e904098 MDL-39472 MUC: Optimise cache::get_from_persist_cache 2013-06-12 14:29:48 +08:00
Dan Poltawski 6117c578e4 Merge branch 'MDL-39773_b_m25' of git://github.com/rwijaya/moodle into MOODLE_25_STABLE
Conflicts:
	theme/bootstrapbase/style/moodle.css
2013-06-12 09:34:04 +08:00
Sam Hemelryk 69303857c7 Merge branch 'MDL-39940-25' of git://github.com/danpoltawski/moodle into MOODLE_25_STABLE 2013-06-12 09:06:33 +12:00
Sam Hemelryk a4848b4044 Merge branch 'MDL-39964-25' of git://github.com/danpoltawski/moodle into MOODLE_25_STABLE 2013-06-12 09:02:40 +12:00
Sam Hemelryk a6b980d13b Merge branch 'MDL-25901-25' of git://github.com/danpoltawski/moodle into MOODLE_25_STABLE 2013-06-12 09:00:11 +12:00
Sam Hemelryk 9e53bbd007 Merge branch 'w24_MDL-38109_m25_pluginlist' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-12 08:53:49 +12:00
Sam Hemelryk e0699a7154 Merge branch 'wip-MDL-39798_M25' of git://github.com/gjb2048/moodle into MOODLE_25_STABLE
Conflicts:
	theme/bootstrapbase/style/moodle.css
2013-06-12 08:51:21 +12:00
Sam Hemelryk aa41f96d1a Merge branch 'wip-MDL-39820_M25_2' of git://github.com/gjb2048/moodle into MOODLE_25_STABLE
Conflicts:
	theme/bootstrapbase/style/moodle.css
2013-06-12 08:46:45 +12:00
Petr Škoda e4fcb52f44 MDL-38109 use always get_plugin_list() to get list of plugins
get_list_of_plugins() is NOT intended for real plugins, only plugin like directories.
2013-06-11 10:23:14 +02:00
Dan Poltawski bacf4c5c71 Merge branch 'wip-MDL-40087_M25' of git://github.com/gjb2048/moodle into MOODLE_25_STABLE
Conflicts:
	theme/bootstrapbase/style/moodle.css
2013-06-11 16:00:51 +08:00
Dan Poltawski 43c5a7d5ad Merge branch 'wip-MDL-40100-m25' of git://github.com/samhemelryk/moodle into MOODLE_25_STABLE 2013-06-11 15:35:31 +08:00
Sam Hemelryk 624f270a26 Merge branch 'MDL-26925-25' of git://github.com/danpoltawski/moodle into MOODLE_25_STABLE 2013-06-11 17:31:36 +12:00
Sam Hemelryk 313bcd5268 Merge branch 'MDL-22390_25' of git://github.com/timhunt/moodle into MOODLE_25_STABLE 2013-06-11 17:07:24 +12:00
Mark Nelson 5a21d336f3 MDL-40010 notes: passing an object rather than an id to avoid debugging message 2013-06-11 13:05:40 +08:00
Dan Poltawski f60ad3fdff Merge branch 'm25_MDL-40002_ETag_value_must_be_a_quoted-string' of https://github.com/scara/moodle into MOODLE_25_STABLE 2013-06-11 12:54:38 +08:00
Jason Fowler 2daa9998ae MDL-39746 - Themes - Hiding the user picture on the forum page when the screensize is too small to allow for it
Conflicts:
	theme/bootstrapbase/style/moodle.css
2013-06-11 11:25:04 +08:00
Rajesh Taneja 6079448098 MDL-39863 Administration: Show confirmation/notice and don't redirect automatically 2013-06-11 10:24:51 +08:00
Tim Lock 992c0ec50d MDL-40120: Fix get_course_records() which breaks in PSQL 2013-06-11 13:47:05 +12:00
sam marshall aca67ae8b0 MDL-39454 Accesslib: is_siteadmin is a bit slow 2013-06-11 12:27:09 +12:00
Matteo Scaramuccia 474c13f27f MDL-40095 SCORM: Trivial comments tidy up 2013-06-10 20:52:24 +02:00
Matteo Scaramuccia 7480e99306 MDL-40095 SCORM: Fixed DML error when saving with completion options locked
Regression caused by MDL-38315.
2013-06-10 20:52:14 +02:00
Dan Poltawski 96241e57e9 Merge branch 'MDL-39947_25' of https://github.com/aolley/moodle into MOODLE_25_STABLE 2013-06-10 16:20:30 +08:00
Dan Poltawski ade2afc29f Merge branch 'MDL-39338_M25' of git://github.com/lazydaisy/moodle into MOODLE_25_STABLE
Conflicts:
	theme/bootstrapbase/style/moodle.css
2013-06-10 16:07:28 +08:00
Dan Poltawski 534f1476f4 Merge branch 'm25_MDL-39832_Fix_Chrome_Issues_ETag_XSendfile' of https://github.com/scara/moodle into MOODLE_25_STABLE 2013-06-10 15:42:53 +08:00
Rajesh Taneja b625e48b87 MDL-39639 Formslib: Hide calendar for datetime element on disable event 2013-06-10 15:29:16 +08:00
Dan Poltawski a327e87866 Merge branch 'MDL-40018_M25' of git://github.com/lazydaisy/moodle into MOODLE_25_STABLE 2013-06-10 15:00:20 +08:00
Dan Poltawski 0f74c82897 Merge branch 'MDL-40013_25stable' of git://github.com/oasychev/moodle into MOODLE_25_STABLE 2013-06-10 14:58:12 +08:00
David Mudrák 3ee63c14e0 MDL-40080 Remove unused strings from the core_hub subsystem 2013-06-10 14:41:53 +08:00
Mark Nelson f31670ef40 MDL-40008 forms: fixed ordering of the calendar icon and enable checkbox for the date selector element 2013-06-10 14:25:14 +08:00
David Monllao b1f151c933 MDL-40109 format_weeks: Removing extra space 2013-06-10 13:31:22 +08:00
Dan Poltawski 7f9f03442a Merge branch 'w24_MDL-39096_m25_obfuscate' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-10 13:12:16 +08:00
Dan Poltawski 9d1a909f0e Merge branch 'w24_MDL-28394_m25_enrolrestore' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-10 12:17:04 +08:00
Yuliya Bozhko 8b7c33ae77 MDL-39825 badges: Remove badges from course navigation when course badges are disabled 2013-06-10 16:16:59 +12:00
Dan Poltawski c9d1c4993d Merge branch 'm25_MDL-37968' of git://github.com/danmarsden/moodle into MOODLE_25_STABLE 2013-06-10 12:09:59 +08:00
Mary Evans 7b9caeb92b MDL-39016 theme_bootstrapbase: Added login info and home link to footer in both boostrapbase & clean layout/general.php 2013-06-10 16:04:22 +12:00
Mark Nelson 49acbbad2f MDL-40009 notes: corrected the form element names passed to setType 2013-06-10 15:55:53 +12:00
Sam Hemelryk afe247a0ca Merge branch 'w24_MDL-39549_m25_cohortsearch' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-10 15:48:11 +12:00
Sam Hemelryk f7c2942961 Merge branch 'MDL-39971_25' of git://github.com/timhunt/moodle into MOODLE_25_STABLE 2013-06-10 15:09:34 +12:00
Dan Poltawski 6f4232974e Merge branch 'wip-MDL-39824-m25' of http://github.com/samhemelryk/moodle into MOODLE_25_STABLE 2013-06-10 10:54:27 +08:00
Sam Hemelryk f981643f58 Merge branch 'MDL-39978_25' of git://github.com/timhunt/moodle into MOODLE_25_STABLE 2013-06-10 14:44:40 +12:00
Sam Hemelryk 2f8602990f Merge branch 'w24_MDL-32480_m25_imscp' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-10 14:41:07 +12:00
Sam Hemelryk 7174920aaa MDL-39824 theme: tweaking upgrade note version 2013-06-10 14:34:25 +12:00
Jason Fowler 57ecff6356 MDL-39608 - Bootstrap - Chaning the thresholds and width values for the Form labels
Conflicts:
	theme/bootstrapbase/style/moodle.css
2013-06-10 14:28:54 +12:00
AMOS bot 2d69af043d Automatically generated installer lang files 2013-06-10 00:39:09 +00:00
Andrew Davis a1d43d9e4f MDL-36090 core_grade: added logging of overriding of student grades 2013-06-10 12:35:03 +12:00
Sam Hemelryk 08c2a3ca1f Merge branch 'wip-MDL-40088-m25' of git://github.com/marinaglancy/moodle into MOODLE_25_STABLE 2013-06-10 12:14:04 +12:00
Jason Fowler fbc635e400 MDL-39227 - SCORM - repositioning the navigation panel to be visible in all themes at all resolutions 2013-06-10 11:59:36 +12:00
Sam Hemelryk 00afef17bb Merge branch 'MDL-39979-25' of git://github.com/FMCorz/moodle into MOODLE_25_STABLE 2013-06-10 11:33:59 +12:00
Sam Hemelryk c174a19207 MDL-40100 cache: removed unneeded includes from add instance forms 2013-06-10 10:26:27 +12:00
Sam Hemelryk d89fbe0285 MDL-39871 themes: can now specify regions to swap when using rtl langs
Conflicts:
	lib/pagelib.php
2013-06-10 09:31:05 +12:00
Sam Hemelryk ddf1b0741d MDL-39824 theme: changes recorded in theme/upgrade.txt 2013-06-10 09:29:58 +12:00
Sam Hemelryk dac7c261ca MDL-39824 output: fixed bug when moving blocks with rtl lang 2013-06-10 09:29:58 +12:00
Sam Hemelryk 8504444307 MDL-40089 theme_clean: simplified layouts and converted to new OUTPUT API 2013-06-10 09:29:58 +12:00
Sam Hemelryk 602255df1a MDL-40089 theme_clean: moved general to columns3.php as prep 2013-06-10 09:29:58 +12:00
Sam Hemelryk ee7a52ae91 MDL-39842 js: fixed block drag and drop 2013-06-10 09:29:58 +12:00
Sam Hemelryk 6964e6a580 MDL-39824 theme_bootstrapbase: converted $PAGE use to new $OUTPUT methods. 2013-06-10 09:29:58 +12:00
Sam Hemelryk 5c8c94eabe MDL-40089 theme_bootstrapbase: moved general to columns3.php as prep 2013-06-10 09:29:57 +12:00
Sam Hemelryk 00f906b114 MDL-39824 output: tidied up output before bootstrap cleaning
Conflicts:
	lib/pagelib.php
2013-06-10 09:29:03 +12:00
Sam Hemelryk 2d28b03d90 MDL-39839 renderer: new methods for calls appearing in theme layouts 2013-06-10 09:28:24 +12:00
Sam Hemelryk 03fa7a2ba3 MDL-39838 renderer: new methods for body classes and id 2013-06-10 09:28:24 +12:00
Sam Hemelryk cd79352fbd MDL-39841 renderer: new method to renderer blocks with predictable structure 2013-06-10 09:28:24 +12:00
Matteo Scaramuccia f082c5e390 MDL-40002 Files: ETag value must be a quoted-string.
ETag value must be double quoted, http://tools.ietf.org/html/rfc2616#section-3.11.
More details about potential impacts in MDL-39832: shortly, at least when using
HTTP Partial GET Requests (HTTP Status: 206) plus X-Sendfile HTTP Header.
In this commit, the impact is to be RFC2616 compliant when using the ETag Header.
2013-06-09 10:14:02 +02:00
Tim Hunt 266ceb16a6 MDL-22390 autolink filter: handle URLs in brackets better.
This breaks some legitimate URLs like
http://en.wikipedia.org/wiki/Slash_(punctuation).
This is a necessary trade-off. Many other web systems do not handle that
case correctly either. The work-around it so escape the ) as %29.

This commit also improves the way the unit tests for this work.

It also fixes a couple of other tricky cases that were spotted in
the forums while this was being discussed. See the new test cases.
2013-06-08 17:28:53 +01:00
Gareth J Barnard 35b76a3507 MDL-39798 theme_bootstrapbase: Improve layout for course participation reports. 2013-06-08 13:55:22 +01:00
Petr Škoda 06de509918 MDL-39096 fix obfuscate_text() utf-8 compatibility 2013-06-07 22:43:26 +02:00
Petr Škoda 788e1924a2 MDL-39549 fix cohort search in course categories 2013-06-07 22:07:53 +02:00
Gareth J Barnard 9bc4a4cc79 MDL-40087 theme_bootstrapbase: Turn editing on button misaligned on nav bar. 2013-06-07 18:15:34 +01:00
Petr Škoda d98b9a945e MDL-28394 fix enrolment method restore logic
This patch fixes:
* missing enrolment methods after 1.9 backup restore - defaults are added
* enrolment methods are restored only when users option is enabled
* manual enrolment override option is disabled if users not available
2013-06-07 10:03:25 +02:00
Petr Škoda 4ab6c6fbc0 MDL-32480 allow external https links in IMSCP too 2013-06-07 09:58:54 +02:00
Nicolas Dunand a2ec961309 MDL-32480 fix display of IMS content packages with external links 2013-06-07 09:58:53 +02:00
Marina Glancy 86882b6dfe MDL-40088 fixed bug with not visible course section 2013-06-07 13:58:57 +10:00
Dan Marsden ff3a54a5f7 MDL-37968 course_overview: add dim class to hidden courses for teachers. 2013-06-07 14:39:29 +12:00
Mary Evans 82887b29e8 MDL-39338 core_question: Make history box a .well 2013-06-06 22:57:09 +01:00
Matteo Scaramuccia 52286ac358 MDL-39832 Files: Fixed ETag format according with RFC2616.
ETag must be double quoted,
http://tools.ietf.org/html/rfc2616#section-3.11:

      entity-tag = [ weak ] opaque-tag
      weak       = "W/"
      opaque-tag = quoted-string
2013-06-06 23:11:15 +02:00
Matteo Scaramuccia d618e3c07d MDL-39832 Files: Revert MDL-39688 commit, 161be92065
MDL-39688 has been successful in providing a solution for the issue but further
investigations done in MDL-39832 have got the evidence that the given solution
is more likely a workaround.
2013-06-06 23:11:14 +02:00
Oleg Sychev 14e5c99960 MDL-40013 Easier to override saving question hints
This adds 3 new methods, called from question_type::save_hints,
to make it easier for different question types to overload this
functionality without duplicating code.

Question types that will benefit from this change include
oumultiresponse, ddmarker, preg and correctwriting.
2013-06-06 19:59:33 +03:00
Frederic Massart 333df7d951 MDL-39979 navigation: Remove the use of an undefined variable 2013-06-06 14:53:27 +08:00
Dan Poltawski 6cf8499253 MDL-26925 User selector: Set label when no results
Thanks to Jonathon Fowler for the fix!
2013-06-06 14:09:39 +08:00
Frederic Massart a3fa82636d MDL-40036 user: Respect capability to ignore user quota 2013-06-06 12:43:34 +08:00
Dan Poltawski 1430866232 weekly release 2.5+ 2013-06-06 12:01:38 +08:00
Dan Poltawski 1c437c97f7 Merge branch 'install_25_STABLE' of git://git.moodle.cz/moodle-install into MOODLE_25_STABLE 2013-06-06 11:55:07 +08:00
Dan Poltawski 0fbfae718d MDL-25901 rss: do not copyright html tag
They copyright field does not permit html, it should be plain text
2013-06-06 11:30:27 +08:00
Eloy Lafuente (stronk7) 61899ac202 Merge branch 'wip-MDL-38773-m25' of git://github.com/marinaglancy/moodle into MOODLE_25_STABLE 2013-06-05 19:32:14 +02:00
Eloy Lafuente (stronk7) 92c46e00eb Merge branch 'w23_MDL-39588_m25_ociinstall' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-05 16:24:22 +02:00
Eloy Lafuente (stronk7) 1127607bce Merge branch 'w23_MDL-39503_m25_signup' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-05 16:18:01 +02:00
Eloy Lafuente (stronk7) ef580461f6 Merge branch 'wip-MDL-21097-m25' of git://github.com/marinaglancy/moodle into MOODLE_25_STABLE 2013-06-05 10:36:22 +02:00
Dan Poltawski 2d0faffb7f MDL-39940 tags: missing setType call 2013-06-05 16:17:26 +08:00
Marina Glancy f9d68a11da MDL-21097 Correctly check capabilities for frontpage editing mode 2013-06-05 18:09:34 +10:00
Dan Poltawski 0a8985843e MDL-39964 report_progress: don't browser sniff for SVG support
Use feature detection, its more robust and better practice.
2013-06-05 15:42:26 +08:00
Dan Poltawski e0e6015416 Merge branch 'wip-MDL-21097-m25' of git://github.com/marinaglancy/moodle into MOODLE_25_STABLE 2013-06-05 13:10:18 +08:00
Rossiani Wijaya 9db4dcb68b MDL-39773 bootstrapbase: add style for .centerpadded.
Relocate .centerpadded styling from standard to base themes
2013-06-05 12:50:15 +08:00
Marina Glancy 6b52abc0af MDL-21097 corrected course editing roles 2013-06-05 14:27:50 +10:00
Mary Evans 179fbc7f11 MDL-39977 theme_bootstrapbase: Changes the way Clean theme's login takes its navbar styles from Bootstrapbase.
Conflicts:
	theme/bootstrapbase/style/moodle.css
2013-06-05 10:13:39 +08:00
Marina Glancy 28ce5653c0 MDL-38773 Purge coursecat cache on course restore 2013-06-05 12:05:05 +10:00
Brian King c017ccff4c MDL-39409 auth/cas pass web proxy settings along to phpCAS 2013-06-05 02:46:25 +02:00
Eloy Lafuente (stronk7) ea85ee6f63 Merge branch 'w23_MDL-39416_m25_pagelog' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-05 02:43:48 +02:00
AMOS bot 2fb98d4bc0 Automatically generated installer lang files 2013-06-05 00:39:45 +00:00
Dan Poltawski 9a0e9776c0 MDL-39968 bootstrapbase: uses non-standard browser sniffing
Also theme_clean does the same sniffing, but is not necessary as its
included by bootstrapbase
2013-06-05 02:34:13 +02:00
Eloy Lafuente (stronk7) 832aa7eb71 Merge branch 'MDL-38731-25' of git://github.com/danpoltawski/moodle into MOODLE_25_STABLE 2013-06-05 02:30:56 +02:00
Eloy Lafuente (stronk7) 0e73df9df6 Merge branch 'MDL-37332-25' of git://github.com/danpoltawski/moodle into MOODLE_25_STABLE 2013-06-05 02:27:35 +02:00
Matteo Scaramuccia ab8e75ef40 MDL-38545 Reports: PHP Strict Standards error in graph.php 2013-06-05 02:23:05 +02:00
Eloy Lafuente (stronk7) ef8a1c4908 Merge branch 'MDL-39790_master' of git://github.com/totara/openbadges into MOODLE_25_STABLE 2013-06-05 01:45:28 +02:00
Eloy Lafuente (stronk7) 0f51a82bae Merge branch 'wip-MDL-21097-m25' of git://github.com/marinaglancy/moodle into MOODLE_25_STABLE 2013-06-05 01:43:11 +02:00
Mary Evans 358544f9b7 MDL-40018 theme_sky_high: Fixed footer in admin pages. 2013-06-05 00:18:33 +01:00
Eloy Lafuente (stronk7) 36e5df06b3 MDL-39723 prevent update_courses() bad habits.
All the external functions end calling require_login() that is
always in charge of setting the $COURSE global. This is not a
problem, but in the case of core_course_external::update_courses()
testing, where we are, in the same "request", both setting and getting
the $COURSE information and it's clearly outdated, so the test fails.

Alternative solution would be to modify the external function to ensure
that, after updating a course, $COURSE is also updated with the changes
but it does not seem to be necessary for "normal" usage (both UI/WS POVs).
2013-06-04 21:49:35 +02:00
Eloy Lafuente (stronk7) 8c8b3eba24 Merge branch 'MDL-39723-m25' of git://github.com/sammarshallou/moodle into MOODLE_25_STABLE 2013-06-04 17:48:57 +02:00
Gareth J Barnard faccb2ba9f MDL-39820 theme_bootstrapbase: Fix form button alignment for badges. 2013-06-04 11:17:23 +01:00
Dan Poltawski bf2360c597 Merge branch 'MDL-39945_25' of git://github.com/timhunt/moodle into MOODLE_25_STABLE 2013-06-04 15:54:01 +08:00
Dan Poltawski e7bb4b1ee7 Merge branch 'MDL-39835_M25' of github.com:lazydaisy/moodle into MOODLE_25_STABLE 2013-06-04 14:43:13 +08:00
Dan Poltawski 2b7e18fded Merge branch 'wip-MDL-39819_M25' of git://github.com/gjb2048/moodle into MOODLE_25_STABLE
Conflicts:
	theme/bootstrapbase/style/moodle.css
2013-06-04 14:38:36 +08:00
Marina Glancy f85cb076aa MDL-39870 correctly determine whether activity is resource or module 2013-06-04 14:14:39 +08:00
Dan Poltawski 836dab79c0 MDL-39931 less: recompile with whitespace removed 2013-06-04 14:09:28 +08:00
Dan Poltawski 2fdfdd49d4 Merge branch 'MDL-39931-M25' of github.com:lazydaisy/moodle into MOODLE_25_STABLE 2013-06-04 13:32:33 +08:00
Dan Poltawski cb1318754e Merge branch 'MDL-39536-25' of git://github.com/FMCorz/moodle into MOODLE_25_STABLE 2013-06-04 13:19:16 +08:00
Frederic Massart 491e196107 MDL-39536 completion: Unit tests for get_activities and has_activities 2013-06-04 12:58:03 +08:00
Frederic Massart 13f73be25b MDL-39536 completion: Prevent DB call when fetching activities 2013-06-04 12:58:02 +08:00
David Mudrák 21c4402280 MDL-39939 Fix the basic SCORM report plugin name 2013-06-04 12:27:42 +08:00
Dan Poltawski 2140e5afa4 Merge branch 'wip-mdl-39827-m25' of git://github.com/rajeshtaneja/moodle into MOODLE_25_STABLE 2013-06-04 11:29:57 +08:00
Dan Poltawski acae4313c8 Merge branch 'MDL-39317-25' of git://github.com/damyon/moodle into MOODLE_25_STABLE 2013-06-04 11:26:33 +08:00
Frederic Massart 16725a78d0 MDL-39865 navigation: Removed unaccessible repositories node
A user is not allowed to edit another user's repositories, in any
circumstances. The repositories node under 'Profile setting for X'
does not make sense under those conditions.
2013-06-04 11:14:02 +08:00
Frederic Massart 3e17b1c4a7 MDL-39865 navigation: 'Profile settings for ...' always displayed
Before this patch, 'Profile settings for X' would only be displayed
if X can access the course, which means that if the course is hidden
the node would be hidden as well. We changed this so that the user
needs to be enrolled in the course.
2013-06-04 11:14:02 +08:00
Dan Poltawski e19a07bfcb Merge branch 'MDL-39743-25' of git://github.com/damyon/moodle into MOODLE_25_STABLE 2013-06-04 11:03:45 +08:00
Dan Poltawski c687e8d741 Merge branch 'wip_25_mdl-34085' of https://github.com/iarenaza/moodle into MOODLE_25_STABLE 2013-06-04 10:42:55 +08:00
AMOS bot 09c0ea1f39 Automatically generated installer lang files 2013-06-04 00:46:34 +00:00
Eloy Lafuente (stronk7) 1fe7be205c Merge branch 'wip-MDL-39801-m25' of git://github.com/marinaglancy/moodle into MOODLE_25_STABLE 2013-06-04 01:58:27 +02:00
Eloy Lafuente (stronk7) 6399a82028 MDL-39764 style: phpdoc changed to inline comments 2013-06-04 01:43:35 +02:00
Eloy Lafuente (stronk7) 8a2cee5091 Merge branch 'wip-MDL-39764_M25_3' of git://github.com/gjb2048/moodle into MOODLE_25_STABLE 2013-06-04 01:43:28 +02:00
Eloy Lafuente (stronk7) ca7f399be7 Merge branch 'w23_MDL-39882_m25_behatconfig' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-04 01:32:12 +02:00
Damyon Wiese 6c37003d05 MDL-39821 assign: missing setType with advanced grading 2013-06-04 01:30:20 +02:00
Eloy Lafuente (stronk7) d1963974a9 MDL-39465 Show env. info instead of part twice. 2013-06-04 00:50:55 +02:00
Gareth J Barnard 3b4752e1f2 MDL-39764 course_format: Allow courses to have numsections > maxsections. 2013-06-03 23:43:24 +01:00
Eloy Lafuente (stronk7) 618737d7e9 Merge branch 'MDL-39936-25' of git://github.com/danpoltawski/moodle into MOODLE_25_STABLE 2013-06-04 00:05:04 +02:00
Eloy Lafuente (stronk7) 06ab9b8888 Merge branch 'w23_MDL-39465_m25_environment' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-06-04 00:00:44 +02:00
Justin Filip c7abb11786 MDL-39166 auth_ldap Add triggers for user created/updated events. 2013-06-03 23:55:30 +02:00
Eloy Lafuente (stronk7) 73f8ab649f Merge branch 'wip-MDL-39710-m25' of git://github.com/marinaglancy/moodle into MOODLE_25_STABLE 2013-06-03 23:22:17 +02:00
Eloy Lafuente (stronk7) 189590bb37 Merge branch 'MDL-39932_25' of git://github.com/grabs/moodle into MOODLE_25_STABLE 2013-06-03 19:10:39 +02:00
Albert Gasset 827e54dde5 MDL-39487 mod/assign: fixed ID number of grade item changing 2013-06-03 19:05:55 +02:00
Eloy Lafuente (stronk7) 42a9e3efc2 Merge branch 'MDL-39987_M25' of git://github.com/lazydaisy/moodle into MOODLE_25_STABLE 2013-06-03 16:07:47 +02:00
Mary Evans f58fadfeba MDL-39987 theme_bootstrapbase: Fixed menunode text in renderers/core_renderer.php. 2013-06-02 23:17:14 +01:00
Petr Škoda f498ae7652 MDL-39416 do not try to get detailed perflog info before PAGE int 2013-06-02 13:35:40 +02:00
Petr Škoda dd85f5aed0 MDL-39588 require MOODLELIB Oracle package
Previously it was kind of optional, but it is now required. We also attempt to install it automatically now.
2013-06-02 11:14:10 +02:00
Mary Evans b91fcb9094 MDL-39835 theme_sky_high: Fix for footer to display correctly in higher resolutions. 2013-06-01 21:53:19 +01:00
Gareth J Barnard dfb83eb132 MDL-39819 theme_bootstrapbase: Fix icon alignment in filepicker. 2013-05-31 22:32:03 +01:00
Petr Škoda 1e558fdf66 MDL-39465 detect environment problems in tests
This should help explain why something else fails.
2013-05-31 22:03:15 +02:00
Tim Hunt 7dad4a5e59 MDL-39978 Essays with files break each attempt builds on last 2013-05-31 20:48:03 +01:00
Tim Hunt 8b2e668a19 MDL-39971 each attempt builds on last broken for previews. 2013-05-31 20:32:52 +01:00
Petr Škoda 65380ecd0a MDL-39503 tell robots to not index login and signup page
This should partially prevent people from googling moodle sites that allow signup. Please note you can not use login block if because it would still disclose the signup link.
2013-05-31 21:26:23 +02:00
Petr Škoda b5ce3bc8f4 MDL-39882 add required behat settings to config-dist 2013-05-31 21:09:05 +02:00
Marina Glancy 51173470b6 MDL-39710 Don't display rubric and guide preview unless option checked 2013-05-31 16:13:18 +10:00
Russell Boyatt 8df87c160f Fixing MDL-39710: setting to limit student access to rubric now checked. 2013-05-31 16:13:13 +10:00
Dan Poltawski e5326193bf MDL-38731 course: split course group settings header
Allow translators to translate the heading differently.

AMOS BEGIN
  CPY [groups,group],[groupsettingsheader,group]
AMOS END
2013-05-31 12:04:51 +08:00
Dan Poltawski e9fd6e5dd6 MDL-37332 portfolio: don't add double slashes
get_filepath() always returns trailing slash.

This breaks windows export - thanks to Jason Platts
2013-05-31 11:52:34 +08:00
Marina Glancy a74edf03ad MDL-39801 navigation_node::remove does not allow to insert nodes afterwards 2013-05-31 13:51:13 +10:00
Marina Glancy bd205fa484 MDL-39801 navigation_node::remove fails if first child does not have a key 2013-05-31 13:51:13 +10:00
Marina Glancy 93ac52a9f3 MDL-21097 Allow user to change current section if he does not have any other manageing capability 2013-05-31 13:20:16 +10:00
Marina Glancy db8d852af9 MDL-21097 Correctly check capabilities for course settings items
- Make sure that items in course settings (and frontpage settings) are shown depending on user capabilities
- Make sure user is able to turn editing on on the page even if he has only limited number of seciton/modules managing capabilities
2013-05-31 13:20:10 +10:00
Dan Poltawski 068373c86a MDL-33562 lib: remove deleted user tokens 2013-05-31 10:23:51 +08:00
Dan Poltawski 4c4c90f4b0 MDL-33562 lib: remove private keys when user deleted 2013-05-31 10:22:02 +08:00
AMOS bot a270a2c129 Automatically generated installer lang files 2013-05-31 00:43:06 +00:00
Adam Olley 096707c01d MDL-39947 mod_forum: Add safety check to variable assignment when counting unread posts 2013-05-31 09:50:05 +09:30
Andreas Grabs b4de832553 MDL-39932 mod_feedback - Mapping Feedback when Course is Deleted - thx to David N. Johnson 2013-05-30 23:11:37 +02:00
Tim Hunt f0dc1a71c1 MDL-39945 qtype multichoice was not comparing blank responses correctly. 2013-05-30 19:22:21 +01:00
Iñaki Arenaza 2f467a82a1 MDL-34085 Fix the backup options names in core_course_duplicate_course
Fix the option names to match those used by the backup subsystem.
2013-05-30 17:45:30 +02:00
Mary Evans 91cf8cc82c MDL-39931 theme_bootstrapbase: ported invisiblefieldset css rules from theme/base/core.css to less/moodle/core.css 2013-05-30 15:51:16 +01:00
Dan Poltawski 2e80405940 MDL-39936 upgrade: 2.5.0 line incorrectly positioned 2013-05-30 13:40:28 +08:00
Damyon Wiese f36066fa9e weekly release 2.5+ 2013-05-30 12:09:08 +08:00
Damyon Wiese a4e0171b2c Merge branch 'install_25_STABLE' of git://git.moodle.cz/moodle-install into MOODLE_25_STABLE 2013-05-30 11:27:46 +08:00
AMOS bot 8352a1b138 Automatically generated installer lang files 2013-05-30 00:38:21 +00:00
Damyon Wiese 028b56f875 MDL-39317 assign: Fix for submitted filter on grading table.
Some old upgraded assignments have no timecreated column so use
a different one to check for a valid submission.
2013-05-29 14:50:42 +08:00
Ashley Holman e9efc2baa2 MDL-39743 assign: Fix missing capability check for "grant extension" 2013-05-29 12:04:22 +08:00
AMOS bot 7e720621ec Automatically generated installer lang files 2013-05-29 00:39:21 +00:00
Yuliya Bozhko 626c84bca9 MDL-39790 Add badges block to the course page 2013-05-29 09:42:11 +12:00
Eloy Lafuente (stronk7) 4bc4844d5b Merge branch 'w22_MDL-39676_m25_whereparam' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-05-28 15:33:33 +02:00
sam marshall 695f6f3c23 MDL-39723 Remove unnecessary queries for COURSE, SITE 2013-05-28 13:16:03 +01:00
Damyon Wiese f4075573b9 MDL-39477: Remove some unused instances of $CFG
Also change @since tags to 2.5.1 because this will be backported.
2013-05-28 11:11:42 +08:00
David Monllao a13cbfb3e7 MDL-39702 generators: Adding MOODLE_INTERNAL
As discussed generators should include MOODLE_INTERNAL
as they make use of CFG and they don't make sense without
requiring config.php.
2013-05-28 11:10:35 +08:00
Frederic Massart 799dbbfbaa MDL-39477 repository: Tests use data generator 2013-05-28 11:08:58 +08:00
Frederic Massart e5c6694de8 MDL-39477 repository: Introducing data generators for repositories 2013-05-28 11:08:54 +08:00
Damyon Wiese d3aeeb93c2 Merge branch 'w22_MDL-39802_m25_deletedusers' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-05-28 09:56:08 +08:00
AMOS bot f5940428a9 Automatically generated installer lang files 2013-05-28 00:38:15 +00:00
Eloy Lafuente (stronk7) e58af1dfcd Merge branch 'wip-MDL-39836_M25' of git://github.com/gjb2048/moodle into MOODLE_25_STABLE
Conflicts:
	theme/bootstrapbase/style/moodle.css
2013-05-28 01:24:58 +02:00
Eloy Lafuente (stronk7) 4f0d6118ff Merge branch 'wip-MDL-39456-m25' of git://github.com/samhemelryk/moodle into MOODLE_25_STABLE 2013-05-28 01:02:34 +02:00
Eloy Lafuente (stronk7) 8b77e81201 Merge branch 'MDL-39853_25' of git://github.com/dmonllao/moodle into MOODLE_25_STABLE 2013-05-28 00:57:06 +02:00
Eloy Lafuente (stronk7) d6e2b0a03e Merge branch 'MDL-39795_25' of git://github.com/dmonllao/moodle into MOODLE_25_STABLE 2013-05-28 00:45:02 +02:00
Damyon Wiese a665577b37 Merge branch 'MDL-39686_25' of git://github.com/dmonllao/moodle into MOODLE_25_STABLE 2013-05-27 16:28:48 +08:00
Damyon Wiese f32e55c8d7 Merge branch 'MDL-39680-M25' of git://github.com/lazydaisy/moodle into MOODLE_25_STABLE
Conflicts:
	theme/bootstrapbase/style/moodle.css
2013-05-27 16:21:44 +08:00
Damyon Wiese 6a3a3bd868 Merge branch 'MDL-39640-master-int' of git://github.com/FMCorz/moodle into MOODLE_25_STABLE 2013-05-27 16:14:59 +08:00
Kordan f70d382498 MDL-39194 theme_formal_white: cleaned the category page showing course listing 2013-05-27 15:23:08 +08:00
David Monllao e132b54a6a MDL-39853 behat: Useful info for $CFG->behat_config setting 2013-05-27 15:14:06 +08:00
David Monllao c0b59154ba MDL-39686 behat: Add option to restart the browser 2013-05-27 15:06:15 +08:00
David Monllao 268cb03e2d MDL-39686 behat: Adding into to config-dist.php 2013-05-27 15:06:09 +08:00
Dan Poltawski ad97a56875 MDL-39806 theme_bootstrapbase: run less with --compress 2013-05-27 14:42:44 +08:00
Dan Poltawski 8d94299b02 Merge branch 'MDL-39806_M25' of git://github.com/lazydaisy/moodle into MOODLE_25_STABLE 2013-05-27 14:36:55 +08:00
David Monllao 0653761da2 MDL-39795 behat: Adapting features to consider spaces 2013-05-27 14:25:40 +08:00
David Monllao e489df87bf MDL-39795 behat: Improving find-texts-in-page-contents performance 2013-05-27 14:25:34 +08:00
Marina Glancy 87a445ec70 MDL-39785 bug fix: filter_activitynames was not applied correctly 2013-05-27 14:17:15 +08:00
Dan Poltawski 121e2492e8 Merge branch 'MDL-38239-moodle25' of git://github.com/StudiUM/moodle into MOODLE_25_STABLE 2013-05-27 14:13:36 +08:00
Dan Poltawski 6c16494f03 Merge branch 'w22_MDL-39688_m25_etag' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-05-27 13:43:37 +08:00
Dan Poltawski 191e70c2e2 Merge branch 'w22_MDL-38408_m25_paypalcost' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-05-27 13:05:14 +08:00
Yuliya Bozhko 7291b026a0 MDL-39778 badges: Fix missing library error in delete_course() 2013-05-27 12:00:38 +08:00
Rajesh Taneja df4da0fe79 MDL-39827 course: Trigger invalidate cache event while changing sort-order of category 2013-05-27 10:55:48 +08:00
Dan Poltawski ceb4030763 Merge branch 'MDL-39768_25' of git://github.com/dmonllao/moodle into MOODLE_25_STABLE 2013-05-27 10:18:32 +08:00
Gareth J Barnard 69ecf8c0a9 MDL-38856 theme_bootstrapbase: Inverse user name text unreadable on nav bar 2013-05-27 02:09:01 +01:00
Mary Evans 039c78702d MDL-39680 theme_bootstrapbase: Fixed footer alignment in less/moodle/debug.less
Squashed commit of the following:

commit 58f5e063a8b333c184ca83ece7f61ec40d2a5080
Author: Mary Evans <lazydaisy@visible-expression.co.uk>
Date:   Sun May 26 13:45:11 2013 +0100

    MDL-39680 theme_bootstrapbase: Fixed footer alignment in less/moodle/debug.less

commit 575594257a9f4fab75977d2334d07659abea0b65
Author: Mary Evans <lazydaisy@visible-expression.co.uk>
Date:   Sun May 26 02:07:00 2013 +0100

    MDL-39680 theme_bootstrapbase: Centralised text in footer.
2013-05-26 13:55:45 +01:00
Petr Škoda 7fa470de03 MDL-38408 always validate PayPal dates and cost, improve float formatting 2013-05-25 22:48:44 +02:00
Mary Evans 6ceb6eb672 MDL-39806 theme_bootstrapbase: Fix for coursesearchbox label alignment. 2013-05-25 17:16:39 +01:00
Petr Škoda 644b1df502 MDL-39676 improve get_role_users() unit tests 2013-05-25 10:10:17 +02:00
Petr Škoda 161be92065 MDL-39688 do not set Etag when sending http ranges 2013-05-25 09:12:03 +02:00
Petr Škoda 5878ac3957 MDL-39802 ignore deleted users in meta enrolments 2013-05-24 21:28:38 +02:00
Petr Škoda 761227cfc6 MDL-39802 ignore deleted users in cohort enrolments 2013-05-24 21:28:38 +02:00
Petr Škoda 97d68b5652 MDL-39802 ignore deleted users in category enrolments 2013-05-24 21:28:37 +02:00
Petr Škoda 3ecf7ddd15 MDL-39802 fix sloppy user deleting and creating in imsenterprise plugin 2013-05-24 21:28:37 +02:00
Petr Škoda b6ef79950e MDL-39802 add cli script for fixing of all deleted users
Use this script if something just set the user.deleted flag instead of calling the delete_user() method.
2013-05-24 21:28:37 +02:00
Damyon Wiese f65261d096 weekly release 2.5+ 2013-05-24 13:26:46 +08:00
Damyon Wiese 93f645f0ae Merge branch 'install_25_STABLE' of git://git.moodle.cz/moodle-install into MOODLE_25_STABLE 2013-05-24 13:12:11 +08:00
Sam Hemelryk e5184cac0e MDL-39456 cachestore_file: added reference to global $CFG
By holding onto a reference of the global $CFG object we
can be sure that it isn't destroyed before we are done
with it.
This allows for caches to be used within destructors for
purposes such as delayed writes.
2013-05-24 09:54:18 +12:00
Marina Glancy caaad8b08d MDL-39177 make sure we don't remove existing directories when cleaning up unzip 2013-05-23 15:59:53 +02:00
AMOS bot cb033b9fab Automatically generated installer lang files 2013-05-22 08:26:02 +00:00
David Monllao 4f629bc677 MDL-39768 behat: Adding focused panels CSS selector 2013-05-22 15:11:55 +08:00
Eloy Lafuente (stronk7) d917dc9e40 MDL-36482 Fix slipped error, thanks Petr! 2013-05-22 04:14:01 +02:00
Damyon Wiese d74f740722 Merge branch 'MDL-39697-M25' of git://github.com/lazydaisy/moodle into MOODLE_25_STABLE 2013-05-22 09:03:31 +08:00
Marina Glancy 6168502474 MDL-39177 Updating userid when file is overwritten 2013-05-22 02:25:18 +02:00
Marina Glancy 751cebfc3d MDL-39177 More comments about confusing files.source field 2013-05-22 02:25:12 +02:00
Marina Glancy acfd39cbf8 MDL-39177 New function file_storage::get_unused_dirname() 2013-05-22 02:25:07 +02:00
Marina Glancy 2efc8ff39f MDL-39177 Ensure files are overwritten correctly on unzip 2013-05-22 02:25:02 +02:00
Marina Glancy 2a0b243137 MDL-39177 Perform all checks on draftfile area before saving the files 2013-05-22 02:24:56 +02:00
Marina Glancy b8aca19538 MDL-39177 Remove code duplication 2013-05-22 02:24:50 +02:00
Marina Glancy 2fbfedb310 MDL-39177 Update file in filearea only if original is present
Now when file was deleted in filemanager and new file with the same file was uploaded the references will be converted to copies exactly like UI warns in filemanager.
Also do not delete original information from draftfiles.
2013-05-22 02:24:40 +02:00
Marina Glancy 6c43ee510f MDL-39177 Discard file origin when moving/renaming a file in filemanager
Created one function that handles file update to be used from both JS and nonJS filepicker
2013-05-22 02:24:32 +02:00
Marina Glancy d262212a1f MDL-39177 Original info is not preserved when overwriting a file in filemanager
original file location is used by filemanager to indicate that file is a source of reference and file the list of referencing files
2013-05-22 02:24:24 +02:00
Marina Glancy d50f747fd8 MDL-39177 File reference is not updated when overwriting the file 2013-05-22 02:24:13 +02:00
Marina Glancy d95506e05a MDL-33719 When overwriting a file the filemanager is not refreshed 2013-05-22 02:24:08 +02:00
Gilles-Philippe Leblanc f7d180a7fb MDL-38239 gradebook: Importing grades from a .txt file with commas for decimals causes an error 2013-05-21 14:43:07 -04:00
Marina Glancy 91af4fa5bf MDL-39720 added missing setType() in moodleform 2013-05-21 12:30:21 +08:00
Dan Poltawski b051261ecc Merge branch 'MDL-39753_25' of git://github.com/stronk7/moodle into MOODLE_25_STABLE 2013-05-21 11:00:32 +08:00
Dan Poltawski aeaf5c1337 MDL-39323 auth_ldap: fix savepoint version 2013-05-21 10:52:44 +08:00
Iñaki Arenaza 9f13f53393 MDL-39323 Make sure start_tls config value is defined
This has been introduced in 2.5, so when upgrading from previous versions
make sure we set a default value. Otherwise, we get a notice about it
being undefined when we try to use it (and we always try to use it!).
2013-05-21 10:44:11 +08:00
Russell Smith 0bc310eb5a MDL-36482 activities block: Allow mod display without intro's 2013-05-21 02:27:58 +02:00
Eloy Lafuente (stronk7) ab6a0ac4f7 Merge branch 'MDL-39758_M25' of git://github.com/lazydaisy/moodle into MOODLE_25_STABLE 2013-05-21 01:48:38 +02:00
David Monllao 18003f2adb MDL-39638 behat: New step to collapse nodes based on the expand nodes
Also updating steps with a collapse behaviour.
2013-05-21 01:00:36 +02:00
Jayesh Anandani 2ab5928066 MDL-26120 mod_forum: use correct object 2013-05-21 00:55:17 +02:00
Eloy Lafuente (stronk7) e3c1bd2593 Merge branch 'MDL-39717_25' of git://github.com/dmonllao/moodle into MOODLE_25_STABLE 2013-05-21 00:44:22 +02:00
Jerome Mouneyrac 99f5bad554 MDL-31209 web service error message: Indication about the service not containg the web service function is confusing 2013-05-20 19:06:59 +02:00
Ruslan Kabalin 666117e830 MDL-39761: mod_assign: Always check submission plugin status and visibility. 2013-05-20 16:32:04 +01:00
Eloy Lafuente (stronk7) 9b19f3f4fb MDL-39753 Add 2.5.0 upgrade line 2013-05-20 15:34:44 +02:00
Mary Evans b6d07f25a1 MDL-39758 theme_bootatrapbase: Added missing layout/embedded.php. 2013-05-20 13:04:44 +01:00
Dan Poltawski e90eb091c5 Merge branch 'w18_MDL-39374_m25_folderlog' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-05-20 17:58:29 +08:00
Damyon Wiese 5cc84acabe Merge branch 'MDL-38498-25' of git://github.com/FMCorz/moodle into MOODLE_25_STABLE 2013-05-20 16:12:34 +08:00
Frederic Massart 543b5feae0 MDL-39719 course: Pointing to wrong category string in requests 2013-05-20 16:10:07 +08:00
Frederic Massart cdd041e631 MDL-38498 repository: Unit tests for repository::check_capability() 2013-05-20 16:00:11 +08:00
Tim Hunt 6d393bd97c MDL-27748 question: grade options should be localised.
question_bank::fraction_options should use format_float to display
decimal pionts correctly.
2013-05-20 15:47:22 +08:00
Tim Hunt f8aec131e9 MDL-39728 List all the classes in question/type/questionbase.php
This should make it easier for people to understand the type hierarchy.
2013-05-20 15:28:59 +08:00
Troy Lee 62cf9a1d2a MDL-36020 overview/report: fix quiz regrade session lock 2013-05-20 12:42:38 +08:00
Damyon Wiese 76f881c83a Merge branch 'MDL-39363-master' of git://github.com/ankitagarwal/moodle into MOODLE_25_STABLE 2013-05-20 12:13:09 +08:00
Damyon Wiese c4500213d6 Merge branch 'MDL-39528_25' of git://github.com/dmonllao/moodle into MOODLE_25_STABLE 2013-05-20 12:06:51 +08:00
Mary Evans 1cdcb6599a MDL-39630 theme_afterburner: Improving the way course/category listings display. 2013-05-20 12:03:00 +08:00
Kordan ff3f1f3ef3 MDL-39198 theme_formal_white: resized and relocated the "Add..." drop down menu of the "Add a block" pseudo block 2013-05-20 11:48:23 +08:00
Frederic Massart b42e6e1c2e MDL-38369 repository: Remove output before redirect 2013-05-20 10:25:34 +08:00
Damyon Wiese d8581a2367 Merge branch 'MDL-28250-25' of git://github.com/danpoltawski/moodle into MOODLE_25_STABLE 2013-05-17 12:11:31 +08:00
David Monllao 236ebcfe78 MDL-39528 behat: Avoid modal window suite failure
Catching JS alerts/confirms exceptions and
auto-accepting them before each test run to
avoid chained scenarios failures.
2013-05-17 12:09:04 +08:00
David Monllao c340dca9b0 MDL-39717 behat: Removing unstable steps and scenarios 2013-05-17 11:48:29 +08:00
David Monllao a3c292ee64 MDL-39717 behat: Add info to experimental steps definitions
JS modal dialogs and drag & drop should be
considered experimental as the steps are not
working as expected in all OS/browser combinations.
2013-05-17 11:48:20 +08:00
Damyon Wiese b7bbb615d7 Merge branch 'w18_MDL-39678_m25_yui' of git://github.com/skodak/moodle into MOODLE_25_STABLE 2013-05-16 15:55:45 +08:00
Mary Evans 9141e05262 MDL-39697 theme_bootstrapbase: Updated $THEME->layouts in config.php to match those in Base theme. 2013-05-16 00:57:40 +01:00
Petr Škoda e9cfbe4290 MDL-39678 fix YUI 20130515 vulnerability 2013-05-15 22:12:12 +02:00
Petr Škoda 2e297423cd MDL-39374 log only real folder edits
Credit goes to Florian Zillner, thanks.
2013-05-15 21:32:27 +02:00
Eloy Lafuente (stronk7) 8daf98a10f NOBUG: Keep backup YYYYMMDD on sync with 2.5 2013-05-14 12:42:15 +02:00
28703 changed files with 1139465 additions and 3677993 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"]
}
-6
View File
@@ -1,6 +0,0 @@
**/yui/build/** -diff
**/amd/build/** -diff
lib/dml/oci_native_moodle_package.sql text eol=lf
**.js.map -diff
**-min.js -diff
**.min.js -diff
-2
View File
@@ -1,2 +0,0 @@
# Primary donations pages.
custom: ["https://moodle.com/donations/", "https://fund.moodle.org"]
-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');
-143
View File
@@ -1,143 +0,0 @@
name: One by One Testing
# Run all the individual unit tests one by one, with
# fully independent PHPUnit executions. Useful to
# detect issues with some tests that are using stuff
# that has been made available by others, but is not
# available when running individually.
#
# Note that we aren't using PHPUnit's own isolation
# here but completely separated runs, one for each
# test.
#
# The workflow will fail reporting all the tests
# that have failed (and will pass if no failure is
# detected, of course).
#
# It's only executed via workflow dispatch (automated
# or manual), not by push/tag. And acceptd configuration
# of phpunit, specially useful to run it with PHPUnit's
# own isolation or any other option.
on:
workflow_dispatch:
inputs:
phpunit_extra_options:
description: Additional options to apply to PHPUnit
required: false
default: ''
env:
chunks: 7
jobs:
collect:
name: Collect individual unit tests
runs-on: ubuntu-latest
outputs:
matrix: ${{steps.individual-tests.outputs.matrix }}
steps:
- name: Checking out code
uses: actions/checkout@v4
- name: Looking for all individual tests
id: individual-tests
run: |
count=0 # Number of individual tests found.
while read -r testfile; do # For each test file.
while read -r testname; do # For each unit test in a file.
count=$((count + 1))
# Sent it to the correct chunk file.
chunk=$(((($count % $chunks)) + 1))
echo "$testname $testfile" >> ./chunk_$chunk.txt
done < <(grep "function test_" "${testfile}" | sed -r "s/^.*function (test_[a-zA-Z0-9_]+).*/\1/")
done < <(find . -name "*_test.php")
# Generate the matrix to run tests.
echo "matrix=$(ls -1 chunk_*.txt | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT
echo "$count individual tests collected in $chunks files"
- name: Upload individual tests files
uses: actions/upload-artifact@v4
with:
name: individual_tests
path: chunk_*.txt
retention-days: 1
test:
name: Run tests
needs: collect
runs-on: ubuntu-latest
services:
exttests:
image: moodlehq/moodle-exttests
ports:
- 8080:80
redis:
image: redis
ports:
- 6379:6379
strategy:
fail-fast: false
matrix:
file: ${{ fromJson(needs.collect.outputs.matrix) }}
steps:
- name: Setting up DB pgsql
uses: m4nu56/postgresql-action@v1
with:
postgresql version: 13
postgresql db: test
postgresql user: test
postgresql password: test
- name: Setting up PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
ini-values: max_input_vars=5000
coverage: none
- name: Checking out code
uses: actions/checkout@v4
- name: Download individual test files
uses: actions/download-artifact@v4
with:
name: individual_tests # Make all the chunk files available for the next steps.
- name: Setting up PHPUnit
env:
dbtype: pgsql
run: |
echo "pathtophp=$(which php)" >> $GITHUB_ENV
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: Run PHPUnit test (one by one)
env:
dbtype: pgsql
run: |
status=0
count=0
while read -r line; do # For each line in the chunk file
count=$((count + 1))
filter="${line% *}"
file="${line#* }"
# Run the individual unit test and report problems if needed to.
if ! php vendor/bin/phpunit \
--fail-on-empty-test-suite \
--fail-on-warning \
--fail-on-risky \
--filter "$filter" ${{ inputs.phpunit_extra_options }} \
"$file" >/dev/null 2>&1; then
if [ $status -eq 0 ]; then
echo "Problems found, list of PHPUnit commands failing:"
fi
echo "vendor/bin/phpunit --filter '${filter}' ${{ inputs.phpunit_extra_options }} $file"
status=$((status + 1))
fi
done < ${{ matrix.file }}
echo "Finished: $count individual tests executed, $status tests failed"
exit $status
-124
View File
@@ -1,124 +0,0 @@
name: Core
on:
push:
branches-ignore:
- main
- 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.2
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.0
extensions:
db: mysqli
# PostgreSQL builds always run with the highest PHP supported version.
- os: ubuntu-22.04
php: 8.2
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 }}
-23
View File
@@ -1,23 +0,0 @@
name: Web Installer Testing
on:
workflow_dispatch:
jobs:
InstallationTest:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
php: [8.1, 8.3]
steps:
- name: Construct repository URL
run: echo "REPOSITORY_URL=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" >> $GITHUB_ENV
shell: bash
- uses: moodlehq/moodle-webinstaller-test@main
with:
repository: ${{ env.REPOSITORY_URL }}
branch: ${{ github.ref_name }}
php: ${{ matrix.php }}
-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.2
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.2
# 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.0
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 }}
+5 -27
View File
@@ -9,23 +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.*
/nbproject/
CVS
.DS_Store
@@ -33,25 +25,11 @@ 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 support - only composer.json is to be in git, the rest is installed in each checkout.
composer.phar
composer.lock
/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
+22 -32
View File
@@ -1,58 +1,48 @@
// 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,
"yui": true,
"bitwise": true,
"curly": true,
"eqeqeq": true,
"forin": false,
"immed": true,
"newcap": true,
"noarg": true,
"noempty": true,
"nomen": false,
"onevar": false,
"plusplus": false,
"regexp": false,
"strict": false,
"trailing": true,
"unused": true,
"white": false,
"asi": false,
"boss": false,
"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
"maxerr": 500,
"maxlen": 150,
"passfail": false,
"latedef": true
}
-1
View File
@@ -1 +0,0 @@
lts/jod
+1 -2
View File
@@ -1,5 +1,4 @@
{
"coverage": false,
"lint": "config",
"clean": true
"lint": "config"
}
-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": {
}
}
]
}
-56
View File
@@ -1,56 +0,0 @@
CONTRIBUTING TO MOODLE
======================
Moodle is made by people like you. We are members of a big worldwide community
of developers, designers, teachers, testers, translators and other users. We
work in universities, schools, companies and other places. You are very welcome
to join us and contribute to the project.
See <https://moodledev.io/general/community/contribute> for the many ways you
can help, not only with coding.
Moodle is open to community contributions to core, though all code must go
through peer-review, automated behaviour testing, continuous integration and
human post-integration checks.
Pull requests
-------------
Please do not open pull requests via Github. The repository there is just a
mirror of the official repository at <https://git.moodle.org>. Issues are
reported and patches provided via <https://tracker.moodle.org>. See below for
more information.
Moodle core bug fixes and new features
--------------------------------------
During the years of intensive development, a mature process of including
submitted patches has evolved.
* 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 moodle.git.
* New features are developed on the master branch. Bug fixes are also
backported to currently supported maintenance (stable) branches.
For further details, see <https://moodledev.io/general/development/process>.
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>.
-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!
-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.txt guidelines for how to contribute patches for Moodle. Thank you.
--
+22 -21
View File
@@ -1,28 +1,29 @@
.-..-.
_____ | || |
/____/-.---_ .---. .---. .-.| || | .---.
| | _ _ |/ _ \/ _ \/ _ || |/ __ \
* | | | | | || |_| || |_| || |_| || || |___/
|_| |_| |_|\_____/\_____/\_____||_|\_____)
QUICK INSTALL
=============
Moodle - the world's open source learning platform
For the impatient, here is a basic outline of the
installation process, which normally takes me only
a few minutes:
Moodle <https://moodle.org> is a learning platform designed to provide
educators, administrators and learners with a single robust, secure and
integrated system to create personalised learning environments.
1) Move the Moodle files into your web directory.
You can download Moodle <https://download.moodle.org> and run it on your own
web server, ask one of our Moodle Partners <https://moodle.com/partners/> to
assist you, or have a MoodleCloud site <https://moodle.com/cloud/> set up for
you.
2) Create a single database for Moodle to store all
its tables in (or choose an existing database).
Moodle is widely used around the world by universities, schools, companies and
all manner of organisations and individuals.
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.
Moodle is provided freely as open source software, under the GNU General Public
License <https://moodledev.io/general/license>.
4) Set up a cron task to call the file admin/cron.php
every five minutes or so.
Moodle is written in PHP and JavaScript and uses an SQL database for storing
the data.
See <https://docs.moodle.org> for details of Moodle's many features.
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"}
-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
-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();
}
}
}
-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();
}
}
-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);
+27 -16
View File
@@ -11,7 +11,8 @@ 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', context_system::instance());
$returnurl = new moodle_url('/admin/settings.php', array('section'=>'manageauths'));
@@ -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_PLUGIN);
$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();
+255 -79
View File
@@ -1,94 +1,270 @@
<?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);
$unprotect = optional_param('unprotect', 0, PARAM_INT);
$protect = optional_param('protect', 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');
$strprotecthdr = get_string('blockprotect', 'admin'). $OUTPUT->help_icon('blockprotect','admin');
$strprotect = get_string('blockprotect', 'admin');
$strunprotect = get_string('blockunprotect', 'admin');
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
);
// Purge all caches related to blocks administration.
cache::make('core', 'plugininfo_block')->purge();
// Settings not required - only pages.
admin_get_root(true, false);
/// If data submitted, then process and store.
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 (!isset($CFG->undeletableblocktypes) || (!is_array($CFG->undeletableblocktypes) && !is_string($CFG->undeletableblocktypes))) {
$undeletableblocktypes = array('navigation', 'settings');
} else if (is_string($CFG->undeletableblocktypes)) {
$undeletableblocktypes = explode(',', $CFG->undeletableblocktypes);
} else {
$undeletableblocktypes = $CFG->undeletableblocktypes;
}
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 (!empty($protect) && confirm_sesskey()) {
if (!$block = $DB->get_record('block', array('id'=>$protect))) {
print_error('blockdoesnotexist', 'error');
}
if (!in_array($block->name, $undeletableblocktypes)) {
$undeletableblocktypes[] = $block->name;
set_config('undeletableblocktypes', implode(',', $undeletableblocktypes));
}
admin_get_root(true, false); // settings not required - only pages
}
if (!empty($unprotect) && confirm_sesskey()) {
if (!$block = $DB->get_record('block', array('id'=>$unprotect))) {
print_error('blockdoesnotexist', 'error');
}
if (in_array($block->name, $undeletableblocktypes)) {
$undeletableblocktypes = array_diff($undeletableblocktypes, array($block->name));
set_config('undeletableblocktypes', implode(',', $undeletableblocktypes));
}
admin_get_root(true, false); // settings not required - only pages
}
if (!empty($delete) && confirm_sesskey()) {
echo $OUTPUT->header();
echo $OUTPUT->heading($strmanageblocks);
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 {
uninstall_plugin('block', $block->name);
$a = new stdClass();
$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', 'undeletable', 'delete', 'settings'));
$table->define_headers(array($strname, $strcourses, $strversion, $strhide.'/'.$strshow, $strprotecthdr, $strdelete, $strsettings));
$table->define_baseurl($CFG->wwwroot.'/'.$CFG->admin.'/blocks.php');
$table->set_attribute('class', 'admintable blockstable generaltable');
$table->set_attribute('id', 'compatibleblockstable');
$table->setup();
$tablerows = array();
// Sort blocks using current locale.
$blocknames = array();
foreach ($blocks as $blockid=>$block) {
$blockname = $block->name;
if (file_exists("$CFG->dirroot/blocks/$blockname/block_$blockname.php")) {
$blocknames[$blockid] = get_string('pluginname', 'block_'.$blockname);
} else {
$blocknames[$blockid] = $blockname;
}
}
collatorlib::asort($blocknames);
foreach ($blocknames as $blockid=>$strblockname) {
$block = $blocks[$blockid];
$blockname = $block->name;
if (!file_exists("$CFG->dirroot/blocks/$blockname/block_$blockname.php")) {
$blockobject = false;
$strblockname = '<span class="notifyproblem">'.$strblockname.' ('.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;
}
}
$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('t/hide') . '" class="iconsmall" alt="'.$strhide.'" /></a>';
} else {
$visible = '<a href="blocks.php?show='.$blockid.'&amp;sesskey='.sesskey().'" title="'.$strshow.'">'.
'<img src="'.$OUTPUT->pix_url('t/show') . '" class="iconsmall" alt="'.$strshow.'" /></a>';
$class = ' class="dimmed_text"'; // Leading space required!
}
if ($block->version == $plugin->version) {
$version = $block->version;
} else {
$version = "$block->version ($plugin->version)";
}
if (!$blockobject) {
// ignore
$undeletable = '';
} else if (in_array($blockname, $undeletableblocktypes)) {
$undeletable = '<a href="blocks.php?unprotect='.$blockid.'&amp;sesskey='.sesskey().'" title="'.$strunprotect.'">'.
'<img src="'.$OUTPUT->pix_url('t/unlock') . '" class="iconsmall" alt="'.$strunprotect.'" /></a>';
} else {
$undeletable = '<a href="blocks.php?protect='.$blockid.'&amp;sesskey='.sesskey().'" title="'.$strprotect.'">'.
'<img src="'.$OUTPUT->pix_url('t/lock') . '" class="iconsmall" alt="'.$strprotect.'" /></a>';
}
$row = array(
'<span'.$class.'>'.$strblockname.'</span>',
$blocklist,
'<span'.$class.'>'.$version.'</span>',
$visible,
$undeletable,
$delete,
$settings
);
$table->add_data($row);
}
$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();
+46 -49
View File
@@ -17,7 +17,7 @@
/**
* This file is used to display a categories sub categories, external pages, and settings.
*
* @since Moodle 2.3
* @since 2.3
* @package admin
* @copyright 2011 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@@ -30,6 +30,7 @@ $category = required_param('category', PARAM_SAFEDIR);
$return = optional_param('return','', PARAM_ALPHA);
$adminediting = optional_param('adminedit', -1, PARAM_BOOL);
/// no guest autologin
require_login(0, false);
$PAGE->set_context(context_system::instance());
$PAGE->set_url('/admin/category.php', array('category' => $category));
@@ -41,47 +42,40 @@ $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/");
print_error('categoryerror', 'admin', "$CFG->wwwroot/$CFG->admin/");
}
if (!($settingspage->check_access())) {
throw new \moodle_exception('accessdenied', 'admin');
print_error('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 (admin_write_settings($data)) {
$statusmsg = get_string('changessaved');
}
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/");
}
switch ($return) {
case 'site': redirect("$CFG->wwwroot/");
case 'admin': redirect("$CFG->wwwroot/$CFG->admin/");
}
} else {
$errormsg = get_string('errorwithsettings', 'admin');
$firsterror = reset($adminroot->errors);
}
$adminroot = admin_get_root(true); //reload tree
$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) {
if ($PAGE->user_allowed_editing()) {
$url = clone($PAGE->url);
if ($PAGE->user_is_editing()) {
$caption = get_string('blockseditoff');
@@ -96,46 +90,46 @@ if ($PAGE->user_allowed_editing() && !$PAGE->theme->haseditswitch) {
$savebutton = false;
$outputhtml = '';
foreach ($settingspage->children as $childpage) {
if ($childpage->is_hidden() || !$childpage->check_access()) {
if ($childpage->is_hidden()) {
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);
} else {
continue;
}
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');
}
}
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::empty_tag('input', array('class' => '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));
$visiblepathtosection = array_reverse($settingspage->visiblepath);
$PAGE->set_title("$SITE->shortname: " . implode(": ",$visiblepathtosection));
$PAGE->set_heading($SITE->fullname);
if ($buttons) {
$PAGE->set_button($buttons);
}
$PAGE->set_button($buttons);
echo $OUTPUT->header();
@@ -145,7 +139,13 @@ if ($errormsg !== '') {
echo $OUTPUT->notification($statusmsg, 'notifysuccess');
}
echo $OUTPUT->heading(get_string('admincategory', 'admin', $settingspage->visiblename), 2);
$path = array_reverse($settingspage->visiblepath);
if (is_array($path)) {
$visiblename = join(' / ', $path);
} else {
$visiblename = $path;
}
echo $OUTPUT->heading(get_string('admincategory', 'admin', $visiblename), 2);
echo html_writer::start_tag('form', array('action' => '', 'method' => 'post', 'id' => 'adminsettings'));
echo html_writer::start_tag('div');
@@ -157,7 +157,4 @@ 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();
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));
}
}
-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,427 +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 and their default aliases
*
* @return array
*/
protected function get_default_table_aliases(): array {
return ['task_log' => 'tl'];
}
/**
* 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,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,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
);
}
}
-285
View File
@@ -1,285 +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\hook\manager::get_instance();
$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 '';
}
$rc = new \ReflectionClass($row->classname);
if (!$rc->implementsInterface(\core\hook\deprecated_callback_replacement::class)) {
return '';
}
$deprecates = call_user_func([$row->classname, 'get_deprecated_plugin_callbacks']);
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 badge-info');
}, $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,514 +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();
}
/**
* Check if the user has the capability to access this table.
*
* Default implementation for plugin management tables is to require 'moodle/site:config' capability
*
* @return bool Return true if capability check passed.
*/
public function has_capability(): bool {
return has_capability('moodle/site:config', $this->get_context());
}
}
@@ -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");
-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/>.
/**
* 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' => '',
'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'])) {
$help = <<<EOL
Perform backup of the given course.
Options:
--courseid=INTEGER Course ID for backup.
--courseshortname=STRING Course shortname for backup.
--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 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);
}
cli_heading('Performing backup...');
$bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
backup::INTERACTIVE_YES, backup::MODE_GENERAL, $admin->id);
// 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 {
mtrace("Backup completed, the new file is listed in the backup area of the given course");
}
$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);
-125
View File
@@ -1,125 +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 fixed incorrectly deleted users.
*
* @package core
* @subpackage cli
* @copyright 2013 Marina Glancy
* @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');
// Get cli options.
list($options, $unrecognized) = cli_get_params(
array(
'courses' => false,
'fix' => false,
'help' => false
),
array(
'h' => 'help',
'c' => 'courses',
'f' => 'fix'
)
);
if ($options['help'] || empty($options['courses'])) {
$help =
"Checks and fixes that course modules and sections reference each other correctly.
Compares DB fields course_sections.sequence and course_modules.section
checking that:
- course_sections.sequence contains each module id not more than once in the course
- for each moduleid from course_sections.sequence the field course_modules.section
refers to the same section id (this means course_sections.sequence is more
important if they are different)
- each module in the course is present in one of course_sections.sequence
- section sequences do not contain non-existing course modules
If there are any mismatches, the message is displayed. If --fix is specified,
the records in DB are corrected.
This script may run for a long time on big systems if called for all courses.
Avoid executing the script when another user may simultaneously edit any of the
courses being checked (recommended to run in mainenance mode).
Options:
-c, --courses List courses that need to be checked (comma-separated
values or * for all). Required
-f, --fix Fix the mismatches in DB. If not specified check only and
report problems to STDERR
-h, --help Print out this help
Example:
\$sudo -u www-data /usr/bin/php admin/cli/fix_course_sequence.php --courses=*
\$sudo -u www-data /usr/bin/php admin/cli/fix_course_sequence.php --courses=2,3,4 --fix
";
echo $help;
die;
}
$courseslist = preg_split('/\s*,\s*/', $options['courses'], -1, PREG_SPLIT_NO_EMPTY);
if (in_array('*', $courseslist)) {
$where = '';
$params = array();
} else {
list($sql, $params) = $DB->get_in_or_equal($courseslist, SQL_PARAMS_NAMED, 'id');
$where = 'WHERE id '. $sql;
}
$coursescount = $DB->get_field_sql('SELECT count(id) FROM {course} '. $where, $params);
if (!$coursescount) {
cli_error('No courses found');
}
echo "Checking $coursescount courses...\n\n";
require_once($CFG->dirroot. '/course/lib.php');
$problems = array();
$courses = $DB->get_fieldset_sql('SELECT id FROM {course} '. $where, $params);
foreach ($courses as $courseid) {
$errors = course_integrity_check($courseid, null, null, true, empty($options['fix']));
if ($errors) {
if (!empty($options['fix'])) {
// Reset the course cache to make sure cache is recalculated next time the course is viewed.
rebuild_course_cache($courseid, true);
}
foreach ($errors as $error) {
cli_problem($error);
}
$problems[] = $courseid;
} else {
echo "Course [$courseid] is OK\n";
}
}
if (!count($problems)) {
echo "\n...All courses are OK\n";
} else {
if (!empty($options['fix'])) {
echo "\n...Found and fixed ".count($problems)." courses with problems". "\n";
} else {
echo "\n...Found ".count($problems)." courses with problems. To fix run:\n";
echo "\$sudo -u www-data /usr/bin/php admin/cli/fix_course_sequence.php --courses=".join(',', $problems)." --fix". "\n";
}
}
-1
View File
@@ -73,7 +73,6 @@ foreach ($rs as $user) {
echo "Redeleting user $user->id: $user->username ($user->email)\n";
delete_user($user);
}
$rs->close();
cli_heading('Deleting all leftovers');
-138
View File
@@ -1,138 +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/>.
/**
* Fix orphaned calendar events that were broken by MDL-67494.
*
* This script will look for all the calendar events which userids
* where broken by a wrong upgrade step, affecting to Moodle 3.9.5
* and up.
*
* It performs checks to both:
* a) Detect if the site was affected (ran the wrong upgrade step).
* b) Look for orphaned calendar events, categorising them as:
* - standard: site / category / course / group / user events
* - subscription: events created via subscriptions.
* - action: normal action events, created to show common important dates.
* - override: user and group override events, particular, that some activities support.
* - custom: other events, not being any of the above, common or particular.
* By specifying it (--fix) try to recover as many broken events (missing userid) as
* possible. Standard, subscription, action, override events in core are fully supported but
* override or custom events should be fixed by each plugin as far as there isn't any standard
* API (plugin-wise) to launch a rebuild of the calendar events.
*
* @package core
* @copyright 2021 onwards Simey Lameze <simey@moodle.com>
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require_once(__DIR__ . '/../../config.php');
require_once($CFG->libdir . "/clilib.php");
require_once($CFG->libdir . '/db/upgradelib.php');
// Supported options.
$long = ['fix' => false, 'help' => false];
$short = ['f' => 'fix', 'h' => 'help'];
// CLI options.
[$options, $unrecognized] = cli_get_params($long, $short);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
if ($options['help']) {
$help = <<<EOT
Fix orphaned calendar events.
This script detects calendar events that have had their
userid lost. By default it will perform various checks
and report them, showing the site status in an easy way.
Also, optionally (--fix), it wil try to recover as many
lost userids as possible from different sources. Note that
this script aims to process well-know events in core,
leaving custom events in 3rd part plugins mostly unmodified
because there isn't any consistent way to regenerate them.
For more details: https://tracker.moodle.org/browse/MDL-71156
Options:
-h, --help Print out this help.
-f, --fix Fix the orphaned calendar events in the DB.
If not specified only check and report problems to STDERR.
Usage:
- Only report: \$ sudo -u www-data /usr/bin/php admin/cli/fix_orphaned_calendar_events.php
- Report and fix: \$ sudo -u www-data /usr/bin/php admin/cli/fix_orphaned_calendar_events.php -f
EOT;
cli_writeln($help);
die;
}
// Check various usual pre-requisites.
if (empty($CFG->version)) {
cli_error('Database is not yet installed.');
}
$admin = get_admin();
if (!$admin) {
cli_error('Error: No admin account was found.');
}
if (moodle_needs_upgrading()) {
cli_error('Moodle upgrade pending, script execution suspended.');
}
// Do everything as admin by default.
\core\session\manager::set_user($admin);
// Report current site status.
cli_heading('Checking the site status');
$needsfix = upgrade_calendar_site_status();
// Report current calendar events status.
cli_heading('Checking the calendar events status');
$info = upgrade_calendar_events_status();
$hasbadevents = $info['total']->bad > 0 || $info['total']->bad != $info['other']->bad;
$needsfix = $needsfix || $hasbadevents;
// If, selected, fix as many calendar events as possible.
if ($options['fix']) {
// If the report has told us that the fix was not needed... ask for confirmation!
if (!$needsfix) {
cli_writeln("This site DOES NOT NEED to run the calendar events fix.");
$input = cli_input('Are you completely sure that you want to run the fix? (y/N)', 'N', ['y', 'Y', 'n', 'N']);
if (strtolower($input) != 'y') {
exit(0);
}
cli_writeln("");
}
cli_heading('Fixing as many as possible calendar events');
upgrade_calendar_events_fix_remaining($info);
// Report current (after fix) calendar events status.
cli_heading('Checking the calendar events status (after fix)');
upgrade_calendar_events_status();
} else if ($needsfix) {
// Fix option was not provided but problem events have been found. Notify the user and provide info how to fix these events.
cli_writeln("This site NEEDS to run the calendar events fix!");
cli_writeln("To fix the calendar events, re-run this script with the --fix option.");
}
@@ -1,103 +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 fixes orphaned question categories.
*
* Orphaned question categories have had their associated context deleted
* but the category itself remains in the database with an invalid context.
*
* @package core
* @subpackage cli
* @copyright 2013 Tyler Bannister (tyler.bannister@remote-learner.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->libdir.'/questionlib.php');
$long = array('fix' => false, 'help' => false);
$short = array('f' => 'fix', 'h' => 'help');
// Now get cli options.
list($options, $unrecognized) = cli_get_params($long, $short);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
if ($options['help']) {
$help =
"Fix orphaned question categories.
This scripts detects question categories that have had their
context deleted, thus severing them from their original purpose.
This script will find the orphaned categories and delete the unused
questions in each category found. Used questions will not be
deleted, instead they will be moved to a rescue question category.
Options:
-h, --help Print out this help
-f, --fix Fix the orphaned question categories in the DB.
If not specified only check and report problems to STDERR.
Example:
\$sudo -u www-data /usr/bin/php admin/cli/fix_orphaned_question_categories.php
\$sudo -u www-data /usr/bin/php admin/cli/fix_orphaned_question_categories.php -f
";
echo $help;
die;
}
cli_heading('Checking for orphaned categories');
$sql = 'SELECT qc.id, qc.contextid, qc.name
FROM {question_categories} qc
LEFT JOIN {context} c ON qc.contextid = c.id
WHERE c.id IS NULL';
$categories = $DB->get_recordset_sql($sql);
$i = 0;
foreach ($categories as $category) {
$i += 1;
echo "Found orphaned category: {$category->name}\n";
if (!empty($options['fix'])) {
echo "Cleaning...";
// One transaction per category.
$transaction = $DB->start_delegated_transaction();
question_category_delete_safe($category);
$transaction->allow_commit();
echo " Done!\n";
}
}
if (($i > 0) && !empty($options['fix'])) {
echo "Found and removed {$i} orphaned question categories\n";
} else if ($i > 0) {
echo "Found {$i} orphaned question categories. To fix, run:\n";
echo "\$sudo -u www-data /usr/bin/php admin/cli/fix_orphaned_question_categories.php --fix\n";
} else {
echo "No orphaned question categories found.\n";
}
$categories->close();
-75
View File
@@ -1,75 +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/>.
/**
* Generates a secure key for the current server (presuming it does not already exist).
*
* @package core_admin
* @copyright 2020 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use \core\encryption;
define('CLI_SCRIPT', true);
require(__DIR__ . '/../../config.php');
require_once($CFG->libdir . '/clilib.php');
// Get cli options.
[$options, $unrecognized] = cli_get_params(
['help' => false, 'method' => null],
['h' => 'help']);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
if ($options['help']) {
echo "Generate secure key
This script manually creates a secure key within the secret data root folder (configured in
config.php as \$CFG->secretdataroot). You must run it using an account with access to write
to that folder.
In normal use Moodle automatically creates the key; this script is intended when setting up
a new Moodle system, for cases where the secure folder is not on shared storage and the key
may be manually installed on multiple servers.
Options:
-h, --help Print out this help
--method <method> Generate key for specified encryption method instead of default (sodium)
Example:
php admin/cli/generate_key.php
";
exit;
}
$method = $options['method'];
if (encryption::key_exists($method)) {
echo 'Key already exists: ' . encryption::get_key_file($method) . "\n";
exit;
}
// Creates key with default permissions (no chmod).
echo "Generating key...\n";
encryption::create_key($method, false);
echo "\nKey created: " . encryption::get_key_file($method) . "\n\n";
echo "If the key folder is not shared storage, then key files should be copied to all servers.\n";
+175 -345
View File
@@ -36,12 +36,6 @@ if (isset($_SERVER['REMOTE_ADDR'])) {
exit(1);
}
// Force OPcache reset if used, we do not want any stale caches
// when preparing test environment.
if (function_exists('opcache_reset')) {
opcache_reset();
}
$help =
"Command line Moodle installer, creates config.php and initializes database.
Please note you must execute this script with the same uid as apache
@@ -64,26 +58,19 @@ Options:
--dbname=NAME Database name. Default is moodle
--dbuser=USERNAME Database user. Default is root
--dbpass=PASSWORD Database password. Default is blank
--dbport=NUMBER Use database port.
--dbsocket=PATH Use database socket, 1 means default. Available for some databases only.
--dbsocket Use database sockets. Available for some databases only.
--prefix=STRING Table prefix for above database tables. Default is mdl_
--fullname=STRING The fullname of the site
--shortname=STRING The shortname of the site
--summary=STRING The summary to be displayed on the front page
--adminuser=USERNAME Username for the moodle admin account. Default is admin
--adminpass=PASSWORD Password for the moodle admin account,
required in non-interactive mode.
--adminemail=STRING Email address for the moodle admin account.
--sitepreset=STRING Admin site preset to be applied during the installation process.
--supportemail=STRING Email address for support and help.
--upgradekey=STRING The upgrade key to be set in the config.php, leave empty to not set it.
--non-interactive No interactive questions, installation fails if any
problem encountered.
--agree-license Indicates agreement with software license,
required in non-interactive mode.
--allow-unstable Install even if the version is not marked as stable yet,
required in non-interactive mode.
--skip-database Stop the installation before installing the database.
-h, --help Print out this help
Example:
@@ -92,7 +79,7 @@ Example:
// distro specific customisation
$distrolibfile = __DIR__.'/../../install/distrolib.php';
$distrolibfile = dirname(dirname(dirname(__FILE__))).'/install/distrolib.php';
$distro = null;
if (file_exists($distrolibfile)) {
require_once($distrolibfile);
@@ -102,7 +89,7 @@ if (file_exists($distrolibfile)) {
}
// Nothing to do if config.php exists
$configfile = __DIR__.'/../../config.php';
$configfile = dirname(dirname(dirname(__FILE__))).'/config.php';
if (file_exists($configfile)) {
require($configfile);
require_once($CFG->libdir.'/clilib.php');
@@ -126,11 +113,10 @@ $olddir = getcwd();
chdir(dirname($_SERVER['argv'][0]));
// Servers should define a default timezone in php.ini, but if they don't then make sure something is defined.
if (!function_exists('date_default_timezone_set') or !function_exists('date_default_timezone_get')) {
fwrite(STDERR, "Timezone functions are not available.\n");
exit(1);
// This is a quick hack. Ideally we should ask the admin for a value. See MDL-22625 for more on this.
if (function_exists('date_default_timezone_set') and function_exists('date_default_timezone_get')) {
@date_default_timezone_set(@date_default_timezone_get());
}
date_default_timezone_set(@date_default_timezone_get());
// make sure PHP errors are displayed - helps with diagnosing of problems
@error_reporting(E_ALL);
@@ -144,19 +130,19 @@ define('MOODLE_INTERNAL', true);
// Disables all caching.
define('CACHE_DISABLE_ALL', true);
define('PHPUNIT_TEST', false);
define('IGNORE_COMPONENT_CACHE', true);
// Check that PHP is of a sufficient version as soon as possible.
require_once(__DIR__.'/../../lib/phpminimumversionlib.php');
moodle_require_minimum_php_version();
// Check that PHP is of a sufficient version
if (version_compare(phpversion(), "5.3.3") < 0) {
$phpversion = phpversion();
// do NOT localise - lang strings would not work here and we CAN NOT move it after installib
fwrite(STDERR, "Moodle 2.5 or later requires at least PHP 5.3.3 (currently using version $phpversion).\n");
fwrite(STDERR, "Please upgrade your server software or install older Moodle version.\n");
exit(1);
}
// set up configuration
global $CFG;
$CFG = new stdClass();
$CFG->lang = 'en';
$CFG->dirroot = dirname(dirname(__DIR__));
$CFG->dirroot = dirname(dirname(dirname(__FILE__)));
$CFG->libdir = "$CFG->dirroot/lib";
$CFG->wwwroot = "http://localhost";
$CFG->httpswwwroot = $CFG->wwwroot;
@@ -164,27 +150,18 @@ $CFG->docroot = 'http://docs.moodle.org';
$CFG->running_installer = true;
$CFG->early_install_lang = true;
$CFG->ostype = (stristr(PHP_OS, 'win') && !stristr(PHP_OS, 'darwin')) ? 'WINDOWS' : 'UNIX';
$CFG->dboptions = array();
$CFG->debug = (E_ALL | E_STRICT);
$CFG->debugdisplay = true;
$CFG->debugdeveloper = true;
$parts = explode('/', str_replace('\\', '/', dirname(__DIR__)));
$parts = explode('/', str_replace('\\', '/', dirname(dirname(__FILE__))));
$CFG->admin = array_pop($parts);
//point pear include path to moodles lib/pear so that includes and requires will search there for files before anywhere else
//the problem is that we need specific version of quickforms and hacked excel files :-(
ini_set('include_path', $CFG->libdir.'/pear' . PATH_SEPARATOR . ini_get('include_path'));
require_once($CFG->libdir.'/classes/component.php');
require_once($CFG->libdir.'/classes/text.php');
require_once($CFG->libdir.'/classes/string_manager.php');
require_once($CFG->libdir.'/classes/string_manager_install.php');
require_once($CFG->libdir.'/classes/string_manager_standard.php');
require_once($CFG->libdir.'/installlib.php');
require_once($CFG->libdir.'/clilib.php');
require_once($CFG->libdir.'/setuplib.php');
require_once($CFG->libdir.'/textlib.class.php');
require_once($CFG->libdir.'/weblib.php');
require_once($CFG->libdir.'/dmllib.php');
require_once($CFG->libdir.'/moodlelib.php');
@@ -193,31 +170,15 @@ require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/componentlib.class.php');
require_once($CFG->dirroot.'/cache/lib.php');
// Register our classloader.
\core_component::register_autoloader();
require($CFG->dirroot.'/version.php');
$CFG->target_release = $release;
\core\session\manager::init_empty_session();
global $SESSION;
global $USER;
global $COURSE;
$COURSE = new stdClass();
$COURSE->id = 1;
global $SITE;
$SITE = $COURSE;
define('SITEID', 1);
//Database types
$databases = array('mysqli' => moodle_database::get_driver_instance('mysqli', 'native'),
'auroramysql' => moodle_database::get_driver_instance('auroramysql', 'native'),
'mariadb'=> moodle_database::get_driver_instance('mariadb', 'native'),
'pgsql' => moodle_database::get_driver_instance('pgsql', 'native'),
'oci' => moodle_database::get_driver_instance('oci', 'native'),
'sqlsrv' => moodle_database::get_driver_instance('sqlsrv', 'native'), // MS SQL*Server PHP driver
'mssql' => moodle_database::get_driver_instance('mssql', 'native'), // FreeTDS driver
);
foreach ($databases as $type=>$database) {
if ($database->driver_installed() !== true) {
@@ -237,28 +198,21 @@ list($options, $unrecognized) = cli_get_params(
'chmod' => isset($distro->directorypermissions) ? sprintf('%04o',$distro->directorypermissions) : '2777', // let distros set dir permissions
'lang' => $CFG->lang,
'wwwroot' => '',
'dataroot' => empty($distro->dataroot) ? str_replace('\\', '/', dirname(dirname(dirname(__DIR__))).'/moodledata'): $distro->dataroot, // initialised later after including libs or by distro
'dataroot' => empty($distro->dataroot) ? str_replace('\\', '/', dirname(dirname(dirname(dirname(__FILE__)))).'/moodledata'): $distro->dataroot, // initialised later after including libs or by distro
'dbtype' => empty($distro->dbtype) ? $defaultdb : $distro->dbtype, // let distro skip dbtype selection
'dbhost' => empty($distro->dbhost) ? 'localhost' : $distro->dbhost, // let distros set dbhost
'dbname' => 'moodle',
'dbuser' => empty($distro->dbuser) ? 'root' : $distro->dbuser, // let distros set dbuser
'dbpass' => '',
'dbport' => '',
'dbsocket' => '',
'dbsocket' => false,
'prefix' => 'mdl_',
'fullname' => '',
'shortname' => '',
'summary' => '',
'adminuser' => 'admin',
'adminpass' => '',
'adminemail' => '',
'sitepreset' => '',
'supportemail' => '',
'upgradekey' => '',
'non-interactive' => false,
'agree-license' => false,
'allow-unstable' => false,
'skip-database' => false,
'help' => false
),
array(
@@ -267,21 +221,13 @@ list($options, $unrecognized) = cli_get_params(
);
$interactive = empty($options['non-interactive']);
$skipdatabase = $options['skip-database'];
// set up language
$lang = clean_param($options['lang'], PARAM_SAFEDIR);
$languages = get_string_manager()->get_list_of_translations();
if (array_key_exists($lang, $languages)) {
if (file_exists($CFG->dirroot.'/install/lang/'.$lang)) {
$CFG->lang = $lang;
}
// Set up site admin preset.
$sitepreset = clean_param($options['sitepreset'], PARAM_RAW);
if (!empty($sitepreset)) {
$CFG->setsitepresetduringinstall = $sitepreset;
}
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
@@ -293,41 +239,28 @@ if ($options['help']) {
}
//Print header
cli_logo();
echo PHP_EOL;
echo get_string('cliinstallheader', 'install', $CFG->target_release)."\n";
//Fist select language
if ($interactive) {
cli_separator();
$languages = get_string_manager()->get_list_of_translations();
// Do not put the langs into columns because it is not compatible with RTL.
$langlist = implode("\n", $languages);
$default = $CFG->lang;
cli_heading(get_string('chooselanguagehead', 'install'));
if (array_key_exists($default, $languages)) {
echo $default.' - '.$languages[$default]."\n";
}
if ($default !== 'en') {
echo 'en - English (en)'."\n";
}
echo '? - '.get_string('availablelangs', 'install')."\n";
cli_heading(get_string('availablelangs', 'install'));
echo $langlist."\n";
$prompt = get_string('clitypevaluedefault', 'admin', $CFG->lang);
$error = '';
do {
echo $error;
$input = cli_input($prompt, $default);
$input = clean_param($input, PARAM_SAFEDIR);
if ($input === '?') {
echo implode("\n", $languages)."\n";
$error = "\n";
if (!file_exists($CFG->dirroot.'/install/lang/'.$input)) {
$error = get_string('cliincorrectvalueretry', 'admin')."\n";
} else {
$input = clean_param($input, PARAM_SAFEDIR);
if (!array_key_exists($input, $languages)) {
$error = get_string('cliincorrectvalueretry', 'admin')."\n";
} else {
$error = '';
}
$error = '';
}
} while ($error !== '');
$CFG->lang = $input;
@@ -361,8 +294,6 @@ if ($interactive) {
}
}
$CFG->directorypermissions = $chmod;
$CFG->filepermissions = ($CFG->directorypermissions & 0666);
$CFG->umaskpermissions = (($CFG->directorypermissions & 0777) ^ 0777);
//We need wwwroot before we test dataroot
$wwwroot = clean_param($options['wwwroot'], PARAM_URL);
@@ -399,6 +330,7 @@ if ($interactive) {
$CFG->wwwroot = $wwwroot;
$CFG->httpswwwroot = $CFG->wwwroot;
//We need dataroot before lang download
$CFG->dataroot = $options['dataroot'];
if ($interactive) {
@@ -448,10 +380,8 @@ if ($interactive) {
cli_error(get_string('pathserrcreatedataroot', 'install', $a));
}
}
$CFG->tempdir = $CFG->dataroot.'/temp';
$CFG->backuptempdir = $CFG->tempdir.'/backup';
$CFG->cachedir = $CFG->dataroot.'/cache';
$CFG->localcachedir = $CFG->dataroot.'/localcache';
$CFG->tempdir = $CFG->dataroot.'/temp';
$CFG->cachedir = $CFG->dataroot.'/cache';
// download required lang packs
if ($CFG->lang !== 'en') {
@@ -521,105 +451,72 @@ if ($interactive) {
$database = $databases[$CFG->dbtype];
// We cannot do any validation until all DB connection data is provided.
$hintdatabase = '';
do {
echo $hintdatabase;
// Ask for db host.
if ($interactive) {
cli_separator();
cli_heading(get_string('databasehost', 'install'));
if ($options['dbhost'] !== '') {
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbhost']);
} else {
$prompt = get_string('clitypevalue', 'admin');
}
$CFG->dbhost = cli_input($prompt, $options['dbhost']);
// ask for db host
if ($interactive) {
cli_separator();
cli_heading(get_string('databasehost', 'install'));
if ($options['dbhost'] !== '') {
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbhost']);
} else {
$CFG->dbhost = $options['dbhost'];
$prompt = get_string('clitypevalue', 'admin');
}
$CFG->dbhost = cli_input($prompt, $options['dbhost']);
// Ask for db name.
if ($interactive) {
cli_separator();
cli_heading(get_string('databasename', 'install'));
if ($options['dbname'] !== '') {
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbname']);
} else {
$prompt = get_string('clitypevalue', 'admin');
}
$CFG->dbname = cli_input($prompt, $options['dbname']);
} else {
$CFG->dbhost = $options['dbhost'];
}
// ask for db name
if ($interactive) {
cli_separator();
cli_heading(get_string('databasename', 'install'));
if ($options['dbname'] !== '') {
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbname']);
} else {
$CFG->dbname = $options['dbname'];
$prompt = get_string('clitypevalue', 'admin');
}
$CFG->dbname = cli_input($prompt, $options['dbname']);
// Ask for db prefix.
if ($interactive) {
cli_separator();
cli_heading(get_string('dbprefix', 'install'));
//TODO: solve somehow the prefix trouble for oci.
if ($options['prefix'] !== '') {
$prompt = get_string('clitypevaluedefault', 'admin', $options['prefix']);
} else {
$prompt = get_string('clitypevalue', 'admin');
}
$CFG->prefix = cli_input($prompt, $options['prefix']);
} else {
$CFG->dbname = $options['dbname'];
}
// ask for db prefix
if ($interactive) {
cli_separator();
cli_heading(get_string('dbprefix', 'install'));
//TODO: solve somehow the prefix trouble for oci
if ($options['prefix'] !== '') {
$prompt = get_string('clitypevaluedefault', 'admin', $options['prefix']);
} else {
$CFG->prefix = $options['prefix'];
$prompt = get_string('clitypevalue', 'admin');
}
$CFG->prefix = cli_input($prompt, $options['prefix']);
// Ask for db port.
if ($interactive) {
cli_separator();
cli_heading(get_string('databaseport', 'install'));
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbport']);
$CFG->dboptions['dbport'] = (int) cli_input($prompt, $options['dbport']);
} else {
$CFG->prefix = $options['prefix'];
}
// ask for db user
if ($interactive) {
cli_separator();
cli_heading(get_string('databaseuser', 'install'));
if ($options['dbuser'] !== '') {
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbuser']);
} else {
$CFG->dboptions['dbport'] = (int) $options['dbport'];
}
if ($CFG->dboptions['dbport'] <= 0) {
$CFG->dboptions['dbport'] = '';
$prompt = get_string('clitypevalue', 'admin');
}
$CFG->dbuser = cli_input($prompt, $options['dbuser']);
// Ask for db socket.
if ($CFG->ostype === 'WINDOWS') {
$CFG->dboptions['dbsocket'] = '';
} else if ($interactive and empty($CFG->dboptions['dbport'])) {
cli_separator();
cli_heading(get_string('databasesocket', 'install'));
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbsocket']);
$CFG->dboptions['dbsocket'] = cli_input($prompt, $options['dbsocket']);
} else {
$CFG->dboptions['dbsocket'] = $options['dbsocket'];
}
// Ask for db user.
if ($interactive) {
cli_separator();
cli_heading(get_string('databaseuser', 'install'));
if ($options['dbuser'] !== '') {
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbuser']);
} else {
$prompt = get_string('clitypevalue', 'admin');
}
$CFG->dbuser = cli_input($prompt, $options['dbuser']);
} else {
$CFG->dbuser = $options['dbuser'];
}
// Ask for db password.
if ($interactive) {
cli_separator();
cli_heading(get_string('databasepass', 'install'));
} else {
$CFG->dbuser = $options['dbuser'];
}
// ask for db password
if ($interactive) {
cli_separator();
cli_heading(get_string('databasepass', 'install'));
do {
if ($options['dbpass'] !== '') {
$prompt = get_string('clitypevaluedefault', 'admin', $options['dbpass']);
} else {
@@ -627,171 +524,113 @@ do {
}
$CFG->dbpass = cli_input($prompt, $options['dbpass']);
if (function_exists('distro_pre_create_db')) { // Hook for distros needing to do something before DB creation.
$distro = distro_pre_create_db($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix,
array('dbpersist' => 0, 'dbport' => $CFG->dboptions['dbport'], 'dbsocket' => $CFG->dboptions['dbsocket']),
$distro);
if (function_exists('distro_pre_create_db')) { // Hook for distros needing to do something before DB creation
$distro = distro_pre_create_db($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbsocket'=>$options['dbsocket']), $distro);
}
$hintdatabase = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix,
array('dbpersist' => 0, 'dbport' => $CFG->dboptions['dbport'], 'dbsocket' => $CFG->dboptions['dbsocket']));
$hint_database = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbsocket'=>$options['dbsocket']));
} while ($hint_database !== '');
} else {
$CFG->dbpass = $options['dbpass'];
$hintdatabase = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix,
array('dbpersist' => 0, 'dbport' => $CFG->dboptions['dbport'], 'dbsocket' => $CFG->dboptions['dbsocket']));
if ($hintdatabase !== '') {
cli_error(get_string('dbconnectionerror', 'install'));
}
}
} while ($hintdatabase !== '');
// If --skip-database option is provided, we do not need to ask for site fullname, shortname, adminuser, adminpass, adminemail.
// These fields will be requested during the database install part.
if (!$skipdatabase) {
// Ask for fullname.
if ($interactive) {
cli_separator();
cli_heading(get_string('fullsitename', 'moodle'));
if ($options['fullname'] !== '') {
$prompt = get_string('clitypevaluedefault', 'admin', $options['fullname']);
} else {
$prompt = get_string('clitypevalue', 'admin');
}
do {
$options['fullname'] = cli_input($prompt, $options['fullname']);
} while (empty($options['fullname']));
} else {
if (empty($options['fullname'])) {
$a = (object)['option' => 'fullname', 'value' => $options['fullname']];
cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
}
}
// Ask for shortname.
if ($interactive) {
cli_separator();
cli_heading(get_string('shortsitename', 'moodle'));
if ($options['shortname'] !== '') {
$prompt = get_string('clitypevaluedefault', 'admin', $options['shortname']);
} else {
$prompt = get_string('clitypevalue', 'admin');
}
do {
$options['shortname'] = cli_input($prompt, $options['shortname']);
} while (empty($options['shortname']));
} else {
if (empty($options['shortname'])) {
$a = (object)['option' => 'shortname', 'value' => $options['shortname']];
cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
}
}
// Ask for admin user name.
if ($interactive) {
cli_separator();
cli_heading(get_string('cliadminusername', 'install'));
if (!empty($options['adminuser'])) {
$prompt = get_string('clitypevaluedefault', 'admin', $options['adminuser']);
} else {
$prompt = get_string('clitypevalue', 'admin');
}
do {
$options['adminuser'] = cli_input($prompt, $options['adminuser']);
} while (empty($options['adminuser']) or $options['adminuser'] === 'guest');
} else {
if ((empty($options['adminuser']) || $options['adminuser'] === 'guest')) {
$a = (object)['option' => 'adminuser', 'value' => $options['adminuser']];
cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
}
}
// Ask for admin user password.
if ($interactive) {
cli_separator();
cli_heading(get_string('cliadminpassword', 'install'));
$prompt = get_string('clitypevalue', 'admin');
do {
$options['adminpass'] = cli_input($prompt);
} while (empty($options['adminpass']) or $options['adminpass'] === 'admin');
} else {
if ((empty($options['adminpass']) or $options['adminpass'] === 'admin')) {
$a = (object)['option' => 'adminpass', 'value' => $options['adminpass']];
cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
}
}
// Ask for the admin email address.
if ($interactive) {
cli_separator();
cli_heading(get_string('cliadminemail', 'install'));
$prompt = get_string('clitypevaluedefault', 'admin', $options['adminemail']);
$options['adminemail'] = cli_input($prompt, $options['adminemail']);
}
// Validate that the address provided was an e-mail address.
if (!empty($options['adminemail']) && !validate_email($options['adminemail'])) {
$a = (object)['option' => 'adminemail', 'value' => $options['adminemail']];
cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
}
// Ask for the support email address.
if ($interactive) {
cli_separator();
cli_heading(get_string('clisupportemail', 'install'));
$prompt = get_string('clitypevaluedefault', 'admin', $options['supportemail']);
$options['supportemail'] = cli_input($prompt, $options['supportemail']);
}
// Validate that the support email address provided is valid.
if (!empty($options['supportemail']) && !validate_email($options['supportemail'])) {
$a = (object)['option' => 'supportemail', 'value' => $options['supportemail']];
cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
} else {
$CFG->dbpass = $options['dbpass'];
$hint_database = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbsocket'=>$options['dbsocket']));
if ($hint_database !== '') {
cli_error(get_string('dbconnectionerror', 'install'));
}
}
// Ask for the upgrade key.
// ask for fullname
if ($interactive) {
cli_separator();
cli_heading(get_string('upgradekeyset', 'admin'));
if ($options['upgradekey'] !== '') {
$prompt = get_string('clitypevaluedefault', 'admin', $options['upgradekey']);
$options['upgradekey'] = cli_input($prompt, $options['upgradekey']);
cli_heading(get_string('fullsitename', 'moodle'));
if ($options['fullname'] !== '') {
$prompt = get_string('clitypevaluedefault', 'admin', $options['fullname']);
} else {
$prompt = get_string('clitypevalue', 'admin');
$options['upgradekey'] = cli_input($prompt);
}
do {
$options['fullname'] = cli_input($prompt, $options['fullname']);
} while (empty($options['fullname']));
} else {
if (empty($options['fullname'])) {
$a = (object)array('option'=>'fullname', 'value'=>$options['fullname']);
cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
}
}
// Set the upgrade key if it was provided.
if ($options['upgradekey'] !== '') {
$CFG->upgradekey = $options['upgradekey'];
// ask for shortname
if ($interactive) {
cli_separator();
cli_heading(get_string('shortsitename', 'moodle'));
if ($options['shortname'] !== '') {
$prompt = get_string('clitypevaluedefault', 'admin', $options['shortname']);
} else {
$prompt = get_string('clitypevalue', 'admin');
}
do {
$options['shortname'] = cli_input($prompt, $options['shortname']);
} while (empty($options['shortname']));
} else {
if (empty($options['shortname'])) {
$a = (object)array('option'=>'shortname', 'value'=>$options['shortname']);
cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
}
}
// The user does not also need to pass agree-license when --skip-database is provided as the user will need to accept
// the license again in the database install part.
if (!$skipdatabase) {
if ($interactive) {
if (!$options['agree-license']) {
cli_separator();
cli_heading(get_string('copyrightnotice'));
echo "Moodle - Modular Object-Oriented Dynamic Learning Environment\n";
echo get_string('gpl3')."\n\n";
echo get_string('doyouagree')."\n";
$prompt = get_string('cliyesnoprompt', 'admin');
$input = cli_input($prompt, '', array(get_string('clianswerno', 'admin'), get_string('cliansweryes', 'admin')));
if ($input == get_string('clianswerno', 'admin')) {
exit(1);
}
}
// ask for admin user name
if ($interactive) {
cli_separator();
cli_heading(get_string('cliadminusername', 'install'));
if (!empty($options['adminuser'])) {
$prompt = get_string('clitypevaluedefault', 'admin', $options['adminuser']);
} else {
if (!$options['agree-license'] && !$skipdatabase) {
cli_error(get_string('climustagreelicense', 'install'));
$prompt = get_string('clitypevalue', 'admin');
}
do {
$options['adminuser'] = cli_input($prompt, $options['adminuser']);
} while (empty($options['adminuser']) or $options['adminuser'] === 'guest');
} else {
if (empty($options['adminuser']) or $options['adminuser'] === 'guest') {
$a = (object)array('option'=>'adminuser', 'value'=>$options['adminuser']);
cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
}
}
// ask for admin user password
if ($interactive) {
cli_separator();
cli_heading(get_string('cliadminpassword', 'install'));
$prompt = get_string('clitypevalue', 'admin');
do {
$options['adminpass'] = cli_input($prompt);
} while (empty($options['adminpass']) or $options['adminpass'] === 'admin');
} else {
if (empty($options['adminpass']) or $options['adminpass'] === 'admin') {
$a = (object)array('option'=>'adminpass', 'value'=>$options['adminpass']);
cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
}
}
if ($interactive) {
if (!$options['agree-license']) {
cli_separator();
cli_heading(get_string('copyrightnotice'));
echo "Moodle - Modular Object-Oriented Dynamic Learning Environment\n";
echo get_string('gpl3')."\n\n";
echo get_string('doyouagree')."\n";
$prompt = get_string('cliyesnoprompt', 'admin');
$input = cli_input($prompt, '', array(get_string('clianswerno', 'admin'), get_string('cliansweryes', 'admin')));
if ($input == get_string('clianswerno', 'admin')) {
exit(1);
}
}
} else {
if (!$options['agree-license']) {
cli_error(get_string('climustagreelicense', 'install'));
}
}
// Finally we have all info needed for config.php
@@ -833,23 +672,14 @@ if (!$envstatus) {
}
// Test plugin dependencies.
require_once($CFG->libdir . '/pluginlib.php');
$failed = array();
if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
if (!plugin_manager::instance()->all_plugins_ok($version, $failed)) {
cli_problem(get_string('pluginscheckfailed', 'admin', array('pluginslist' => implode(', ', array_unique($failed)))));
cli_error(get_string('pluginschecktodo', 'admin'));
}
if (!$skipdatabase) {
install_cli_database($options, $interactive);
// This needs to happen at the end to ensure it occurs after all caches
// have been purged for the last time.
// This will build a cached version of the current theme for the user
// to immediately start browsing the site.
require_once($CFG->libdir.'/upgradelib.php');
upgrade_themes();
} else {
echo get_string('cliskipdatabase', 'install')."\n";
}
install_cli_database($options, $interactive);
echo get_string('cliinstallfinished', 'install')."\n";
exit(0); // 0 means success
+16 -49
View File
@@ -36,12 +36,6 @@ if (isset($_SERVER['REMOTE_ADDR'])) {
exit(1);
}
// Force OPcache reset if used, we do not want any stale caches
// when preparing test environment.
if (function_exists('opcache_reset')) {
opcache_reset();
}
$help =
"Advanced command line Moodle database installer.
Please note you must execute this script with the same uid as apache.
@@ -52,24 +46,26 @@ Options:
--lang=CODE Installation and default site language. Default is en.
--adminuser=USERNAME Username for the moodle admin account. Default is admin.
--adminpass=PASSWORD Password for the moodle admin account.
--adminemail=STRING Email address for the moodle admin account.
--agree-license Indicates agreement with software license.
--fullname=STRING Name of the site
--shortname=STRING Name of the site
--summary=STRING The summary to be displayed on the front page
--supportemail=STRING Email address for support and help.
-h, --help Print out this help
Example:
\$sudo -u www-data /usr/bin/php admin/cli/install_database.php --lang=cs --adminpass=soMePass123 --agree-license
";
// Check that PHP is of a sufficient version as soon as possible.
require_once(__DIR__.'/../../lib/phpminimumversionlib.php');
moodle_require_minimum_php_version();
// Check that PHP is of a sufficient version
if (version_compare(phpversion(), "5.3.3") < 0) {
$phpversion = phpversion();
// do NOT localise - lang strings would not work here and we CAN NOT move it after installib
fwrite(STDERR, "Moodle 2.5 or later requires at least PHP 5.3.3 (currently using version $phpversion).\n");
fwrite(STDERR, "Please upgrade your server software or install older Moodle version.\n");
exit(1);
}
// Nothing to do if config.php does not exist
$configfile = __DIR__.'/../../config.php';
$configfile = dirname(dirname(dirname(__FILE__))).'/config.php';
if (!file_exists($configfile)) {
fwrite(STDERR, 'config.php does not exist, can not continue'); // do not localize
fwrite(STDERR, "\n");
@@ -84,6 +80,11 @@ require_once($CFG->libdir.'/installlib.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/componentlib.class.php');
// make sure no tables are installed yet
if ($DB->get_tables() ) {
cli_error(get_string('clitablesexist', 'install'));
}
$CFG->early_install_lang = true;
get_string_manager(true);
@@ -95,11 +96,8 @@ list($options, $unrecognized) = cli_get_params(
'lang' => 'en',
'adminuser' => 'admin',
'adminpass' => '',
'adminemail' => '',
'fullname' => '',
'shortname' => '',
'summary' => '',
'supportemail' => '',
'agree-license' => false,
'help' => false
),
@@ -108,22 +106,12 @@ list($options, $unrecognized) = cli_get_params(
)
);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
// We show help text even if tables are installed.
if ($options['help']) {
echo $help;
die;
}
// Make sure no tables are installed yet.
if ($DB->get_tables() ) {
cli_error(get_string('clitablesexist', 'install'));
}
if (!$options['agree-license']) {
cli_error('You have to agree to the license. --help prints out the help'); // TODO: localize
}
@@ -132,21 +120,6 @@ if ($options['adminpass'] === true or $options['adminpass'] === '') {
cli_error('You have to specify admin password. --help prints out the help'); // TODO: localize
}
// Validate that the address provided was an e-mail address.
if (!empty($options['adminemail']) && !validate_email($options['adminemail'])) {
$a = (object) array('option' => 'adminemail', 'value' => $options['adminemail']);
cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
}
// Validate that the supportemail provided was an e-mail address.
if (!empty($options['supportemail']) && !validate_email($options['supportemail'])) {
$a = (object) [
'option' => 'supportemail',
'value' => $options['supportemail']
];
cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
}
$options['lang'] = clean_param($options['lang'], PARAM_SAFEDIR);
if (!file_exists($CFG->dirroot.'/install/lang/'.$options['lang'])) {
$options['lang'] = 'en';
@@ -188,20 +161,14 @@ if (!$envstatus) {
}
// Test plugin dependencies.
require_once($CFG->libdir . '/pluginlib.php');
$failed = array();
if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
if (!plugin_manager::instance()->all_plugins_ok($version, $failed)) {
cli_problem(get_string('pluginscheckfailed', 'admin', array('pluginslist' => implode(', ', array_unique($failed)))));
cli_error(get_string('pluginschecktodo', 'admin'));
}
install_cli_database($options, true);
// This needs to happen at the end to ensure it occurs after all caches
// have been purged for the last time.
// This will build a cached version of the current theme for the user
// to immediately start browsing the site.
require_once($CFG->libdir.'/upgradelib.php');
upgrade_themes();
echo get_string('cliinstallfinished', 'install')."\n";
exit(0); // 0 means success
-55
View File
@@ -1,55 +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 kill all user sessions without asking for confirmation.
*
* @package core
* @subpackage cli
* @copyright 2017 Alexander Bias <alexander.bias@uni-ulm.de>
* @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(array('help' => false), array('h' => 'help'));
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized), 2);
}
if ($options['help']) {
$help =
"Kill all Moodle sessions
Options:
-h, --help Print out this help
Example:
\$sudo -u www-data /usr/bin/php admin/cli/kill_all_sessions.php
";
echo $help;
exit(0);
}
\core\session\manager::kill_all_sessions();
exit(0);
+11 -128
View File
@@ -24,7 +24,7 @@
define('CLI_SCRIPT', true);
require(__DIR__.'/../../config.php');
require(dirname(dirname(dirname(__FILE__))).'/config.php');
require_once($CFG->libdir.'/clilib.php'); // cli only functions
if ($DB->get_dbfamily() !== 'mysql') {
@@ -53,7 +53,7 @@ Options:
-h, --help Print out this help
Example:
\$ sudo -u www-data /usr/bin/php admin/cli/mysql_collation.php --collation=utf8mb4_unicode_ci
\$ sudo -u www-data /usr/bin/php admin/cli/mysql_collation.php --collation=utf8_general_ci
";
if (!empty($options['collation'])) {
@@ -64,71 +64,6 @@ if (!empty($options['collation'])) {
cli_error("Error: collation '$collation' is not available on this server!");
}
$collationinfo = explode('_', $collation);
$charset = reset($collationinfo);
$engine = strtolower($DB->get_dbengine());
// Do checks for utf8mb4.
if (strpos($collation, 'utf8mb4') === 0) {
// Do we have the right engine?
if ($engine !== 'innodb' && $engine !== 'xtradb') {
cli_error("Error: '$collation' requires InnoDB or XtraDB set as the engine.");
}
// Are we using Barracuda?
if ($DB->get_row_format() != 'Barracuda') {
// Try setting it here.
try {
$DB->execute("SET GLOBAL innodb_file_format=Barracuda");
} catch (dml_exception $e) {
cli_error("Error: '$collation' requires the file format to be set to Barracuda.
An attempt was made to change the format, but it failed. Please try doing this manually.");
}
echo "GLOBAL SETTING: innodb_file_format changed to Barracuda\n";
}
// Is one file per table being used?
if (!$DB->is_file_per_table_enabled()) {
try {
$DB->execute("SET GLOBAL innodb_file_per_table=1");
} catch (dml_exception $e) {
cli_error("Error: '$collation' requires the setting 'innodb_file_per_table' be set to 'ON'.
An attempt was made to change the format, but it failed. Please try doing this manually.");
}
echo "GLOBAL SETTING: innodb_file_per_table changed to 1\n";
}
// Is large prefix set?
if (!$DB->is_large_prefix_enabled()) {
try {
$DB->execute("SET GLOBAL innodb_large_prefix=1");
} catch (dml_exception $e) {
cli_error("Error: '$collation' requires the setting 'innodb_large_prefix' be set to 'ON'.
An attempt was made to change the format, but it failed. Please try doing this manually.");
}
echo "GLOBAL SETTING: innodb_large_prefix changed to 1\n";
}
}
$sql = "SHOW VARIABLES LIKE 'collation_database'";
if (!$dbcollation = $DB->get_record_sql($sql)) {
cli_error("Error: Could not access collation information on the database.");
}
$sql = "SHOW VARIABLES LIKE 'character_set_database'";
if (!$dbcharset = $DB->get_record_sql($sql)) {
cli_error("Error: Could not access character set information on the database.");
}
if ($dbcollation->value !== $collation || $dbcharset->value !== $charset) {
// Try to convert the DB.
echo "Converting database to '$collation' for $CFG->wwwroot:\n";
$sql = "ALTER DATABASE `$CFG->dbname` DEFAULT CHARACTER SET $charset DEFAULT COLLATE = $collation";
try {
$DB->change_database_structure($sql);
} catch (exception $e) {
cli_error("Error: Tried to alter the database with no success. Please try manually changing the database
to the new collation and character set and then run this script again.");
}
echo "DATABASE CONVERTED\n";
}
echo "Converting tables and columns to '$collation' for $CFG->wwwroot:\n";
$prefix = $DB->get_prefix();
$prefix = str_replace('_', '\\_', $prefix);
@@ -145,25 +80,12 @@ if (!empty($options['collation'])) {
$skipped++;
} else {
try {
$DB->change_database_structure("ALTER TABLE `$table->name` CONVERT TO CHARACTER SET $charset COLLATE $collation");
echo "CONVERTED\n";
$converted++;
} catch (ddl_exception $e) {
$result = mysql_set_row_format($table->name, $charset, $collation, $engine);
if ($result) {
echo "CONVERTED\n";
$converted++;
} else {
// We don't know what the problem is. Stop the conversion.
cli_error("Error: Tried to convert $table->name, but there was a problem. Please check the details of this
table and try again.");
die();
}
}
$DB->change_database_structure("ALTER TABLE $table->name DEFAULT COLLATE = $collation");
echo "CONVERTED\n";
$converted++;
}
$sql = "SHOW FULL COLUMNS FROM `$table->name` WHERE collation IS NOT NULL";
$sql = "SHOW FULL COLUMNS FROM $table->name WHERE collation IS NOT NULL";
$rs2 = $DB->get_recordset_sql($sql);
foreach ($rs2 as $column) {
$column = (object)array_change_key_case((array)$column, CASE_LOWER);
@@ -174,36 +96,22 @@ if (!empty($options['collation'])) {
continue;
}
// Check for utf8mb4 collation.
$rowformat = $DB->get_row_format_sql($engine, $collation);
if ($column->type === 'tinytext' or $column->type === 'mediumtext' or $column->type === 'text' or $column->type === 'longtext') {
$notnull = ($column->null === 'NO') ? 'NOT NULL' : 'NULL';
$default = (!is_null($column->default) and $column->default !== '') ? "DEFAULT '$column->default'" : '';
// primary, unique and inc are not supported for texts
$sql = "ALTER TABLE `$table->name`
MODIFY COLUMN $column->field $column->type
CHARACTER SET $charset
COLLATE $collation $notnull $default";
$sql = "ALTER TABLE $table->name MODIFY COLUMN $column->field $column->type COLLATE $collation $notnull $default";
$DB->change_database_structure($sql);
} else if (strpos($column->type, 'varchar') === 0) {
$notnull = ($column->null === 'NO') ? 'NOT NULL' : 'NULL';
$default = !is_null($column->default) ? "DEFAULT '$column->default'" : '';
if ($rowformat != '') {
$sql = "ALTER TABLE `$table->name` $rowformat";
$DB->change_database_structure($sql);
}
$sql = "ALTER TABLE `$table->name`
MODIFY COLUMN $column->field $column->type
CHARACTER SET $charset
COLLATE $collation $notnull $default";
// primary, unique and inc are not supported for texts
$sql = "ALTER TABLE $table->name MODIFY COLUMN $column->field $column->type COLLATE $collation $notnull $default";
$DB->change_database_structure($sql);
} else {
echo "ERROR (unknown column type: $column->type)\n";
$errors++;
$error++;
continue;
}
echo "CONVERTED\n";
@@ -272,9 +180,7 @@ function mysql_get_collations() {
global $DB;
$collations = array();
$sql = "SHOW COLLATION
WHERE Collation LIKE 'utf8\_%' AND Charset = 'utf8'
OR Collation LIKE 'utf8mb4\_%' AND Charset = 'utf8mb4'";
$sql = "SHOW COLLATION WHERE Collation LIKE 'utf8\_%' AND Charset = 'utf8'";
$rs = $DB->get_recordset_sql($sql);
foreach ($rs as $collation) {
$collations[$collation->collation] = $collation->collation;
@@ -303,26 +209,3 @@ function mysql_get_column_collations($tablename) {
$rs->close();
return $collations;
}
function mysql_set_row_format($tablename, $charset, $collation, $engine) {
global $DB;
$sql = "SELECT row_format
FROM INFORMATION_SCHEMA.TABLES
WHERE table_schema = DATABASE() AND table_name = ?";
$rs = $DB->get_record_sql($sql, array($tablename));
if ($rs) {
if ($rs->row_format == 'Compact' || $rs->row_format == 'Redundant') {
$rowformat = $DB->get_row_format_sql($engine, $collation);
// Try to convert to compressed format and then try updating the collation again.
$DB->change_database_structure("ALTER TABLE `$tablename` $rowformat");
$DB->change_database_structure("ALTER TABLE `$tablename` CONVERT TO CHARACTER SET $charset COLLATE $collation");
} else {
// Row format may not be the problem. Can not diagnose problem. Send fail reply.
return false;
}
} else {
return false;
}
return true;
}
-202
View File
@@ -1,202 +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/>.
/**
* MySQL table row compression tool tool.
*
* @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');
if ($DB->get_dbfamily() !== 'mysql') {
cli_error('This script is used for MySQL databases only.');
}
$engine = strtolower($DB->get_dbengine());
if ($engine !== 'innodb' and $engine !== 'xtradb') {
cli_error('This script is for MySQL servers using InnoDB or XtraDB engines only.');
}
list($options, $unrecognized) = cli_get_params(
array('help' => false, 'info' => false, 'list' => false, 'fix' => false, 'showsql' => false),
array('h' => 'help', 'i' => 'info', 'l' => 'list', 'f' => 'fix', 's' => 'showsql')
);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
$help =
"Script for detection of row size problems in MySQL InnoDB tables.
By default InnoDB storage table is using legacy Antelope file format
which has major restriction on database row size.
Use this script to detect and fix database tables with potential data
overflow problems.
Options:
-i, --info Show database information
-l, --list List problematic tables
-f, --fix Attempt to fix all tables (requires SUPER privilege)
-s, --showsql Print SQL statements for fixing of tables
-h, --help Print out this help
Example:
\$ sudo -u www-data /usr/bin/php admin/cli/mysql_compressed_rows.php -l
";
/** @var mysql_sql_generator $generator */
$generator = $DB->get_manager()->generator;
$info = $DB->get_server_info();
$filepertable = $DB->get_record_sql("SHOW VARIABLES LIKE 'innodb_file_per_table'");
$filepertable = $filepertable ? $filepertable->value : '';
$fileformat = $DB->get_record_sql("SHOW VARIABLES LIKE 'innodb_file_format'");
$fileformat = $fileformat ? $fileformat->value : '';
$prefix = $DB->get_prefix();
$database = $CFG->dbname;
if (!empty($options['info'])) {
echo "Database version: " . $info['description'] . "\n";
echo "Database name: $database\n";
echo "Database engine: " . $DB->get_dbengine() . "\n";
echo "innodb_file_per_table: $filepertable\n";
echo "innodb_file_format: $fileformat\n";
exit(0);
} else if (!empty($options['list'])) {
$problem = false;
foreach ($DB->get_tables(false) as $table) {
$columns = $DB->get_columns($table, false);
$size = $generator->guess_antelope_row_size($columns);
$format = $DB->get_row_format($table);
if ($size <= $generator::ANTELOPE_MAX_ROW_SIZE) {
continue;
}
echo str_pad($prefix . $table, 32, ' ', STR_PAD_RIGHT);
echo str_pad($format, 11, ' ', STR_PAD_RIGHT);
if ($format === 'Compact' or $format === 'Redundant') {
$problem = true;
echo " (needs fixing)\n";
} else if ($format !== 'Compressed' and $format !== 'Dynamic') {
echo " (unknown)\n";
} else {
echo "\n";
}
}
if ($problem) {
exit(1);
}
exit(0);
} else if (!empty($options['fix'])) {
$fixtables = array();
foreach ($DB->get_tables(false) as $table) {
$columns = $DB->get_columns($table, false);
$size = $generator->guess_antelope_row_size($columns);
$format = $DB->get_row_format($table);
if ($size <= $generator::ANTELOPE_MAX_ROW_SIZE) {
continue;
}
if ($format === 'Compact' or $format === 'Redundant') {
$fixtables[$table] = $table;
}
}
if (!$fixtables) {
echo "No changes necessary\n";
exit(0);
}
if ($filepertable !== 'ON') {
try {
$DB->execute("SET GLOBAL innodb_file_per_table=1");
} catch (dml_exception $e) {
echo "Cannot enable GLOBAL innodb_file_per_table setting, use --showsql option and execute the statements manually.";
throw $e;
}
}
if ($fileformat !== 'Barracuda') {
try {
$DB->execute("SET GLOBAL innodb_file_format=Barracuda");
} catch (dml_exception $e) {
echo "Cannot change GLOBAL innodb_file_format setting, use --showsql option and execute the statements manually.";
throw $e;
}
}
if (!$DB->is_compressed_row_format_supported(false)) {
echo "MySQL server is not compatible with compressed row format.";
exit(1);
}
foreach ($fixtables as $table) {
$DB->change_database_structure("ALTER TABLE `{$prefix}$table` ROW_FORMAT=Compressed");
echo str_pad($prefix . $table, 32, ' ', STR_PAD_RIGHT) . " ... Compressed\n";
}
exit(0);
} else if (!empty($options['showsql'])) {
$fixtables = array();
foreach ($DB->get_tables(false) as $table) {
$columns = $DB->get_columns($table, false);
$size = $generator->guess_antelope_row_size($columns);
$format = $DB->get_row_format($table);
if ($size <= $generator::ANTELOPE_MAX_ROW_SIZE) {
continue;
}
if ($format === 'Compact' or $format === 'Redundant') {
$fixtables[$table] = $table;
}
}
if (!$fixtables) {
echo "No changes necessary\n";
exit(0);
}
echo "Copy the following SQL statements and execute them using account with SUPER privilege:\n\n";
echo "USE $database;\n";
echo "SET SESSION sql_mode=STRICT_ALL_TABLES;\n";
echo "SET GLOBAL innodb_file_per_table=1;\n";
echo "SET GLOBAL innodb_file_format=Barracuda;\n";
foreach ($fixtables as $table) {
echo "ALTER TABLE `{$prefix}$table` ROW_FORMAT=Compressed;\n";
}
echo "\n";
exit(0);
} else {
echo $help;
die;
}
+180
View File
@@ -0,0 +1,180 @@
<?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/>.
/**
* MySQL engine conversion tool.
*
* @package core
* @subpackage cli
* @copyright 2009 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(dirname(dirname(dirname(__FILE__))).'/config.php');
require_once($CFG->libdir.'/clilib.php'); // cli only functions
if ($DB->get_dbfamily() !== 'mysql') {
cli_error('This function is designed for MySQL databases only!');
}
// now get cli options
list($options, $unrecognized) = cli_get_params(array('help'=>false, 'list'=>false, 'engine'=>false, 'available'=>false),
array('h'=>'help', 'l'=>'list', 'a'=>'available'));
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
$help =
"MySQL engine conversions script.
It is recommended to stop the web server before the conversion.
Do not use MyISAM if possible, because it is not ACID compliant
and does not support transactions.
Options:
--engine=ENGINE Convert MySQL tables to different engine
-l, --list Show table information
-a, --available Show list of available engines
-h, --help Print out this help
Example:
\$sudo -u www-data /usr/bin/php admin/cli/mysql_engine.php --engine=InnoDB
";
if (!empty($options['engine'])) {
$engines = mysql_get_engines();
$engine = clean_param($options['engine'], PARAM_ALPHA);
if (!isset($engines[strtoupper($engine)])) {
cli_error("Error: engine '$engine' is not available on this server!");
}
echo "Converting tables to '$engine' for $CFG->wwwroot:\n";
$prefix = $DB->get_prefix();
$prefix = str_replace('_', '\\_', $prefix);
$sql = "SHOW TABLE STATUS WHERE Name LIKE BINARY '$prefix%'";
$rs = $DB->get_recordset_sql($sql);
$converted = 0;
$skipped = 0;
$errors = 0;
foreach ($rs as $table) {
if (strtoupper($table->engine) === strtoupper($engine)) {
$newengine = mysql_get_table_engine($table->name);
echo str_pad($table->name, 40). " - NO CONVERSION NEEDED ($newengine)\n";
$skipped++;
continue;
}
echo str_pad($table->name, 40). " - ";
try {
$DB->change_database_structure("ALTER TABLE {$table->name} ENGINE = $engine");
$newengine = mysql_get_table_engine($table->name);
if (strtoupper($newengine) !== strtoupper($engine)) {
echo "ERROR ($newengine)\n";
$errors++;
continue;
}
echo "DONE ($newengine)\n";
$converted++;
} catch (moodle_exception $e) {
echo $e->getMessage()."\n";
$errors++;
continue;
}
}
$rs->close();
echo "Converted: $converted, skipped: $skipped, errors: $errors\n";
exit(0); // success
} else if (!empty($options['list'])) {
echo "List of tables for $CFG->wwwroot:\n";
$prefix = $DB->get_prefix();
$prefix = str_replace('_', '\\_', $prefix);
$sql = "SHOW TABLE STATUS WHERE Name LIKE BINARY '$prefix%'";
$rs = $DB->get_recordset_sql($sql);
$counts = array();
foreach ($rs as $table) {
if (isset($counts[$table->engine])) {
$counts[$table->engine]++;
} else {
$counts[$table->engine] = 1;
}
echo str_pad($table->engine, 10);
echo $table->name . "\n";
}
$rs->close();
echo "\n";
echo "Table engines summary for $CFG->wwwroot:\n";
foreach ($counts as $engine => $count) {
echo "$engine: $count\n";
}
exit(0); // success
} else if (!empty($options['available'])) {
echo "List of available MySQL engines for $CFG->wwwroot:\n";
$engines = mysql_get_engines();
foreach ($engines as $engine) {
echo " $engine\n";
}
die;
} else {
echo $help;
die;
}
// ========== Some functions ==============
function mysql_get_engines() {
global $DB;
$sql = "SHOW Engines";
$rs = $DB->get_recordset_sql($sql);
$engines = array();
foreach ($rs as $engine) {
if (strtoupper($engine->support) !== 'YES' and strtoupper($engine->support) !== 'DEFAULT') {
continue;
}
$engines[strtoupper($engine->engine)] = $engine->engine;
if (strtoupper($engine->support) === 'DEFAULT') {
$engines[strtoupper($engine->engine)] .= ' (default)';
}
}
$rs->close();
return $engines;
}
function mysql_get_table_engine($tablename) {
global $DB;
$engine = null;
$sql = "SHOW TABLE STATUS WHERE Name = '$tablename'"; // no special chars expected here
$rs = $DB->get_recordset_sql($sql);
if ($rs->valid()) {
$record = $rs->current();
$engine = $record->engine;
}
$rs->close();
return $engine;
}
+8 -31
View File
@@ -15,8 +15,6 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* CLI script to purge caches without asking for confirmation.
*
* @package core
* @subpackage cli
* @copyright 2011 David Mudrak <david@moodle.com>
@@ -25,19 +23,10 @@
define('CLI_SCRIPT', true);
require(__DIR__.'/../../config.php');
require(dirname(dirname(dirname(__FILE__))).'/config.php');
require_once($CFG->libdir.'/clilib.php');
$longoptions = [
'help' => false,
'muc' => false,
'theme' => false,
'lang' => false,
'js' => false,
'filter' => false,
'other' => false
];
list($options, $unrecognized) = cli_get_params($longoptions, ['h' => 'help']);
list($options, $unrecognized) = cli_get_params(array('help' => false), array('h' => 'help'));
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
@@ -45,32 +34,20 @@ if ($unrecognized) {
}
if ($options['help']) {
// The indentation of this string is "wrong" but this is to avoid a extra whitespace in console output.
$help = <<<EOF
Invalidates Moodle internal caches
Specific caches can be defined (alone or in combination) using arguments. If none are specified,
all caches will be purged.
$help =
"Invalidates all Moodle internal caches
Options:
-h, --help Print out this help
--muc Purge all MUC caches (includes lang cache)
--theme Purge theme cache
--lang Purge language string cache
--js Purge JavaScript cache
--filter Purge text filter cache
--other Purge all file caches and other miscellaneous caches (may include MUC
if using cachestore_file).
Example:
\$ sudo -u www-data /usr/bin/php admin/cli/purge_caches.php
EOF;
\$sudo -u www-data /usr/bin/php admin/cli/purge_caches.php
";
echo $help;
exit(0);
}
purge_caches(array_filter($options));
purge_all_caches();
exit(0);
exit(0);
+13 -40
View File
@@ -26,26 +26,13 @@
define('CLI_SCRIPT', true);
require(__DIR__.'/../../config.php');
require(dirname(dirname(dirname(__FILE__))).'/config.php');
require_once($CFG->libdir.'/clilib.php'); // cli only functions
// Define the input options.
$longparams = array(
'help' => false,
'username' => '',
'password' => '',
'ignore-password-policy' => false
);
$shortparams = array(
'h' => 'help',
'u' => 'username',
'p' => 'password',
'i' => 'ignore-password-policy'
);
// now get cli options
list($options, $unrecognized) = cli_get_params($longparams, $shortparams);
list($options, $unrecognized) = cli_get_params(array('help'=>false),
array('h'=>'help'));
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
@@ -60,43 +47,29 @@ 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
-u, --username=username Specify username to change
-p, --password=newpassword Specify new password
--ignore-password-policy Ignore password policy when setting password
-h, --help Print out this help
Example:
\$sudo -u www-data /usr/bin/php admin/cli/reset_password.php
\$sudo -u www-data /usr/bin/php admin/cli/reset_password.php --username=rosaura --password=jiu3jiu --ignore-password-policy
";
"; //TODO: localize - to be translated later when everything is finished
echo $help;
die;
}
if ($options['username'] == '' ) {
cli_heading('Password reset');
$prompt = "Enter username (manual authentication only)";
$username = cli_input($prompt);
} else {
$username = $options['username'];
}
cli_heading('Password reset'); // TODO: localize
$prompt = "enter username (manual authentication only)"; // TODO: localize
$username = cli_input($prompt);
if (!$user = $DB->get_record('user', array('auth'=>'manual', 'username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id))) {
cli_error("Can not find user '$username'");
}
if ($options['password'] == '' ) {
$prompt = "Enter new password";
$password = cli_input($prompt);
} else {
$password = $options['password'];
}
$prompt = "Enter new password"; // TODO: localize
$password = cli_input($prompt);
$errmsg = '';//prevent eclipse warning
if (!$options['ignore-password-policy'] ) {
if (!check_password_policy($password, $errmsg, $user)) {
cli_error(html_to_text($errmsg, 0));
}
if (!check_password_policy($password, $errmsg)) {
cli_error($errmsg);
}
$hashedpassword = hash_internal_user_password($password);
@@ -105,4 +78,4 @@ $DB->set_field('user', 'password', $hashedpassword, array('id'=>$user->id));
echo "Password changed\n";
exit(0); // 0 means success.
exit(0); // 0 means success

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