Compare commits

..

313 Commits

Author SHA1 Message Date
Eloy Lafuente (stronk7) 3e90b10ad8 Moodle release 2.4.1 2013-01-11 22:23:29 +01:00
Eloy Lafuente (stronk7) 8a646b55f7 weekly release 2.4+ 2013-01-11 12:33:57 +01:00
Eloy Lafuente (stronk7) 72cece6ff4 Merge branch 'install_24_STABLE' of git://git.moodle.cz/moodle-install into MOODLE_24_STABLE 2013-01-11 12:16:34 +01:00
Eloy Lafuente (stronk7) 9b7b6fa33c MDL-37420 rework pg schema (partial backport of 92b00c3) 2013-01-11 10:33:33 +01:00
Eloy Lafuente (stronk7) 43bfcff941 Merge branch 'MDL-37467-blogs_24' of git://github.com/mudrd8mz/moodle into MOODLE_24_STABLE 2013-01-10 18:20:48 +01:00
David Mudrák a4611b9d02 MDL-37467 Do not provide blog posts via RSS when blogging is disabled 2013-01-10 16:57:10 +01:00
Mark Nelson 5be009245b MDL-36741 mod_forum: fixed the function forum_rss_newstuff that was using count_records_sql incorrectly as well as generating RSS content when no data exists 2013-01-10 11:13:45 +01:00
Andrew Robert Nicols a50bf852e4 MDL-36600 Add missing sesskey check when previewing the message 2013-01-10 10:20:45 +08:00
AMOS bot 9c3ef35763 Automatically generated installer lang files 2013-01-10 00:44:11 +00:00
Marina Glancy 562782a48d MDL-37404 add class 'format-xxx' to the body for the current course format 2013-01-09 14:35:31 +13:00
Eloy Lafuente (stronk7) b56324b829 Merge branch 'w51_MDL-37420_m24_pgschema' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-09 01:32:33 +01:00
Andrew Robert Nicols 47ee76344e MDL-36600 user: improve course messaging checks 2013-01-09 09:22:08 +13:00
Eloy Lafuente (stronk7) 844cb63fed Merge branch 'w51_MDL-37152_m24_smtp' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-08 18:02:37 +01:00
Petr Škoda a668f57e75 MDL-37420 add support for db schemas in enrol test 2013-01-08 17:35:59 +01:00
Petr Škoda 543fc34ed9 MDL-37420 add support for more PostgreSQL schemas 2013-01-08 17:35:51 +01:00
Jean-Michel Vedrine 0a32dca4bb MDL-28183 fix for problem during integration
Conflicts:
	question/type/multianswer/question.php
2013-01-08 12:05:45 +01:00
Eloy Lafuente (stronk7) 65a550c484 Merge branch 'MDL-37220_dnd_icon_24' of git://github.com/davosmith/moodle into MOODLE_24_STABLE 2013-01-08 11:54:31 +01:00
Eloy Lafuente (stronk7) 0f83a143b0 Merge branch 'w51_MDL-37186_m24_tinymceconfig' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-08 10:34:15 +01:00
Eloy Lafuente (stronk7) 793c431543 Merge branch 'w51_MDL-37323_m24_dlgstrings' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-08 10:22:15 +01:00
Dan Poltawski 2e25f806fb Merge branch 'MDL-32639-24' of git://github.com/FMCorz/moodle into MOODLE_24_STABLE 2013-01-08 14:46:25 +08:00
Petr Škoda f923f7aefe MDL-xxxx show appropriate instance status on user enrolments page 2013-01-08 14:22:28 +08:00
Jason Fowler cb838a36b2 MDL-36620 - Blog, RSS - Preventing Guests from viewing the RSS of site level blogs 2013-01-08 14:06:47 +08:00
Ankit Agarwal 56f4d0ed80 MDL-37106 calendar: Proper capability checks should be done before processing an ical request 2013-01-08 14:02:54 +08:00
Ankit Agarwal 6c806e8384 MDL-36738 calendar: Display events imported details
We should display a the status of events when a new subscription is added. Although this means we need to redirect through an itermediate page.
2013-01-08 13:58:21 +08:00
Ankit Agarwal 5b6e74c85f MDL-36738 calendar: Ical subscription list should display type of event 2013-01-08 13:58:21 +08:00
Dan Poltawski c33a96f676 MDL-28183 - fix trailing whitespace 2013-01-08 12:18:31 +08:00
Dan Poltawski 9910a5d7eb Merge branch 'MDL-28183_24' of git://github.com/jmvedrine/moodle into MOODLE_24_STABLE 2013-01-08 12:17:58 +08:00
Dan Poltawski 7c8fb69b1c Merge branch 'w51_MDL-37191_m24_plugindelete' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-08 11:16:49 +08:00
Dan Poltawski ef42830b87 Merge branch 'MDL-31641_drag_drop_help_24' of git://github.com/davosmith/moodle into MOODLE_24_STABLE 2013-01-08 10:54:14 +08:00
Charles Fulton 05f8741cee MDL-35130 shibboleth: remove hard-coded string 2013-01-08 10:51:17 +08:00
Dan Poltawski 9a1ac11640 Merge branch 'MDL-36456-24' of git://github.com/FMCorz/moodle into MOODLE_24_STABLE 2013-01-08 10:30:58 +08:00
Dan Poltawski 04ab55dd4f Merge branch 'w51_MDL-37301_m24_coursecatinfoas' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-08 10:27:00 +08:00
Dan Poltawski b2e3fb3dc9 Merge branch 'w51_MDL-37254_m24_courseinfoas' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-08 10:24:11 +08:00
Sam Hemelryk d72a0aa2c8 Merge branch 'wip-mdl-36958-m24' of git://github.com/rajeshtaneja/moodle into MOODLE_24_STABLE 2013-01-08 14:06:50 +13:00
Sam Hemelryk 2be82d5e13 Merge branch 'w51_MDL-35893_m24_perfinstall' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-08 14:00:23 +13:00
David Mudrák 019cc4eae4 MDL-36977 Be more picky when it comes to migrating files in moodle1 backups 2013-01-08 08:54:20 +08:00
David Mudrák 2726d2a6a4 MDL-36977 Add more unit tests for moodle1 backup conversion 2013-01-08 08:54:20 +08:00
Thanh Le 0c49b6c445 MDL-35794 quiz: config for whether Outcomes are advanced settings 2013-01-07 22:40:31 +01:00
Thanh Le 83a23ea878 MDL-35792 question preview: way to update options without restarting.
It is useful, and possible, to update the display options without
restarting the question. You now only have to restart if you are
changing the maximum mark or the behaviour.
2013-01-07 22:36:17 +01:00
Eloy Lafuente (stronk7) f849d1f6fc Merge branch 'w51_MDL-36807_m24_mysocket' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-07 11:44:54 +01:00
Davo Smith 2e8da707a0 MDL-31641 filemanager - display help message when drag and drop upload is not supported 2013-01-07 08:14:13 +00:00
Didier Raboud f4537d5676 MDL-37157: Normalize short answers before comparison
This ensures that ambiguous Unicode representations can be compared sanely.
Also add some tests to ensure it keeps working.

Thanks to Tim Hunt for the syntax check.
2013-01-07 14:00:13 +08:00
Dan Poltawski 850aa189e4 Merge branch 'w51_MDL-37128_m24_rssrestore' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-07 13:57:02 +08:00
Dan Poltawski c5e96ad32f Merge branch 'w51_MDL-36893_m24_mnetestrict' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-07 13:53:19 +08:00
Dan Poltawski 9acc49eb93 Merge branch 'MDL-27884-m24' of git://github.com/ankitagarwal/moodle into MOODLE_24_STABLE 2013-01-07 13:30:34 +08:00
Damyon Wiese d389a2f7f4 MDL-37244 Assignment: Submission comments plugin does not implement comments callbacks.
This allows anyone to view or modify anyone elses submission comments.
2013-01-07 10:49:12 +08:00
Ankit Agarwal 321554d35f MDL-33340 completion: Incorrect logic in hidden field check
Credit to Jody Steele
2013-01-07 10:37:26 +08:00
Simon Coggins 102903f7ab MDL-35991 - use PARAM_LOCALURL for local urls 2013-01-07 10:10:22 +08:00
Dan Poltawski 181b7f5fea Merge branch 'MDL-36667-24' of git://github.com/damyon/moodle into MOODLE_24_STABLE 2013-01-07 09:44:59 +08:00
Sam Hemelryk e04f57c95b Merge branch 'MDL-37161-moodle24' of https://github.com/StudiUM/moodle into MOODLE_24_STABLE 2013-01-07 14:15:17 +13:00
Sam Hemelryk b9639c524d Merge branch 'w51_MDL-36212_m24_entities' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-07 13:44:54 +13:00
Tim Hunt 56003ca491 MDL-37068 qformat_learnwise: fix UTF8 and other issues. 2013-01-07 13:40:24 +13:00
Sam Hemelryk 192cb8f894 Merge branch 'w51_MDL-37281_m24_othersnav' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-07 13:35:04 +13:00
Sam Hemelryk 2d7c42275f Merge branch 'MDL-36855-m24' of git://github.com/ankitagarwal/moodle into MOODLE_24_STABLE 2013-01-07 12:05:07 +13:00
Charles Fulton ac41913cea MDL-4781 forum: show discussion link when user has ability to view discussion 2013-01-07 11:15:32 +13:00
Davo Smith 359c790b1c MDL-35887 course dndupload - correct default used for completion tracking when resources created by drag and drop 2013-01-07 11:07:37 +13:00
Sam Hemelryk 49a66d4b68 Merge branch 'MDL-16730-m24' of git://github.com/ankitagarwal/moodle into MOODLE_24_STABLE 2013-01-07 10:15:11 +13:00
Sam Hemelryk 1a5a5f8e9c Merge branch 'w51_MDL-37105_m24_catlimit' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-07 09:52:50 +13:00
Mark Nelson 3735e3d289 MDL-37359 groups: fixing typo in the web service function create_groupings_parameters 2013-01-07 09:47:43 +13:00
Sam Hemelryk 2c6b79365b Merge branch 'MDL-29327_none_24' of git://github.com/andyjdavis/moodle into MOODLE_24_STABLE 2013-01-07 09:35:07 +13:00
Mark Nelson 1565171e07 MDL-36741 mod_forum: fixed SQL that was generated when either timed posts or groups were enabled 2013-01-07 09:16:35 +13:00
Sam Hemelryk 0c1be2af21 Merge branch 'w51_MDL-37315_m24_ldappaging' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2013-01-07 09:09:25 +13:00
Jean-Philippe Gaudreau e5d70123c9 MDL-37161 Backup: Database error caused by grade book settings when importing or restoring a course 2013-01-04 09:54:42 -05:00
Damyon Wiese 3d6b1bf4e3 MDL-36712: Assignment - Do not always send feedback available emails when grade is created
The entry in the assign_grades table contains grades feedback and other settings such as locked.
Only send the student an email about new feedback if it is actually grades or feedback that
has been added. This is done by using another state for the mailed flag (2) which means "unset".
2013-01-04 14:52:14 +08:00
Andrew Davis b164a8904a MDL-29327 core_grade: removing some code that is messing up the categories and items screen when no grade categories are used 2013-01-04 08:58:34 +08:00
Ankit Agarwal 78ac8558e0 MDL-36855 scorm: update to api get_scorm_question_count()
get_scorm_question_count() should return 0 not 1 when no attempt is made in a scorm by any student
2013-01-03 14:42:39 +08:00
Ankit Agarwal 363ed37e76 MDL-27884 Implement proper username validation during csv import 2013-01-02 14:10:30 +08:00
Ankit Agarwal 3b800be056 MDL-27884 blocks: Correcting config title string
AMOS BEGIN
 CPY [configtitle,block_html],[configtitle,block_tags]
AMOS END
2013-01-02 10:56:58 +08:00
Petr Škoda ef227f1e0f MDL-35893 do not track performance before installation 2013-01-01 15:51:06 +01:00
Petr Škoda 6606784c3f MDL-37323 add support for _dlg strings in our TinyMCE subplugins
Credit goes to Étienne FLORENT, thanks.
2013-01-01 15:26:27 +01:00
AMOS bot a17b3a200e Automatically generated installer lang files 2013-01-01 00:42:59 +00:00
Eloy Lafuente (stronk7) d63a406b02 weekly release 2.4+ 2012-12-30 01:06:09 +01:00
Petr Škoda e6a3c6e1a1 MDL-37315 fix connection reopening in ldap paging 2012-12-30 00:58:20 +01:00
Eloy Lafuente (stronk7) 356513cc80 Merge branch 'install_24_STABLE' of git://git.moodle.cz/moodle-install into MOODLE_24_STABLE 2012-12-30 00:56:49 +01:00
Petr Škoda b1dc798512 MDL-36893 fix mnet E_STRICT issue 2012-12-27 14:33:09 +01:00
Petr Škoda 697871ddcf MDL-37105 prevent fatal error when category limit reached 2012-12-27 13:52:50 +01:00
Petr Škoda 602150068a MDL-37128 fix E_STRICT rss restore issue 2012-12-27 13:40:47 +01:00
AMOS bot eeeac779c8 Automatically generated installer lang files 2012-12-27 00:42:49 +00:00
Petr Škoda c0333c98ea MDL-37301 fix missing AS in select
This was breaking pg 8.3. Credit goes to Thomas Robb, thanks.
2012-12-26 22:31:47 +01:00
Damyon Wiese d59467dfc9 MDL-37037 Assignment Some minor refactoring of the previous patch
This just makes the control flow a bit easier to read.

I also added a continue button to take you back to the grading page.
2012-12-24 12:23:33 +08:00
Michael Aherne c731018d50 MDL-37037 assign Show message if no files to download 2012-12-24 12:23:33 +08:00
Dan Poltawski e17644cca3 Merge branch 'w51_MDL-37233_m24_skype' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-24 11:34:45 +08:00
Dan Poltawski d71b142446 Merge branch 'wip-MDL-37263-m24' of git://github.com/samhemelryk/moodle into MOODLE_24_STABLE 2012-12-24 11:22:48 +08:00
Dan Poltawski b61dd8b299 Merge branch 'wip-MDL-37290-m24' of git://github.com/samhemelryk/moodle into MOODLE_24_STABLE 2012-12-24 10:55:24 +08:00
Dan Poltawski 41bbc35471 Merge branch 'w51_MDL-37283_m24_spell' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-24 10:35:40 +08:00
Sam Hemelryk 0af4bf40ee MDL-37290 course: fixed up navbar when editing categories 2012-12-24 13:06:52 +13:00
Sam Hemelryk b09c5ac837 Merge branch 'wip-MDL-35833-stable24' of git://github.com/phalacee/moodle into MOODLE_24_STABLE 2012-12-24 11:43:46 +13:00
Raymond Wijaya 3f897939b2 MDL-36966 Assignment: Bug - Student submission appears graded by themselves.
Fix a bug when a student editing his submission after it has been graded, it changes the 'graded by' details to the student's name in the feeback summary
2012-12-24 11:18:43 +13:00
Sam Hemelryk 3124654247 Merge branch 'wip-mdl-37015-m24' of git://github.com/rajeshtaneja/moodle into MOODLE_24_STABLE 2012-12-24 10:55:19 +13:00
Sam Hemelryk 9a57563b9f Merge branch 'wip-MDL-35653-MOODLE_24_STABLE' of git://github.com/abgreeve/moodle into MOODLE_24_STABLE 2012-12-24 09:40:44 +13:00
Brian King 190b5e7cbe MDL-37225 reset cachstore_static store when purging 2012-12-24 09:36:13 +13:00
Mark Nelson a8f8537965 MDL-36455 blocks: removed ability to add blocks to the My Moodle page that did not make sense in that context 2012-12-24 09:31:02 +13:00
Petr Škoda f938b1a89b MDL-37283 import tinymce spellchecker 2.0.6.1 2012-12-22 16:45:22 +01:00
Petr Škoda 2a39a4e01c MDL-37281 fix other users current navigation node 2012-12-22 14:59:12 +01:00
Petr Škoda ab7d4ea7ba MDL-37152 remove bogus $mail->IsSMTP() breaking non-smtp mailing
Credit goes to Aurelijus Bruzas, thanks.
2012-12-22 14:48:25 +01:00
Petr Škoda ac4404e0cc MDL-37254 add missing AS keyword 2012-12-22 14:12:30 +01:00
Petr Škoda 0fe16db123 MDL-37233 do not show skype status icon on https sites
Funny that the same company that implemented the IE security warnings is unable to set up SSL on the Skype status server…
2012-12-21 17:27:12 +01:00
Petr Škoda 5c0dd1fadd MDL-37191 do not timeout when uninstalling plugins 2012-12-21 15:59:15 +01:00
Jean-Michel Vedrine a7eec81ec6 MDL-28183 add num parts correct and clear wrong to multianswer 2012-12-21 05:18:02 +01:00
Sam Hemelryk e03782efdd MDL-37263 cache: fixed event invalidation coding error 2012-12-21 10:50:24 +13:00
Petr Škoda eb2d6a23ab MDL-36212 rework html entity conversions
This should resolve all html entity conversion problems in different PHP versions.
2012-12-20 22:48:30 +01:00
Petr Škoda a48b376978 MDL-36807 use our mysql socket logic in adodb tests 2012-12-20 22:41:57 +01:00
Petr Škoda 544f0f9b11 MDL-37186 add the ultimate TinyMCE custom parameter setting 2012-12-20 22:24:46 +01:00
Eloy Lafuente (stronk7) 322af447f5 weekly release 2.4+ 2012-12-20 21:12:50 +01:00
Eloy Lafuente (stronk7) ec39bd51dd Merge branch 'install_24_STABLE' of git://git.moodle.cz/moodle-install into MOODLE_24_STABLE 2012-12-20 20:57:11 +01:00
Eloy Lafuente (stronk7) bbb750e1ba Merge branch 'w50_MDL-37171_m24_outdatedexcel' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-20 10:41:55 +01:00
Jason Fowler 73dd2ee2d5 MDL-35833 - Blocks - Adding alt and title attributes to the docked-block panel close button 2012-12-20 10:28:23 +08:00
Rajesh Taneja a1ccc146ac MDL-37015 Resource: link in onclick should not contain html special char 2012-12-20 10:26:29 +08:00
Dan Poltawski 7a6f073415 Revert "MDL-36704: wrap pending conditions in span tags"
This reverts commit 5e9fd83df8.
2012-12-20 08:45:15 +08:00
AMOS bot 76fa287781 Automatically generated installer lang files 2012-12-20 00:40:36 +00:00
Tim Hunt 94c5a8fe82 MDL-32245 question bank: update URL after moving question between categories.
Also, improve the URL when you go to edit the questions in a category.
2012-12-20 12:12:28 +13:00
Petr Škoda d3fdafc358 MDL-37171 fix some more E_STRICT issues
Credit goes to Mark Nelson, thanks.
2012-12-19 09:55:25 +01:00
Davo Smith 606c07ae3e MDL-37220 course dndupload - improved 'add files' icon 2012-12-19 07:46:23 +00:00
Adrian Greeve c6b458edce MDL-35653 - mod/wiki: Force format now lets users edit wiki pages.
Thanks to Tõnis Tartes for part of this solution.

When force format was selected in the options, trying to view the
wiki would redirect to the front page with a message saying that
you do not have permission to edit this page. Students had no way
of viewing any entries or creating the first page.

When in force format it skips a page , which would be considered
uncessary for students, but this means that the group id is not set
and this is what is causing most of the problems.

I also have included a check to see if the title is default and whether
the user has permission to view all groups. In these situations the
intermediate page is displayed.
2012-12-19 08:51:48 +08:00
Eloy Lafuente (stronk7) a8ccdd2880 Merge branch 'w50_MDL-36245_m24_convert' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-18 23:42:53 +01:00
Marina Glancy 656581fa2c MDL-36339 Modify themes to add course headers and footers provided by course format 2012-12-18 21:37:05 +01:00
Matt Oquist 785a713b82 MDL-36958 Forum: Replaced hard-coded numbers with proper defines 2012-12-18 17:24:31 +08:00
Jay Knight cf04387947 MDL-36704: wrap pending conditions in span tags 2012-12-18 15:09:55 +08:00
Dan Poltawski eddec24752 Merge branch 'MDL-37014_webdav_folders_24' of git://github.com/davosmith/moodle into MOODLE_24_STABLE 2012-12-18 14:52:33 +08:00
Dan Poltawski f454c9884f MDL-36259 - change case of table alias to match definition 2012-12-18 14:27:40 +08:00
Thanh Le 53f7c0c641 MDL-36259 course short names: ensure it displays when enabled in admin
Fixed 5 Moodle pages that does not display the course short name even if "Display extended course names" setting is on.  In fixing this, search.php also included related minor code to change to minimise DB calls to improve performance.
2012-12-18 14:26:02 +08:00
Dan Poltawski 2de5b09d84 Merge branch 'MDL-30899_24' of git://github.com/dmonllao/moodle into MOODLE_24_STABLE 2012-12-18 09:11:02 +08:00
Damyon Wiese 70c31beef7 MDL-35740 Assignment: Change the assignment index page to use a renderer.
This also cleans up the patch to prevent students from seeing the number of submissions for the assignment.
2012-12-18 08:44:59 +08:00
Raymond Wijaya 901dd625f8 MDL-35740 Assignment: Hide the number of assignment submissions for all students that was visible to all students through the activities block 2012-12-18 08:44:59 +08:00
Tim Hunt 7d3341b000 MDL-36842 quiz: update accessrule upgrade.txt file in master. 2012-12-18 00:18:59 +01:00
Eloy Lafuente (stronk7) 1f60fe8a23 Merge branch 'w50_MDL-37171_m24_outdatedexcel' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-18 00:13:55 +01:00
Sam Hemelryk 730271af49 Merge branch 'w50_MDL-36475_m24_zindexesgrrr' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-18 10:42:49 +13:00
Sam Hemelryk 8a35efacd7 Merge branch 'MDL-36490_M24' of git://github.com/lazydaisy/moodle into MOODLE_24_STABLE 2012-12-18 10:40:32 +13:00
Adrian Fish cfa9c7e1d0 MDL-37035 Pass user locale after course locale. 2012-12-18 10:32:24 +13:00
Sam Hemelryk acb011dc20 Merge branch 'w50_MDL-37111_m24_forumsinglefirst' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-18 10:24:42 +13:00
Sam Hemelryk a7a1adf4e0 Merge branch 'MDL-37176_24' of git://github.com/stronk7/moodle into MOODLE_24_STABLE 2012-12-18 10:12:22 +13:00
Rossiani Wijaya e60bae3575 MDL-35530 Lesson Module: Add question text to facilitate grading 2012-12-18 10:08:53 +13:00
Sam Hemelryk ddb5fb5980 Merge branch 'wip-MDL-35056-stable24' of git://github.com/phalacee/moodle into MOODLE_24_STABLE 2012-12-18 09:57:11 +13:00
Tim Hunt b70a545c88 MDL-32245 question bank: fix coding style in categories.php 2012-12-18 09:52:30 +13:00
Thanh Le c383f33bbf MDL-32245 question bank: fix moving categories between contexts. 2012-12-18 09:52:30 +13:00
Sam Hemelryk 13683200d6 MDL-36812 grader_report: Fixed whitespace on integration 2012-12-18 09:48:01 +13:00
Nadav Kavalerchik 7be97bd89d MDL-36812 - Enabling grade_report_fixedstudents, shows the user's list on the left side of the page, in RTL mode (theme/standard) 2012-12-18 09:48:01 +13:00
Sam Hemelryk cd281ce1b4 Merge branch 'MDL-29733_master' of git://github.com/andyjdavis/moodle into MOODLE_24_STABLE 2012-12-18 09:42:41 +13:00
David Monllao adc1d7b2a5 MDL-30899 moodle-core-notification Overridding default footer padding 2012-12-17 16:27:30 +08:00
David Monllao b4454b133f MDL-30899 moodle-core-notification Adding ARIA attributes 2012-12-17 16:27:08 +08:00
David Monllao 520a601f14 MDL-30899 moodle-core-notification Adding close button title for accessibility 2012-12-17 16:27:01 +08:00
David Monllao 0409483d5f MDL-30899 moodle-core-notification Adding focus to non modal windows 2012-12-17 16:26:56 +08:00
David Monllao 2d07933c17 MDL-30899 moodle-core-notification Adding H1 to notifications headers 2012-12-17 16:26:43 +08:00
David Monllao 6eabdb6d8c MDL-30899 block_community Changing from overlay to panel references 2012-12-17 16:26:09 +08:00
David Monllao 291455cecd MDL-30899 modchooser Changing from overlay to panel references 2012-12-17 16:26:03 +08:00
David Monllao b76204d589 MDL-30899 moodle-core-notification Replacing Y.Overlay for Y.Panel
References to lightbox are still there to maintain the same
external API but are converted to a modal attribute internally,
same with closeButton, now it uses the Y.Panel close button
2012-12-17 16:25:51 +08:00
Dan Poltawski e8e9393e76 Merge branch 'w50_MDL-36493_m24_mysql' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-17 15:29:07 +08:00
Joseph Rezeau 231f758404 MDL-37114 quiz form validation: feedback boundaries.
The feedback boundaries must either be a number or a percentage.
We were not correctly validating the non-percentage case.
2012-12-17 15:26:07 +08:00
Andrew Davidson e92d664dd2 MDL-37034 - Added appropriate name to category settings navigation node 2012-12-17 15:21:57 +08:00
Dan Poltawski 973fe984cd Merge branch 'w50_MDL-36597_m24_courselang' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-17 13:53:06 +08:00
Mark Nelson 4548df3155 MDL-29334 mod_data: if site-wide presets created in <= 1.9 exist convert them to 2.x 2012-12-17 13:48:15 +08:00
Rajesh Taneja c874cee03a MDL-36736 course: Availability information should not be visible for hidden modules
If user is viewing hidden module, availability information should only be visible to
user when editing and module is visible. If editing and module is hidden, then hide
availablilty information as it's invalid.
When user is not editing and activity is hidden then don't add availability information.
2012-12-17 12:09:30 +08:00
Rajesh Taneja caeb7f90b0 MDL-36736 course: Availability information will use hide class for hidding 2012-12-17 12:09:30 +08:00
Dan Poltawski da264959f2 Merge branch 'wip-MDL-25928-m24' of git://github.com/samhemelryk/moodle into MOODLE_24_STABLE 2012-12-17 12:05:12 +08:00
Dan Poltawski 41e1109c5f Merge branch 'wip-MDL-35874-stable24' of git://github.com/phalacee/moodle into MOODLE_24_STABLE 2012-12-17 11:52:59 +08:00
Dan Poltawski 209b2f7b3e Merge branch 'wip-MDL-35979-s24' of git://github.com/phalacee/moodle into MOODLE_24_STABLE 2012-12-17 11:48:39 +08:00
Dan Poltawski 503325ddcb Merge branch 'MDL-37078-m24' of git://github.com/sammarshallou/moodle into MOODLE_24_STABLE 2012-12-17 11:37:02 +08:00
Damyon Wiese 41575c5571 MDL-37165 Assignment: Oracle fix for count_submissions_with_status (and friends).
Oracle does not like the use of AS after a join.
2012-12-17 11:31:31 +08:00
Sam Hemelryk 73f60c1886 MDL-25928 course: Fixed course info on the combo list to be a popup 2012-12-17 16:03:34 +13:00
Petr Škoda dc7c91ae67 MDL-37171 fix multiple spreadsheet related PHP4isms 2012-12-15 12:38:12 +01:00
Petr Škoda 15dbd27efa MDL-36245 add tests for the buggy utf-8 to utf-8 conversion 2012-12-15 11:50:44 +01:00
Petr Škoda 9a05bd0f8f MDL-36245 try to workaround buggy iconv() when converting text from utf-8 to utf-8 2012-12-15 11:50:43 +01:00
Petr Škoda 524e1ce789 MDL-36475 tweak zIndexes once more
The expected ranges are now:
* 0-1000 for normal code
* 5000-5999 for TinyMCE
* 7000-8999 for filepicker and filemanager

This patch fixes also incorrect location of "MDL-25736 - French spellchecker fixes." modification reminder.
2012-12-15 11:47:16 +01:00
Petr Škoda 3d4de0bc4e MDL-36493 improve timeout handling in mysql upgrade
Credit goes to Mark Nielsen, thanks.
2012-12-15 11:44:18 +01:00
Petr Škoda 1264dd11a0 MDL-36493 improve performance of mysql unsigned/lob upgrade 2012-12-15 11:44:18 +01:00
Petr Škoda 599e7fe006 MDL-37111 copy first post files in single type forum using intro options
This patch also removed the delete button from the first post in single
discussion type forum.

Edit button in the first post leads to the forum edit page now, this should
prevent other problems throwing first post and intro out of sync.
2012-12-15 11:24:16 +01:00
Petr Škoda b768c6f77a MDL-36597 strip '_utf8' from all course.lang fields 2012-12-15 11:16:12 +01:00
Eloy Lafuente (stronk7) 4651e4e464 MDL-37176 timezones: updated to 2012j 2012-12-14 20:08:19 +01:00
Eloy Lafuente (stronk7) 4c7e6ea021 weekly release 2.4+ 2012-12-14 13:31:08 +01:00
Frederic Massart 46b0673732 MDL-36456 repository: S3 handles errors in a nicer way 2012-12-14 17:57:19 +08:00
Damyon Wiese 9c2d41fce2 MDL-30700 tablelib: Fixed get_sort_for_table to work with text sorting columns for oracle
This change stores the list of columns that require text sorting in the session,
so the static function "get_sort_for_table" can work without any additional parameters.
2012-12-13 10:32:23 +01:00
Jason Fowler c5148e0ab6 MDL-35874 - Blocks - Adding a label to the 'Add a block' block's select field 2012-12-13 15:09:45 +08:00
Jason Fowler 8bd65e7aaa MDL-35056 - Backup/Restore - Converting the 'Unmet Dependencies' Message to be a Developer Debug message to make moodle less confusing to the average user 2012-12-13 11:10:56 +08:00
Tim Hunt 4dbdc18fd8 MDL-36897 quiz review: fixup previous commit. 2012-12-13 09:54:28 +08:00
sam marshall 9789d825c2 MDL-37078: Restore - section restore fails with multiple availability conditions 2012-12-12 12:15:41 +00:00
Davo Smith 8f0019214e MDL-37014 repository_webdav - strip the server URL from file & folder paths returned by webdav lib (if present) 2012-12-12 08:55:17 +00:00
Frederic Massart 45cff73a1b MDL-32639 files: Zipping a folder does not overwrite existing files 2012-12-12 14:50:10 +08:00
Frederic Massart aa0cd4a7ef MDL-32639 files: Improved File Manager download and zip support
Conflicts:

	lib/filestorage/zip_packer.php
2012-12-12 14:50:10 +08:00
Vadim Dvorovenko 785e3deb4d MDL-32639 files: Removing '0' start folder in archives created with filemanager
Signed-off-by: Vadim Dvorovenko <vadimon@mail.ru>

Conflicts:

	lib/filestorage/zip_archive.php
2012-12-12 14:43:45 +08:00
Sam Hemelryk 7a4b7c5e18 MDL-33448 enrol_manual: Reduced JS get_string calls for the yui widget 2012-12-12 09:37:36 +08:00
Sam Hemelryk dc5c56e90c MDL-33448 enrol_manual: Reordered things to improve JS performance when enrolling users 2012-12-12 09:37:36 +08:00
Cathal O'Riordan 26679bee33 MDL-36644 Assignment: prepend the course upload limit to the options array
Ensure that the course upload limit is the first option in the list of available choices for the Maximum submission size select.
2012-12-12 08:57:13 +08:00
Dan Marsden 1076d79905 MDL-35737 SCORM: when scorm_parse is called and external scorm manifest use always update the package. 2012-12-12 08:44:00 +08:00
Paul Nicholls 0034f04164 MDL-27619: Prevent teachers from turning course Outcomes into site-wide ones
Similar to the issue which allowed teachers to create site-wide scales by editing a course-specific scale (MDL-24682), teachers could also promote a course-specific scale to a site-wide (standard) by editing it.  As with MDL-24682, removing the course ID check (leaving just the capability check) prevents this unauthorised creation of site-wide (standard) outcomes.
2012-12-12 11:10:35 +13:00
Paul Nicholls 1ff2b56319 MDL-32880: Add moodle1 backup converter for rss_client block.
Creates a shell rss_client.xml file, which contains no feeds.  This makes it behave the same as restoring a Moodle 1.9 backup into a different 1.9 site - the block is created, its title is as per the original course, but it has no feeds selected (and none are added to the site's feed list).
2012-12-11 22:49:13 +01:00
Paul Nicholls 4d9b4e9782 MDL-32880: Add moodle1 backup converter for html block.
This converter processes the HTML block's content, migrating any referenced files.
2012-12-11 22:49:05 +01:00
Paul Nicholls f51fdc9bb7 MDL-32880: moodle1 backup converter: add generic block handler
This handler will automatically be used for any block which does not provide its own handler, and should be subclassed by any block which needs to do anything extra (or different).

This commit incorporates contributions from Mike Churchward and API design suggestions from David Mudrak.
2012-12-11 22:48:54 +01:00
Matteo Scaramuccia f1c403dcec MDL-36641 Missing HTTP check when using an external resource 2012-12-11 22:42:42 +01:00
Sam Hemelryk ab1f9aa3a4 MDL-29805 redirect: added check REQUIRE_CORRECT_ACCESS is true 2012-12-12 09:46:55 +13:00
Sam Hemelryk 1ce6bdd1dd Merge branch 'MDL-29805-24' of git://github.com/mouneyrac/moodle into MOODLE_24_STABLE 2012-12-12 09:45:17 +13:00
Jerome Mouneyrac e20e67161c MDL-35593 core_webservice_get_site_info returns version number set to PARAM_TEXT and PARAM_FLOAT 2012-12-12 09:39:04 +13:00
Sam Hemelryk c545dcad4f Merge branch 'wip_24_mdl-37088_bad_operator_ldap_sync_user' of https://github.com/iarenaza/moodle into MOODLE_24_STABLE 2012-12-12 09:34:44 +13:00
Sam Hemelryk 9e9dd64c35 Merge branch 'MDL-36321_message_participants_24' of git://github.com/andyjdavis/moodle into MOODLE_24_STABLE 2012-12-12 09:29:12 +13:00
Damyon Wiese c6c7a7be8c MDL-30700 Assignment 2.2: Added text_sorting function to tablelib
Allows oracle to sort by text (clob) columns (should be used sparingly).
2012-12-11 19:57:57 +01:00
Eloy Lafuente (stronk7) e958133ca6 MDL-37032 upgrade: define all the 2.4.0 points 2012-12-11 13:12:03 +01:00
Dan Poltawski 9261144beb Merge branch 'w49_MDL-36447_m24_gdtest' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-11 17:54:37 +08:00
Tim Hunt 71cc181bac MDL-37098 quiz: event handler fails deleting group with no overrides
This was a regression from MDL-35717.
2012-12-11 17:52:04 +08:00
Jean-Michel Vedrine cf617e112f MDL-36243 question->questiontext and question->generalfeedback should always be strings 2012-12-11 16:24:24 +08:00
Dan Poltawski 865485fc54 Merge branch 'wip-MDL-36108-stable24' of git://github.com/phalacee/moodle into MOODLE_24_STABLE 2012-12-11 16:03:34 +08:00
Jason Fowler a595b4dacb MDL-36108 - Blocks - Online User block's user image's alt attribute is now empty to prevent repetition of text for screen readers 2012-12-11 15:50:31 +08:00
Dan Poltawski 7c431a345f Merge branch 'wip-MDL-36938-m24' of git://github.com/samhemelryk/moodle into MOODLE_24_STABLE 2012-12-11 15:19:08 +08:00
Damyon Wiese 4dbc9b0a64 MDL-36712: Assignment grading with custom scale is affected by the current language 2012-12-11 14:01:01 +08:00
Damyon Wiese 9242214612 MDL-36591 Assignment - rewrite image urls in online text summaries 2012-12-11 13:56:58 +08:00
Tim Hunt 4832c70022 MDL-36897 quiz: redirect teachers from summary to review page.
There is already logic like this at the top of attempt.php. If the
teacher arrives at one of these pages for a student's attempt, for
example by clicking a link in the logs, then they should be redirected
to the review page to see the students attempt in its current state.
2012-12-11 13:54:47 +08:00
Andrew Robert Nicols 54fdd4c0e9 MDL-34791 AJAX Update a graded activity's title in the gradebook on rename 2012-12-11 13:22:56 +08:00
sam marshall 0324045e4f MDL-36808 Completion: Incorrect escaping for activity name in completion tickbox 2012-12-11 12:19:32 +08:00
Dan Poltawski fac9ba991b Merge branch 'w49_MDL-36983_m24_ordertext' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-11 12:09:34 +08:00
Dan Poltawski ae5ecea065 Merge branch 'w49_MDL-36892_m24_enrolreadme' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-11 12:07:13 +08:00
Tim Hunt 6357dd08b6 MDL-36845 excel export: don't output invalid sheet names.
We should clean any proposed worksheet name, to remove the characters
that Excel does not allow.
2012-12-11 11:59:20 +08:00
Adam Olley c15b286f88 MDL-36828: mod_scorm: Allow deletion of scorm modules if no scorm_scoes data exists 2012-12-11 11:44:24 +08:00
Dan Poltawski 4afa0b50c7 Merge branch 'wip-MDL-36110-s24' of git://github.com/phalacee/moodle into MOODLE_24_STABLE 2012-12-11 11:33:14 +08:00
Dan Poltawski b90b9b83bd Merge branch 'wip-MDL-35884-s24' of git://github.com/phalacee/moodle into MOODLE_24_STABLE 2012-12-11 11:17:33 +08:00
Tim Hunt 79bf1e37ca MDL-36650 don't change question timecreated or timemodified on restore. 2012-12-11 16:11:17 +13:00
Matt Petro 95ae9d9e5e MDL-36025 qtype calculated: Fix handling of "-0" 2012-12-11 11:07:24 +08:00
Sam Hemelryk 7027fe4538 Merge branch 'w49_MDL-36731_m24_uploadestrict' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-11 16:04:19 +13:00
Damyon Wiese 50f3d7b5ea MDL-35476 Assignment: Make submissions_open public so it can be used by plugins 2012-12-11 10:49:30 +08:00
Rossiani Wijaya 22fc0c5e9b MDL-36905 accessibility for assignment grading: add label for grading actions and operations select options 2012-12-11 15:44:57 +13:00
Sam Hemelryk 6ff1745873 Merge branch 'MDL-36916' of git://github.com/timhunt/moodle into MOODLE_24_STABLE 2012-12-11 15:42:08 +13:00
Luke Tucker 505530eaf5 MDL-34052 assignment - Disable submission and feedback plugins based on assign type when upgrading 2012-12-11 10:39:32 +08:00
Sam Hemelryk a2630c08ae Merge branch 'w49_MDL-36936_m24_composerdev' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-11 15:35:04 +13:00
Dan Poltawski fddbdb79db Merge branch 'w49_MDL-31424_m24_paypalcase' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-11 10:31:36 +08:00
Sam Hemelryk 1c90b9976d Merge branch 'w49_MDL-37060_m24_sessinit' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-11 15:27:14 +13:00
Sam Hemelryk 35cb0e4a4c Merge branch 'MDL-36942' of git://github.com/timhunt/moodle into MOODLE_24_STABLE 2012-12-11 15:04:22 +13:00
Sam Hemelryk ada098890b Merge branch 'MDL-36954' of git://github.com/timhunt/moodle into MOODLE_24_STABLE 2012-12-11 15:00:00 +13:00
Jerome Mouneyrac 2e8b836243 MDL-36773 timezone inconsistency => some number support decimal other don\'t - Original patch author: Nathan Mares 2012-12-11 09:57:22 +08:00
Sam Hemelryk b004c163ee Merge branch 'wip-mdl-37017-m24' of git://github.com/rajeshtaneja/moodle into MOODLE_24_STABLE 2012-12-11 14:53:49 +13:00
Sam Hemelryk 6d0c04c208 Merge branch 'w49_MDL-37061_m24_invalidlang' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-11 14:44:21 +13:00
Andrew Davis 29a7d7a1c1 MDL-36321 core_message: made the course participants list correctly show if course participants are contacts or not 2012-12-11 08:55:16 +08:00
Sam Hemelryk eda1d3f37a Merge branch 'w49_MDL-29442_m24_entityutf8' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-11 10:22:06 +13:00
Damyon Wiese 6466396f38 MDL-36263 Assignment: Do not count submissions of unenrolled users 2012-12-11 10:12:26 +13:00
Damyon Wiese 5b2313c26c MDL-36908 Assignment: Additional fixes for preventing empty submissions 2012-12-11 10:12:23 +13:00
Charles Fulton 473eadfe10 MDL-37020 auth_shibboleth: check if target variable is actually set 2012-12-11 09:48:41 +13:00
Sam Hemelryk 52d4168038 Merge branch 'MDL-36601-master' of git://git.luns.net.uk/moodle into MOODLE_24_STABLE 2012-12-11 09:42:29 +13:00
Sam Hemelryk c6bd16545e Merge branch 'MDL-36805-master' of git://git.luns.net.uk/moodle into MOODLE_24_STABLE 2012-12-11 09:16:43 +13:00
Adrian Greeve 517a2dbe84 MDL-37011 - themes: Fixed the alignment of the edit and help icon on the participants page. 2012-12-11 08:43:19 +13:00
Iñaki Arenaza 88d48cd51f MDL-37088 auth_ldap: bad operator in ldap sync_user() method 2012-12-10 16:51:49 +01:00
Dan Poltawski a5ff28cc9f MDL-36755 course_overview block: fix for fix.
I will stop commiting now.
2012-12-10 19:44:26 +08:00
Dan Poltawski 1b2cf98bb0 MDL-36755 course_overview block: fix idiot version
I will shoot the programmer responsible for this mess.
2012-12-10 19:42:29 +08:00
Dan Poltawski 86e28894f4 Merge branch 'w49_MDL-36737_m24_glossstrict' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-10 19:37:31 +08:00
Dan Poltawski c125bb553d Merge branch 'MDL-36755_master' of git://github.com/markn86/moodle into MOODLE_24_STABLE
Conflicts:
	blocks/course_overview/version.php
2012-12-10 19:33:21 +08:00
Dan Poltawski 4ac455d9a4 MDL-36167 fix trailing whitespace 2012-12-10 17:36:31 +08:00
Damyon Wiese 3b7a18f2e5 MDL-36167: Assignment quickgrading fix when there is no grade (ensure feedback still saves)
This patch modifies the previous on by Luke Tucker so that the lastmodified hidden field
is always added to the quickgrading form even if there is no grade for the assignment. This
ensures that the lastmodified checks still work (to prevent teachers overwriting each
others feedback) and feedback is saved when there is no grade.
2012-12-10 17:36:31 +08:00
Luke Tucker a13a1707bc MDL-36167: Update all users in assignment quickgrade form to ensure feedback is saved 2012-12-10 17:36:30 +08:00
Tim Hunt 6812769f8f MDL-36732 Cloze qtype: review marks should not depend on feedback.
The various review options should be independent.
2012-12-10 15:18:14 +08:00
Rossiani Wijaya 2f860b4ded MDL-36549 Lesson module: fixed strict standards errors 2012-12-10 14:18:55 +08:00
Kirill Astashov d84c21dfcc MDL-36856: Fix method name misspelled in two places in mod_assign 2012-12-10 14:04:37 +08:00
Damyon Wiese 0e3c1231da MDL-36835 Assignment: Do not show javascript warning when toggling quickgrading 2012-12-10 14:02:14 +08:00
Dan Poltawski 71e8ee1c33 Merge branch 'MDL-36538_webdav_memory_24' of git://github.com/davosmith/moodle into MOODLE_24_STABLE 2012-12-10 13:13:03 +08:00
Dan Poltawski 8938f0848e Merge branch 'w49_MDL-35456_m24_paypal' of git://github.com/skodak/moodle into MOODLE_24_STABLE 2012-12-10 12:03:23 +08:00
Tim Hunt 0e73e16460 MDL-35404 question export: make it work with slasharguments off.
Thanks to Brian King for working out the problem and how to fix it. I am
just committing his patch.
2012-12-10 11:37:17 +08:00
Dan Poltawski 9321379b10 Merge branch 'wip-MDL-36939-m24' of git://github.com/samhemelryk/moodle into MOODLE_24_STABLE 2012-12-10 11:08:51 +08:00
Jerome Mouneyrac c91c7fcded MDL-30961 get_course_contents: returned activity name should not be check against PARAM_TEXT, it could be anything 2012-12-10 15:00:00 +13:00
Andrew Davis 0663096dc6 MDL-36680 core_grade: fixed grade_report::blank_hidden_total() and added unit tests 2012-12-10 09:56:45 +08:00
Michael Aherne 7a9e253405 MDL-36946 theme Use server default theme before standard 2012-12-10 14:43:56 +13:00
Sam Hemelryk 5e6b64571c Merge branch 'MDL-34592_M24' of git://github.com/lazydaisy/moodle into MOODLE_24_STABLE 2012-12-10 14:28:33 +13:00
Sam Hemelryk c446a0fca2 Merge branch 'MDL-36608_M24' of git://github.com/lazydaisy/moodle into MOODLE_24_STABLE 2012-12-10 14:24:18 +13:00
Sam Hemelryk 07f89a017c Merge branch 'MDL-36978_M24' of git://github.com/lazydaisy/moodle into MOODLE_24_STABLE 2012-12-10 14:21:25 +13:00
Sam Hemelryk b8b1926e2f Merge branch 'MDL-36579_modLtiBackupFields' of git://github.com/moodlerooms/moodle into MOODLE_24_STABLE 2012-12-10 14:15:54 +13:00
Mark Nielsen 4d5970ea63 MDL-36607: Improve block drag and drop
* Adding support for subpage
* Adding support for regions that do not start with "side-"
2012-12-10 14:12:50 +13:00
Mark Nelson 656cea20a6 MDL-35954 javascript: prevent JS from being triggered on select box by clicking on already selected option 2012-12-10 14:08:52 +13:00
Mary Evans e9f8261131 MDL-34592 theme_magazine: Fixes the report grader fixed column misalignment in magazine/style/core.css theme, plus fixes all CORE themes with minor adjustment to grade/report/grader/style.css 2012-12-09 23:32:04 +00:00
Itamar Tzadok d87a55fc2b MDL-36433 Forms lib - Added dash escaping to validation script getter. 2012-12-10 12:13:54 +13:00
Dan Poltawski b0c87306a6 MDL-36764 ddl/oracle: fix misleading comment 2012-12-10 11:54:28 +13:00
Dan Poltawski 38ab68e6ab MDL-36778 course - prevent undefined index warnings
When logged in as a guest the mform didn't set userid.
2012-12-10 11:51:57 +13:00
Dan Poltawski 0e7cadca03 MDL-36887 quiz: reorder upgrade.txt's 2012-12-10 11:49:03 +13:00
Andrew Robert Nicols efd08cdc2b MDL-36640 Logging: Correct testing of log->info.
log->info sometimes contains additional information which can be looked up
from other tables. This should only be attempted if the field is numeric,
but the CSV, XLS, and ODS outputs were not making this check.

There is an additional case for the print_mnet_log function which has not
been covered - this is because the mnet logging is currently broken beyond
the scope of this bug rendering this section untestable. These issues are
raised in MDL-36642.
2012-12-10 11:43:35 +13:00
Andrew Robert Nicols 7e61737f3d MDL-36918 Decode HTML entities on ajax update title 2012-12-10 11:15:55 +13:00
Andrew Davis 138025470f MDL-36162 core_message: string clean up 2012-12-10 11:04:18 +13:00
Andrew Davis 8b72167d0e MDL-36162 core_message: removed the unnecessary and confusing course URL parameter as well as fixing the broken breadcrumb 2012-12-10 11:04:13 +13:00
Andrew Davis f92305bfa9 MDL-36761 core_grade:shifted a call to count() out of a loop 2012-12-10 10:54:43 +13:00
Sam Hemelryk 0b4767ca50 Merge branch 'MDL-34591_24' of git://github.com/grabs/moodle into MOODLE_24_STABLE 2012-12-10 10:48:59 +13:00
Kevin Wiliarty 1c50e53a05 MDL-35221 mod_feedback: replace checkbox with advcheckbox for all requirable feedback question types 2012-12-10 10:41:48 +13:00
Adrian Greeve a3301036d6 MDL-36906 - mod_data: Latitude and Logitude error fix.
Thanks to Marcus Boon for providing a patch for this issue.

mod/data/field/latlong/kml.php was missing a context variable.
This was causing an error when trying to download a kml file for
google earth. This variable has now been included.
2012-12-10 10:34:29 +13:00
Adrian Greeve c7a34ab704 MDL-36915 - mod/data: A database with no entries no longer displays the export to portfolio button. 2012-12-10 10:27:42 +13:00
Sam Hemelryk 1b696de61b Merge branch 'wip-MDL-37026-MOODLE_24_STABLE' of git://github.com/abgreeve/moodle into MOODLE_24_STABLE 2012-12-10 10:23:44 +13:00
Adam Olley f4d4c73899 MDL-33125: core_backup: Prevent import/activity duplication from duplicating manual grade items 2012-12-10 10:20:03 +13:00
Petr Škoda da0cf7f883 MDL-36447 skip file preview test if GD not available 2012-12-09 19:22:36 +01:00
Andreas Grabs 7c3f70812e MDL-34591 - paypal does not allow unenrol 2012-12-09 19:10:26 +01:00
Mary Evans f5eabd12db MDL-36490 theme_standard: reduces the 15px radius down to 3px in style/css3.css for .generalbox objects 2012-12-09 18:05:03 +00:00
Mary Evans db1dd3e550 MDL-36608 theme_sky_high: Fixes missing background in sdmin pages by removing incorrect CSS in style/admin.css 2012-12-09 17:44:37 +00:00
Petr Škoda f48902e139 MDL-29442 add missing utf-8 to entity html encoding and decoding 2012-12-09 18:22:16 +01:00
Petr Škoda 7b03292e24 MDL-37061 always validate lang when setting session user
This should resolve problems with TinyMCE langs and some unknown MUCH caching issue.
2012-12-09 11:30:05 +01:00
Mary Evans 2a6e3435d0 MDL-36978 theme_standardold: fixes misplaced block region check in layout/frontpage.php which, with hindsight should have been fixed in MDL-23226. 2012-12-08 22:50:33 +00:00
Petr Škoda 0411eed2cb MDL-36737 fix random glossary E_STRICT issue 2012-12-08 14:44:49 +01:00
Petr Škoda 365ef195b5 MDL-31424 compare PayPal emails case-insensitively
Credit goes to Chris Follin, thanks!
2012-12-08 13:17:10 +01:00
Petr Škoda 8c3547554b MDL-37060 prevent notice in session_get_instance() 2012-12-08 12:31:40 +01:00
Petr Škoda 324efa55e3 MDL-35456 use HTTP 1.1 for PayPal IPN 2012-12-08 11:58:43 +01:00
Eloy Lafuente (stronk7) 0b7c52dd4c weekly release 2.4+ 2012-12-08 02:56:23 +01:00
Petr Škoda cf31390c6f MDL-36936 require phpunit as composer --dev only 2012-12-07 18:20:42 +01:00
Petr Škoda 5af62aa90a MDL-36892 delete outdated enrol info 2012-12-07 17:16:28 +01:00
Davo Smith cf839b4396 MDL-36538 repository_webdav - save files directly to local filesystem, rather than buffering in memory 2012-12-07 09:19:21 +00:00
Jason Fowler 257a5d1299 MDL-35884 - Course - Remove text from ALT attribute for activity chooser 2012-12-07 13:52:32 +08:00
Rajesh Taneja e6d4708fcf MDL-37017 My Moodle: Course name should be fullname of course 2012-12-07 11:58:07 +08:00
Sam Hemelryk f9870842bd MDL-36939 cache: instance form validation now handles errors properly 2012-12-07 08:37:03 +13:00
Sam Hemelryk 2d7173d0c7 MDL-36938 cache: test performance script checks requirements now 2012-12-07 08:36:56 +13:00
Petr Škoda 4682fc7c38 MDL-36983 fix incorrect test method name
Credit goes to David Scotson, thanks.
2012-12-06 15:32:46 +01:00
Petr Škoda 15a77653e4 MDL-36731 fix E_STRICt issue when creating new groups in user upload 2012-12-06 13:15:41 +01:00
Dan Poltawski 9b1f784178 Merge branch 'wip-MDL-36960-m24' of git://github.com/amygroshek/moodle into MOODLE_24_STABLE 2012-12-06 12:04:08 +08:00
Adrian Greeve 44bfbe1e15 MDL-37026 - lib: The function user_can_assign() returns true for admins. 2012-12-06 11:04:34 +08:00
Dan Poltawski 0c767bf6cc Merge branch 'MDL-36963-deployment-backup_24' of git://github.com/mudrd8mz/moodle into MOODLE_24_STABLE 2012-12-06 10:57:13 +08:00
David Mudrák 81c89dd705 MDL-36963 Do not attempt to remove the root folder of a plugin during the update deployment
There is no need to move the root folder of a plugin during the update
deployment. We just need to operate with its contents. So the web server
process can have the write access for the plugin folder only.
2012-12-06 02:45:16 +01:00
David Mudrák b46ee168ef MDL-36963 Add unit tests for directory operations in mdeploy.php 2012-12-06 02:45:16 +01:00
David Mudrák e399d32694 MDL-36963 Improve mdeploy worker::remove_directory() method
The additional parameter allows to use this method without actual
removing the root of the path. That is, it is now possible to remove
the content of a folder only.
2012-12-06 02:35:22 +01:00
David Mudrák 606e2c8ebe MDL-36963 Improve mdeploy worker::move_directory() method
The additional parameter allows to use this method without actual
removing the root of the source location. That is, it is now possible to
move the content of a folder only.

Also, a small refactoring happened here as we will need a variant of
this method that does not throw exception if the target already exists.
2012-12-06 02:31:36 +01:00
Sam Hemelryk c6e0d385c3 MDL-36341 calendar: fixed JS bug with popup node setter 2012-12-06 02:25:14 +01:00
David Mudrák 31a3bcc933 MDL-37003 Fix repeated line in update notification email
There was a problem experienced after 2.4.0 release because the version
of the 2.4.0 release was the same as 2.5dev release. So the version
value matched twice in the loop and the line was repeated in the email.
2012-12-06 01:50:32 +01:00
David Mudrák acade5443f MDL-36943 Do not notify about already installed updates
Before this patch, the available_update_checker::cron_notifications()
method accessed the availableupdates property. But for plugins, that property
basically contains the most recent version available. So we were missing
the check against the actual version installed.

The fix was simple - obtain available updates via the available_updates()
method that performs the check.
2012-12-06 01:50:21 +01:00
Marina Glancy 113105bf9c MDL-36840 Set course.format to 'site' for site-course 2012-12-05 10:28:12 +08:00
Jason Fowler dd9f2b1cca MDL-35979 - Commenting - Adding an empty alt attribute to the javascript generated delete icon for comments 2012-12-05 10:02:31 +08:00
Amy Groshek 90d740364b MDL-36960 - fix broken horizontal positioning of titles for docked blocks 2012-12-04 18:07:25 -06:00
Jason Fowler 89bc7e7b6e MDL-36110 - Blocks - Community finder icon alt attribute is now empty 2012-12-04 15:17:07 +08:00
Jerome Mouneyrac 5c58a540ea MDL-29805 add REQUIRE_CORRECT_ACCESS define + return error code when AJAX_SCRIPT fails 2012-12-04 13:44:48 +08:00
Eloy Lafuente (stronk7) 325e266e95 NOBUG: Bump backup version for 2.4 2012-12-03 21:46:18 +01:00
32835 changed files with 1264947 additions and 4022341 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"]
}
-5
View File
@@ -1,5 +0,0 @@
**/yui/build/** -diff
**/amd/build/** -diff
**.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"]
-7
View File
@@ -1,7 +0,0 @@
*** PLEASE DO NOT OPEN PULL REQUESTS VIA GITHUB ***
The moodle.git repository at Github is just a mirror of the official repository. We do not accept pull requests at Github.
See CONTRIBUTING.md guidelines for how to contribute patches for Moodle. Thank you.
--
-6
View File
@@ -1,6 +0,0 @@
> [!IMPORTANT]
> PLEASE DO NOT PUBLISH SECURITY FINDINGS PUBLICLY.
If you discover a potential security issue, please report it via our [Security Submission Form](https://moodle.org/security/report).
For more information about our security processes and responsible disclosure policy, see the [Security Procedures documentation](https://moodledev.io/general/development/process/security).
-1
View File
@@ -1 +0,0 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1231.24 314.44" preserveAspectRatio="xMinYMid meet"><defs><style>.cls-1{fill:#f48020;}.cls-2,.cls-3{fill:#383738;stroke:#4a4a4c;}.cls-2{stroke-width:2.37px;}.cls-3{stroke-width:2.38px;}.cls-4{fill:#585858;}</style></defs><title>Moodle_Logo_UseonBlack_RGB</title><path class="cls-1" d="M1086.3,235.34a53.42,53.42,0,0,0,16.71,29q13.26,12.27,29.48,12.27,11.29,0,24.56-5.89,16.23-6.88,16.22-16.22l58-.49q-12.29,29-41.76,45.2-27.52,15.23-60.43,15.24-42.25,0-70-26.29t-27.75-68.54q0-43.73,27.75-69.51t72-25.8q48.15,0,75.17,31.94,25.07,29.47,25.06,79.1Zm89.91-33.91q-3.43-17.69-14.73-29-12.8-12.28-29.49-12.29-17.21,0-28.74,11.79t-15.47,29.48Z"/><path class="cls-1" d="M965.45,308.54V0h55V308.54Z"/><path class="cls-1" d="M892.69,308.54V290.36q-7.38,9.83-25.06,15.72a92.31,92.31,0,0,1-30,5.41q-39.31,0-63.13-27T750.7,217.65q0-39.3,23.34-65.84t62.15-26.53q43.71,0,56.5,23.58V0h53.55V308.54Zm0-102.68q0-14.75-14-28.26t-28.74-13.51q-21.12,0-33.41,17.2-10.8,15.24-10.8,37.33,0,21.63,10.8,36.85,12.29,17.71,33.41,17.69,12.78,0,27.76-12t15-24.81Z"/><path class="cls-1" d="M647,313.45q-41.77,0-69-26.53t-27.27-68.3q0-41.73,27.27-68.28t69-26.53q41.75,0,69.27,26.53t27.52,68.28q0,41.79-27.52,68.3T647,313.45ZM647,168q-19.89,0-30.58,15.07t-10.68,35.79q0,20.73,9.94,35.05,11.43,16.31,31.32,16.28t31.32-16.28q10.44-14.31,10.44-35.05t-9.94-35Q667.42,168,647,168Z"/><path class="cls-1" d="M448.07,313.45q-41.76,0-69-26.53t-27.27-68.3q0-41.73,27.27-68.28t69-26.53q41.74,0,69.28,26.53t27.5,68.28q0,41.79-27.5,68.3T448.07,313.45Zm0-145.43q-19.89,0-30.59,15.07t-10.69,35.79q0,20.73,9.94,35.05,11.43,16.31,31.34,16.28t31.32-16.28q10.42-14.31,10.44-35.05t-9.94-35Q468.45,168,448.07,168Z"/><path class="cls-1" d="M288.89,309.11V201q0-33.9-28-33.9t-28,33.9V309.11h-55V201q0-33.9-27.52-33.9-28,0-28,33.9V309.11h-55V194.65q0-35.37,24.56-53.54,21.61-16.22,58.46-16.22,37.35,0,55,19.16,15.23-19.16,55.51-19.16,36.86,0,58.45,16.22,24.57,18.16,24.57,53.54V309.11Z"/><path class="cls-2" d="M.76,145.76"/><path class="cls-3" d="M1.73,145.32"/><path class="cls-4" d="M173.86,116.85l55.08-40.1-.7-2.44C135,83.92,80.42,96.44,0,144.13l.73,2.38,21-.67c15.17.52,30.85,1.26,46,1.32-1,24.09.83,32.65.83,32.65,51.92,17.58,101.22,1.53,127.14-43.9A248.72,248.72,0,0,0,173.86,116.85Z"/><path class="cls-4" d="M113.78,110c-.3-1.08-2.23-1.48-4.28-.9a4.73,4.73,0,0,0-3.14,2.19c-18.53,5-49.16,13.72-70.73,21l-1.33.45c-16.68,5.61-24.73,8.49-25.22,12.55-2.29,18.44-.87,42.18-.86,42.48l.19,6.4c-9.74,27.56.06,49.6,8.38,69.77.06-24.92,1.07-45.22-6-71-.2-3.82-1.49-30.36.61-47.34.34-2.75,11.65-6.56,23.63-10.59l1.33-.44c22-7.42,53.57-16.39,71.9-21.29a6.75,6.75,0,0,0,2.29-.25C112.64,112.43,114.08,111.09,113.78,110Z"/></svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

-31
View File
@@ -1,31 +0,0 @@
name: 'Close Pull Requests'
on:
pull_request_target:
types: opened
workflow_dispatch:
jobs:
close:
# Avoid forks to run this job
if: github.repository_owner == 'moodle'
name: Close Pull Requests
runs-on: ubuntu-latest
steps:
- uses: dessant/repo-lockdown@v4
with:
process-only: prs
close-pr: true
skip-closed-pr-comment: true
pr-comment: >
**Please, don't open pull requests via GitHub.**
This repository (moodle.git) at GitHub is just a mirror of the official Moodle repository.
For how to contribute patches for Moodle see
[CONTRIBUTING.md](https://github.com/moodle/moodle/blob/main/CONTRIBUTING.md).
Closing this pull request.
-71
View File
@@ -1,71 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Template configuraton file for github actions CI/CD.
*
* @package core
* @copyright 2020 onwards Eloy Lafuente (stronk7) {@link https://stronk7.com}
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// This cannot be used out from a github actions workflow, so just exit.
getenv('GITHUB_WORKFLOW') || die; // phpcs:ignore moodle.Files.MoodleInternal.MoodleInternalGlobalState
unset($CFG);
global $CFG;
$CFG = new stdClass();
$CFG->dbtype = getenv('dbtype');
$CFG->dblibrary = 'native';
$CFG->dbhost = '127.0.0.1';
$CFG->dbname = 'test';
$CFG->dbuser = 'test';
$CFG->dbpass = 'test';
$CFG->prefix = 'm_';
$CFG->dboptions = ['dbcollation' => 'utf8mb4_bin'];
$host = 'localhost';
$CFG->wwwroot = "http://{$host}";
$CFG->dataroot = realpath(dirname(__DIR__)) . '/moodledata';
$CFG->admin = 'admin';
$CFG->directorypermissions = 0777;
// Debug options - possible to be controlled by flag in future.
$CFG->debug = (E_ALL); // 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: 14
postgresql db: test
postgresql user: test
postgresql password: test
- name: Setting up PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.4
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
-127
View File
@@ -1,127 +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.4
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.2
extensions:
db: mysqli
# PostgreSQL builds always run with the highest PHP supported version.
- os: ubuntu-22.04
php: 8.4
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.4
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: 14
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 }}
- name: Git status
run: git status
-23
View File
@@ -1,23 +0,0 @@
name: Web Installer Testing
on:
workflow_dispatch:
jobs:
InstallationTest:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
php: [8.2, 8.4]
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.4
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.4
# 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.2
db: pgsql
extensions: exif, fileinfo, gd, intl, pgsql, mysql, redis, soap, sodium, zip
steps:
- name: Setting up DB mysql
if: ${{ matrix.db == 'mysqli' }}
uses: shogo82148/actions-setup-mysql@v1
with:
mysql-version: 8.4
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 --version 5.0.7 --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 -35
View File
@@ -9,26 +9,15 @@
#
# See gitignore(5) man page for more details
#
# Swap files (vim)
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Temporary files including undo
*~
#
/config.php
/lib/editor/tinymce/extra/tools/temp/
*~
*.swp
/tags
/TAGS
/cscope.*
/.patches/
/.idea/
.phpstorm.*
!/.phpstorm.meta.php/
/.phpstorm.meta.php/*
!/.phpstorm.meta.php/di.php
/nbproject/
CVS
.DS_Store
@@ -36,27 +25,8 @@ 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
UPGRADING-CURRENT.md
.phpunit.cache
-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);
}
}
}
};
};
-432
View File
@@ -1,432 +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 = [];
componentData.components = {};
componentData.standardComponents = {};
// Fetch the component definiitions from the distributed JSON file.
const components = JSON.parse(fs.readFileSync(`${gruntFilePath}/lib/components.json`));
const pluginData = JSON.parse(fs.readFileSync(`${gruntFilePath}/lib/plugins.json`));
componentData.pluginTypes = components.plugintypes;
const standardPlugins = Object.entries(pluginData.standard).map(
([pluginType, pluginNames]) => {
return pluginNames.map(pluginName => `${pluginType}_${pluginName}`);
}
).reduce((acc, val) => acc.concat(val), []);
// Build the list of moodle subsystems.
componentData.subsystems.lib = 'core';
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 includes the list of subsystems.
componentData.components = {...componentData.subsystems};
const subpluginAdder = (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);
});
};
// 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)));
if (subpluginList.subplugintypes) {
Object.entries(subpluginList.subplugintypes).forEach(([subpluginType, subpluginTypePath]) => {
subpluginAdder(
subpluginType,
`${pluginTypePath}/${componentName}/${subpluginTypePath}`
);
});
} else if (subpluginList.plugintypes) {
Object.entries(subpluginList.plugintypes).forEach(([subpluginType, subpluginTypePath]) => {
subpluginAdder(subpluginType, subpluginTypePath);
});
}
}
});
});
// Create a list of the standard subsystem and plugins.
componentData.standardComponents = Object.fromEntries(
Object.entries(componentData.components).filter(([, name]) => {
if (name === 'core' || name.startsWith('core_')) {
return true;
}
return standardPlugins.indexOf(name) !== -1;
})
);
componentData.componentMapping = Object.fromEntries(
Object.entries(componentData.components).map(([path, name]) => [name, path])
);
}
return componentData;
};
/**
* 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,
},
};
-138
View File
@@ -1,138 +0,0 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
import * as Components from '../../components.js';
const componentData = Components.fetchComponentData();
/**
* The standard components shipped with core Moodle.
*
* @type {Object}
*/
export const standardComponents = componentData.standardComponents;
/**
* All components of the current Moodle instance.
*
* @type {Object}
*/
export const allComponents = componentData.components;
/**
* Get all components of the current Moodle instance.
*
* @returns {Array}
*/
export const getAllComponents = () => {
let components = new Map(Object.entries(componentData.pluginTypes).map(([value, path]) => ([path,{
path,
value,
name: `${value} (plugin type)`,
}])));
Object
.entries(componentData.components)
.filter(([path, value]) => Object.values(componentData.standardComponents).includes(value))
.forEach(([path, value]) => {
const entry = {
path,
value,
name: value,
};
if (Object.values(componentData.subsystems).includes(value)) {
if (components.has(path)) {
entry.name = `${value} (subsystem / plugintype)`;
} else {
entry.name = `${value} (subsystem)`;
}
}
components.set(path, entry);
});
return Array.from(components.values());
};
/**
* Whether the specified component is a standard component shipped with core Moodle.
*
* @param {string} componentName
* @returns {boolean}
*/
export const isStandardComponent = (componentName) => {
if (Object.values(componentData.standardComponents).includes(componentName)) {
return true;
}
if (Object.keys(componentData.pluginTypes).includes(componentName)) {
return true;
}
return false;
};
export const rewritePlugintypeAsSubsystem = (componentName) => {
if (Object.keys(componentData.pluginTypes).includes(componentName)) {
const pluginTypePath = componentData.pluginTypes[componentName];
if (Object.keys(componentData.subsystems).includes(pluginTypePath)) {
return true;
}
}
return false;
};
/**
* Whether the specified component is a community component.
*
* @param {string} componentName
* @returns {boolean}
*/
export const isCommunityComponent = (componentName) => {
if (isStandardComponent(componentName)) {
return false;
}
return Object.values(componentData.components).indexOf(componentName) !== -1;
};
/**
* Sort method for components.
*
* This method sorts components putting `core` first, followed by core subsystems, then everything else.
*
* @param {String} a
* @param {String} b
* @returns {Number}
*/
export const sortComponents = (a, b) => {
// Always put 'core' first.
if (a === 'core') {
return -1;
} else if (b === 'core') {
return 1;
}
// Put core subsystems next.
if (a.startsWith('core_') && !b.startsWith('core_')) {
return -1;
} else if (b.startsWith('core_') && !a.startsWith('core_')) {
return 1;
}
// Sort alphabetically for everything else.
return a.localeCompare(b);
};
-85
View File
@@ -1,85 +0,0 @@
#!/usr/bin/env node
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
import inquirer from 'inquirer';
import chalk from 'chalk';
import { createNote } from './note.mjs';
import { getInitialValues } from './helpers.mjs';
import * as Prompts from './prompts.mjs';
import logger from './logger.mjs';
export default async (options) => {
// Processs the initial values.
const initialValues = getInitialValues(options);
// Fetch information.
const messages = [];
const { issueNumber } = await inquirer.prompt([
Prompts.getIssuePrompt(),
], initialValues);
let selection = {};
let notePath;
do {
selection = {};
selection = await inquirer.prompt([
Prompts.getComponentsPrompt(),
Prompts.getTypePrompt(),
Prompts.getMessagePromptInput(),
], initialValues);
if (selection.message === '') {
selection = Object.assign(
selection,
await inquirer.prompt([
Prompts.getMessagePromptEditor(),
]),
);
}
logger.info(`
Creating upgrade note with the following options:
- Issue: ${chalk.bold(issueNumber)}
- Component: ${chalk.bold(selection.components)}
- Type: ${chalk.bold(selection.type)}
- Message:
${chalk.bold(selection.message)}
`);
messages.push({
components: [selection.components],
type: selection.type,
message: selection.message,
});
// Save the note so far.
if (notePath) {
await createNote(issueNumber, messages, notePath);
logger.info(`Updated note at: ${chalk.underline(chalk.bold(notePath))}`);
} else {
notePath = await createNote(issueNumber, messages);
logger.info(`Note created at: ${chalk.underline(chalk.bold(notePath))}`);
}
selection = Object.assign(
selection,
await inquirer.prompt([
Prompts.getAddAnotherPrompt(),
], initialValues),
);
} while (selection.addAnother);
};
-357
View File
@@ -1,357 +0,0 @@
#!/usr/bin/env node
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
import chalk from 'chalk';
import { getAllComponents } from './components.mjs';
import { getCombinedNotesByComponent, deleteAllNotes } from './note.mjs';
import { getNoteName } from './noteTypes.mjs';
import { writeFile, readFile, unlink } from 'fs/promises';
import { join as joinPath } from 'path';
import logger from './logger.mjs';
import { getCurrentVersion } from './helpers.mjs';
/**
* Helper to fetch the current notes from a file.
*
* @param {string} file
* @returns {Promise<string>}
*/
const getCurrentNotes = async (file) => {
try {
return await readFile(file, 'utf8');
} catch (error) {
return null;
}
}
/**
* Update the UPGRADING.md file.
*
* @param {string} upgradeNotes
* @param {Object} options
* @param {boolean} options.deleteNotes
* @returns {Promise<void>}
*/
const updateUpgradeNotes = async (upgradeNotes, options) => {
const fileName = 'UPGRADING.md';
// Write the notes to a file.
logger.info(`Writing notes to ${chalk.underline(chalk.bold(fileName))}`);
// Prepend to the existing file.
const existingContent = await getCurrentNotes(fileName);
if (existingContent) {
await writeFile(fileName, getUpdatedNotes(existingContent, upgradeNotes));
} else {
// This should not normally happen.
await writeFile(fileName, upgradeNotes);
}
if (options.deleteNotes) {
logger.warn(`>>> Deleting all notes <<<`)
// Delete the notes.
deleteAllNotes();
}
};
/**
* Create the current summary notes.
*
* @param {string} upgradeNotes
* @returns {Promise<void>}
*/
const createCurrentSummary = async (upgradeNotes) => {
const fileName = 'UPGRADING-CURRENT.md';
const notes = `# Moodle upgrade notes\n\n${upgradeNotes}`;
await writeFile(fileName, notes);
logger.info(`Running upgrade notes written to ${chalk.underline(chalk.bold(fileName))}`);
};
/**
* Get the indexes of the lines that contain the version headings.
*
* @param {array<string>} lines
* @returns {array<object>}
*/
const getVersionLineIndexes = (lines) => {
const h2Indexes = [];
lines.forEach((line, index) => {
const matches = line.match(/^##\s(?<version>.*)$/);
if (matches) {
h2Indexes.push({
index,
line,
version: matches.groups.version,
});
}
});
return h2Indexes;
};
/**
* Find the index of the Unreleased heading.
*
* @param {array<object>} versionHeadings
* @returns {number}
*/
const findUnreleasedHeadingIndex = (versionHeadings) => versionHeadings.findIndex((heading) => {
if (heading.version === 'Unreleased') {
// Used if version cannot be guessed.
return true;
}
if (heading.version.endsWith('+')) {
// Weekly release for a stable branch.
return true;
}
if (heading.version.match(/beta|rc\d/)) {
// Beta and RC rolls are treated as weeklies.
return true;
}
if (heading.version.endsWith('dev')) {
// Development version.
return true;
}
return false;
});
/**
* Get the before and after content, to facilitate replacing any existing Unreleased notes.
*
* @param {array<string>} lines
* @returns {Object} {beforeContent: string, afterContent: string}
*/
const getBeforeAndAfterContent = (lines) => {
const existingLines = lines.split('\n');
const versionHeadings = getVersionLineIndexes(existingLines);
if (versionHeadings.length > 0) {
const unreleasedHeadingIndex = findUnreleasedHeadingIndex(versionHeadings);
if (unreleasedHeadingIndex !== -1) {
const beforeContent = existingLines.slice(0, versionHeadings[unreleasedHeadingIndex].index).join('\n');
if (versionHeadings.length > unreleasedHeadingIndex + 1) {
const afterContent = existingLines.slice(versionHeadings[unreleasedHeadingIndex + 1].index).join('\n');
return {
beforeContent,
afterContent,
};
}
return {
beforeContent,
afterContent: '',
};
}
return {
beforeContent: existingLines.slice(0, versionHeadings[0].index).join('\n'),
afterContent: existingLines.slice(versionHeadings[0].index).join('\n'),
};
}
return {
beforeContent: existingLines.join('\n'),
afterContent: '',
}
};
/**
* Get the notes for the component.
*
* @param {string} types
* @param {Number} headingLevel
* @returns {string}
*/
const getNotesForComponent = (types, headingLevel) => {
let upgradeNotes = '';
Object.entries(types).forEach(([type, notes]) => {
upgradeNotes += '#'.repeat(headingLevel);
upgradeNotes += ` ${getNoteName(type)}\n\n`;
notes.forEach(({ message, issueNumber }) => {
// Split the message into lines, removing empty lines.
const messageLines = message
.split('\n')
// Remove empty lines between tables, and list entries, but not after lists.
.filter((line, index, lines) => {
if (line.trim().length === 0) {
// This line is empty.
// If it's the first line in the file, remove it.
if (index === 0) {
return false;
}
// This is the last line in the file, remove it.
if (index === lines.length - 1) {
return false;
}
// If the previous line relates to a table, remove this line.
if (lines[index - 1].match(/^\s*\|/)) {
return false;
}
// If the next line is also empty, do not remove this line.
if (lines[index + 1].trim().length === 0) {
return true;
}
// Do not remove the line if the previous line was a list item.
if (lines[index - 1].match(/^\s*[-*]\s/)) {
return true;
}
if (lines[index - 1].match(/^\s*\d+\.\s/)) {
return true;
}
// Preserve all other empty lines by default.
return true;
}
// Keep any line which has content.
return true;
});
const firstLine = messageLines.shift().trim();
upgradeNotes += `- ${firstLine}\n`;
messageLines
.forEach((line) => {
upgradeNotes += ` ${line}`.trimEnd() + `\n`;
});
upgradeNotes += `\n For more information see [${issueNumber}](https://tracker.moodle.org/browse/${issueNumber})\n`;
});
upgradeNotes += '\n';
});
return upgradeNotes;
};
/**
* Get the updated notes mixed with existing content.
*
* @param {string} existingContent
* @param {string} upgradeNotes
*/
const getUpdatedNotes = (existingContent, upgradeNotes) => {
const { beforeContent, afterContent } = getBeforeAndAfterContent(existingContent);
const newContent = `${beforeContent}\n${upgradeNotes}\n${afterContent}`
.split('\n')
.filter((line, index, lines) => {
if (line === '' && lines[index - 1] === '') {
// Remove multiple consecutive empty lines.
return false;
}
return true;
})
.join('\n');
return newContent;
};
/**
* Update the notes for each component.
*/
const updateComponentNotes = (
notes,
version,
notesFileName = 'UPGRADING.md',
removeEmpty = false,
) => {
return getAllComponents().map(async (component) => {
logger.verbose(`Updating notes for ${component.name} into ${component.path}`);
const fileName = joinPath(component.path, notesFileName);
const existingContent = await getCurrentNotes(fileName);
if (!existingContent) {
if (!notes[component.value]) {
// No existing notes, and no new notes to add.
return;
}
} else {
if (!notes[component.value]) {
// There is existing content, but nothing to add.
if (removeEmpty) {
logger.verbose(`Removing empty notes file ${fileName}`);
await unlink(fileName);
}
return;
}
}
const componentNotes = notes[component.value];
let upgradeNotes = `## ${version}\n\n`;
upgradeNotes += getNotesForComponent(componentNotes, 3);
if (existingContent) {
await writeFile(fileName, getUpdatedNotes(existingContent, upgradeNotes));
} else {
await writeFile(
fileName,
`# ${component.name} Upgrade notes\n\n${upgradeNotes}`,
);
}
});
}
/**
* Generate the upgrade notes for a new release.
*
* @param {string|undefined} version
* @param {Object} options
* @param {boolean} options.generateUpgradeNotes
* @param {boolean} options.deleteNotes
* @returns {Promise<void>}
*/
export default async (version, options = {}) => {
const notes = await getCombinedNotesByComponent();
if (Object.keys(notes).length === 0) {
logger.warn('No notes to generate');
return;
}
if (!version) {
version = await getCurrentVersion();
}
// Generate the upgrade notes for this release.
// We have
// - a title with the release name
// - the change types
// - which contain the components
// - which document each change
let upgradeNotes = `## ${version}\n\n`;
Object.entries(notes).forEach(([component, types]) => {
upgradeNotes += `### ${component}\n\n`;
upgradeNotes += getNotesForComponent(types, 4);
});
await Promise.all([
createCurrentSummary(upgradeNotes),
...updateComponentNotes(notes, version, 'UPGRADING-CURRENT.md', true),
]);
if (options.generateUpgradeNotes) {
await Promise.all(updateComponentNotes(notes, version));
await updateUpgradeNotes(upgradeNotes, options);
}
};
-230
View File
@@ -1,230 +0,0 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
import chalk from 'chalk';
import { isStandardComponent, isCommunityComponent, rewritePlugintypeAsSubsystem } from './components.mjs';
import { isValidNoteName } from './noteTypes.mjs';
import logger from './logger.mjs';
import { readFile } from 'fs/promises';
/**
* Validate an issue number input
*
* @param {string} input
* @returns {string|boolean}
*/
export const validateIssueNumber = (input) => {
if (!input) {
return 'You must provide a tracker issue number';
}
if (input.match(/^[a-zA-Z]*-\d+$/)) {
return true;
}
if (input.match(/^\d+$/)) {
return true;
}
return 'The issue number was not recognised as a valid issue number';
};
/**
* Format an issue number input.
*
* @param {string} input
* @returns {string}
*/
export const formatIssueNumber = (input) => {
if (input.match(/^[a-zA-Z]*-\d+$/)) {
return input;
}
if (input.match(/^\d+$/)) {
return `MDL-${input}`;
}
return input;
};
/**
* Validate a component.
*
* @param {string} input
* @returns {string|boolean}
*/
export const validateComponent = (input) => {
if (isStandardComponent(input)) {
return true;
}
if (isCommunityComponent(input)) {
return 'Currently only core plugins are supported.';
}
return 'The component was not recognised as a standard component';
};
export const formatComponent = (input) => {
if (rewritePlugintypeAsSubsystem(input)) {
return `core_${input}`;
}
return input;
}
/**
* Get the initial values from the options.
*
* @param {object} options
* @returns {object}
*/
export const getInitialValues = (options) => {
const initialValues = {};
const type = getInitialTypeValue(options);
if (type) {
initialValues.type = type;
}
const issueNumber = getInitialIssueValue(options);
if (issueNumber) {
initialValues.issueNumber = issueNumber;
}
const component = getInitialComponentValue(options);
if (component) {
initialValues.components = component;
}
const message = getInitialMessageValue(options);
if (message) {
initialValues.message = message
initialValues.addAnother = false;
}
return initialValues;
};
/**
* Get the initial type value.
*
* @param {Object} options
* @returns {string|undefined}
*/
const getInitialTypeValue = (options) => {
if (!options.type) {
return;
}
options.type = options.type.trim().toLowerCase();
if (isValidNoteName(options.type)) {
return options.type;
}
logger.warn(`Note type "${chalk.underline(chalk.red(options.type))}" is not valid.`);
};
/**
* Get the initial issue number value.
*
* @param {Object} options
* @returns {string|undefined}
*/
const getInitialIssueValue = (options) => {
if (!options.issue) {
return;
}
options.issue = options.issue.trim().toUpperCase();
const issueNumberValidated = validateIssueNumber(options.issue);
if (issueNumberValidated === true) {
const issueNumber = formatIssueNumber(options.issue);
if (issueNumber !== options.issue) {
logger.warn(
`Issue number "${chalk.underline(chalk.red(options.issue))}" was updated to ` +
`"${chalk.underline(chalk.green(issueNumber))}"`
);
}
return issueNumber;
} else {
logger.warn(`Issue number "${chalk.underline(chalk.red(options.issue))}" is not valid: ${issueNumberValidated}`);
}
};
/**
* Get the initial component value.
*
* @param {Object} options
* @returns {string|undefined}
*/
const getInitialComponentValue = (options) => {
if (!options.component) {
return;
}
options.component = options.component.trim().toLowerCase();
const componentValidated = validateComponent(options.component);
if (componentValidated === true) {
const component = formatComponent(options.component);
if (component !== options.component) {
logger.warn(
`Component "${chalk.underline(chalk.red(options.component))}" was updated to ` +
`"${chalk.underline(chalk.green(component))}"`
);
}
return component;
} else {
logger.warn(`Component "${chalk.underline(chalk.red(options.component))}" is not valid: ${componentValidated}`);
}
};
/**
* Get the initial message value.
*
* @param {Object} options
* @returns {string|undefined}
*/
const getInitialMessageValue = (options) => {
if (!options.message) {
return;
}
return options.message.trim();
};
/**
* Get the current version from the project /version.php file.
*
* @returns {Promise<string>}
*/
export const getCurrentVersion = async () => {
const versionRegex = new RegExp(/^ *\$release *= *['\"](?<release>[^ \+]+\+?) *\(Build:.*/m);
try {
const versionFile = await readFile('version.php', 'utf8');
const match = versionFile.match(versionRegex);
if (match) {
return match.groups.release;
}
} catch(error) {
logger.error('Unable to read the version file');
}
return "Unreleased";
}
-30
View File
@@ -1,30 +0,0 @@
#!/usr/bin/env node
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
import winston from 'winston';
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
),
transports: [
new winston.transports.Console(),
],
});
export default logger;
-194
View File
@@ -1,194 +0,0 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
import yaml from 'js-yaml';
import path from 'path';
import { writeFile, mkdir, readdir, readFile, unlink } from 'fs/promises';
import { isValidNoteName, sortNoteTypes } from './noteTypes.mjs';
import { sortComponents } from './components.mjs';
const unreleasedPath = path.resolve('.upgradenotes');
/**
* Get the filename for the note.
*
* @param {string} issueNumnber The issue number
* @returns {string}
*/
const getFilename = (issueNumber) => {
const dateTimeFormat = new Intl.DateTimeFormat(undefined, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
fractionalSecondDigits: 2,
timeZone: 'UTC',
});
const date = Object.fromEntries(
dateTimeFormat.formatToParts(new Date())
.filter((p) => p.type !== 'literal')
.map((p) => ([p.type, p.value]))
);
const dateString = [
date.year,
date.month,
date.day,
date.hour,
date.minute,
date.second,
date.fractionalSecond,
].join('');
return `${issueNumber}-${dateString}.yml`;
};
/**
* Create a new note.
*
* @param {string} issueNumber
* @param {[type: string]: {message: string}} messages
* @returns {string} The path to the note on disk
*/
export const createNote = async (
issueNumber,
messages,
notePath,
) => {
const note = {
issueNumber,
notes: {},
};
messages.forEach(({components, type, message}) => {
note.notes[components] = note.notes[components] || [];
note.notes[components].push({message, type});
});
if (!notePath) {
notePath = path.resolve(unreleasedPath, getFilename(issueNumber));
}
const noteContent = yaml.dump(note);
await mkdir(unreleasedPath, {recursive: true});
await writeFile(notePath, noteContent);
return notePath;
};
/**
* Get all unreleased notes.
*
* @returns {Promise<{issueNumber: string, components: string[], types: {[type: string]: {message: string}[]}}[]>
*/
export const getAllNotes = async () => {
const files = await readdir(unreleasedPath);
const notes = files
.filter((file) => file.endsWith('.yml'))
.map(async (file) => {
const filePath = path.resolve(unreleasedPath, file);
const fileContent = await readFile(filePath, 'utf8');
return {
...yaml.load(fileContent),
filePath,
};
});
return Promise.all(notes);
};
/**
* Get the list of notes, grouped by note type, then component.
*
* @returns {Promise<{[type: string]: {[components: string]: {message: string, issueNumber: string}[]}}>}
*/
export const getCombinedNotes = async () => {
const notes = await getAllNotes();
const combinedNotes = {};
notes.forEach((note) => {
Object.entries(note.notes).forEach(([components, data]) => {
data.forEach((entry) => {
if (!isValidNoteName(entry.type)) {
throw new Error(`Invalid note type: "${entry.type}" in file ${note.filePath}`);
}
combinedNotes[entry.type] = combinedNotes[entry.type] || {};
combinedNotes[entry.type][components] = combinedNotes[entry.type][components] || [];
combinedNotes[entry.type][components].push({message: entry.message, issueNumber: note.issueNumber});
});
});
});
return combinedNotes;
};
/**
* Get the list of notes, grouped by component, then by note type.
*
* @returns {Promise<{[component: string]: {[type: string]: {message: string, issueNumber: string}[]}}>}
*/
export const getCombinedNotesByComponent = async () => {
const notes = await getAllNotes();
const combinedNotes = {};
notes.forEach((note) => {
Object.entries(note.notes).forEach(([component, data]) => {
combinedNotes[component] = combinedNotes[component] || {};
data.forEach((entry) => {
if (!isValidNoteName(entry.type)) {
throw new Error(`Invalid note type: "${entry.type}" in file ${note.filePath}`);
}
combinedNotes[component][entry.type] = combinedNotes[component][entry.type] || [];
combinedNotes[component][entry.type].push({
component,
message: entry.message,
issueNumber: note.issueNumber,
type: entry.type,
});
});
});
});
// Sort notes by note type.
Object.entries(combinedNotes).forEach(([component, types]) => {
combinedNotes[component] = Object.fromEntries(
Object.entries(types).sort(([a], [b]) => sortNoteTypes(a, b))
);
});
// Sort components.
return Object.fromEntries(
Object.entries(combinedNotes).sort(([a], [b]) => sortComponents(a, b))
);
};
/**
* Delete all unreleased notes.
*
* @returns {Promise<void>}
*/
export const deleteAllNotes = async () => {
const files = await readdir(unreleasedPath);
return Promise.all(
files
.filter((item, index) => files.indexOf(item) === index)
.filter((file) => file.endsWith('.yml'))
.map((file) => unlink(`${unreleasedPath}/${file}`))
);
};
-80
View File
@@ -1,80 +0,0 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The note types.
*
* Matches the suggested order in https://keepachangelog.com/en/1.1.0/.
* @type {Object<string, string>}
*/
const noteTypes = {
'improved': 'Added',
'changed': 'Changed',
'deprecated': 'Deprecated',
'removed': 'Removed',
'fixed': 'Fixed',
};
/**
* The preferred order of note types.
*
* @type {string[]}
*/
const preferredOrder = Object.keys(noteTypes);
/**
* Comparison method to sort note types.
*
* @param {String} a
* @param {String} b
* @returns {Number}
*/
export const sortNoteTypes = (a, b) => {
const aIndex = preferredOrder.indexOf(a);
const bIndex = preferredOrder.indexOf(b);
if (aIndex === -1) {
return 1;
}
if (bIndex === -1) {
return -1;
}
return aIndex - bIndex;
};
/**
* Get the note names.
*
* @returns {string[]}
*/
export const getNoteNames = () => Object.keys(noteTypes);
/**
* Get the human-readable note name.
*
* @param {string} type
* @returns {string}
*/
export const getNoteName = (type) => noteTypes[type];
/**
* Whether the note name is valid.
*
* @param {string} type
* @returns {boolean}
*/
export const isValidNoteName = (type) => noteTypes[type] !== undefined;
-153
View File
@@ -1,153 +0,0 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
import inquirer from 'inquirer';
import SearchList from 'inquirer-search-list';
import { getNoteNames } from './noteTypes.mjs';
import { getAllComponents } from './components.mjs';
import {
formatComponent,
formatIssueNumber,
validateComponent,
validateIssueNumber,
} from './helpers.mjs';
/**
* A Search List which accepts an initial value.
*/
class SearchListWithInitialValue extends SearchList {
constructor(options, ...args) {
super(options, ...args);
if (options.default) {
const pointer = this.filterList.findIndex((item) => {
return item.value === options.default;
});
if (pointer > -1) {
this.pointer = pointer;
}
}
}
}
inquirer.registerPrompt('search-list', SearchListWithInitialValue);
/**
* Get the issue type prompt.
*
* @param {string} defaultData The initially selected value
* @returns {Object}
*/
export const getTypePrompt = (defaultData) => ({
default: defaultData,
type: 'search-list',
message: 'Type of change',
name: 'type',
choices: getNoteNames(),
validate: (selection) => {
if (selection.length < 1) {
return 'You must select at least one type of change';
}
return true;
},
});
/**
* Get the component prompt.
*
* @param {string} [defaultValue='core'] The initally selected value.
* @returns
*/
export const getComponentsPrompt = (defaultValue) => {
if (!defaultValue ) {
defaultValue = 'core';
}
return {
choices: getAllComponents(),
default: defaultValue,
type: 'search-list',
message: 'Component',
name: 'components',
validate: validateComponent,
filter: formatComponent,
};
};
/**
* Get the issue number prompt as an inline input.
*
* @param {string} defaultData
* @returns {object}
*/
export const getIssuePrompt = (defaultData) => ({
default: defaultData,
type: 'input',
message: 'Tracker issue number',
name: 'issueNumber',
validate: validateIssueNumber,
filter: formatIssueNumber,
});
/**
* Get a message prompt.
*
* @param {string} defaultData
* @returns
*/
export const getMessagePromptEditor = (defaultData) => ({
default: defaultData,
type: process.stdin.isTTY ? 'editor' : 'input',
postfix: '.md',
message: 'Message',
name: 'message',
waitUserInput: false,
validate: (input) => {
if (!input) {
return 'You must provide a message';
}
return true;
},
// Remove any trailing whitespace.
filter: (input) => input.split('\n').map((line) => line.trimEnd()).join('\n'),
});
/**
* Get a message prompt.
*
* @param {string} defaultData
* @returns
*/
export const getMessagePromptInput = (defaultData) => ({
default: defaultData,
type: 'input',
message: 'Message (leave empty to use editor)',
name: 'message',
filter: (input) => input.trim(),
});
/**
* Get a prompt to ask the user if they wish to add another entry.
*
* @returns {Object}
*/
export const getAddAnotherPrompt = () => ({
type: 'confirm',
message: 'Do you want to add another note?',
default: false,
name: 'addAnother',
});
-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;
};
-103
View File
@@ -1,103 +0,0 @@
#!/usr/bin/env node
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
import { Argument, Option, program } from 'commander';
import chalk from 'chalk';
import { getNoteNames } from './notes/src/noteTypes.mjs';
import createAction from './notes/src/create.mjs';
import generateAction from './notes/src/generate.mjs';
import logger from './notes/src/logger.mjs';
console.log(`
${chalk.bold(chalk.underline(chalk.green('Moodle Upgrade Notes Generator')))}
This tool is used to generate the upgrade notes for changes you make in Moodle.
Please remember that the intended audience of these changes is
${chalk.italic('plugin developers')} who need to know how to update their plugins
for a new Moodle version.
Upgrade notes should not be used to document changes for site administrators, or
for internal API changes which are not expected to be used outside of the
relevant component.
`)
program.configureHelp({
helpWidth: 100,
});
program.on('option:verbose', () => {
logger.level = 'verbose';
});
program.addOption(
new Option(
'-v, --verbose',
'Output more information during the generation process',
)
.default(false)
);
// Define the command line options.
program
.command('create')
.summary('Generate a new upgrade note')
.addOption(
new Option('-t, --type <type>', `The type of change to document. Valid types are: ${getNoteNames().join(', ')}`)
)
.addOption(new Option('-i, --issue <issue>', 'The tracker issue number'))
.addOption(new Option('-c, --component <component>', 'The component to write a note for'))
.addOption(new Option(
'-m, --message <message>',
'The message to use for the upgrade note',
))
.action((options) => createAction(options));
program
.command('summary')
.summary('Generate a local copy of the upgrade notes summary')
.addArgument(
new Argument('[version]', 'The Moodle version to create the summary notes for')
)
.action((version) => generateAction(version));
program
.command('release')
.summary('Generate the markdown copies of the upgrade notes for a Moodle release')
.addArgument(
new Argument('[version]', 'The Moodle version to create the release notes for')
)
.addOption(
new Option(
'--generate-upgrade-notes',
'Generate the UPGRADING.md notes for the release. ' +
'Note: This option is intended for use by the release manager when generating the upgrade notes.',
)
.default(true)
)
.addOption(
new Option(
'-d, --delete-notes',
'Delete the notes after generating the UPGRADING.md notes for the release. ' +
'Note: This option is intended for use by the release manager when generating the upgrade notes.' +
'This option has no effect unless --generate-upgrade-notes is also set.'
)
.default(false)
)
.action((version, options) => generateAction(version, options));
program.parse();
-2
View File
@@ -1,2 +0,0 @@
**/amd/**
/*.js
-58
View File
@@ -1,58 +0,0 @@
// NOTE: We use eslint now. This file is used only by shifter. We keep the configuration
// here because shifter uses jshint after modules have been concating. Eslint can't
// currently do this.
{
"asi": false,
"bitwise": true,
"boss": false,
"browser": true,
"curly": true,
"debug": false,
"eqeqeq": false,
"eqnull": false,
"es5": false,
"esnext": false,
"evil": false,
"expr": false,
"forin": false,
"funcscope": false,
"globalstrict": false,
"immed": true,
"indent": 4,
"iterator": false,
"lastsemic": false,
"latedef": true,
"laxbreak": true,
"laxcomma": false,
"loopfunc": false,
"maxerr": 500,
"maxlen": 132,
"multistr": false,
"newcap": true,
"noarg": true,
"noempty": true,
"nomen": false,
"onecase": false,
"onevar": false,
"passfail": false,
"plusplus": false,
"predef": [
"M",
"define",
"require"
],
"proto": false,
"regexdash": false,
"regexp": false,
"scripturl": false,
"shadow": false,
"smarttabs": false,
"strict": false,
"sub": false,
"supernew": false,
"trailing": true,
"undef": true,
"unused": true,
"white": false,
"yui": true
}
-1
View File
@@ -1 +0,0 @@
lts/jod
-40
View File
@@ -1,40 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Helper file for PhpStorm, and other IDEs, to provide better code completion.
*
* @package core
* @copyright 2024 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace PHPSTORM_META;
// Assume that anything returned by \core\di::get('...') is an instance of the first argument.
override(\core\di::get(0), map([
'' => '@',
]));
// Assume that anything returned by \Psr\Container\ContainerInterface::get('...') is an instance of the first argument.
override(\Psr\Container\ContainerInterface::get(0), map([
'' => '@',
]));
// Assume that anything returned by \DI\Container::get('...') is an instance of the first argument.
override(\DI\Container::get(0), map([
'' => '@',
]));
-5
View File
@@ -1,5 +0,0 @@
{
"coverage": false,
"lint": "config",
"clean": true
}
-107
View File
@@ -1,107 +0,0 @@
{
"customSyntax": "postcss-scss",
"plugins": [
"stylelint-csstree-validator"
],
"rules": {
"csstree/validator": {
"syntaxExtensions": [
"sass"
]
},
"at-rule-empty-line-before": [ "always",
{"except": [ "blockless-after-blockless"], ignore: ["after-comment", "inside-block"]}
],
"at-rule-name-case": "lower",
"at-rule-name-space-after": "always-single-line",
"at-rule-no-unknown": null, # Enabled for non-scss in grunt.
"at-rule-semicolon-newline-after": "always",
"at-rule-semicolon-space-before": "never",
"block-closing-brace-newline-after": "always",
"block-closing-brace-newline-before": "always",
"block-closing-brace-space-before": "always-single-line",
"block-no-empty": true,
"block-opening-brace-newline-after": "always",
"block-opening-brace-space-after": "always-single-line",
"block-opening-brace-space-before": "always",
"color-hex-case": ["lower", { "severity": "warning" }],
"color-hex-length": ["short", { "severity": "warning" }],
"color-no-invalid-hex": true,
"declaration-bang-space-after": "never",
"declaration-bang-space-before": "always",
"declaration-block-no-duplicate-properties": true,
"declaration-block-no-shorthand-property-overrides": true,
"declaration-block-semicolon-newline-after": "always-multi-line",
"declaration-block-semicolon-space-after": "always-single-line",
"declaration-block-semicolon-space-before": "never",
"declaration-block-single-line-max-declarations": 1,
"declaration-block-trailing-semicolon": "always",
"declaration-colon-newline-after": "always-multi-line",
"declaration-colon-space-after": "always-single-line",
"declaration-colon-space-before": "never",
"declaration-no-important": true,
"font-family-no-duplicate-names": true,
"function-calc-no-unspaced-operator": true,
"function-comma-newline-after": "always-multi-line",
"function-comma-space-after": "always-single-line",
"function-comma-space-before": "never",
"function-linear-gradient-no-nonstandard-direction": true,
"function-max-empty-lines": 0,
"function-name-case": "lower",
"function-parentheses-newline-inside": "always-multi-line",
"function-parentheses-space-inside": "never-single-line",
"function-url-scheme-disallowed-list": ["data"],
"function-whitespace-after": "always",
"indentation": 4,
"keyframe-declaration-no-important": true,
"length-zero-no-unit": [true, { "severity": "warning" }],
"max-empty-lines": 2,
"max-line-length": [132, { "severity": "warning" }],
"media-feature-colon-space-after": "always",
"media-feature-colon-space-before": "never",
"media-feature-parentheses-space-inside": "never",
"media-feature-range-operator-space-after": "always",
"media-feature-range-operator-space-before": "always",
"media-query-list-comma-newline-after": "always-multi-line",
"media-query-list-comma-space-after": "always-single-line",
"media-query-list-comma-space-before": "never",
"no-empty-source": true,
"no-eol-whitespace": true,
"no-extra-semicolons": [true, { "severity": "warning" }],
"no-invalid-double-slash-comments": true,
"no-unknown-animations": true,
"property-case": "lower",
"property-no-unknown": true,
"selector-attribute-brackets-space-inside": "never",
"selector-attribute-operator-space-after": "never",
"selector-attribute-operator-space-before": "never",
"selector-combinator-space-after": "always",
"selector-combinator-space-before": "always",
"selector-list-comma-newline-after": "always",
"selector-list-comma-space-before": "never",
"selector-max-empty-lines": 0,
"selector-pseudo-class-case": "lower",
"selector-pseudo-class-no-unknown": true,
"selector-pseudo-class-parentheses-space-inside": "never",
"selector-pseudo-element-case": "lower",
"selector-pseudo-element-no-unknown": true,
"selector-type-case": "lower",
"selector-type-no-unknown": true,
"string-no-newline": true,
"time-min-milliseconds": 100,
"unit-disallowed-list": ["pt"],
"unit-case": "lower",
"unit-no-unknown": true,
"value-keyword-case": ["lower", {"ignoreKeywords": ["/(@|$)/"]}],
"value-list-comma-newline-after": "always-multi-line",
"value-list-comma-space-after": "always-single-line",
"value-list-comma-space-before": "never",
},
"overrides": [
{
"files": ["**/yui/**/*.css"],
"rules": {
}
}
]
}
@@ -1,10 +0,0 @@
issueNumber: MDL-46739
notes:
core:
- message: >-
The {user_preferences}.value database field is now TEXT instead of CHAR.
This means that any queries with a condition on this field in a WHERE or
JOIN statement will need updating to use `$DB->sql_compare_text()`. See
the `$newusers` query in
`\core\task\send_new_users_password_task::execute` for an example.
type: changed
@@ -1,6 +0,0 @@
issueNumber: MDL-61232
notes:
core:
- message: |
moodle_process_email() has been deprecated with the removal of the unused and non-functioning admin/process_email.php.
type: removed
@@ -1,5 +0,0 @@
issueNumber: MDL-66873
notes:
core_backup:
- message: Remove all MODE_HUB related code.
type: removed
@@ -1,21 +0,0 @@
issueNumber: MDL-68806
notes:
mod_quiz:
- message: >-
quiz_attempt now has 2 additional state values, NOT_STARTED and
SUBMITTED. These represent attempts when an attempt has been
type: improved
- message: >
quiz_attempt_save_started now sets the IN_PROGRESS state, timestarted,
and saves the attempt, while the new quiz_attempt_save_not_started
function sets the NOT_STARTED state and saves the attempt.
type: changed
- message: >
quiz_attempt::process_finish is now deprecated, and its functionality is
split between ::process_submit, which saves the
submission, sets the finish time and sets the SUBMITTED status, and
::process_grade_submission which performs automated
grading and sets the FINISHED status.
type: deprecated
@@ -1,20 +0,0 @@
issueNumber: MDL-68806
notes:
mod_quiz:
- message: >
New quiz setting "precreateperiod" controls the period before timeopen
during which attempts will be pre-created using the new
NOT_STARTED state. This setting is marked advanced and locked by
default, so can only be set by administrators. This setting
is read by the \mod_quiz\task\precreate_attempts task to identify
quizzes due for pre-creation.
type: improved
- message: >
quiz_attempt_save_started Now takes an additional $timenow parameter, to
specify the timestart of the attempt. This was previously
set in quiz_create_attempt, but is now set in quiz_attempt_save_started
and quiz_attempt_save_not_started.
type: changed
@@ -1,15 +0,0 @@
issueNumber: MDL-68806
notes:
core_question:
- message: >
question_attempt_step's constructor now accepts the class constant
TIMECREATED_ON_FIRST_RENDER as a value for the
$timecreated parameter. Calling question_attempt::render for the first
time will now set the first step's timecreated
to the current time if it is set to this value. Note, null could not be
used here as it is already used to indicate
timecreated should be set to the current time.
type: changed
@@ -1,24 +0,0 @@
issueNumber: MDL-68806
notes:
mod_quiz:
- message: >+
The webservice function `mod_quiz_get_user_attempts` is now deprecated
in favour of `mod_quiz_get_user_quiz_attempts`.
With the introduction of the new NOT_STARTED quiz attempt state,
`mod_quiz_get_user_attempts` has been modified to not return NOT_STARTED
attempts, allowing clients such as the mobile app to continue working
without modifications.
`mod_quiz_get_user_quiz_attempts` will return attempts in all states, as
`mod_quiz_get_user_attempts` did before. Once clients are updated to
handle NOT_STARTED attempts, they can migrate to use this function.
A minor modification to `mod_quiz_start_attempt` has been made to allow
it to transparently start an existing attempt that is in the NOT_STARTED
state, rather than creating a new one.
type: deprecated
@@ -1,14 +0,0 @@
issueNumber: MDL-71257
notes:
core:
- message: >-
The methods `want_read_slave` and `perf_get_reads_slave` in
`lib/dml/moodle_database.php` have been deprecated in favour of
renamed versions that substitute `slave` for `replica`.
type: deprecated
- message: >-
The trait `moodle_read_slave_trait` has been deprecated in favour of a
functionally identical version called `moodle_read_replica_trait`.
The renamed trait substitutes the terminology of `slave` with `replica`,
and `master` with `primary`.
type: deprecated
@@ -1,17 +0,0 @@
issueNumber: MDL-71378
notes:
tool_brickfield:
- message: >
tool_brickfield\local\areas\core_question\answerbase::find_system_areas
No replacement. System context no longer a valid context to assign a
question category
type: deprecated
- message: >
tool_brickfield\local\areas\core_question\base::find_system_areas
No replacement. System context no longer a valid context to assign a
question category
type: deprecated
@@ -1,42 +0,0 @@
issueNumber: MDL-71378
notes:
core:
- message: >
question_make_default_categories()
No longer creates a default category in either CONTEXT_SYSTEM,
CONTEXT_COURSE, or CONTEXT_COURSECAT.
Superceded by question_get_default_category which can optionally create one if it does not exist.
type: deprecated
- message: |
question_delete_course()
No replacement. Course contexts no longer hold question categories.
type: deprecated
- message: |
question_delete_course_category()
Course category contexts no longer hold question categories.
type: deprecated
qbank_bulkmove:
- message: >
qbank_bulkmove/helper::get_displaydata
Superceded by a modal and webservice, see
qbank_bulkmove/modal_question_bank_bulkmove and
core_question_external\move_questions
type: deprecated
- message: |
qbank_bulkmove\output\renderer::render_bulk_move_form
Superceded by qbank_bulkmove\output\bulk_move
type: deprecated
core_question:
- message: |
question_type::generate_test
No replacement, not used anywhere in core.
type: deprecated
@@ -1,7 +0,0 @@
issueNumber: MDL-71472
notes:
core:
- message: >
The method `site_registration_form::add_select_with_email()` has been
finally deprecated and will now throw an exception if called.
type: removed
@@ -1,9 +0,0 @@
issueNumber: MDL-72293
notes:
core:
- message: >-
The `core/sortable_list` Javascript module now emits native events,
removing the jQuery dependency from calling code that wants to listen
for the events. Backwards compatibility with existing code using jQuery
is preserved
type: improved
@@ -1,13 +0,0 @@
issueNumber: MDL-74488
notes:
core_reportbuilder:
- message: >-
New `report` helper class `get_report_row_count` method for retrieving
row count of custom or system report, without having to retrieve the
report content
type: improved
- message: >-
The `schedule` helper class `get_schedule_report_count` method is now
deprecated, existing code should instead use
`report::get_report_row_count`
type: deprecated
@@ -1,21 +0,0 @@
issueNumber: MDL-75075
notes:
core:
- message: >-
The `core_renderer::tag_list` function now has a new parameter named `displaylink`.
When `displaylink` is set to `true`, the tag name will be displayed as a clickable hyperlink.
Otherwise, it will be rendered as plain text.
type: changed
core_tag:
- message: >-
The `core_tag\taglist` class now includes a new property called `displaylink`,
which has a default value of `true`. When `displaylink` is set to `true`,
the tag name will be displayed as a clickable hyperlink. If `displaylink` is set to `false`,
the tag name will be rendered as plain text instead.
type: changed
mod_quiz:
- message: >-
The `quiz_question_tostring` method now includes a new boolean parameter, `displaytaglink`.
This parameter specifies whether the tag name in the question bank should be displayed
as a clickable hyperlink (`true`) or as plain text (`false`).
type: changed
@@ -1,24 +0,0 @@
issueNumber: MDL-75189
notes:
mod_data:
- message: |
Final deprecation and removal of the following classes:
- data_preset_importer
- data_preset_existing_importer
- data_preset_upload_importer
- data_import_preset_zip_form
type: removed
- message: >
- Final deprecation of \mod_data_renderer::import_setting_mappings(). Please use \mod_data_renderer::importing_preset() instead.
- Final deprecation of data_print_template() function. Please use mod_data\manager::get_template and mod_data\template::parse_entries instead.
- Final deprecation of data_preset_name(). Please use preset::get_name_from_plugin() instead.
- Final deprecation of data_get_available_presets(). Please use manager::get_available_presets() instead.
- Final deprecation of data_get_available_site_presets(). Please use manager::get_available_saved_presets() instead.
- Final deprecation of data_delete_site_preset(). Please use preset::delete() instead.
- Final deprecation of is_directory_a_preset(). Please use preset::is_directory_a_preset() instead.
- Final deprecation of data_presets_save(). Please use preset::save() instead.
- Final deprecation of data_presets_generate_xml(). Please use preset::generate_preset_xml() instead.
- Final deprecation of data_presets_export(). Please use preset::export() instead.
- Final deprecation of data_user_can_delete_preset(). Please use preset::can_manage() instead.
- Final deprecation of data_view(). Please use mod_data\manager::set_module_viewed() instead.
type: removed
@@ -1,19 +0,0 @@
issueNumber: MDL-75610
notes:
core:
- message: >
`\core\output\activity_header` now uses the `is_title_allowed()` method
when setting the title in the constructor.
This method has been improved to give priority to the 'notitle' option
in the theme config for the current page layout, over the top-level
option in the theme.
For example, the Boost theme sets
`$THEME->activityheaderconfig['notitle'] = true;` by default, but in its
`secure` pagelayout, it has `'notitle' = false`.
This prevents display of the title in all layouts except `secure`.
type: improved
@@ -1,8 +0,0 @@
issueNumber: MDL-75875
notes:
core:
- message: >-
Remove support deprecated boolean 'primary' parameter in
\core\output\single_button. The 4th parameter is now a string and not a
boolean (the use was to set it to true to have a primary button)
type: removed
@@ -1,7 +0,0 @@
issueNumber: MDL-77107
notes:
gradereport_grader:
- message: >-
The `behat_gradereport_grader::get_grade_item_id` step helper has been
removed, please use the equivalent `behat_grades` method instead
type: removed
@@ -1,18 +0,0 @@
issueNumber: MDL-77293
notes:
core:
- message: >
url class now correctly supports multi level query parameter parsing and
output.
This was previously supported in some methods such as get_query_string,
but not others. This has been fixed to be properly supported.
For example https://example.moodle.net?test[2]=a&test[0]=b will be
parsed as ['test' => [2 => 'a', 0 => 'b']]
All parameter values that are not arrays are casted to strings.
type: fixed
@@ -1,5 +0,0 @@
issueNumber: MDL-77668
notes:
core_grades:
- message: Removed unused grade_edit_tree_column_select class
type: removed
@@ -1,10 +0,0 @@
issueNumber: MDL-77713
notes:
core_question:
- message: >-
Deprecated method
`mod_quiz\question\bank\qbank_helper::get_version_options` in favour of
`core_question\local\bank\version_options::get_version_options` so that
the method is in core rather than a module, and can safely be used from
anywhere as required.
type: deprecated
@@ -1,22 +0,0 @@
issueNumber: MDL-78090
notes:
core_question:
- message: |
Final deprecation of:
- core_question\local\bank\action_column_base::class
- core_question\local\bank\menu_action_column_base::class
- core_question\local\bank\menuable_action::interface
- core_question\local\bank\view::print_choose_category_message()
- core_question\local\bank\view::display_advanced_search_form()
- core_question\local\bank\view::display_showtext_checkbox()
- core_question\local\bank\view::init_search_conditions()
- core_question\local\bank\view::get_current_category()
- core_question\local\bank\view::display_options_form()
- core_question\local\bank\view::start_table()
- core_question\local\bank\view::end_table()
- core_question\statistics\questions\all_calculated_for_qubaid_condition::TIME_TO_CACHE
- core_question\statistics\responses\analyser::TIME_TO_CACHE
- core_question_bank_renderer::render_category_condition_advanced()
- core_question_bank_renderer::render_hidden_condition_advanced()
- core_question_bank_renderer::render_category_condition()
type: removed
@@ -1,17 +0,0 @@
issueNumber: MDL-78090
notes:
qbank:
- message: |
Final deprecation of:
- qbank_managecategories\output\renderer::class
- qbank_statistics\helper::calculate_average_question_discriminative_efficiency()
- qbank_statistics\helper::calculate_average_question_discrimination_index()
- qbank_statistics\helper::get_all_places_where_questions_were_attempted()
- qbank_statistics\helper::calculate_average_question_stats_item()
- qbank_statistics\helper::calculate_average_question_facility()
- qbank_statistics\helper::load_statistics_for_place()
- qbank_statistics\helper::extract_item_value()
- template qbank_managecategories/category_condition_advanced
- template qbank_managecategories/category_condition
- template qbank_managecategories/listitem
type: removed
@@ -1,32 +0,0 @@
issueNumber: MDL-78118
notes:
core_reportbuilder:
- message: >-
New `get_deprecated_tables` method in base entity, to be overridden when
an entity no longer uses a table (due to column/filter re-factoring,
etc) in order to avoid breaking third-party reports
type: improved
- message: >-
The following deprecated report entity elements have been removed:
- `comment:context`
- `comment:contexturl`
- `enrolment:method` (plus enrolment formatter `enrolment_name` method)
- `enrolment:role`
- `file:context`
- `file:contexturl`
- `instance:context` (tag)
- `instance:contexturl` (tag)
Use of the `context` table is also deprecated in the `file` and
`instance` (tag) entities
type: removed
@@ -1,5 +0,0 @@
issueNumber: MDL-78304
notes:
core_mnet:
- message: 'Remove deprecated mnet_peer::get_public_key()'
type: removed
@@ -1,10 +0,0 @@
issueNumber: MDL-78449
notes:
mod_choice:
- message: >
The WebService `mod_choice_get_choice_results` has a new parameter
`groupid` that allows specifying the group to get the results for. The
default behaviour hasn't changed: if a choice has groups and the
parameter isn't specified the WebService will return the results for the
active group.
type: changed
@@ -1,9 +0,0 @@
issueNumber: MDL-78449
notes:
mod_choice:
- message: >
The function `choice_get_response_data` has a new parameter that allows
specifying the group to get the results for. The default behaviour
hasn't changed: if a choice has groups and the parameter isn't used, the
function will return the results for the active group.
type: changed
@@ -1,10 +0,0 @@
issueNumber: MDL-78489
notes:
core_courseformat:
- message: >-
Protected function
`core_courseformat\output\local\content\section\availability::availability_info()`
has been fully removed. Use
`core_courseformat\output\local\content\section\availability::get_availability_data()`
instead.
type: removed
@@ -1,10 +0,0 @@
issueNumber: MDL-78635
notes:
mod_imscp:
- message: >-
Final removal of deprecated `imscp_libxml_disable_entity_loader`
function
type: removed
mod_lti:
- message: Final removal of deprecated `lti_libxml_disable_entity_loader` function
type: removed
@@ -1,5 +0,0 @@
issueNumber: MDL-78706
notes:
core_repository:
- message: Final removal of base `repository` class method `get_file_size`
type: removed
@@ -1,5 +0,0 @@
issueNumber: MDL-78711
notes:
core_course:
- message: Final deprecation of edit_default_completion()
type: removed
@@ -1,7 +0,0 @@
issueNumber: MDL-78778
notes:
core_auth:
- message: >-
Cas authentication is removed from core and added to the following git repository:
https://github.com/moodlehq/moodle-auth_cas
type: removed
@@ -1,12 +0,0 @@
issueNumber: MDL-78780
notes:
core_grades:
- message: >-
The previously deprecated `grade_helper::get_lang_string` method has
been removed
type: removed
gradereport:
- message: >-
The previously deprecated `grade_report::get_lang_string` method has
been removed
type: removed
@@ -1,14 +0,0 @@
issueNumber: MDL-78869
notes:
core:
- message: >-
Final removal of the following constants/methods from the
`\core\encyption` class, removing support for OpenSSL fallback:
- `METHOD_OPENSSL`
- `OPENSSL_CIPHER`
- `is_sodium_installed`
type: removed
@@ -1,7 +0,0 @@
issueNumber: MDL-78890
notes:
gradereport_grader:
- message: >-
The method `gradereport_grader::get_right_avg_row()` has been finally
deprecated and will now throw an exception if called.
type: deprecated
@@ -1,13 +0,0 @@
issueNumber: MDL-78926
notes:
core_course:
- message: Final removal of core_course\output\activity_information
type: removed
- message: Final deprecation of core_course_renderer\render_activity_information()
type: removed
core:
- message: Final deprecation of core_renderer\activity_information()
type: removed
mod_wiki:
- message: Final deprecation of mod_wiki_renderer\wiki_info()
type: removed
@@ -1,5 +0,0 @@
issueNumber: MDL-79086
notes:
core:
- message: 'Final removal of `share_activity()` in `core\moodlenet\activity_sender`, please use `share_resource()` instead.'
type: removed
@@ -1,20 +0,0 @@
issueNumber: MDL-79121
notes:
core:
- message: >
A new core\ip_utils::normalize_internet_address() method is created to
sanitize an IP address, a range of IP addresses, a domain name or a
wildcard domain matching pattern.
Moodle previously allowed entries such as 192.168. or .moodle.org for
certain variables (eg: $CFG->proxybypass). Since MDL-74289, these
formats are no longer allowed. This method converts this informations
into an authorized format. For example, 192.168. becomes 192.168.0.0/16
and .moodle.org becomes *.moodle.org.
Also a new core\ip_utils::normalize_internet_address_list() method is
created. Based on core\ip_utils::normalize_internet_address(), this
method normalizes a string containing a series of Internet addresses.
type: improved
@@ -1,14 +0,0 @@
issueNumber: MDL-79124
notes:
core_user:
- message: >-
Final removal of the following user preference helpers, please use the
`core_user/repository` module instead:
- `user_preference_allow_ajax_update`
- `M.util.set_user_preference`
- `lib/ajax/setuserpref.php`
type: removed
@@ -1,9 +0,0 @@
issueNumber: MDL-79162
notes:
core_badges:
- message: |-
The following previously deprecated renderer methods have been removed:
* `print_badge_table_actions`
* `render_badge_management`
type: removed
@@ -1,7 +0,0 @@
issueNumber: MDL-79214
notes:
mod_folder:
- message: >-
Method htmllize_tree() has been removed. Please use
renderable_tree_elements instead
type: removed
@@ -1,9 +0,0 @@
issueNumber: MDL-79281
notes:
core_question:
- message: >-
The `get_bulk_actions()` method on the base `plugin_features_base` class
has been changed to allow a qbank view object to be passed through. This
is nullable and therefore optional for qbank plugins which don't need
to do so.
type: improved
@@ -1,9 +0,0 @@
issueNumber: MDL-79434
notes:
core_calendar:
- message: >
Final removal of calendar functions:
- calendar_top_controls()
- calendar_get_link_previous()
- calendar_get_link_next()
type: removed
@@ -1,7 +0,0 @@
issueNumber: MDL-79434
notes:
core_calendar:
- message: >-
Initial deprecation of calendar_sub_month.
Use \core_calendar\type_factory::get_calendar_instance()->get_prev_month() instead.
type: deprecated
@@ -1,12 +0,0 @@
issueNumber: MDL-79122
notes:
core_question:
- message: >-
Behat steps `behat_qbank_comment::i_should_see_on_the_column`
and `behat_qbank_comment::i_click_on_the_row_containing` have been
deprecated in favour of the new component named selectors,
`qbank_comment > Comment count link` and
`qbank_comment > Comment count text` which can be used with the
standard `should exist` and `I click on` steps to replace the
custom steps.
type: deprecated
@@ -1,10 +0,0 @@
issueNumber: MDL-79907
notes:
core_grades:
- message: >-
Final deprecation of
grade_structure::get_element_type_string(),
grade_structure::get_element_header(),
grade_structure::get_element_icon(),
grade_structure::get_activity_link()
type: removed
@@ -1,12 +0,0 @@
issueNumber: MDL-80116
notes:
core_courseformat:
- message: >-
The state actions section_move and all related functions are final
deprecated and cannot be used anymore. Use the newer section_move_after
from now on.
type: deprecated
- message: >-
The core_courseformat::base get_section_number and set_section_number
are now final deprecated. Use get_sectionum and set_sectionnum instead.
type: deprecated
@@ -1,5 +0,0 @@
issueNumber: MDL-80156
notes:
theme_boost:
- message: Remove SCSS deprecated in 4.4
type: removed
@@ -1,7 +0,0 @@
issueNumber: MDL-80173
notes:
core_reportbuilder:
- message: >-
Various Oracle-specific support/workarounds in APIs and component report
entities have been removed
type: removed
@@ -1,8 +0,0 @@
issueNumber: MDL-80430
notes:
core_reportbuilder:
- message: >-
Final removal of support for `get_default_table_aliases` method.
Entities must now implement `get_default_tables`, which is now abstract,
to define the tables they use
type: removed
@@ -1,5 +0,0 @@
issueNumber: MDL-80491
notes:
core_enrol:
- message: Final removal of base `enrol_plugin` class method `update_communication`
type: removed
@@ -1,7 +0,0 @@
issueNumber: MDL-80519
notes:
theme_boost:
- message: >-
Added new bs4-compat SCSS file (initially deprecated) to help
third-party plugins the migration process from BS4 to BS5
type: deprecated
@@ -1,5 +0,0 @@
issueNumber: MDL-80944
notes:
mod_quiz:
- message: Final removal of quiz_delete_override() and quiz_delete_all_overrides()
type: removed

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