Compare commits

..

174 Commits

Author SHA1 Message Date
Eloy Lafuente (stronk7) fa0b5768cc Moodle release 3.5.9 2019-11-09 11:57:38 +01:00
Sara Arjona 7b33c5e9ec weekly release 3.5.8+ 2019-11-08 15:39:52 +01:00
Sara Arjona d394a22ee6 Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2019-11-08 15:39:50 +01:00
Adrian Greeve 83fe3c44c0 Merge branch 'MDL-67169_35' of https://github.com/stronk7/moodle into MOODLE_35_STABLE 2019-11-08 10:46:34 +08:00
Sara Arjona 282c4224da Merge branch 'MDL-66683-35' of git://github.com/cescobedo/moodle into MOODLE_35_STABLE 2019-11-07 17:41:53 +01:00
cescobedo 1312197a82 MDL-66683 core: Revert MDL-66181 because created regressions
This reverts commit ed080a580f.
This issue reverts the behaviour modified in MDL-66181, as that change
negatively impacted course-creation workflows. As this appears to be
the intended functionality, the Security team has agreed to revert
this change.
2019-11-07 17:31:25 +01:00
Jake Dallimore 5dc6fd3b23 MDL-66257 tool_cohortroles: unit test for users in multiple cohorts 2019-11-07 23:48:43 +08:00
cescobedo b49b76f397 MDL-66257 tool_cohortroles: Unassign the role on removal cohortroles. 2019-11-07 23:48:43 +08:00
Eloy Lafuente (stronk7) a4f4656110 MDL-67169 phpunit: fix clear flaw in unit test provider
Basically the provider is ignoring the CRLF to LF normalization
results and loading the original file again.

This doesn't have any impact normally, because all moodle
files are LF ones and people using other systems have their
git configurations set to work that way (not modify or force LF).

But there may be checkouts out there (for example travis) where
the git configuration by defult is to convert to the OS, causing
windows runs to fail badly there. See the issue for more info
and links.
2019-11-07 13:42:35 +01:00
Jun Pataleta 83bdb872dc MDL-66598 auth_oauth2: OAuth2 upgrade step
This basically does two things:
1. Updates all non-Facebook/Google/Microsoft issuers and sets their
'requireconfirmation' field to 1 so that future OAuth2 logins on these
issuers will always have an email confirmation sent to them.
2. Deletes the linked logins of users with non-Facebook/Google/Microsoft
OAuth2 issuers.
2019-11-07 18:46:05 +08:00
Jun Pataleta 39a83aa250 MDL-66598 auth_oauth2: Add warning messages in confirm emails 2019-11-07 18:46:05 +08:00
Jun Pataleta e70853386d MDL-66598 tool_oauth2: Make account confirmation required by default
* Only Facebook, Google, and Microsoft issuers can optionally offer to
require account confirmation via email. We will require email
confirmation for the rest of the issuers.
2019-11-07 18:46:05 +08:00
AMOS bot 451d514d84 Automatically generated installer lang files 2019-11-06 00:16:53 +00:00
Michael Hawkins 72f5a6e119 MDL-66161 output: Escaping fatal error message and URL strings 2019-11-05 13:51:42 +01:00
Paul Holden 7f624852e4 MDL-66228 mod_lesson: ensure return URL parameters are always local. 2019-11-05 13:51:42 +01:00
Eloy Lafuente (stronk7) 8abe8980d6 weekly release 3.5.8+ 2019-11-05 13:34:17 +01:00
Eloy Lafuente (stronk7) 431c7ba150 Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2019-11-05 13:34:14 +01:00
Jake Dallimore 9b632e17d8 Merge branch 'MDL-67009_35' of https://github.com/stronk7/moodle into MOODLE_35_STABLE 2019-11-04 14:57:19 +08:00
AMOS bot b731ea9798 Automatically generated installer lang files 2019-11-02 00:09:46 +00:00
Eloy Lafuente (stronk7) 472b1b095e weekly release 3.5.8+ 2019-10-29 21:01:29 +01:00
Eloy Lafuente (stronk7) 2cddf32f7e Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2019-10-29 21:01:22 +01:00
Jake Dallimore 9b898bf8b4 Merge branch 'MDL-66945_35' of git://github.com/stronk7/moodle into MOODLE_35_STABLE 2019-10-29 14:57:04 +08:00
Andrew Nicols a87d3a64bb MDL-66945 composer: Update behat extension
(using php70 for this branch)

This uses a new version of the moodlehq-behat-extension which ensures
that a node is visible and in the view port before it is clicked on.

This resolves an issue with Firefox on Ubuntu 18.04 whereby the node is
only brought into the viewport instead of clicked on.
2019-10-28 13:44:27 +01:00
AMOS bot 55cd43d397 Automatically generated installer lang files 2019-10-28 00:09:34 +00:00
AMOS bot 9c1462ecfe Automatically generated installer lang files 2019-10-27 00:09:31 +00:00
Eloy Lafuente (stronk7) e22aba78ca weekly release 3.5.8+ 2019-10-25 15:29:30 +02:00
Eloy Lafuente (stronk7) d952f227d0 Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2019-10-25 15:29:26 +02:00
Jake Dallimore d80cec9c2b Merge branch 'MOODLE_35_MDL-66973' of https://github.com/golenkovm/moodle into MOODLE_35_STABLE 2019-10-25 11:11:16 +08:00
Eloy Lafuente (stronk7) 77fe56108a MDL-67009 behat: get rid of invalid void type hinting for php70 2019-10-24 10:50:30 +02:00
AMOS bot 03550b7b8f Automatically generated installer lang files 2019-10-24 00:09:42 +00:00
Mikhail Golenkov 274ccc948b MDL-66973 search: Reset Content writer after test. 2019-10-22 09:41:46 +11:00
AMOS bot bc8d5da483 Automatically generated installer lang files 2019-10-19 00:09:35 +00:00
Eloy Lafuente (stronk7) 94bf3e9c78 weekly release 3.5.8+ 2019-10-18 10:31:16 +02:00
Andrew Nicols e92bd648db Merge branch 'MDL-66919_35' of https://github.com/stronk7/moodle into MOODLE_35_STABLE 2019-10-18 08:29:48 +08:00
Eloy Lafuente (stronk7) e4bb1cc603 MDL-66919 behat: fix typo in error message
Also fix a couple of phpdocs that were incorrect.
2019-10-16 00:49:32 +02:00
Eloy Lafuente (stronk7) c2f12aa742 weekly release 3.5.8+ 2019-10-15 22:59:44 +02:00
Jun Pataleta 454af7cf52 Merge branch 'MDL-66821_35' of https://github.com/timhunt/moodle into MOODLE_35_STABLE 2019-10-15 07:35:57 +08:00
Andrew Nicols f73e64f166 Merge branch 'MDL-66335_35' of https://github.com/stronk7/moodle into MOODLE_35_STABLE 2019-10-15 07:32:17 +08:00
Eloy Lafuente (stronk7) 18a738f88f MDL-66335 behat: Avoid double processing the page type
resolve_page_instance_helper() already processes the type, returning
the correct context that should be processing the navigation URL.

With that extra call to parse_page_name() the 2nd call always returns
"core", ultimately leading to tons of behat failures because "core" is
not aware of those (plugin, quiz for now) pages typology.
2019-10-15 00:04:54 +02:00
Eloy Lafuente (stronk7) 493a30cdc2 Merge branch 'MDL-66335_35' of https://github.com/timhunt/moodle into MOODLE_35_STABLE 2019-10-14 19:22:28 +02:00
Eloy Lafuente (stronk7) 65edc59a51 Merge branch 'MDL-66559-35' of git://github.com/andrewnicols/moodle into MOODLE_35_STABLE 2019-10-14 16:41:29 +02:00
Tim Hunt 28b9209af5 MDL-66335 quiz behat: using the new generic navigation steps
This converts all the navigation to quiz pages when that is not
the thing under test. Also, I decided that the Quiz settings form
belonged to the course, so did not do that here.
2019-10-14 13:20:29 +01:00
Tim Hunt 8b2c91e627 MDL-66335 behat: new step to log in and go straight to a particular page
I did not think thought through if this new URL parameter would be a
potential security hole. The simple cop-out option is to make it only
work on Behat sites for now.
2019-10-14 13:17:11 +01:00
Tim Hunt 8256d18453 MDL-66335 behat: generic step for navigating direct to specific pages 2019-10-14 13:15:27 +01:00
Tim Hunt ab11164395 MDL-66821 question behat: new step for acting on question in the bank
There is a proposed change MDL-66816 which will change the question
bank UI. This will break any Behat tests which uses low-level steps
like

    When I click on "Duplicate" "link" in the "Test question" "table_row"

to perform an action on a question in the question bank. This commit
introduces a new step:

    When I "Duplicate" the "Test question" question in the question bank

This commit also converts all core Behat tests to use the new step.
2019-10-14 12:02:42 +01:00
Andrew Nicols 29933a945f MDL-66559 behat: Register component selectors earlier 2019-10-14 14:24:05 +08:00
Andrew Nicols c7b384ce66 MDL-66559 behat: Update running suite 2019-10-14 14:17:47 +08:00
Jake Dallimore 665e256961 Merge branch 'MDL-66559-35' of git://github.com/andrewnicols/moodle into MOODLE_35_STABLE 2019-10-14 11:10:56 +08:00
Andrew Nicols 879532b41f MDL-66779 phpunit: Correct component name for core_grades 2019-10-13 02:34:16 +02:00
Eloy Lafuente (stronk7) 4e83a79cd5 weekly release 3.5.8+ 2019-10-11 22:43:20 +02:00
Eloy Lafuente (stronk7) 3ecba892e3 Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2019-10-11 22:43:07 +02:00
Tim Hunt b92f4ba9f1 MDL-66559 behat: Documentation for component selectors 2019-10-11 12:58:16 +08:00
Andrew Nicols 6a3a83af93 MDL-66559 behat: Allow per-component selectors and replacements 2019-10-11 12:58:16 +08:00
AMOS bot 6e3a070030 Automatically generated installer lang files 2019-10-11 00:11:51 +00:00
Tim Hunt 5d701a706c MDL-66815 behat: question category generator must find right parent id
This is implemented in a bit of a hacky way, because the only other
alternative would be a large rewrite of the whole class, which is
not feasible for me right now. Note that many other types of thing
that can be generated suffer from the same issue. (E.g. if you ever
wanted to generate two groups with the same name in two different
courses and then add group memebers.)

It is worth doing a special-case fix for question categories, because
way Moodle works, it is inevitable that we end up with many categories
called 'top' in the database.
2019-10-11 00:51:39 +02:00
AMOS bot 612e4d9d02 Automatically generated installer lang files 2019-10-10 00:09:44 +00:00
Jun Pataleta df6f010a86 Merge branch 'MDL-66835_35' of https://github.com/timhunt/moodle into MOODLE_35_STABLE 2019-10-09 20:44:17 +08:00
Tim Hunt bf4904c32b MDL-66835 behat: steps for setting/checking fields in containers 2019-10-09 13:24:03 +01:00
Jun Pataleta 2821f78e6c weekly release 3.5.8+ 2019-09-27 11:12:16 +08:00
Jun Pataleta f904215fa7 Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2019-09-27 11:12:15 +08:00
Jun Pataleta 635b605eab Merge branch 'MDL-66262_35' of git://github.com/stronk7/moodle into MOODLE_35_STABLE 2019-09-26 11:34:26 +08:00
Eloy Lafuente (stronk7) 08dd41b304 Merge branch 'MDL-66675-35' of git://github.com/andrewnicols/moodle into MOODLE_35_STABLE 2019-09-25 20:06:38 +02:00
AMOS bot 04cd0cb2ca Automatically generated installer lang files 2019-09-22 00:09:35 +00:00
Andrew Nicols 5cacda33bb MDL-66675 behat: Fix typo in step function name 2019-09-19 07:30:56 +08:00
Andrew Nicols 864ef22534 MDL-66675 behat: Add option to pause on behat failures 2019-09-19 07:30:55 +08:00
Andrew Nicols f008a03c60 MDL-66675 CLI: Add ANSI formatting options 2019-09-19 07:30:54 +08:00
Eloy Lafuente (stronk7) 8831cc14c7 MDL-66262 env: Moodle 3.6.x and 3.7.x do not support PHP 7.4 2019-09-13 17:44:58 +02:00
Jake Dallimore ebb87a1e63 weekly release 3.5.8+ 2019-09-13 11:30:14 +08:00
Jake Dallimore 93d9b19607 Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2019-09-13 11:30:13 +08:00
Eloy Lafuente (stronk7) 92d04a7913 Merge branch 'MDL-66550-35-2' of git://github.com/andrewnicols/moodle into MOODLE_35_STABLE 2019-09-12 15:32:14 +02:00
Andrew Nicols a5cdde3c79 MDL-66550 admin: The execute wrapper MUST be used in behat 2019-09-12 15:46:04 +08:00
Andrew Nicols d073f5daf0 MDL-66550 theme: Wrap JS calls in pendingJS 2019-09-12 14:59:03 +08:00
AMOS bot 3972ef0a32 Automatically generated installer lang files 2019-09-12 00:09:38 +00:00
Jake Dallimore 01b63287ef Merge branch 'MDL-66550-35-2' of git://github.com/andrewnicols/moodle into MOODLE_35_STABLE 2019-09-11 10:42:07 +08:00
Andrew Nicols b3259b51bb MDL-66550 forum: Target fields in a fieldset for behat test 2019-09-10 21:24:20 +08:00
Andrew Nicols b683a48f69 MDL-66550 behat: Transform fields to search in a fieldset 2019-09-10 21:24:20 +08:00
Andrew Nicols 944888a509 MDL-66550 behat: Standardise finding in core functionality 2019-09-10 21:24:20 +08:00
Andrew Nicols 21ac04e12e MDL-66550 behat: Add ability to transform a specific selector 2019-09-10 21:24:15 +08:00
Andrew Nicols ab9b65b6a7 MDL-66550 behat: Normalise selectors better 2019-09-10 21:24:07 +08:00
Jake Dallimore 4a852e8467 Moodle release 3.5.8 2019-09-06 11:50:39 +08:00
Eloy Lafuente (stronk7) 9c211250ed weekly release 3.5.7+ 2019-09-05 17:14:30 +02:00
Eloy Lafuente (stronk7) a4eb84f12f Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2019-09-05 17:14:27 +02:00
AMOS bot 1e3da12a5f Automatically generated installer lang files 2019-09-05 00:12:14 +00:00
Ryan Wyllie d5ac2c69cf MDL-62284 javascript: prevent nested JS mustache handler calls
Prevent the JS mustache helper from being executed from within
the render call of another mustache helper because it can allow
users to inject JS into the page.
2019-09-04 11:24:34 +08:00
Ryan Wyllie 3cf2cce110 MDL-62284 output: prevent nested JS mustache handler calls
Prevent the JS mustache helper from being executed from within
the render call of another mustache helper because it can allow
users to inject JS into the page.
2019-09-04 11:24:34 +08:00
Jake Dallimore 1df418416b Merge branch 'MDL-66265-35' of git://github.com/andrewnicols/moodle into MOODLE_35_STABLE 2019-09-04 11:07:44 +08:00
Andrew Nicols fdee2f256c MDL-66265 javascript: More specific overrides in eslintrc 2019-09-04 10:49:17 +08:00
Eloy Lafuente (stronk7) 15974729df Merge branch 'MDL-66265-35' of git://github.com/andrewnicols/moodle into MOODLE_35_STABLE 2019-09-04 02:15:49 +02:00
Eloy Lafuente (stronk7) 99f8dea24b Merge branch 'MDL-66532-35' of git://github.com/andrewnicols/moodle into MOODLE_35_STABLE 2019-09-04 02:01:46 +02:00
Andrew Nicols 27829f24e1 MDL-66265 javascript: Add jshint ignore for ES6 files in root dir 2019-09-04 06:53:54 +08:00
Andrew Nicols 6fd3764d99 MDL-66265 javascript: Add basic jshint ignores 2019-09-04 06:53:53 +08:00
Andrew Nicols 8563f23446 MDL-66265 javascript: Bump jshint version 2019-09-04 06:53:52 +08:00
Andrew Nicols 033afe1028 MDL-66532 javascript: Lock NPM version for NVM 2019-09-04 06:53:52 +08:00
Andrew Nicols c21f3b540b MDL-66532 javascript: Lock NPM version for NVM 2019-09-04 06:45:51 +08:00
AMOS bot 9fa3c6756e Automatically generated installer lang files 2019-09-03 00:11:49 +00:00
Eloy Lafuente (stronk7) d543802d90 Merge branch 'MDL-66457-35' of git://github.com/andrewnicols/moodle into MOODLE_35_STABLE 2019-09-03 00:55:14 +02:00
Juan Leyva 374d0b4702 MDL-66501 tool_mobile: Prevent URLs in custom scheme 2019-09-02 15:30:19 +08:00
Andrew Nicols 043da28910 MDL-66457 behat: simplify selector search 2019-08-30 15:13:07 +08:00
Jun Pataleta b35de2fdf0 MDL-66187 format_singleactivity: Make sure available types is not empty 2019-08-30 07:21:39 +02:00
Andrew Nicols 2b1b27dfbd MDL-66187 course: Respect :addinstance in single activity format 2019-08-30 07:21:39 +02:00
Simey Lameze bd370cda69 MDL-65749 lib: fix phpmailer file handling 2019-08-30 07:21:39 +02:00
Andrew Nicols 86cbf2a643 MDL-66181 course: Only assign roles that the user is allowed to assign 2019-08-30 07:21:39 +02:00
Andrew Nicols d33ae6c0cc MDL-66181 behat: You check checkboxes, not click them 2019-08-30 07:21:39 +02:00
Michael Hawkins 61b51f93ef MDL-55451 mod_forum: Limit subscription redirect to internal URLs 2019-08-30 07:21:39 +02:00
David Monllaó eb2f9593a7 MDL-66069 mlbackend_python: Bump to a new version of the package 2019-08-30 07:21:39 +02:00
Jake Dallimore e8b6cb418f weekly release 3.5.7+ 2019-08-30 13:12:06 +08:00
Jake Dallimore f8798a6b07 Merge branch 'MDL-66139_35' of https://github.com/stronk7/moodle into MOODLE_35_STABLE 2019-08-29 11:52:15 +08:00
Netica Informática 75259ad00f MDL-66139 cachestore_redis: Fix connection problems when port is not set
Due to some changes in Redis 5 php extension Moodle fails to connect via TCP when
no port is set. It's fixed upstream in phpredis repo (version 5.0.0, 5.0.1 and
5.0.2 affected so far), but with this patch we'll send the right port anyway.
2019-08-28 20:59:59 +02:00
Luca Bösch e149838c2a MDL-63852 behat: Using correct exception class path. 2019-08-28 17:43:27 +02:00
Eloy Lafuente (stronk7) 7e0c5d1c38 MDL-66184 core: Update composer to apply for php 7.0.0 as min
With behat extension v3.35.3 we started requiring php 7.0.0
(same requirement than core 3.5.x). This bumps all the components
in the lock file to suit that requirement.

Generated with php 7.0.x.
2019-08-27 00:25:28 +02:00
Eloy Lafuente (stronk7) b78a289579 Merge branch 'MDL-66312-35' of git://github.com/andrewnicols/moodle into MOODLE_35_STABLE 2019-08-26 19:43:10 +02:00
Adrian Greeve de168198e6 weekly release 3.5.7+ 2019-08-23 12:55:16 +08:00
Adrian Greeve d4cd76a5c8 Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2019-08-23 12:55:08 +08:00
Adrian Greeve 7430be37c5 Merge branch 'MDL-66378-35' of git://github.com/andrewnicols/moodle into MOODLE_35_STABLE 2019-08-22 09:32:16 +08:00
Andrew Nicols 327c830b2a MDL-66378 behat: Increase window size for rubric tests 2019-08-22 07:56:19 +08:00
Andrew Nicols f2c38390c9 MDL-66378 behat: Change screen size to small to work around UI bugs 2019-08-22 07:56:19 +08:00
Adrian Greeve 55bbdac9d9 Merge branch 'MDL-66378-35' of git://github.com/andrewnicols/moodle into MOODLE_35_STABLE 2019-08-21 09:04:02 +08:00
Andrew Nicols 7ed867fa20 MDL-66378 behat: Correct grammar of resize step 2019-08-21 08:39:51 +08:00
Andrew Nicols fc6f41655b MDL-66378 grade: Set window size to large for rubric tests 2019-08-21 08:39:28 +08:00
Andrew Nicols 15ddbf3907 MDL-66378 behat: All args are required for document.evaluate 2019-08-21 08:24:08 +08:00
AMOS bot 27b547db5a Automatically generated installer lang files 2019-08-21 00:11:34 +00:00
Adrian Greeve 0d13442b29 Merge branch 'MDL-66378-35' of git://github.com/andrewnicols/moodle into MOODLE_35_STABLE 2019-08-20 14:13:57 +08:00
AMOS bot d1bc79e02a Automatically generated installer lang files 2019-08-20 00:10:59 +00:00
Andrew Nicols 3abca9b368 MDL-66378 course: Navigation tests need small window 2019-08-19 08:16:41 +08:00
Andrew Nicols 3a000a8def MDL-66378 competency: Add pending promises to competency chooser 2019-08-19 08:16:41 +08:00
Andrew Nicols 1c3c38fb00 MDL-66378 behat: JS does not work with newlines in an xpath 2019-08-19 08:16:40 +08:00
Andrew Nicols 86cbd0840b MDL-66378 behat: Fix use of before/after selectors 2019-08-19 08:16:40 +08:00
Andrew Nicols 2ea211e78a MDL-66378 behat: Backport expand autocomplete step 2019-08-18 07:15:00 +08:00
Andrew Nicols b60ec99fec MDL-66378 behat: Add partial named selector for whole autocomplete field 2019-08-18 07:15:00 +08:00
Andrew Nicols 953b1b85da MDL-66378 behat: Before/after detection should be constrainable
The default before/after detection checks the entire body. Any match of
the text will therefore be found.

Add support for specification of a container for the nodes.
2019-08-18 07:15:00 +08:00
Andrew Nicols 45e2df1ce4 MDL-66378 behat: Add non-JS fallback for before/after 2019-08-18 07:15:00 +08:00
sam marshall 9f1149ac13 MDL-66378 Behat: Speed up 'before/after' step in Chrome
On some Chrome versions the xpath used for before/after is slow
and (sometimes) uses a lot of memory. This change uses pure
JavaScript to do the before/after calculation, which should always
be fast.
2019-08-18 07:15:00 +08:00
Andrew Nicols a157baa65a MDL-66378 behat: Correct invalid uses of selection from dropdown 2019-08-18 07:15:00 +08:00
Andrew Nicols 15d2dcf037 MDL-66378 report_participation: Correct label for target 2019-08-18 07:15:00 +08:00
Andrew Nicols 9eab90a827 MDL-66378 gradereport_singleview: Add missing label for dropdown 2019-08-18 07:15:00 +08:00
Andrew Nicols 2bee1f66b9 MDL-66378 behat: Update screen sizes to Y2K standards 2019-08-17 10:32:19 +08:00
Andrew Nicols c3731344e5 MDL-66378 behat: Update dependencies for chrome webdriver fix 2019-08-17 10:32:19 +08:00
Andrew Nicols c46b2eaf18 MDL-66312 js: Autocomplete promises were inside out
The pending promise should nott be resolved until the changes are
complete.
Previously the resolution of the pendingPromise was triggering the other
changes.
2019-08-16 11:56:25 +08:00
Andrew Nicols d6af305bb9 weekly release 3.5.7+ 2019-08-16 11:07:12 +08:00
Andrew Nicols fa319a92b8 Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2019-08-16 11:07:09 +08:00
AMOS bot e11d0e35de Automatically generated installer lang files 2019-08-15 00:13:11 +00:00
Jun Pataleta 313fd64469 Merge branch 'MDL-65925-grade-page-broken-for-deleted-submission-MOODLE_35_STABLE' of https://github.com/ilya-catalyst/moodle into MOODLE_35_STABLE 2019-08-14 15:25:36 +08:00
Ilya Tregubov 9f51263a37 MDL-65925 assign: Grade page broken if submission was deleted. 2019-08-12 09:25:50 +10:00
AMOS bot b375088f23 Automatically generated installer lang files 2019-08-10 00:11:37 +00:00
Jun Pataleta 6cdfbdc119 weekly release 3.5.7+ 2019-08-09 16:23:59 +08:00
Jun Pataleta c3e7a570f3 Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2019-08-09 16:23:58 +08:00
Sara Arjona b12ee51f8b Merge branch 'MDL-66281-35' of git://github.com/junpataleta/moodle into MOODLE_35_STABLE 2019-08-05 13:41:18 +08:00
AMOS bot 9192613a6a Automatically generated installer lang files 2019-08-02 00:11:41 +00:00
AMOS bot 74fc0a9620 Automatically generated installer lang files 2019-08-02 00:11:40 +00:00
AMOS bot 264ea8c7fc Automatically generated installer lang files 2019-07-31 00:09:31 +00:00
Jun Pataleta 06079db957 MDL-66281 tool_dataprivacy: Set sensitive data processing default value 2019-07-30 13:59:42 +08:00
Eloy Lafuente (stronk7) 59c9dd780b weekly release 3.5.7+ 2019-07-25 13:44:49 +02:00
Eloy Lafuente (stronk7) 5ec6a4e462 Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2019-07-25 13:44:48 +02:00
Eloy Lafuente (stronk7) 4ce08a765d Merge branch 'MDL-65788-35' of git://github.com/Chocolate-lightning/moodle into MOODLE_35_STABLE 2019-07-22 21:33:28 +02:00
sam marshall 0498684ae2 MDL-66132 Search: Behat tests should use simpledb, fix mock count
When searching using mock results (the 'global search expects the
query' step), the result count is not correctly set. As a result, the
page incorrectly reports that there are no results and doesn't
correctly show the first page of multi-page results.

Additionally, some of the core Behat tests can now be moved to use
real searching with the simpledb engine, rather than using mock
results at all. This gives better tests.

Unfortunately it was not possible to move all of the core Behat tests
and deprecate the mock step, because some of the tests are related to
the UI for 'special' features searching by user or group, neither of
which are supported by the simpledb engine.
2019-07-22 20:57:10 +02:00
Mihail Geshoski 9a0078f100 MDL-66136 assign: Verify presence of resources in online text submission 2019-07-22 10:44:53 +08:00
Mihail Geshoski d9126f03dc MDL-66136 assignfeedback_comments: Extend testcase 2019-07-22 10:44:41 +08:00
Eloy Lafuente (stronk7) 382fc36eba Merge branch 'MDL-50733_35' of git://github.com/stronk7/moodle into MOODLE_35_STABLE 2019-07-20 00:25:27 +02:00
Mathew May defe05cbcf MDL-50733 core: Update composer to updated behat-extension 2019-07-19 16:41:03 +02:00
Mathew May 71548acf7e MDL-65788 tool_policy: Move accept_policy JS call into an array 2019-07-18 10:51:19 +08:00
Mathew May de75e7471d MDL-65788 tool_policy: Prevent two modals rendering for guests.
With the Guest registration calling policyactions.js the [data-action="view"]
was having two click handlers set on the inital policy modal trgger.
With this commit we state what we want the click event to be set on.
2019-07-18 10:49:58 +08:00
AMOS bot 8635cc3871 Automatically generated installer lang files 2019-07-18 00:09:21 +00:00
Eloy Lafuente (stronk7) a8d72a836c weekly release 3.5.7+ 2019-07-11 23:33:37 +02:00
Eloy Lafuente (stronk7) 140b417065 Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2019-07-11 23:33:35 +02:00
Jun Pataleta 0094a77ccc Merge branch 'MDL-65926-MOODLE_35_STABLE_lowercase_username_sql_query' of https://github.com/andrewmadden/moodle into MOODLE_35_STABLE 2019-07-11 10:59:29 +08:00
Andrew Madden c7f46956d4 MDL-65926 moodlelib: Convert username to lowercase prior to SQL query
Within a very large project, it was found that searching for a user with their username in the user table using a case insensitive
search was inefficient. Instead it is proposed that the username input is made to be lowercase prior to executing a database query
as the username for each user in the database must be lowercase. This allows for a case sensitive query to find the user data.
Essentially we are moving a case insensitive search for a user from SQL into PHP to increase performance.
2019-07-10 20:04:23 +10:00
Andrew Nicols 648e72316d Merge branch 'MDL-65965-35-fix' of git://github.com/junpataleta/moodle into MOODLE_35_STABLE 2019-07-09 11:01:05 +08:00
Jun Pataleta 6b8c4ddacc MDL-65965 behat: Do not fetch grouping IDs for empty grouping idnumbers 2019-07-09 10:49:25 +08:00
Shamim Rezaie 3bf1e69a3e MDL-66054 behat: typo fix 2019-07-09 00:58:05 +02:00
Eloy Lafuente (stronk7) df4041b745 Merge branch 'MDL-65965-35' of git://github.com/junpataleta/moodle into MOODLE_35_STABLE 2019-07-09 00:55:26 +02:00
AMOS bot ac9e0f9ad5 Automatically generated installer lang files 2019-07-08 00:10:29 +00:00
AMOS bot c0cfb0ee09 Automatically generated installer lang files 2019-07-07 00:09:13 +00:00
Jun Pataleta 91998db5f5 MDL-65965 behat: Map grouping for activity data generator 2019-06-21 15:41:14 +08:00
Jun Pataleta e76b0e1014 MDL-65965 availability: Behat test for activity grouping restriction 2019-06-21 15:41:14 +08:00
186 changed files with 4220 additions and 1108 deletions
+1 -1
View File
@@ -203,7 +203,7 @@
}
},
{
files: ["**/amd/src/*.js"],
files: ["**/amd/src/*.js", "Gruntfile.js"],
// Check AMD with some slightly stricter rules.
rules: {
'no-unused-vars': 'error',
+2
View File
@@ -0,0 +1,2 @@
**/amd/**
/*.js
+1
View File
@@ -0,0 +1 @@
v8.16.1
+1
View File
@@ -24,6 +24,7 @@
* Grunt configuration
*/
/* eslint-env node */
module.exports = function(grunt) {
var path = require('path'),
tasks = {},
+2
View File
@@ -2451,6 +2451,7 @@
<VENDOR name="oracle" version="11.2" />
</DATABASE>
<PHP version="7.0.0" level="required">
<RESTRICT function="restrict_php_version_74" message="unsupportedphpversion74" />
</PHP>
<PCREUNICODE level="optional">
<FEEDBACK>
@@ -2632,6 +2633,7 @@
<VENDOR name="oracle" version="11.2" />
</DATABASE>
<PHP version="7.1.0" level="required">
<RESTRICT function="restrict_php_version_74" message="unsupportedphpversion74" />
</PHP>
<PCREUNICODE level="optional">
<FEEDBACK>
+5 -29
View File
@@ -48,7 +48,6 @@ class behat_admin extends behat_base {
* @param TableNode $table
*/
public function i_set_the_following_administration_settings_values(TableNode $table) {
if (!$data = $table->getRowsHash()) {
return;
}
@@ -57,17 +56,12 @@ class behat_admin extends behat_base {
// We expect admin block to be visible, otherwise go to homepage.
if (!$this->getSession()->getPage()->find('css', '.block_settings')) {
$this->getSession()->visit($this->locate_path('/'));
$this->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
$this->execute('behat_forms::i_am_on_homepage');
}
// Search by label.
$searchbox = $this->find_field(get_string('searchinsettings', 'admin'));
$searchbox->setValue($label);
$submitsearch = $this->find('css', 'form.adminsearchform input[type=submit]');
$submitsearch->press();
$this->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
$this->execute('behat_forms::i_set_the_field_to', [get_string('searchinsettings', 'admin'), $label]);
$this->execute("behat_general::i_click_on_in_the", [get_string('search', 'admin'), 'button', '.block_settings', 'css_element']);
// Admin settings does not use the same DOM structure than other moodle forms
// but we also need to use lib/behat/form_field/* to deal with the different moodle form elements.
@@ -83,35 +77,17 @@ class behat_admin extends behat_base {
"@id=//span[contains(normalize-space(.), $label)]/preceding-sibling::label[1]/@for]";
$fieldnode = $this->find('xpath', $fieldxpath, $exception);
$formfieldtypenode = $this->find('xpath', $fieldxpath . "/ancestor::div[@class='form-setting']" .
"/child::div[contains(concat(' ', @class, ' '), ' form-')]/child::*/parent::div");
} catch (ElementNotFoundException $e) {
// Multi element settings, interacting only the first one.
$fieldxpath = "//*[label[normalize-space(.)= $label]|span[normalize-space(.)= $label]]/" .
"ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' form-item ')]" .
"/descendant::div[@class='form-group']/descendant::*[self::input | self::textarea | self::select]" .
"[not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]";
$fieldnode = $this->find('xpath', $fieldxpath);
// It is the same one that contains the type.
$formfieldtypenode = $fieldnode;
}
// Getting the class which contains the field type.
$classes = explode(' ', $formfieldtypenode->getAttribute('class'));
foreach ($classes as $class) {
if (substr($class, 0, 5) == 'form-') {
$type = substr($class, 5);
}
}
// Instantiating the appropiate field type.
$field = behat_field_manager::get_field_instance($type, $fieldnode, $this->getSession());
$field->set_value($value);
$this->find_button(get_string('savechanges'))->press();
$this->execute('behat_forms::i_set_the_field_with_xpath_to', [$fieldxpath, $value]);
$this->execute("behat_general::i_click_on", [get_string('savechanges'), 'button']);
}
}
+1
View File
@@ -9,6 +9,7 @@ Feature: Manage tokens
| username | password | firstname | lastname |
| testuser | testuser | Joe | Bloggs |
| testuser2 | testuser2 | TestFirstname | TestLastname |
And I change window size to "small"
And I log in as "admin"
And I am on site homepage
@@ -0,0 +1,27 @@
@tool_behat
Feature: Behat steps for interacting with form work
In order to test my Moodle code
As a developer
I need the Behat steps for form elements to work reliably
@javascript
Scenario: Test fields in containers
Given the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
When I log in as "admin"
And I am on "Course 1" course homepage
# Just get to any form.
And I navigate to "Edit settings" in current page administration
And I set the field "Course full name" in the "General" "fieldset" to "Frog"
And I set the following fields in the "Appearance" "fieldset" to these values:
| Show activity reports | Yes |
| Number of announcements | 1 |
Then the field "Show activity reports" in the "Appearance" "fieldset" matches value "Yes"
And the field "Show activity reports" in the "Appearance" "fieldset" does not match value "No"
And the following fields in the "region-main" "region" match these values:
| Course full name | Frog |
| Number of announcements | 1 |
And the following fields in the "region-main" "region" do not match these values:
| Course full name | Course 1 |
| Number of announcements | 5 |
+42
View File
@@ -222,6 +222,48 @@ class api {
}
}
// Clean the legacy role assignments which are stale.
$paramsclean['usercontext'] = CONTEXT_USER;
$paramsclean['component'] = 'tool_cohortroles';
$sql = 'SELECT DISTINCT(ra.id), ra.roleid, ra.userid, ra.contextid, ctx.instanceid
FROM {role_assignments} ra
JOIN {context} ctx ON ctx.id = ra.contextid AND ctx.contextlevel = :usercontext
JOIN {cohort_members} cm ON cm.userid = ctx.instanceid
LEFT JOIN {tool_cohortroles} tc ON tc.cohortid = cm.cohortid
AND tc.userid = ra.userid
AND tc.roleid = ra.roleid
WHERE ra.component = :component
AND tc.id is null';
if ($candidatelegacyassignments = $DB->get_records_sql($sql, $paramsclean)) {
$sql = 'SELECT DISTINCT(ra.id)
FROM {role_assignments} ra
JOIN {context} ctx ON ctx.id = ra.contextid AND ctx.contextlevel = :usercontext
JOIN {cohort_members} cm ON cm.userid = ctx.instanceid
JOIN {tool_cohortroles} tc ON tc.cohortid = cm.cohortid AND tc.userid = ra.userid
WHERE ra.component = :component';
if ($currentvalidroleassignments = $DB->get_records_sql($sql, $paramsclean)) {
foreach ($candidatelegacyassignments as $candidate) {
if (!array_key_exists($candidate->id, $currentvalidroleassignments)) {
role_unassign($candidate->roleid, $candidate->userid, $candidate->contextid, 'tool_cohortroles');
$rolesremoved[] = array(
'useridassignedto' => $candidate->userid,
'useridassignedover' => $candidate->instanceid,
'roleid' => $candidate->roleid
);
}
}
} else {
foreach ($candidatelegacyassignments as $candidate) {
role_unassign($candidate->roleid, $candidate->userid, $candidate->contextid, 'tool_cohortroles');
$rolesremoved[] = array(
'useridassignedto' => $candidate->userid,
'useridassignedover' => $candidate->instanceid,
'roleid' => $candidate->roleid
);
}
}
}
return array('rolesadded' => $rolesadded, 'rolesremoved' => $rolesremoved);
}
@@ -103,5 +103,4 @@ class cohort_role_assignment extends persistent {
return true;
}
}
+82 -7
View File
@@ -50,8 +50,6 @@ class tool_cohortroles_api_testcase extends advanced_testcase {
* Setup function- we will create a course and add an assign instance to it.
*/
protected function setUp() {
global $DB;
$this->resetAfterTest(true);
// Create some users.
@@ -133,14 +131,91 @@ class tool_cohortroles_api_testcase extends advanced_testcase {
public function test_delete_cohort_role_assignment() {
$this->setAdminUser();
$params = (object) array(
// Create a cohort role assigment.
$params = (object) [
'userid' => $this->userassignto->id,
'roleid' => $this->roleid,
'cohortid' => $this->cohort->id
);
$result = api::create_cohort_role_assignment($params);
$worked = api::delete_cohort_role_assignment($result->get('id'));
$this->assertTrue($worked);
];
$cohortroleassignment = api::create_cohort_role_assignment($params);
$sync = api::sync_all_cohort_roles();
$rolesadded = [
[
'useridassignedto' => $this->userassignto->id,
'useridassignedover' => $this->userassignover->id,
'roleid' => $this->roleid
]
];
$expected = [
'rolesadded' => $rolesadded,
'rolesremoved' => []
];
$this->assertEquals($sync, $expected);
// Delete the cohort role assigment and confirm the roles are removed.
$result = api::delete_cohort_role_assignment($cohortroleassignment->get('id'));
$this->assertTrue($result);
$sync = api::sync_all_cohort_roles();
$expected = [
'rolesadded' => [],
'rolesremoved' => $rolesadded
];
$this->assertEquals($expected, $sync);
}
/**
* Test case verifying that syncing won't remove role assignments if they are valid for another cohort role assignment.
*/
public function test_delete_cohort_role_assignment_cohorts_having_same_members() {
$this->setAdminUser();
// Create 2 cohorts, with a 1 user (user1) present in both,
// and user2 and user3 members of 1 cohort each.
$cohort1 = $this->getDataGenerator()->create_cohort();
$cohort2 = $this->getDataGenerator()->create_cohort();
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
cohort_add_member($cohort1->id, $user1->id);
cohort_add_member($cohort1->id, $user2->id);
cohort_add_member($cohort2->id, $user1->id);
cohort_add_member($cohort2->id, $user3->id);
// And a role and a user to assign that role to.
$user4 = $this->getDataGenerator()->create_user(); // A cohort manager, for example.
$roleid = create_role('Role 1', 'myrole', 'test');
// Assign the role for user4 in both cohorts.
$params = (object) [
'userid' => $user4->id,
'roleid' => $roleid,
'cohortid' => $cohort1->id
];
$cohort1roleassignment = api::create_cohort_role_assignment($params);
$params->cohortid = $cohort2->id;
$cohort2roleassignment = api::create_cohort_role_assignment($params);
$sync = api::sync_all_cohort_roles();
// There is no guarantee about the order of roles assigned.
// so confirm we have 3 role assignments, and they are for the users 1, 2 and 3.
$this->assertCount(3, $sync['rolesadded']);
$addedusers = array_column($sync['rolesadded'], 'useridassignedover');
$this->assertContains($user1->id, $addedusers);
$this->assertContains($user2->id, $addedusers);
$this->assertContains($user3->id, $addedusers);
// Remove the role assignment for user4/cohort1.
// Verify only 1 role is unassigned as the others are still valid for the other cohort role assignment.
$result = api::delete_cohort_role_assignment($cohort1roleassignment->get('id'));
$this->assertTrue($result);
$sync = api::sync_all_cohort_roles();
$this->assertCount(0, $sync['rolesadded']);
$this->assertCount(1, $sync['rolesremoved']);
$removedusers = array_column($sync['rolesremoved'], 'useridassignedover');
$this->assertContains($user2->id, $removedusers);
}
public function test_list_cohort_role_assignments() {
@@ -440,6 +440,9 @@ class purpose extends persistent {
}
if (!empty($data->sensitivedatareasons) && is_array($data->sensitivedatareasons)) {
$data->sensitivedatareasons = implode(',', $data->sensitivedatareasons);
} else {
// Nothing selected. Set default value of null.
$data->sensitivedatareasons = null;
}
// A single value.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
define(["jquery","core/notification","tool_lp/dialogue","core/str","core/ajax","core/templates"],function(a,b,c,d,e,f){var g=function(b){a(b).on("click",this.configureSettings.bind(this))};return g.prototype._dialogue=null,g.prototype.configureSettings=function(e){var g=a(e.target).closest("a").data("courseid"),h=a(e.target).closest("a").data("pushratingstouserplans"),i={courseid:g,settings:{pushratingstouserplans:h}};e.preventDefault(),f.render("tool_lp/course_competency_settings",i).done(function(a){d.get_string("configurecoursecompetencysettings","tool_lp").done(function(b){this._dialogue=new c(b,a,this.addListeners.bind(this))}.bind(this)).fail(b.exception)}.bind(this)).fail(b.exception)},g.prototype.addListeners=function(){var a=this._find('[data-action="save"]');a.on("click",this.saveSettings.bind(this));var b=this._find('[data-action="cancel"]');b.on("click",this.cancelChanges.bind(this))},g.prototype.cancelChanges=function(a){a.preventDefault(),this._dialogue.close()},g.prototype._find=function(b){return a('[data-region="coursecompetencysettings"]').find(b)},g.prototype.saveSettings=function(a){a.preventDefault();var c=this._find('input[name="pushratingstouserplans"]:checked').val(),d=this._find('input[name="courseid"]').val(),f={pushratingstouserplans:c};e.call([{methodname:"core_competency_update_course_competency_settings",args:{courseid:d,settings:f}}])[0].done(function(){this.refreshCourseCompetenciesPage()}.bind(this)).fail(b.exception)},g.prototype.refreshCourseCompetenciesPage=function(){var c=this._find('input[name="courseid"]').val();e.call([{methodname:"tool_lp_data_for_course_competencies_page",args:{courseid:c}}])[0].done(function(c){f.render("tool_lp/course_competencies_page",c).done(function(b,c){a('[data-region="coursecompetenciespage"]').replaceWith(b),f.runTemplateJS(c),this._dialogue.close()}.bind(this)).fail(b.exception)}.bind(this)).fail(b.exception)},g});
define(["jquery","core/notification","tool_lp/dialogue","core/str","core/ajax","core/templates","core/pending"],function(a,b,c,d,e,f,g){var h=function(b){a(b).on("click",this.configureSettings.bind(this))};return h.prototype._dialogue=null,h.prototype.configureSettings=function(e){var h=new g,i=a(e.target).closest("a").data("courseid"),j=a(e.target).closest("a").data("pushratingstouserplans"),k={courseid:i,settings:{pushratingstouserplans:j}};e.preventDefault(),a.when(d.get_string("configurecoursecompetencysettings","tool_lp"),f.render("tool_lp/course_competency_settings",k)).then(function(a,b){return this._dialogue=new c(a,b[0],this.addListeners.bind(this)),this._dialogue}.bind(this)).then(h.resolve)["catch"](b.exception)},h.prototype.addListeners=function(){var a=this._find('[data-action="save"]');a.on("click",this.saveSettings.bind(this));var b=this._find('[data-action="cancel"]');b.on("click",this.cancelChanges.bind(this))},h.prototype.cancelChanges=function(a){a.preventDefault(),this._dialogue.close()},h.prototype._find=function(b){return a('[data-region="coursecompetencysettings"]').find(b)},h.prototype.saveSettings=function(a){var c=new g;a.preventDefault();var d=this._find('input[name="pushratingstouserplans"]:checked').val(),f=this._find('input[name="courseid"]').val(),h={pushratingstouserplans:d};e.call([{methodname:"core_competency_update_course_competency_settings",args:{courseid:f,settings:h}}])[0].then(function(){return this.refreshCourseCompetenciesPage()}.bind(this)).then(c.resolve)["catch"](b.exception)},h.prototype.refreshCourseCompetenciesPage=function(){var c=this._find('input[name="courseid"]').val(),d=new g;e.call([{methodname:"tool_lp_data_for_course_competencies_page",args:{courseid:c}}])[0].then(function(a){return f.render("tool_lp/course_competencies_page",a)}).then(function(b,c){f.replaceNode(a('[data-region="coursecompetenciespage"]'),b,c),this._dialogue.close()}.bind(this)).then(d.resolve)["catch"](b.exception)},h});
+1 -1
View File
@@ -1 +1 @@
define(["jquery","core/notification","core/templates","tool_lp/dialogue","tool_lp/event_base","core/str"],function(a,b,c,d,e,f){var g=function(a){e.prototype.constructor.apply(this,[]),this._ratingOptions=a};return g.prototype=Object.create(e.prototype),g.prototype._popup=null,g.prototype._ratingOptions=null,g.prototype._afterRender=function(){var b=this._find('[data-action="rate"]'),c=this._find('[name="rating"]'),d=this._find('[name="comment"]');this._find('[data-action="cancel"]').click(function(a){a.preventDefault(),this._trigger("cancelled"),this.close()}.bind(this)),c.change(function(){var c=a(this);c.val()?b.prop("disabled",!1):b.prop("disabled",!0)}).change(),b.click(function(a){a.preventDefault();var b=c.val();b&&(this._trigger("rated",{rating:b,note:d.val()}),this.close())}.bind(this))},g.prototype.close=function(){this._popup.close(),this._popup=null},g.prototype.display=function(){return this._render().then(function(a){return f.get_string("rate","tool_lp").then(function(b){this._popup=new d(b,a,this._afterRender.bind(this))}.bind(this))}.bind(this)).fail(b.exception)},g.prototype._find=function(b){return a(this._popup.getContent()).find(b)},g.prototype._render=function(){var a={cangrade:this._canGrade,ratings:this._ratingOptions};return c.render("tool_lp/competency_grader",a)},g});
define(["jquery","core/notification","core/templates","tool_lp/dialogue","tool_lp/event_base","core/str"],function(a,b,c,d,e,f){var g=function(a){e.prototype.constructor.apply(this,[]),this._ratingOptions=a};return g.prototype=Object.create(e.prototype),g.prototype._popup=null,g.prototype._ratingOptions=null,g.prototype._afterRender=function(){var b=this._find('[data-action="rate"]'),c=this._find('[name="rating"]'),d=this._find('[name="comment"]');this._find('[data-action="cancel"]').click(function(a){a.preventDefault(),this._trigger("cancelled"),this.close()}.bind(this)),c.change(function(){var c=a(this);c.val()?b.prop("disabled",!1):b.prop("disabled",!0)}).change(),b.click(function(a){a.preventDefault();var b=c.val();b&&(this._trigger("rated",{rating:b,note:d.val()}),this.close())}.bind(this))},g.prototype.close=function(){this._popup.close(),this._popup=null},g.prototype.display=function(){return a.when(f.get_string("rate","tool_lp"),this._render()).then(function(a,b){return this._popup=new d(a,b[0],this._afterRender.bind(this)),this._popup}.bind(this))["catch"](b.exception)},g.prototype._find=function(b){return a(this._popup.getContent()).find(b)},g.prototype._render=function(){var a={cangrade:this._canGrade,ratings:this._ratingOptions};return c.render("tool_lp/competency_grader",a)},g});
@@ -1 +1 @@
define(["jquery","core/notification","core/ajax","core/log","tool_lp/grade_dialogue","tool_lp/event_base","tool_lp/scalevalues"],function(a,b,c,d,e,f,g){var h=function(b,c,d,e,g,h,i){f.prototype.constructor.apply(this,[]);var j=a(b);if(!j.length)throw new Error("Could not find the trigger");this._scaleId=c,this._competencyId=d,this._userId=e,this._planId=g,this._courseId=h,this._chooseStr=i,this._setUp(),j.click(function(a){a.preventDefault(),this._dialogue.display()}.bind(this)),this._planId?(this._methodName="core_competency_grade_competency_in_plan",this._args={competencyid:this._competencyId,planid:this._planId}):this._courseId?(this._methodName="core_competency_grade_competency_in_course",this._args={competencyid:this._competencyId,courseid:this._courseId,userid:this._userId}):(this._methodName="core_competency_grade_competency",this._args={userid:this._userId,competencyid:this._competencyId})};return h.prototype=Object.create(f.prototype),h.prototype._setUp=function(){var a=[],d=this,f=g.get_values(d._scaleId);f.done(function(f){a.push({value:"",name:d._chooseStr});for(var g=0;g<f.length;g++){var h=f[g];a.push({value:h.id,name:h.name})}d._dialogue=new e(a),d._dialogue.on("rated",function(a,e){var f=d._args;f.grade=e.rating,f.note=e.note,c.call([{methodname:d._methodName,args:f,done:function(a){d._trigger("competencyupdated",{args:f,evidence:a})},fail:b.exception}])})}).fail(b.exception)},h.prototype._scaleId=null,h.prototype._competencyId=null,h.prototype._userId=null,h.prototype._planId=null,h.prototype._courseId=null,h.prototype._chooseStr=null,h.prototype._dialogue=null,h});
define(["jquery","core/notification","core/ajax","core/log","tool_lp/grade_dialogue","tool_lp/event_base","tool_lp/scalevalues"],function(a,b,c,d,e,f,g){var h=function(b,c,d,e,g,h,i){f.prototype.constructor.apply(this,[]);var j=a(b);if(!j.length)throw new Error("Could not find the trigger");this._scaleId=c,this._competencyId=d,this._userId=e,this._planId=g,this._courseId=h,this._chooseStr=i,this._setUp(),j.click(function(a){a.preventDefault(),this._dialogue.display()}.bind(this)),this._planId?(this._methodName="core_competency_grade_competency_in_plan",this._args={competencyid:this._competencyId,planid:this._planId}):this._courseId?(this._methodName="core_competency_grade_competency_in_course",this._args={competencyid:this._competencyId,courseid:this._courseId,userid:this._userId}):(this._methodName="core_competency_grade_competency",this._args={userid:this._userId,competencyid:this._competencyId})};return h.prototype=Object.create(f.prototype),h.prototype._setUp=function(){var a=[],d=this,f=g.get_values(d._scaleId);f.then(function(b){a.push({value:"",name:d._chooseStr});for(var c=0;c<b.length;c++){var e=b[c];a.push({value:e.id,name:e.name})}return a}).then(function(a){return new e(a)}).then(function(a){return a.on("rated",function(a,e){var f=d._args;f.grade=e.rating,f.note=e.note,c.call([{methodname:d._methodName,args:f,done:function(a){d._trigger("competencyupdated",{args:f,evidence:a})},fail:b.exception}])}),a}).then(function(a){d._dialogue=a}).fail(b.exception)},h.prototype._scaleId=null,h.prototype._competencyId=null,h.prototype._userId=null,h.prototype._planId=null,h.prototype._courseId=null,h.prototype._chooseStr=null,h.prototype._dialogue=null,h});
+28 -15
View File
@@ -27,8 +27,9 @@ define(['jquery',
'core/templates',
'core/str',
'tool_lp/competencypicker',
'tool_lp/dragdrop-reorder'],
function($, notification, ajax, templates, str, Picker, dragdrop) {
'tool_lp/dragdrop-reorder',
'core/pending'],
function($, notification, ajax, templates, str, Picker, dragdrop, Pending) {
/**
* Constructor
@@ -117,6 +118,7 @@ define(['jquery',
* Pick a competency
*
* @method pickCompetency
* @return {Promise}
*/
competencies.prototype.pickCompetency = function() {
var self = this;
@@ -132,6 +134,7 @@ define(['jquery',
self.pickerInstance = new Picker(self.pageContextId, false, pageContextIncludes);
self.pickerInstance.on('save', function(e, data) {
var compIds = data.competencyIds;
var pendingPromise = new Pending();
if (self.itemtype === "course") {
requests = [];
@@ -181,17 +184,20 @@ define(['jquery',
pagerender = 'tool_lp/plan_page';
pageregion = 'plan-page';
}
ajax.call(requests)[requests.length - 1].then(function(context) {
ajax.call(requests)[requests.length - 1]
.then(function(context) {
return templates.render(pagerender, context);
}).then(function(html, js) {
$('[data-region="' + pageregion + '"]').replaceWith(html);
templates.runTemplateJS(js);
})
.then(function(html, js) {
templates.replaceNode($('[data-region="' + pageregion + '"]'), html, js);
return;
}).catch(notification.exception);
})
.then(pendingPromise.resolve)
.catch(notification.exception);
});
}
self.pickerInstance.display();
return self.pickerInstance.display();
};
/**
@@ -302,6 +308,7 @@ define(['jquery',
if (localthis.itemtype == 'course') {
// Course completion rule handling.
$('[data-region="coursecompetenciespage"]').on('change', 'select[data-field="ruleoutcome"]', function(e) {
var pendingPromise = new Pending();
var requests = [];
var pagerender = 'tool_lp/course_competencies_page';
var pageregion = 'coursecompetenciespage';
@@ -314,18 +321,24 @@ define(['jquery',
args: {courseid: localthis.itemid}}
]);
requests[1].done(function(context) {
templates.render(pagerender, context).done(function(html, js) {
$('[data-region="' + pageregion + '"]').replaceWith(html);
templates.runTemplateJS(js);
}).fail(notification.exception);
}).fail(notification.exception);
requests[1].then(function(context) {
return templates.render(pagerender, context);
})
.then(function(html, js) {
return templates.replaceNode($('[data-region="' + pageregion + '"]'), html, js);
})
.then(pendingPromise.resolve)
.catch(notification.exception);
});
}
$('[data-region="actions"] button').click(function(e) {
var pendingPromise = new Pending();
e.preventDefault();
localthis.pickCompetency();
localthis.pickCompetency()
.then(pendingPromise.resolve)
.catch();
});
$('[data-action="delete-competency-link"]').click(function(e) {
e.preventDefault();
+10 -3
View File
@@ -33,8 +33,12 @@ define(['jquery',
'tool_lp/menubar',
'tool_lp/competencypicker',
'tool_lp/competency_outcomes',
'tool_lp/competencyruleconfig'],
function($, url, templates, notification, str, ajax, dragdrop, Ariatree, Dialogue, menubar, Picker, Outcomes, RuleConfig) {
'tool_lp/competencyruleconfig',
'core/pending',
],
function(
$, url, templates, notification, str, ajax, dragdrop, Ariatree, Dialogue, menubar, Picker, Outcomes, RuleConfig, Pending
) {
// Private variables and functions.
/** @var {Object} treeModel - This is an object representing the nodes in the tree. */
@@ -412,6 +416,7 @@ define(['jquery',
if (!pickerInstance) {
pickerInstance = new Picker(pageContextId, relatedTarget.competencyframeworkid);
pickerInstance.on('save', function(e, data) {
var pendingPromise = new Pending();
var compIds = data.competencyIds;
var calls = [];
@@ -436,7 +441,9 @@ define(['jquery',
templates.runTemplateJS(js);
updatedRelatedCompetencies();
return;
}).catch(notification.exception);
})
.then(pendingPromise.resolve)
.catch(notification.exception);
});
}
+8 -2
View File
@@ -31,8 +31,10 @@ define(['jquery',
'core/templates',
'tool_lp/dialogue',
'core/str',
'tool_lp/tree'],
function($, Notification, Ajax, Templates, Dialogue, Str, Tree) {
'tool_lp/tree',
'core/pending'
],
function($, Notification, Ajax, Templates, Dialogue, Str, Tree, Pending) {
/**
* Competency picker class.
@@ -157,6 +159,7 @@ define(['jquery',
// Add listener for add.
self._find('[data-region="competencylinktree"] [data-action="add"]').click(function(e) {
e.preventDefault();
var pendingPromise = new Pending();
if (!self._selectedCompetencies.length) {
return;
}
@@ -168,7 +171,10 @@ define(['jquery',
self._trigger('save', {competencyId: self._selectedCompetencies[0]});
}
// The dialogue here is a YUI dialogue and doesn't support Promises at all.
// However, it is typically synchronous so this shoudl suffice.
self.close();
pendingPromise.resolve();
});
// The list of selected competencies will be modified while looping (because of the listeners above).
@@ -26,8 +26,10 @@ define(['jquery',
'tool_lp/dialogue',
'core/str',
'core/ajax',
'core/templates'],
function($, notification, Dialogue, str, ajax, templates) {
'core/templates',
'core/pending'
],
function($, notification, Dialogue, str, ajax, templates, Pending) {
/**
* Constructor
@@ -48,6 +50,7 @@ define(['jquery',
* @method configureSettings
*/
settingsMod.prototype.configureSettings = function(e) {
var pendingPromise = new Pending();
var courseid = $(e.target).closest('a').data('courseid');
var currentValue = $(e.target).closest('a').data('pushratingstouserplans');
var context = {
@@ -56,16 +59,21 @@ define(['jquery',
};
e.preventDefault();
templates.render('tool_lp/course_competency_settings', context).done(function(html) {
str.get_string('configurecoursecompetencysettings', 'tool_lp').done(function(title) {
this._dialogue = new Dialogue(
title,
html,
this.addListeners.bind(this)
);
}.bind(this)).fail(notification.exception);
}.bind(this)).fail(notification.exception);
$.when(
str.get_string('configurecoursecompetencysettings', 'tool_lp'),
templates.render('tool_lp/course_competency_settings', context)
)
.then(function(title, templateResult) {
this._dialogue = new Dialogue(
title,
templateResult[0],
this.addListeners.bind(this)
);
return this._dialogue;
}.bind(this))
.then(pendingPromise.resolve)
.catch(notification.exception);
};
/**
@@ -108,6 +116,7 @@ define(['jquery',
* @method saveSettings
*/
settingsMod.prototype.saveSettings = function(e) {
var pendingPromise = new Pending();
e.preventDefault();
var newValue = this._find('input[name="pushratingstouserplans"]:checked').val();
@@ -117,9 +126,12 @@ define(['jquery',
ajax.call([
{methodname: 'core_competency_update_course_competency_settings',
args: {courseid: courseId, settings: settings}}
])[0].done(function() {
this.refreshCourseCompetenciesPage();
}.bind(this)).fail(notification.exception);
])[0]
.then(function() {
return this.refreshCourseCompetenciesPage();
}.bind(this))
.then(pendingPromise.resolve)
.catch(notification.exception);
};
@@ -131,18 +143,23 @@ define(['jquery',
*/
settingsMod.prototype.refreshCourseCompetenciesPage = function() {
var courseId = this._find('input[name="courseid"]').val();
var pendingPromise = new Pending();
ajax.call([
{methodname: 'tool_lp_data_for_course_competencies_page',
args: {courseid: courseId}}
])[0].done(function(context) {
templates.render('tool_lp/course_competencies_page', context).done(function(html, js) {
$('[data-region="coursecompetenciespage"]').replaceWith(html);
templates.runTemplateJS(js);
this._dialogue.close();
}.bind(this)).fail(notification.exception);
}.bind(this)).fail(notification.exception);
])[0]
.then(function(context) {
return templates.render('tool_lp/course_competencies_page', context);
})
.then(function(html, js) {
templates.replaceNode($('[data-region="coursecompetenciespage"]'), html, js);
this._dialogue.close();
return;
}.bind(this))
.then(pendingPromise.resolve)
.catch(notification.exception);
};
return /** @alias module:tool_lp/configurecoursecompetencysettings */ settingsMod;
+14 -9
View File
@@ -102,15 +102,20 @@ define(['jquery',
* @return {Promise}
*/
Grade.prototype.display = function() {
return this._render().then(function(html) {
return Str.get_string('rate', 'tool_lp').then(function(title) {
this._popup = new Dialogue(
title,
html,
this._afterRender.bind(this)
);
}.bind(this));
}.bind(this)).fail(Notification.exception);
return $.when(
Str.get_string('rate', 'tool_lp'),
this._render()
)
.then(function(title, templateResult) {
this._popup = new Dialogue(
title,
templateResult[0],
this._afterRender.bind(this)
);
return this._popup;
}.bind(this))
.catch(Notification.exception);
};
/**
@@ -95,7 +95,7 @@ define(['jquery',
self = this;
var promise = ScaleValues.get_values(self._scaleId);
promise.done(function(scalevalues) {
promise.then(function(scalevalues) {
options.push({
value: '',
name: self._chooseStr
@@ -109,8 +109,13 @@ define(['jquery',
});
}
self._dialogue = new GradeDialogue(options);
self._dialogue.on('rated', function(e, data) {
return options;
})
.then(function(options) {
return new GradeDialogue(options);
})
.then(function(dialogue) {
dialogue.on('rated', function(e, data) {
var args = self._args;
args.grade = data.rating;
args.note = data.note;
@@ -123,7 +128,15 @@ define(['jquery',
fail: notification.exception
}]);
});
}).fail(notification.exception);
return dialogue;
})
.then(function(dialogue) {
self._dialogue = dialogue;
return;
})
.fail(notification.exception);
};
/** @type {Number} The scale id for this competency. */
@@ -6,6 +6,7 @@ Feature: Manage competency frameworks
Background:
Given I log in as "admin"
And I change window size to "small"
And I am on site homepage
Scenario: Create a new framework
@@ -6,6 +6,7 @@ Feature: Manage plearning plan templates
Background:
Given I log in as "admin"
And I change window size to "small"
And I am on site homepage
Scenario: Create a new learning plan template
+1 -1
View File
@@ -30,7 +30,7 @@ require_once($CFG->libdir . '/externallib.php');
$serviceshortname = required_param('service', PARAM_ALPHANUMEXT);
$passport = required_param('passport', PARAM_RAW); // Passport send from the app to validate the response URL.
$urlscheme = optional_param('urlscheme', 'moodlemobile', PARAM_NOTAGS); // The URL scheme the app supports.
$urlscheme = optional_param('urlscheme', 'moodlemobile', PARAM_ALPHANUM); // The URL scheme the app supports.
$confirmed = optional_param('confirmed', false, PARAM_BOOL); // If we are being redirected after user confirmation.
$oauthsso = optional_param('oauthsso', 0, PARAM_INT); // Id of the OpenID issuer (for OAuth direct SSO).
+1 -1
View File
@@ -63,7 +63,7 @@ if ($hassiteconfig) {
$temp->add(new admin_setting_configtext('tool_mobile/forcedurlscheme',
new lang_string('forcedurlscheme_key', 'tool_mobile'),
new lang_string('forcedurlscheme', 'tool_mobile'), '', PARAM_NOTAGS));
new lang_string('forcedurlscheme', 'tool_mobile'), '', PARAM_ALPHANUM));
$ADMIN->add('mobileapp', $temp);
+2 -1
View File
@@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
use \tool_monitor\privacy\provider;
use \core_privacy\local\request\approved_contextlist;
use \core_privacy\local\request\approved_userlist;
use \core_privacy\tests\provider_testcase;
/**
* Privacy test for the event monitor
@@ -37,7 +38,7 @@ use \core_privacy\local\request\approved_userlist;
* @copyright 2018 Adrian Greeve <adriangreeve.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tool_monitor_privacy_testcase extends advanced_testcase {
class tool_monitor_privacy_testcase extends provider_testcase {
/**
* Set up method.
+6 -3
View File
@@ -123,9 +123,12 @@ class issuer extends persistent {
$mform->addElement('checkbox', 'showonloginpage', get_string('issuershowonloginpage', 'tool_oauth2'));
$mform->addHelpButton('showonloginpage', 'issuershowonloginpage', 'tool_oauth2');
// Require confirmation email for new accounts.
$mform->addElement('advcheckbox', 'requireconfirmation', get_string('issuerrequireconfirmation', 'tool_oauth2'));
$mform->addHelpButton('requireconfirmation', 'issuerrequireconfirmation', 'tool_oauth2');
$showrequireconfirm = $this->_customdata['showrequireconfirm'] ?? false;
if ($showrequireconfirm) {
// Require confirmation email for new accounts.
$mform->addElement('advcheckbox', 'requireconfirmation', get_string('issuerrequireconfirmation', 'tool_oauth2'));
$mform->addHelpButton('requireconfirmation', 'issuerrequireconfirmation', 'tool_oauth2');
}
$mform->addElement('hidden', 'sortorder');
$mform->setType('sortorder', PARAM_INT);
+33 -5
View File
@@ -58,7 +58,25 @@ if ($action == 'edit') {
$PAGE->navbar->add(get_string('createnewissuer', 'tool_oauth2'));
}
$mform = new \tool_oauth2\form\issuer(null, ['persistent' => $issuer]);
if (!empty($issuerid)) {
// Show the "Require confirmation email" checkbox for trusted issuers like Google, Facebook and Microsoft.
$likefacebook = $DB->sql_like('url', ':facebook');
$likegoogle = $DB->sql_like('url', ':google');
$likemicrosoft = $DB->sql_like('url', ':microsoft');
$params = [
'issuerid' => $issuerid,
'facebook' => '%facebook%',
'google' => '%google%',
'microsoft' => '%microsoft%',
];
$select = "issuerid = :issuerid AND ($likefacebook OR $likegoogle OR $likemicrosoft)";
// We're querying from the oauth2_endpoint table because the base URLs of FB and Microsoft can be empty in the issuer table.
$showrequireconfirm = $DB->record_exists_select('oauth2_endpoint', $select, $params);
} else {
$showrequireconfirm = optional_param('showrequireconfirm', false, PARAM_BOOL);
}
$mform = new \tool_oauth2\form\issuer(null, ['persistent' => $issuer, 'showrequireconfirm' => $showrequireconfirm]);
}
if ($mform && $mform->is_cancelled()) {
@@ -157,21 +175,31 @@ if ($mform && $mform->is_cancelled()) {
$issuers = core\oauth2\api::get_all_issuers();
echo $renderer->issuers_table($issuers);
// Google template.
$docs = 'admin/tool/oauth2/issuers/google';
$params = ['action' => 'edittemplate', 'type' => 'google', 'sesskey' => sesskey(), 'docslink' => $docs];
$params = ['action' => 'edittemplate', 'type' => 'google', 'sesskey' => sesskey(), 'docslink' => $docs,
'showrequireconfirm' => true];
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', $params);
echo $renderer->single_button($addurl, get_string('createnewgoogleissuer', 'tool_oauth2'));
// Microsoft template.
$docs = 'admin/tool/oauth2/issuers/microsoft';
$params = ['action' => 'edittemplate', 'type' => 'microsoft', 'sesskey' => sesskey(), 'docslink' => $docs];
$params = ['action' => 'edittemplate', 'type' => 'microsoft', 'sesskey' => sesskey(), 'docslink' => $docs,
'showrequireconfirm' => true];
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', $params);
echo $renderer->single_button($addurl, get_string('createnewmicrosoftissuer', 'tool_oauth2'));
// Facebook template.
$docs = 'admin/tool/oauth2/issuers/facebook';
$params = ['action' => 'edittemplate', 'type' => 'microsoft', 'sesskey' => sesskey(), 'docslink' => $docs];
$params = ['action' => 'edittemplate', 'type' => 'facebook', 'sesskey' => sesskey(), 'docslink' => $docs];
$params = ['action' => 'edittemplate', 'type' => 'facebook', 'sesskey' => sesskey(), 'docslink' => $docs,
'showrequireconfirm' => true];
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', $params);
echo $renderer->single_button($addurl, get_string('createnewfacebookissuer', 'tool_oauth2'));
// Generic issuer.
$addurl = new moodle_url('/admin/tool/oauth2/issuers.php', ['action' => 'edit']);
echo $renderer->single_button($addurl, get_string('createnewissuer', 'tool_oauth2'));
echo $OUTPUT->footer();
}
+1 -1
View File
@@ -1 +1 @@
define(["jquery","core/ajax","core/notification","core/modal_factory","core/modal_events"],function(a,b,c,d,e){var f={VIEW_POLICY:'[data-action="view"]'},g=function(){this.registerEvents()};return g.prototype.registerEvents=function(){a(f.VIEW_POLICY).click(function(f){f.preventDefault();var g=a(this).data("versionid"),h=a(this).data("behalfid"),i={versionid:g,behalfid:h},j={methodname:"tool_policy_get_policy_version",args:i},k=a.Deferred(),l=a.Deferred(),m=d.create({title:k,body:l,large:!0}).then(function(a){return a.getRoot().on(e.hidden,function(){a.destroy()}),a}).then(function(a){return a.show(),a})["catch"](c.exception),n=b.call([j]);a.when(n[0]).then(function(a){if(a.result.policy)return k.resolve(a.result.policy.name),l.resolve(a.result.policy.content),a;throw new Error(a.warnings[0].message)})["catch"](function(a){return m.then(function(a){return a.hide(),a.destroy(),a})["catch"](c.exception),c.addNotification({message:a,type:"error"})})})},{init:function(){return new g}}});
define(["jquery","core/ajax","core/notification","core/modal_factory","core/modal_events"],function(a,b,c,d,e){var f=function(a){this.registerEvents(a)};return f.prototype.registerEvents=function(f){f.on("click",function(f){f.preventDefault();var g=a(this).data("versionid"),h=a(this).data("behalfid"),i={versionid:g,behalfid:h},j={methodname:"tool_policy_get_policy_version",args:i},k=a.Deferred(),l=a.Deferred(),m=d.create({title:k,body:l,large:!0}).then(function(a){return a.getRoot().on(e.hidden,function(){a.destroy()}),a}).then(function(a){return a.show(),a})["catch"](c.exception),n=b.call([j]);a.when(n[0]).then(function(a){if(a.result.policy)return k.resolve(a.result.policy.name),l.resolve(a.result.policy.content),a;throw new Error(a.warnings[0].message)})["catch"](function(a){return m.then(function(a){return a.hide(),a.destroy(),a})["catch"](c.exception),c.addNotification({message:a,type:"error"})})})},{init:function(b){return b=a(b),new f(b)}}});
+7 -15
View File
@@ -29,27 +29,18 @@ define([
'core/modal_events'],
function($, Ajax, Notification, ModalFactory, ModalEvents) {
/**
* List of action selectors.
*
* @type {{VIEW_POLICY: string}}
*/
var ACTIONS = {
VIEW_POLICY: '[data-action="view"]'
};
/**
* PolicyActions class.
*/
var PolicyActions = function() {
this.registerEvents();
var PolicyActions = function(root) {
this.registerEvents(root);
};
/**
* Register event listeners.
*/
PolicyActions.prototype.registerEvents = function() {
$(ACTIONS.VIEW_POLICY).click(function(e) {
PolicyActions.prototype.registerEvents = function(root) {
root.on("click", function(e) {
e.preventDefault();
var versionid = $(this).data('versionid');
@@ -127,8 +118,9 @@ function($, Ajax, Notification, ModalFactory, ModalEvents) {
* @method init
* @return {PolicyActions}
*/
'init': function() {
return new PolicyActions();
'init': function(root) {
root = $(root);
return new PolicyActions(root);
}
};
});
@@ -97,7 +97,7 @@ class accept_policy extends \moodleform {
}
}
$PAGE->requires->js_call_amd('tool_policy/policyactions', 'init');
$PAGE->requires->js_call_amd('tool_policy/policyactions', 'init', ['[data-action="view"]']);
}
/**
@@ -178,4 +178,4 @@ class accept_policy extends \moodleform {
}
}
}
}
}
@@ -58,7 +58,7 @@ require(['jquery', 'tool_policy/jquery-eu-cookie-law-popup', 'tool_policy/policy
"<ul>{{#policies}}" +
"<li>" +
"<a href=\"{{pluginbaseurl}}/view.php?versionid={{id}}{{#returnurl}}&amp;returnurl={{.}}{{/returnurl}}\" " +
" data-action=\"view\" data-versionid=\"{{id}}\" data-behalfid=\"1\" >" +
" data-action=\"view-guest\" data-versionid=\"{{id}}\" data-behalfid=\"1\" >" +
"{{{name}}}" +
"</a>" +
"</li>" +
@@ -81,7 +81,7 @@ require(['jquery', 'tool_policy/jquery-eu-cookie-law-popup', 'tool_policy/policy
{{/haspolicies}}
// Initialise the JS for the modal window which displays the policy versions.
ActionsMod.init();
ActionsMod.init('[data-action="view-guest"]');
});
});
@@ -123,6 +123,6 @@
{{#js}}
// Initialise the JS for the modal window which displays the policy versions.
require(['tool_policy/policyactions'], function(ActionsMod) {
ActionsMod.init();
ActionsMod.init('[data-action="view"]');
});
{{/js}}
@@ -73,6 +73,6 @@
{{#js}}
// Initialise the JS for the modal window which displays the policy versions.
require(['tool_policy/policyactions'], function(ActionsMod) {
ActionsMod.init();
ActionsMod.init('[data-action="view"]');
});
{{/js}}
+34
View File
@@ -47,5 +47,39 @@ function xmldb_auth_oauth2_upgrade($oldversion) {
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
if ($oldversion < 2018051401) {
// Fetch Facebook, Google, and Microsoft issuers. We use the URL field to determine the issuer type as it's the only
// field that contains the keyword that can somewhat let us reliably determine the issuer type.
$likefacebook = $DB->sql_like('oe.url', ':facebook');
$likegoogle = $DB->sql_like('oe.url', ':google');
$likemicrosoft = $DB->sql_like('oe.url', ':microsoft');
$params = [
'facebook' => '%facebook%',
'google' => '%google%',
'microsoft' => '%microsoft%',
];
// We're querying from the oauth2_endpoint table because the base URLs of FB and Microsoft can be empty in the issuer table.
$subsql = "
SELECT DISTINCT oe.issuerid
FROM {oauth2_endpoint} oe
WHERE $likefacebook
OR $likegoogle
OR $likemicrosoft";
// Update non-Facebook/Google/Microsoft issuers and set requireconfirmation to 1.
$updatesql = "
UPDATE {oauth2_issuer}
SET requireconfirmation = 1
WHERE id NOT IN ({$subsql})";
$DB->execute($updatesql, $params);
// Delete linked logins for non-Facebook/Google/Microsoft issuers. They can easily re-link their logins anyway.
$DB->delete_records_select('auth_oauth2_linked_login', "issuerid NOT IN ($subsql)", $params);
upgrade_plugin_savepoint(true, 2018051401, 'auth', 'oauth2');
}
return true;
}
+8 -2
View File
@@ -40,7 +40,10 @@ then cut and paste the address into the address
line at the top of your web browser window.
If you need help, please contact the site administrator,
{$a->admin}';
{$a->admin}
If you did not do this, someone else could be trying to compromise your account.
Please contact the site administrator immediately.';
$string['confirmaccountemailsubject'] = '{$a}: account confirmation';
$string['confirmationinvalid'] = 'The confirmation link is either invalid, or has expired. Please start the login process again to generate a new confirmation email.';
$string['confirmationpending'] = 'This account is pending email confirmation.';
@@ -60,7 +63,10 @@ then cut and paste the address into the address
line at the top of your web browser window.
If you need help, please contact the site administrator,
{$a->admin}';
{$a->admin}
If you did not do this, someone else could be trying to compromise your account.
Please contact the site administrator immediately.';
$string['confirmlinkedloginemailsubject'] = '{$a}: linked login confirmation';
$string['createaccountswarning'] = 'This authentication plugin allows users to create accounts on your site. You may want to enable the setting "authpreventaccountcreation" if you use this plugin.';
$string['createnewlinkedlogin'] = 'Link a new account ({$a})';
+1 -1
View File
@@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
$plugin->version = 2018051401; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2018050800; // Requires this Moodle version.
$plugin->component = 'auth_oauth2'; // Full name of the plugin (used for diagnostics).
+10 -2
View File
@@ -42,10 +42,18 @@ class behat_auth extends behat_base {
* Logs in the user. There should exist a user with the same value as username and password.
*
* @Given /^I log in as "(?P<username_string>(?:[^"]|\\")*)"$/
* @param string $username the user to log in as.
* @param moodle_url|null $wantsurl optional, URL to go to after logging in.
*/
public function i_log_in_as($username) {
public function i_log_in_as(string $username, moodle_url $wantsurl = null) {
$loginurl = new moodle_url('/login/index.php');
if ($wantsurl !== null) {
$loginurl->param('wantsurl', $wantsurl->out_as_local_url());
}
// Visit login page.
$this->getSession()->visit($this->locate_path('login/index.php'));
$this->getSession()->visit($this->locate_path($loginurl->out_as_local_url()));
// Enter username and password.
$this->execute('behat_forms::i_set_the_field_to', array('Username', $this->escape($username)));
@@ -92,3 +92,25 @@ Feature: availability_grouping
# P1 should show but not B2.
Then I should see "P1" in the "region-main" "region"
And I should not see "P2" in the "region-main" "region"
@javascript
Scenario: Check grouping access restriction message on course homepage
Given the following "groupings" exist:
| name | course | idnumber |
| Grouping A | C1 | GA |
And the following "grouping groups" exist:
| grouping | group |
| GA | GI1 |
And the following "activities" exist:
| activity | name | intro | course | idnumber | groupmode | grouping |
| assign | Test assign | Assign description | C1 | assign1 | 1 | GA |
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I turn editing mode on
And I open "Test assign" actions menu
And I choose "Edit settings" in the open action menu
And I expand all fieldsets
And the field "groupingid" matches value "Grouping A"
And I press "Add group/grouping access restriction"
When I press "Save and return to course"
Then I should see "Not available unless: You belong to a group in Grouping A"
@@ -114,12 +114,8 @@ class behat_block_site_main_menu extends behat_base {
protected function get_site_menu_activity_element($element, $selectortype, $activityname) {
$activitynode = $this->get_site_menu_activity_node($activityname);
// Transforming to Behat selector/locator.
list($selector, $locator) = $this->transform_selector($selectortype, $element);
$exception = new ElementNotFoundException($this->getSession(), '"' . $element . '" "' .
$selectortype . '" in "' . $activityname . '" ');
return $this->find($selector, $locator, $exception, $activitynode);
$exception = new ElementNotFoundException($this->getSession(), "'{$element}' '{$selectortype}' in '${activityname}'");
return $this->find($selectortype, $element, $exception, $activitynode);
}
/**
@@ -114,12 +114,8 @@ class behat_block_social_activities extends behat_base {
protected function get_social_block_activity_element($element, $selectortype, $activityname) {
$activitynode = $this->get_social_block_activity_node($activityname);
// Transforming to Behat selector/locator.
list($selector, $locator) = $this->transform_selector($selectortype, $element);
$exception = new ElementNotFoundException($this->getSession(), '"' . $element . '" "' .
$selectortype . '" in "' . $activityname . '" ');
return $this->find($selector, $locator, $exception, $activitynode);
$exception = new ElementNotFoundException($this->getSession(), "'{$element}' '{$selectortype}' in '${activityname}'");
return $this->find($selectortype, $element, $exception, $activitynode);
}
/**
+2 -1
View File
@@ -150,7 +150,8 @@ class cachestore_redis extends cache_store implements cache_is_key_aware, cache_
*/
protected function new_redis($server, $prefix = '', $password = '') {
$redis = new Redis();
$port = null;
// Check if it isn't a Unix socket to set default port.
$port = ($server[0] === '/') ? null : 6379;
if (strpos($server, ':')) {
$serverconf = explode(':', $server);
$server = $serverconf[0];
+9 -2
View File
@@ -4,10 +4,17 @@
"description": "Moodle - the world's open source learning platform",
"type": "project",
"homepage": "https://moodle.org",
"repositories": [
{
"type": "vcs",
"url": "https://github.com/moodlehq/php-webdriver.git"
}
],
"require-dev": {
"phpunit/phpunit": "6.5.*",
"phpunit/dbunit": "3.0.*",
"moodlehq/behat-extension": "3.35.1",
"mikey179/vfsstream": "^1.6"
"moodlehq/behat-extension": "3.35.4",
"mikey179/vfsstream": "^1.6",
"instaclick/php-webdriver": "dev-local as 1.x-dev"
}
}
Generated
+347 -233
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -812,6 +812,10 @@ $CFG->admin = 'admin';
// Example:
// $CFG->behat_faildump_path = '/my/path/to/save/failure/dumps';
//
// You can make behat pause upon failure to help you diagnose and debug problems with your tests.
//
// $CFG->behat_pause_on_fail = true;
//
// You can specify db, selenium wd_host etc. for behat parallel run by setting following variable.
// Example:
// $CFG->behat_parallel_run = array (
+2
View File
@@ -161,6 +161,8 @@ if ($editform->is_cancelled()) {
if (!empty($CFG->creatornewroleid) and !is_viewing($context, NULL, 'moodle/role:assign') and !is_enrolled($context, NULL, 'moodle/role:assign')) {
// Deal with course creators - enrol them internally with default role.
// Note: This does not respect capabilities, the creator will be assigned the default role.
// This is an expected behaviour. See MDL-66683 for further details.
enrol_try_internal_enrol($course->id, $USER->id, $CFG->creatornewroleid);
}
+37 -1
View File
@@ -36,6 +36,9 @@ class format_singleactivity extends format_base {
/** @var cm_info the current activity. Use get_activity() to retrieve it. */
private $activity = false;
/** @var int The category ID guessed from the form data. */
private $categoryid = false;
/**
* The URL to use for the specified course
*
@@ -145,6 +148,30 @@ class format_singleactivity extends format_base {
*/
public function course_format_options($foreditform = false) {
static $courseformatoptions = false;
$fetchtypes = $courseformatoptions === false;
$fetchtypes = $fetchtypes || ($foreditform && !isset($courseformatoptions['activitytype']['label']));
if ($fetchtypes) {
$availabletypes = $this->get_supported_activities();
if ($this->course) {
// The course exists. Test against the course.
$testcontext = context_course::instance($this->course->id);
} else if ($this->categoryid) {
// The course does not exist yet, but we have a category ID that we can test against.
$testcontext = context_coursecat::instance($this->categoryid);
} else {
// The course does not exist, and we somehow do not have a category. Test capabilities against the system context.
$testcontext = context_system::instance();
}
foreach (array_keys($availabletypes) as $activity) {
$capability = "mod/{$activity}:addinstance";
if (!has_capability($capability, $testcontext)) {
unset($availabletypes[$activity]);
}
}
}
if ($courseformatoptions === false) {
$config = get_config('format_singleactivity');
$courseformatoptions = array(
@@ -153,9 +180,13 @@ class format_singleactivity extends format_base {
'type' => PARAM_TEXT,
),
);
if (!empty($availabletypes) && !isset($availabletypes[$config->activitytype])) {
$courseformatoptions['activitytype']['default'] = array_keys($availabletypes)[0];
}
}
if ($foreditform && !isset($courseformatoptions['activitytype']['label'])) {
$availabletypes = $this->get_supported_activities();
$courseformatoptionsedit = array(
'activitytype' => array(
'label' => new lang_string('activitytype', 'format_singleactivity'),
@@ -183,6 +214,11 @@ class format_singleactivity extends format_base {
*/
public function create_edit_form_elements(&$mform, $forsection = false) {
global $PAGE;
if (!$this->course && $submitvalues = $mform->getSubmitValues()) {
$this->categoryid = $submitvalues['category'];
}
$elements = parent::create_edit_form_elements($mform, $forsection);
if (!$forsection && ($course = $PAGE->course) && !empty($course->format) &&
$course->format !== 'site' && $course->format !== 'singleactivity') {
@@ -0,0 +1,38 @@
@format @format_singleactivity
Feature: Courses can be created in Single Activity mode
In order to create a single activity course
As a manager
I need to create courses and set default values on them
Scenario: Create a course as a custom course creator
Given the following "users" exist:
| username | firstname | lastname | email |
| kevin | Kevin | the | kevin@example.com |
And the following "roles" exist:
| shortname | name | archetype |
| creator | Creator | |
And the following "system role assigns" exist:
| user | role | contextlevel |
| kevin | creator | System |
And I log in as "admin"
And I set the following system permissions of "Creator" role:
| capability | permission |
| moodle/course:create | Allow |
| moodle/course:update | Allow |
| moodle/course:manageactivities | Allow |
| moodle/course:viewparticipants | Allow |
| moodle/role:assign | Allow |
| mod/quiz:addinstance | Allow |
And I log out
And I log in as "kevin"
And I am on site homepage
When I press "Add a new course"
And I set the following fields to these values:
| Course full name | My first course |
| Course short name | myfirstcourse |
| Format | Single activity format |
And I press "Update format"
Then I should see "Quiz" in the "Type of activity" "field"
And I should not see "Forum" in the "Type of activity" "field"
And I press "Save and display"
And I should see "Adding a new Quiz"
+2 -5
View File
@@ -1123,11 +1123,8 @@ class behat_course extends behat_base {
protected function get_activity_element($element, $selectortype, $activityname) {
$activitynode = $this->get_activity_node($activityname);
// Transforming to Behat selector/locator.
list($selector, $locator) = $this->transform_selector($selectortype, $element);
$exception = new ElementNotFoundException($this->getSession(), '"' . $element . '" "' . $selectortype . '" in "' . $activityname . '" ');
return $this->find($selector, $locator, $exception, $activitynode);
$exception = new ElementNotFoundException($this->getSession(), "'{$element}' '{$selectortype}' in '${activityname}'");
return $this->find($selectortype, $element, $exception, $activitynode);
}
/**
@@ -68,3 +68,31 @@ Feature: Managers can create courses
| id_enddate_day | 24 |
| id_enddate_month | October |
| id_enddate_year | 2016 |
Scenario: Create a course as a custom course creator
Given the following "users" exist:
| username | firstname | lastname | email |
| kevin | Kevin | the | kevin@example.com |
And the following "roles" exist:
| shortname | name | archetype |
| creator | Creator | |
And the following "system role assigns" exist:
| user | role | contextlevel |
| kevin | creator | System |
And I log in as "admin"
And I set the following system permissions of "Creator" role:
| capability | permission |
| moodle/course:create | Allow |
| moodle/course:manageactivities | Allow |
| moodle/course:viewparticipants | Allow |
And I log out
And I log in as "kevin"
And I am on site homepage
When I press "Add a new course"
And I set the following fields to these values:
| Course full name | My first course |
| Course short name | myfirstcourse |
And I press "Save and display"
And I follow "Participants"
Then I should see "Kevin the"
And I should see "Teacher"
@@ -42,6 +42,9 @@ Feature: Browse course list and return back from enrolment page
And I press "Save changes"
And I log out
When I log in as "user2"
# In boost, if the viewport is such that we have the standard layout, then the click for the navigation block is in
# the link"
And I change window size to "small"
And I open my profile in edit mode
And I expand "Courses" node
And I expand "Sample category" node
@@ -23,6 +23,7 @@ Feature: Converting rubric score to grades
| activity | name | intro | course | idnumber | grade | advancedgradingmethod_submissions |
| assign | Test assignment 1 | Test | C1 | assign1 | <grade> | rubric |
When I log in as "teacher1"
And I change window size to "large"
And I am on "Course 1" course homepage with editing mode on
And I go to "Test assignment 1" advanced grading definition page
And I set the following fields to these values:
@@ -26,6 +26,7 @@ Feature: Rubrics can have levels with negative scores
And the following "activities" exist:
| activity | name | intro | course | idnumber | grade | advancedgradingmethod_submissions |
| assign | Test assignment 1 | Test | C1 | assign1 | 100 | rubric |
And I change window size to "large"
When I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I go to "Test assignment 1" advanced grading definition page
@@ -77,7 +77,7 @@ Feature: A teacher checks the grade history report in a course
| Student 2 | The greatest assignment ever | 50.00 | 70.00 | Teacher 2 |
| Student 2 | Rewarding assignment | 60.00 | 80.00 | Teacher 2 |
# Test filtering by assignment.
And I click on "The greatest assignment ever" "option" in the "#id_itemid" "css_element"
And I set the field "Grade item" to "The greatest assignment ever"
And I press "Submit"
And the following should exist in the "gradereport_history" table:
| First name/Surname | Grade item | Original grade | Revised grade | Grader |
@@ -87,7 +87,7 @@ Feature: A teacher checks the grade history report in a course
| Student 1 | Rewarding assignment | | 60.00 | Teacher 1 |
| Student 1 | Rewarding assignment | 60.00 | 80.00 | Teacher 2 |
# Test filtering by grader.
And I click on "Teacher 1" "option" in the "#id_grader" "css_element"
And I set the field "Grader" to "Teacher 1"
And I press "Submit"
And the following should exist in the "gradereport_history" table:
| First name/Surname | Grade item | Original grade | Revised grade | Grader |
@@ -93,7 +93,8 @@ class dropdown_attribute extends element {
'value' => $option,
'selected' => $selected == $option
];
}, array_keys($options))
}, array_keys($options)),
'label' => get_string('gradefor', 'gradereport_singleview', $this->label),
);
return $OUTPUT->render_from_template('gradereport_singleview/dropdown_attribute', $context);
@@ -17,6 +17,7 @@
{{!
Dropdown attribute.
}}
<label for="{{name}}" class="accesshide">{{label}}</label>
<select id="{{name}}" name="{{name}}" tabindex="1" {{#disabled}}disabled{{/disabled}}>
{{#options}}
<option value="{{value}}" {{#selected}}selected{{/selected}}>{{name}}</option>
@@ -87,7 +87,7 @@ Feature: We can use Single view
And the field "Grade for james (Student) 1" matches value "12.05"
And the field "Exclude for holly (Student) 2" matches value "1"
And I select "new grade item 1" from the "Select grade item..." singleselect
And I click on "Very good" "option"
And I set the field "Grade for james (Student) 1" to "Very good"
And I press "Save"
Then I should see "Grades were set for 1 items"
And I press "Continue"
@@ -98,7 +98,7 @@ Feature: We can use Single view
And I log in as "teacher2"
And I am on "Course 1" course homepage
Given I navigate to "View > Single view" in the course gradebook
And I click on "Student 4" "option"
And I select "Student 4" from the "Select user..." singleselect
And the "Exclude for Test assignment one" "checkbox" should be disabled
And the "Override for Test assignment one" "checkbox" should be enabled
+5 -5
View File
@@ -195,9 +195,9 @@ Feature: We can set the grade to pass value
And I add a "Forum" to section "1" and I fill the form with:
| Forum name | Test Forum 1 |
| Description | Test |
| Aggregate type | Average of ratings |
| Grade to pass | 90 |
| scale[modgrade_point] | 60 |
| Ratings > Aggregate type | Average of ratings |
| Ratings > scale[modgrade_point] | 60 |
| Grade > Grade to pass | 90 |
Then I should see "The grade to pass can not be greater than the maximum possible grade 60"
Scenario: Set a valid grade to pass for forum activity
@@ -205,8 +205,8 @@ Feature: We can set the grade to pass value
And I add a "Forum" to section "1" and I fill the form with:
| Forum name | Test Forum 1 |
| Description | Test |
| Aggregate type | Average of ratings |
| Grade to pass | 90 |
| Ratings > Aggregate type | Average of ratings |
| Grade > Grade to pass | 90 |
And I navigate to "View > Grader report" in the course gradebook
And I turn editing mode on
And I click on "Edit forum Test Forum 1" "link"
@@ -0,0 +1,33 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Automatically generated strings for Moodle installer
*
* Do not edit this file manually! It contains just a subset of strings
* needed during the very first steps of installation. This file was
* generated automatically by export-installer.php (which is part of AMOS
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
* list of strings defined in /install/stringnames.txt.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$string['thislanguage'] = 'Valencià_RACV';
+3 -2
View File
@@ -36,8 +36,9 @@ $string['cliincorrectvalueerror'] = 'Σφάλμα, λανθασμένη τιμή
$string['cliincorrectvalueretry'] = 'Λανθασμένη τιμή. Παρακαλούμε προσπαθήστε ξανά.';
$string['clitypevalue'] = 'πληκτρολογήστε μια τιμή';
$string['clitypevaluedefault'] = 'πληκτρολογήστε μια τιμή· πατήστε Enter για να χρησιμοποιήσετε τηνπροεπιλεγμένη τιμή ({$a})';
$string['cliunknowoption'] = 'Μη αναγνωρίσιμες επιλογές: {$a}<br />
Παρακαλούμε χρησιμοποιήστε την επιλογή --βοήθεια';
$string['cliunknowoption'] = 'Μη αναγνωρίσιμες επιλογές:
{$a}<br />
Παρακαλούμε χρησιμοποιήστε την επιλογή --help (βοήθεια)';
$string['cliyesnoprompt'] = 'πατήστε y (σημαίνει yes=ναι) ή πατήστε n (σημαίνει no=όχι)';
$string['environmentrequireinstall'] = 'απαιτείται να εγκατασταθεί/ ενεργοποιηθεί';
$string['environmentrequireversion'] = 'απαιτείται η έκδοση {$a->needed} ενώ εσείς έχετε την {$a->current}';
+2 -2
View File
@@ -40,9 +40,9 @@ $string['cannotsavemd5file'] = 'Αδυναμία αποθήκευσης αρχε
$string['cannotsavezipfile'] = 'Αδυναμία αποθήκευσης συμπιεσμένου αρχείου';
$string['cannotunzipfile'] = 'Αδυναμία αποσυμπίεσης αρχείου';
$string['componentisuptodate'] = 'Το στοιχείο λογισμικού είναι ενημερωμένο.';
$string['dmlexceptiononinstall'] = '<p>Παρουσιάστηκε σφάλμα βάσης δεδομένων [{$a->errorcode}].<br />{$a->debuginfo}</p>';
$string['dmlexceptiononinstall'] = '<p>Παρουσιάστηκε κάποιο σφάλμα βάσης δεδομένων [{$a->errorcode}].<br />{$a->debuginfo}</p>';
$string['downloadedfilecheckfailed'] = 'Αποτυχία ελέγχου αρχείου που έγινε λήψη';
$string['invalidmd5'] = 'Μη έγκυρο md5';
$string['invalidmd5'] = 'Η μεταβλητή ελέγχου ήταν λανθασμένη - δοκιμάστε ξανά';
$string['missingrequiredfield'] = 'Κάποιο απαιτούμενο πεδίο λείπει';
$string['remotedownloaderror'] = '<p>Απέτυχε η λήψη του τμήματος στον εξυπηρετητή σας. Παρακαλούμε επιβεβαιώστε τις ρυθμίσεις του διακομιστή μεσολάβησης (proxy)· η επέκταση PHP cURL συνιστάται θερμά.</p> <p>Πρέπει να κατεβάσετε το <a href="{$a->url}">{$a->url}</a> χειροκίνητα, να το αντιγράψετε στο «{$a->dest}» στον εξυπηρετητή σας και να το αποσυμπιέσετε εκεί.</p>';
$string['wrongdestpath'] = 'Λανθασμένη διαδρομή προορισμού (πλήρες όνομα).';
+2 -2
View File
@@ -37,7 +37,7 @@ $string['chooselanguagesub'] = 'Παρακαλούμε, επιλέξτε γλώ
$string['clialreadyconfigured'] = 'Το αρχείο ρυθμίσεων config.php υπάρχει ήδη. Χρησιμοποιήστε το admin/cli/install_database.php για να εγκαταστήσετε το Moodle για αυτόν τον ιστότοπο.';
$string['clialreadyinstalled'] = 'Το αρχείο ρυθμίσεων config.php υπάρχει ήδη. Χρησιμοποιήστε το admin/cli/install_database.php για να αναβαθμίσετε το Moodle για αυτόν τον ιστότοπο.';
$string['cliinstallheader'] = 'Moodle {$a} πρόγραμμα εγκατάστασης γραμμής εντολών';
$string['databasehost'] = 'Κεντρικός Υπολογιστής Βάσης Δεδομένων';
$string['databasehost'] = 'Κεντρικός H/Y που φιλοξενεί την Βάση Δεδομένων';
$string['databasename'] = 'Όνομα Βάσης Δεδομένων';
$string['databasetypehead'] = 'Επιλογή οδηγού βάσης δεδομένων';
$string['dataroot'] = 'Φάκελος δεδομένων';
@@ -67,7 +67,7 @@ $string['pathserrcreatedataroot'] = 'Ο Φάκελος δεδομένων ({$a->
$string['pathshead'] = 'Επιβεβαίωση Διαδρομών';
$string['pathsrodataroot'] = 'Ο Φάκελος Δεδομένων δεν είναι εγγράψιμος.';
$string['pathsroparentdataroot'] = 'Ο γονικός φάκελος ({$a->parent}) δεν είναι εγγράψιμος. Ο φάκελος δεδομένων ({$a->dataroot}) δεν μπορεί να δημιουργθεί από το πρόγραμμα εγκατάστασης.';
$string['pathssubadmindir'] = 'Κάποιοι λίγοι κεντρικοί υπολογιστές ιστού χρησιμοποιούν το /admin ως ειδική διεύθυνση URL για την πρόσβαση σε κάποιο πίνακα ελέγχου ή κάτι τέτοιο. Δυστυχώς αυτό έρχεται σε αντίθεση με την τυπική τοποθεσία των σελίδων διαχείρισης (admin) του Moodle. Αυτό μπορεί να διορθωθεί με την μετονομασία του admin φακέλου στην εγκατάστασή σας, και βάζοντας αυτό το καινούργιο όνομα εδώ. Για παράδειγμα: <em>moodleadmin</em>. Αυτό θα διορθώσει όλους τους συνδέσμους με το admin στην διεύθυνσή τους σε όλη την εγκατάσταση του Moodle σας.';
$string['pathssubadmindir'] = 'Κάποιοι λίγοι κεντρικοί υπολογιστές ιστού χρησιμοποιούν το /admin ως ειδική διεύθυνση URL για την πρόσβαση σε κάποιο πίνακα ελέγχου ή κάτι τέτοιο. Δυστυχώς αυτό έρχεται σε αντίθεση με την τυπική τοποθεσία των σελίδων διαχείρισης (admin) του Moodle. Αυτό μπορεί να διορθωθεί με την μετονομασία του admin φακέλου στην εγκατάστασή σας, και βάζοντας αυτό το καινούργιο όνομα εδώ. Για παράδειγμα: <em>moodleadmin</em>. Αυτό θα διορθώσει όλους τους συνδέσμους με το admin στην διεύθυνσή τους σε όλη την εγκατάσταση του Moodle σας.';
$string['pathssubdataroot'] = '<p>Ένας φάκελος όπου το Moodle θα αποθηκεύει όλα τα ανεβασμένα από τους χρήστες αρχεία.</p> <p>Αυτος ο φάκελος θα πρέπει να είναι αναγνώσιμος ΚΑΙ ΕΓΓΡΑΨΙΜΟΣ από τον χρήστη του εξυπηρετητή ιστού (συνήθως «nobody» ή «apache»).</p> <p>Δεν πρέπει να είναι προσβάσιμος κατευθείαν από τον ιστό.</p> <p>Αν ο φάκελος δεν υπάρχει, η διαδικασία εγκατάστασης θα προσπαθήσει να τον δημιουργήσει.</p>';
$string['pathssubdirroot'] = '<p>Η πλήρης διαδρομή του φακέλου που περιέχει τα αρχεία κώδικα του Moodle.</p>';
$string['pathssubwwwroot'] = '<p>Η πλήρης διεύθυνση (ιστού) από την οποία θα γίνεται η πρόσβαση στο Moodle, δηλαδή η διεύθυνση που οι χρήστες θα εισάγουν στην γραμμή διεύθυνσης του περιηγητή, για να έχουν πρόσβαση στου Moodle.</p>
+1 -1
View File
@@ -46,7 +46,7 @@ $string['dmlexceptiononinstall'] = '<p>Datu-basearen errorea gertatu da [{$a->er
$string['downloadedfilecheckfailed'] = 'Jaitsitako fitxategiaren egiaztatzeak huts egin du.';
$string['invalidmd5'] = 'Kontrolerako aldagaia gaizki zegoen - saiatu berriz ere';
$string['missingrequiredfield'] = 'Beharrezko eremuren bat falta da.';
$string['remotedownloaderror'] = '<p>Errorea osagaia zure zerbitzarian jaistean, mesedez egiaztatu proxy-ezarpenak, PHP cURL luzapena erabat gomendatzen da.</p>
$string['remotedownloaderror'] = '<p>Errorea osagaia zure zerbitzarian jaistean, mesedez egiaztatu proxy-ezarpenak, PHP cURL hedapena erabat gomendatzen da.</p>
<p> <a href="{$a->url}">{$a->url}</a> fitxategia eskuz jaitsi beharko zenuke, zure zerbitzariko "{$a->dest}"-ra kopiatu eta bertan deskonprimatu.</p>';
$string['wrongdestpath'] = 'Bide desegokia';
$string['wrongsourcebase'] = 'URL iturriaren oinarri akastuna';
+2 -2
View File
@@ -45,7 +45,7 @@ $string['datarootpermission'] = 'Datu-direktorioen baimena';
$string['dbprefix'] = 'Taulen aurrizkia';
$string['dirroot'] = 'Moodle direktorioa';
$string['environmenthead'] = 'Zure ingurunea egiaztatzen...';
$string['environmentsub2'] = 'Moodle-ko bertsio bakoitzak PHPko gutxieneko bertsioa eta derrigorrez instalatu beharreko PHP luzapen batzuk ditu. Ingurunearen azterketa oso bat egiten da instalazioa eta eguneraketa bakoitza egin aurretik. Mesedez, jarri harremanetan zerbitzariaren kudeatzailearekin ez badakizu bertsio berria edo PHP luzapenak nola instalatu.';
$string['environmentsub2'] = 'Moodle-ko bertsio bakoitzak PHPko gutxieneko bertsioa eta derrigorrez instalatu beharreko PHP hedapen batzuk ditu. Ingurunearen azterketa oso bat egiten da instalazioa eta eguneraketa bakoitza egin aurretik. Mesedez, jarri harremanetan zerbitzariaren kudeatzailearekin ez badakizu bertsio berria edo PHP hedapenak nola instalatu.';
$string['errorsinenvironment'] = 'Huts egin du ingurunearen egiaztatzeak!';
$string['installation'] = 'Instalazioa';
$string['langdownloaderror'] = 'Zoritxarrez "{$a}" hizkuntza ezin izan da jaitsi. Instalazio-prozesuak ingelesez jarraituko du.';
@@ -83,7 +83,7 @@ $string['pathssubwwwroot'] = '<p>Moodle eskuragarri egongo den helbide osoa, hau
<p>Oraingo helbidea egokia ez bada, mesedez aldatu ezazu URLa zure nabigatzailean eta instalazioa berriz abiatu ezazu.</p>';
$string['pathsunsecuredataroot'] = 'Dataroot-en kokapena ez da segurua';
$string['pathswrongadmindir'] = 'Admin direktorioa ez da existitzen';
$string['phpextension'] = '{$a} PHP luzapena';
$string['phpextension'] = '{$a} PHP hedapena';
$string['phpversion'] = 'PHP bertsioa';
$string['phpversionhelp'] = '<p>Moodle-k PHP 5.6.5 edo 7.1 bertsioetako bat behar du (7.0.x bertsioek muga batzuk dituzte). </p>
<p>Zure bertsioa: {$a}</p>
+2 -2
View File
@@ -44,11 +44,11 @@ $string['dataroot'] = 'Dossier de données';
$string['datarootpermission'] = 'Droits d\'accès au dossier de données';
$string['dbprefix'] = 'Préfixe des tables';
$string['dirroot'] = 'Dossier Moodle';
$string['environmenthead'] = 'Vérification de l\'environnement...';
$string['environmenthead'] = 'Vérification de l\'environnement';
$string['environmentsub2'] = 'Chaque version de Moodle nécessite une version minimale de certains composants PHP et des extensions de PHP obligatoires. Une vérification complète de l\'environnement est effectuée avec chaque installation et chaque mise à jour. Veuillez contacter l\'administrateur du serveur si vous ne savez pas comment installer une nouvelle version ou activer des extensions de PHP.';
$string['errorsinenvironment'] = 'Échec de la vérification de l\'environnement !';
$string['installation'] = 'Installation';
$string['langdownloaderror'] = 'La langue {$a} n\'a pas pu être téléchargée. La suite de l\'installation se déroulera en anglais. Vous pourrez télécharger et installer d\'autres langues à la fin de l\'installation';
$string['langdownloaderror'] = 'La langue « {$a} » n\'a pas pu être téléchargée. La suite de l\'installation se déroulera en anglais. Vous pourrez télécharger et installer d\'autres langues à la fin de l\'installation';
$string['memorylimithelp'] = '<p>La limite de mémoire de PHP sur votre serveur est actuellement de {$a}.</p>
<p>Cette valeur trop basse risque de générer des problèmes de manque de mémoire pour Moodle, notamment si vous utilisez beaucoup de modules et/ou si vous avez un grand nombre d\'utilisateurs.</p>
<p>Il est recommandé de configurer PHP avec une limite de mémoire aussi élevée que possible, par exemple 40 Mo. Vous pouvez obtenir cela de différentes façons :</p>
+34
View File
@@ -0,0 +1,34 @@
<?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/>.
/**
* Automatically generated strings for Moodle installer
*
* Do not edit this file manually! It contains just a subset of strings
* needed during the very first steps of installation. This file was
* generated automatically by export-installer.php (which is part of AMOS
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
* list of strings defined in /install/stringnames.txt.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$string['parentlanguage'] = 'fr';
$string['thislanguage'] = 'Français pour Workplace';
+1 -1
View File
@@ -37,7 +37,7 @@ $string['cliincorrectvalueretry'] = 'Netočna vrijednost, pokušajte ponovno';
$string['clitypevalue'] = 'unesite vrijednost';
$string['clitypevaluedefault'] = 'unesite vrijednost ili pritisnite Enter za korištenje zadane vrijednosti ({$a})';
$string['cliunknowoption'] = 'Nepoznate opcije: {$a} Molimo koristite --help opciju.';
$string['cliyesnoprompt'] = 'unesite y (znači da) ili n (znači ne)';
$string['cliyesnoprompt'] = 'unesite d (znači da) ili n (znači ne)';
$string['environmentrequireinstall'] = 'je neophodno instalirati/omogućiti';
$string['environmentrequireversion'] = 'neophodna inačica je {$a->needed}, a vi trenutačno koristite inačicu {$a->current}';
$string['upgradekeyset'] = 'Ključ za ažuriranje (ostavite prazno kako ga ne bi zadali)';
+34
View File
@@ -0,0 +1,34 @@
<?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/>.
/**
* Automatically generated strings for Moodle installer
*
* Do not edit this file manually! It contains just a subset of strings
* needed during the very first steps of installation. This file was
* generated automatically by export-installer.php (which is part of AMOS
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
* list of strings defined in /install/stringnames.txt.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$string['parentlanguage'] = 'hr';
$string['thislanguage'] = 'Croatian schools';
+16
View File
@@ -36,12 +36,28 @@ $string['chooselanguagehead'] = 'Pilih bahasa';
$string['chooselanguagesub'] = 'Silakan pilih bahasa untuk instalasi. Bahasa ini juga akan digunakan sebagai bahasa default untuk situs, meskipun mungkin akan diubah kemudian.';
$string['clialreadyconfigured'] = 'Berkas konfigurasi config.php sudah ada. Silakan gunakan admin /cli/install_database.php untuk menginstal Moodle untuk situs ini.';
$string['clialreadyinstalled'] = 'File konfigurasi config.php sudah ada. Silakan gunakan admin/cli/install_database.php untuk menginstal Moodle untuk situs ini.';
$string['cliinstallheader'] = 'Program pemasangan baris perintah Moodle {$a}';
$string['databasehost'] = 'Host basis data';
$string['databasename'] = 'Nama basis data';
$string['databasetypehead'] = 'Pilih pengandar basis data';
$string['dataroot'] = 'Direktori data';
$string['datarootpermission'] = 'Izin direktori data';
$string['dbprefix'] = 'Prefiks tabel';
$string['dirroot'] = 'Direktori Moodle';
$string['environmenthead'] = 'Memeriksa sistem Anda ...';
$string['environmentsub2'] = 'Setiap rilis Moodle memiliki beberapa persyaratan versi PHP minimum dan sejumlah ekstensi PHP wajib. Pemeriksaan komponen sistem akan dilakukan sebelum pemasangan dan peningkatan versi. Silakan hubungi administrator peladen jika Anda tidak tahu cara mamasang versi baru atau mengaktifkan ekstensi PHP.';
$string['errorsinenvironment'] = 'Pemeriksaan sistem gagal!';
$string['installation'] = 'Instalasi';
$string['langdownloaderror'] = 'Sayangnya bahasa "{$a}" tidak dapat diunduh. Proses instalasi akan dilanjutkan dalam bahasa Inggris.';
$string['memorylimithelp'] = '<p> Batas memori PHP untuk server Anda saat ini diatur ke {$a}. </p> <p> Ini dapat menyebabkan Moodle memiliki masalah memori di kemudian hari, terutama jika Anda memiliki banyak modul yang diaktifkan dan/atau banyak pengguna. </p> <p> Kami menyarankan Anda mengkonfigurasi PHP dengan batas yang lebih tinggi jika memungkinkan, seperti 40M. Ada beberapa cara untuk melakukan ini yang dapat Anda coba: </p>
<ol> <li> Jika Anda bisa, kompilasi ulang PHP dengan <i> --enable-memory-limit </i>. Ini memungkinkan Moodle untuk mengatur batas memori itu sendiri. </li>
<li> Jika Anda memiliki akses ke file php.ini, Anda dapat mengubah pengaturan <b> memory_limit </b> di sana menjadi sekitar 40M. Jika Anda tidak memiliki akses, Anda mungkin dapat meminta administrator untuk melakukan ini untuk Anda. </li>
<li> Pada beberapa server PHP, Anda dapat membuat file .htaccess di direktori Moodle yang berisi baris ini: <blockquote> <div> php_value memory_limit 40M </div> </blockquote> <p> Namun, pada beberapa server ini tidak diizinkan <b> semua </b> halaman PHP tidak berfungsi (Anda akan melihat kesalahan ketika Anda melihat halaman) sehingga Anda Anda harus menghapus file .htaccess. </p> </li> </ol>';
$string['paths'] = 'Jalur';
$string['pathserrcreatedataroot'] = 'Direktori data ({$a->dataroot}) tidak dapat dibuat oleh installer.';
$string['pathshead'] = 'Konfirmasi jalur';
$string['pathsrodataroot'] = 'Direktori data root tidak dapat ditulisi.';
$string['pathsroparentdataroot'] = 'Direktori induk ({$a->parent}) tidak dapat ditulisi. Direktori data ({$a->dataroot}) tidak dapat dibuat oleh installer.';
$string['pathssubadmindir'] = 'Beberapa hosting menggunakan / admin sebagai URL khusus untuk Anda mengakses panel kontrol atau sesuatu. Sayangnya ini bertentangan dengan lokasi standar untuk halaman admin Moodle. Anda dapat memperbaikinya dengan mengganti nama direktori admin di instalasi Anda, dan meletakkan nama baru itu di sini. Misalnya: <em> moodleadmin </em>. Ini akan memperbaiki tautan admin di Moodle.';
$string['pathssubdataroot'] = '<p> Direktori tempat Moodle akan menyimpan semua konten file yang diunggah oleh pengguna. </p> <p> Direktori ini harus dapat dibaca dan ditulis oleh pengguna server web (biasanya \'www-data\', \'nobody\', atau \' apache \'). </p> <p> Itu tidak boleh diakses secara langsung melalui web. </p> <p> Jika direktori saat ini tidak ada, proses instalasi akan berusaha membuatnya. </p>';
$string['pathssubdirroot'] = '<p> Jalur lengkap ke direktori yang berisi kode Moodle. </p>';
+1 -1
View File
@@ -57,7 +57,7 @@ $string['memorylimithelp'] = '<p>現在、サーバのPHPメモリ制限が {$a}
<ol>
<li>あなたがリコンパイル可能な場合、PHPを<i>--enable-memory-limit</i>オプションでコンパイルしてください。これにより、Moodle自身がメモリ制限を設定することが可能になります。</li>
<li>あなたがphp.iniファイルにアクセスできる場合、<b>memory_limit</b>設定を40Mのように変更することができます。php.iniファイルにアクセスできない場合、管理者に変更を依頼してください。</li>
<li>いくつかのPHPサーバでは下の行を含む.htaccessファイルをMoodleディレクトリに作成することができます:
<li>いくつかのPHPサーバでは下の行を含む.htaccessファイルをMoodleディレクトリに作成することができます:
<blockquote><div>php_value memory_limit 40M</div></blockquote>
<p>しかし、この設定が<b>すべての</b>PHPページの動作を妨げる場合もあります。ページ閲覧中にエラーが表示される場合、.htaccessファイルを削除してください。</p></li>
</ol>';
+1 -1
View File
@@ -40,6 +40,6 @@ $string['cliunknowoption'] = 'Nieznana opcja:
{$a}
Proszę użyć pomocy.';
$string['cliyesnoprompt'] = 'wpisz y (czyli tak) lub n (czyli nie)';
$string['environmentrequireinstall'] = 'jest niezbędnę, żeby było zainstalowane/włączone';
$string['environmentrequireinstall'] = 'jest niezbędne, aby było zainstalowane i włączone';
$string['environmentrequireversion'] = 'wersja {$a->needed} jest niezbędna a ty używasz wersji {$a->current}';
$string['upgradekeyset'] = 'Zaktualizuj klucz (pozostaw puste jeśli nie jest to skonfigurowane)';
+37
View File
@@ -0,0 +1,37 @@
<?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/>.
/**
* Automatically generated strings for Moodle installer
*
* Do not edit this file manually! It contains just a subset of strings
* needed during the very first steps of installation. This file was
* generated automatically by export-installer.php (which is part of AMOS
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
* list of strings defined in /install/stringnames.txt.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$string['clianswerno'] = 'n';
$string['cliansweryes'] = 'y';
$string['cliincorrectvalueretry'] = 'Valoarea nu este corectă, încercați din nou';
$string['environmentrequireinstall'] = 'trebuie să fie instalat și activat';
$string['environmentrequireversion'] = 'versiunea {$a->needed} este necesară iar dumneavoastră rulați {$a->current}';
+37
View File
@@ -0,0 +1,37 @@
<?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/>.
/**
* Automatically generated strings for Moodle installer
*
* Do not edit this file manually! It contains just a subset of strings
* needed during the very first steps of installation. This file was
* generated automatically by export-installer.php (which is part of AMOS
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
* list of strings defined in /install/stringnames.txt.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$string['language'] = 'Limbă';
$string['moodlelogo'] = 'Logo Moodle';
$string['next'] = 'Următoarea';
$string['previous'] = 'Anterior';
$string['reload'] = 'Reîncarcă';
+1
View File
@@ -31,6 +31,7 @@
defined('MOODLE_INTERNAL') || die();
$string['language'] = 'மொழி';
$string['moodlelogo'] = 'Moodle சின்னம்';
$string['next'] = 'அடுத்தது';
$string['previous'] = 'முன்னைய';
$string['reload'] = 'மீள ஏற்றுக';
+34
View File
@@ -0,0 +1,34 @@
<?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/>.
/**
* Automatically generated strings for Moodle installer
*
* Do not edit this file manually! It contains just a subset of strings
* needed during the very first steps of installation. This file was
* generated automatically by export-installer.php (which is part of AMOS
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
* list of strings defined in /install/stringnames.txt.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$string['parentlanguage'] = 'zh_cn';
$string['thislanguage'] = 'Workplace 简体中文版';
+34
View File
@@ -0,0 +1,34 @@
<?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/>.
/**
* Automatically generated strings for Moodle installer
*
* Do not edit this file manually! It contains just a subset of strings
* needed during the very first steps of installation. This file was
* generated automatically by export-installer.php (which is part of AMOS
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
* list of strings defined in /install/stringnames.txt.
*
* @package installer
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$string['parentlanguage'] = 'zh_tw';
$string['thislanguage'] = 'Workplace 繁體中文版';
+1
View File
@@ -1197,6 +1197,7 @@ $string['unsupportedphpversion7'] = 'PHP version 7 is not supported.';
$string['unsupportedphpversion71'] = 'PHP version 7.1 is not supported.';
$string['unsupportedphpversion72'] = 'PHP version 7.2 is not supported.';
$string['unsupportedphpversion73'] = 'PHP version 7.3 is not supported.';
$string['unsupportedphpversion74'] = 'PHP version 7.4 is not supported.';
$string['unsuspenduser'] = 'Activate user account';
$string['updateaccounts'] = 'Update existing accounts';
$string['updatecomponent'] = 'Update component';
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+8 -3
View File
@@ -712,24 +712,29 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
window.setTimeout(function() {
// Get the current element with focus.
var focusElement = $(document.activeElement);
var timeoutPromise = $.Deferred();
// Only close the menu if the input hasn't regained focus, and if the element still exists.
// Due to the half a second delay, it is possible that the input element no longer exist
// by the time this code is being executed.
if (focusElement.attr('id') != inputElement.attr('id') && $('#' + state.inputId).length) {
if (options.tags) {
pendingPromise.then(function() {
timeoutPromise.then(function() {
return createItem(options, state, originalSelect);
})
.catch();
}
pendingPromise.then(function() {
timeoutPromise.then(function() {
return closeSuggestions(state);
})
.catch();
}
pendingPromise.resolve();
timeoutPromise.then(function() {
return pendingPromise.resolve();
})
.catch();
timeoutPromise.resolve();
}, 500);
});
if (options.showSuggestions) {
+63 -18
View File
@@ -59,6 +59,9 @@ define([
/** @var {Object} iconSystem - Object extending core/iconsystem */
var iconSystem = {};
/** @var {Array} blacklistedNestedHelpers - List of helpers that can't be called within other helpers */
var blacklistedNestedHelpers = ['js'];
/**
* Constructor
*
@@ -357,6 +360,60 @@ define([
return '[[_t_' + index + ']]';
};
/**
* Return a helper function to be added to the context for rendering the a
* template.
*
* This will parse the provided text before giving it to the helper function
* in order to remove any blacklisted nested helpers to prevent one helper
* from calling another.
*
* In particular to prevent the JS helper from being called from within another
* helper because it can lead to security issues when the JS portion is user
* provided.
*
* @param {function} helperFunction The helper function to add
* @param {object} context The template context for the helper function
* @return {Function} To be set in the context
*/
Renderer.prototype.addHelperFunction = function(helperFunction, context) {
return function() {
return function(sectionText, helper) {
// Override the blacklisted helpers in the template context with
// a function that returns an empty string for use when executing
// other helpers. This is to prevent these helpers from being
// executed as part of the rendering of another helper in order to
// prevent any potential security issues.
var originalHelpers = blacklistedNestedHelpers.reduce(function(carry, name) {
if (context.hasOwnProperty(name)) {
carry[name] = context[name];
}
return carry;
}, {});
blacklistedNestedHelpers.forEach(function(helperName) {
context[helperName] = function() {
return '';
};
});
// Execute the helper with the modified context that doesn't include
// the blacklisted nested helpers. This prevents the blacklisted
// helpers from being called from within other helpers.
var result = helperFunction.apply(this, [context, sectionText, helper]);
// Restore the original helper implementation in the context so that
// any further rendering has access to them again.
for (var name in originalHelpers) {
context[name] = originalHelpers[name];
}
return result;
}.bind(this);
}.bind(this);
};
/**
* Add some common helper functions to all context objects passed to templates.
* These helpers match exactly the helpers available in php.
@@ -371,24 +428,12 @@ define([
this.requiredStrings = [];
this.requiredJS = [];
context.uniqid = (uniqInstances++);
context.str = function() {
return this.stringHelper.bind(this, context);
}.bind(this);
context.pix = function() {
return this.pixHelper.bind(this, context);
}.bind(this);
context.js = function() {
return this.jsHelper.bind(this, context);
}.bind(this);
context.quote = function() {
return this.quoteHelper.bind(this, context);
}.bind(this);
context.shortentext = function() {
return this.shortenTextHelper.bind(this, context);
}.bind(this);
context.userdate = function() {
return this.userDateHelper.bind(this, context);
}.bind(this);
context.str = this.addHelperFunction(this.stringHelper, context);
context.pix = this.addHelperFunction(this.pixHelper, context);
context.js = this.addHelperFunction(this.jsHelper, context);
context.quote = this.addHelperFunction(this.quoteHelper, context);
context.shortentext = this.addHelperFunction(this.shortenTextHelper, context);
context.userdate = this.addHelperFunction(this.userDateHelper, context);
context.globals = {config: config};
context.currentTheme = themeName;
};
+227 -113
View File
@@ -32,8 +32,12 @@ use Behat\Mink\Exception\DriverException;
use Behat\Mink\Exception\ExpectationException;
use Behat\Mink\Exception\ElementNotFoundException;
use Behat\Mink\Element\NodeElement;
use Behat\Mink\Element\Element;
use Behat\Mink\Session;
require_once(__DIR__ . '/classes/component_named_selector.php');
require_once(__DIR__ . '/classes/component_named_replacement.php');
/**
* Steps definitions base class.
*
@@ -103,14 +107,6 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
* @return NodeElement
*/
protected function find($selector, $locator, $exception = false, $node = false, $timeout = false) {
// Throw exception, so dev knows it is not supported.
if ($selector === 'named') {
$exception = 'Using the "named" selector is deprecated as of 3.1. '
.' Use the "named_partial" or use the "named_exact" selector instead.';
throw new ExpectationException($exception, $this->getSession());
}
// Returns the first match.
$items = $this->find_all($selector, $locator, $exception, $node, $timeout);
return count($items) ? reset($items) : null;
@@ -125,12 +121,11 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
* @param string $selector The selector type (css, xpath, named...)
* @param mixed $locator It depends on the $selector, can be the xpath, a name, a css locator...
* @param Exception $exception Otherwise we throw expcetion with generic info
* @param NodeElement $node Spins around certain DOM node instead of the whole page
* @param NodeElement $container Restrict the search to just children of the specified container
* @param int $timeout Forces a specific time out (in seconds). If 0 is provided the default timeout will be applied.
* @return array NodeElements list
*/
protected function find_all($selector, $locator, $exception = false, $node = false, $timeout = false) {
protected function find_all($selector, $locator, $exception = false, $container = false, $timeout = false) {
// Throw exception, so dev knows it is not supported.
if ($selector === 'named') {
$exception = 'Using the "named" selector is deprecated as of 3.1. '
@@ -140,7 +135,6 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
// Generic info.
if (!$exception) {
// With named selectors we can be more specific.
if (($selector == 'named_exact') || ($selector == 'named_partial')) {
$exceptiontype = $locator[0];
@@ -159,12 +153,6 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
$exception = new ElementNotFoundException($this->getSession(), $exceptiontype, null, $exceptionlocator);
}
$params = array('selector' => $selector, 'locator' => $locator);
// Pushing $node if required.
if ($node) {
$params['node'] = $node;
}
// How much we will be waiting for the element to appear.
if (!$timeout) {
$timeout = self::get_timeout();
@@ -176,44 +164,87 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
$microsleep = true;
}
// Normalise the values in order to perform the search.
$normalised = $this->normalise_selector($selector, $locator, $container ?: $this->getSession()->getPage());
$selector = $normalised['selector'];
$locator = $normalised['locator'];
$container = $normalised['container'];
if (empty($container)) {
$container = $this->getSession()->getPage();
}
// Waits for the node to appear if it exists, otherwise will timeout and throw the provided exception.
return $this->spin(
function($context, $args) {
// If no DOM node provided look in all the page.
if (empty($args['node'])) {
return $context->getSession()->getPage()->findAll($args['selector'], $args['locator']);
}
// For nodes contained in other nodes we can not use the basic named selectors
// as they include unions and they would look for matches in the DOM root.
$elementxpath = $context->getSession()->getSelectorsHandler()->selectorToXpath($args['selector'], $args['locator']);
// Split the xpath in unions and prefix them with the container xpath.
$unions = explode('|', $elementxpath);
foreach ($unions as $key => $union) {
$union = trim($union);
// We are in the container node.
if (strpos($union, '.') === 0) {
$union = substr($union, 1);
} else if (strpos($union, '/') !== 0) {
// Adding the path separator in case it is not there.
$union = '/' . $union;
}
$unions[$key] = $args['node']->getXpath() . $union;
}
// We can not use usual Element::find() as it prefixes with DOM root.
return $context->getSession()->getDriver()->find(implode('|', $unions));
},
$params,
$timeout,
$exception,
$microsleep
function() use ($selector, $locator, $container) {
return $container->findAll($selector, $locator);
}, [], $timeout, $exception, $microsleep
);
}
/**
* Normalise the locator and selector.
*
* @param string $selector The type of thing to search
* @param mixed $locator The locator value. Can be an array, but is more likely a string.
* @param Element $container An optional container to search within
* @return array The selector, locator, and container to search within
*/
public function normalise_selector(string $selector, $locator, Element $container): array {
// Check for specific transformations for this selector type.
$transformfunction = "transform_find_for_{$selector}";
if (method_exists('behat_selectors', $transformfunction)) {
// A selector-specific transformation exists.
// Perform initial transformation of the selector within the current container.
$normalised = behat_selectors::{$transformfunction}($this, $locator, $container);
$selector = $normalised['selector'];
$locator = $normalised['locator'];
$container = $normalised['container'];
}
// Normalise the css and xpath selector types.
if ('css_element' === $selector) {
$selector = 'css';
} else if ('xpath_element' === $selector) {
$selector = 'xpath';
}
// Convert to a named selector where the selector type is not a known selector.
$converttonamed = !$this->getSession()->getSelectorsHandler()->isSelectorRegistered($selector);
$converttonamed = $converttonamed && 'xpath' !== $selector;
if ($converttonamed) {
if (behat_partial_named_selector::is_deprecated_selector($selector)) {
if ($replacement = behat_partial_named_selector::get_deprecated_replacement($selector)) {
error_log("The '{$selector}' selector has been replaced with {$replacement}");
$selector = $replacement;
}
} else if (behat_exact_named_selector::is_deprecated_selector($selector)) {
if ($replacement = behat_exact_named_selector::get_deprecated_replacement($selector)) {
error_log("The '{$selector}' selector has been replaced with {$replacement}");
$selector = $replacement;
}
}
$allowedpartialselectors = behat_partial_named_selector::get_allowed_selectors();
$allowedexactselectors = behat_exact_named_selector::get_allowed_selectors();
if (isset($allowedpartialselectors[$selector])) {
$locator = behat_selectors::normalise_named_selector($allowedpartialselectors[$selector], $locator);
$selector = 'named_partial';
} else if (isset($allowedexactselectors[$selector])) {
$locator = behat_selectors::normalise_named_selector($allowedexactselectors[$selector], $locator);
$selector = 'named_exact';
} else {
throw new ExpectationException("The '{$selector}' selector type is not registered.", $this->getSession()->getDriver());
}
}
return [
'selector' => $selector,
'locator' => $locator,
'container' => $container,
];
}
/**
* Finds DOM nodes in the page using named selectors.
*
@@ -237,27 +268,14 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
* @return NodeElement
*/
public function __call($name, $arguments) {
if (substr($name, 0, 5) !== 'find_') {
throw new coding_exception('The "' . $name . '" method does not exist');
if (substr($name, 0, 5) === 'find_') {
return call_user_func_array([$this, 'find'], array_merge(
[substr($name, 5)],
$arguments
));
}
// Only the named selector identifier.
$cleanname = substr($name, 5);
// All named selectors shares the interface.
if (count($arguments) !== 1) {
throw new coding_exception('The "' . $cleanname . '" named selector needs the locator as it\'s single argument');
}
// Redirecting execution to the find method with the specified selector.
// It will detect if it's pointing to an unexisting named selector.
return $this->find('named_partial',
array(
$cleanname,
behat_context_helper::escape($arguments[0])
)
);
throw new coding_exception("The '{$name}' method does not exist");
}
/**
@@ -368,12 +386,7 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
* @return NodeElement
*/
protected function get_selected_node($selectortype, $element) {
// Getting Mink selector and locator.
list($selector, $locator) = $this->transform_selector($selectortype, $element);
// Returns the NodeElement.
return $this->find($selector, $locator);
return $this->find($selectortype, $element);
}
/**
@@ -385,7 +398,6 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
* @return NodeElement
*/
protected function get_text_selector_node($selectortype, $element) {
// Getting Mink selector and locator.
list($selector, $locator) = $this->transform_text_selector($selectortype, $element);
@@ -404,18 +416,13 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
* @return NodeElement
*/
protected function get_node_in_container($selectortype, $element, $containerselectortype, $containerelement) {
// Gets the container, it will always be text based.
$containernode = $this->get_text_selector_node($containerselectortype, $containerelement);
list($selector, $locator) = $this->transform_selector($selectortype, $element);
// Specific exception giving info about where can't we find the element.
$locatorexceptionmsg = $element . '" in the "' . $containerelement. '" "' . $containerselectortype. '"';
$exception = new ElementNotFoundException($this->getSession(), $selectortype, null, $locatorexceptionmsg);
// Looks for the requested node inside the container node.
return $this->find($selector, $locator, $exception, $containernode);
return $this->find($selectortype, $element, $exception, $containernode);
}
/**
@@ -434,14 +441,14 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
* @return array Contains the selector and the locator expected by Mink.
*/
protected function transform_selector($selectortype, $element) {
// Here we don't know if an allowed text selector is being used.
$selectors = behat_selectors::get_allowed_selectors();
if (!isset($selectors[$selectortype])) {
throw new ExpectationException('The "' . $selectortype . '" selector type does not exist', $this->getSession());
}
return behat_selectors::get_behat_selector($selectortype, $element, $this->getSession());
$normalised = $this->normalise_selector($selectortype, $element, $this->getSession()->getPage());
return [$normalised['selector'], $normalised['locator']];
}
/**
@@ -478,63 +485,66 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
* Spins around an element until it exists
*
* @throws ExpectationException
* @param string $element
* @param string $locator
* @param string $selectortype
* @return void
*/
protected function ensure_element_exists($element, $selectortype) {
// Getting the behat selector & locator.
list($selector, $locator) = $this->transform_selector($selectortype, $element);
protected function ensure_element_exists($locator, $selectortype) {
// Exception if it timesout and the element is still there.
$msg = 'The "' . $element . '" element does not exist and should exist';
$msg = "The '{$locator}' element does not exist and should";
$exception = new ExpectationException($msg, $this->getSession());
// Normalise the values in order to perform the search.
$normalised = $this->normalise_selector($selectortype, $locator, $this->getSession()->getPage());
$selector = $normalised['selector'];
$locator = $normalised['locator'];
$container = $normalised['container'];
// It will stop spinning once the find() method returns true.
$this->spin(
function($context, $args) {
// We don't use behat_base::find as it is already spinning.
if ($context->getSession()->getPage()->find($args['selector'], $args['locator'])) {
function() use ($selector, $locator, $container) {
if ($container->find($selector, $locator)) {
return true;
}
return false;
},
array('selector' => $selector, 'locator' => $locator),
[],
self::get_extended_timeout(),
$exception,
true
);
}
/**
* Spins until the element does not exist
*
* @throws ExpectationException
* @param string $element
* @param string $locator
* @param string $selectortype
* @return void
*/
protected function ensure_element_does_not_exist($element, $selectortype) {
// Getting the behat selector & locator.
list($selector, $locator) = $this->transform_selector($selectortype, $element);
protected function ensure_element_does_not_exist($locator, $selectortype) {
// Exception if it timesout and the element is still there.
$msg = 'The "' . $element . '" element exists and should not exist';
$msg = "The '{$locator}' element exists and should not exist";
$exception = new ExpectationException($msg, $this->getSession());
// Normalise the values in order to perform the search.
$normalised = $this->normalise_selector($selectortype, $locator, $this->getSession()->getPage());
$selector = $normalised['selector'];
$locator = $normalised['locator'];
$container = $normalised['container'];
// It will stop spinning once the find() method returns false.
$this->spin(
function($context, $args) {
// We don't use behat_base::find() as we are already spinning.
if (!$context->getSession()->getPage()->find($args['selector'], $args['locator'])) {
return true;
function() use ($selector, $locator, $container) {
if ($container->find($selector, $locator)) {
return false;
}
return false;
return true;
},
array('selector' => $selector, 'locator' => $locator),
// Note: We cannot use $this because the find will then be $this->find(), which leads us to a nested spin().
// We cannot nest spins because the outer spin times out before the inner spin completes.
[],
self::get_extended_timeout(),
$exception,
true
@@ -665,11 +675,11 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
switch ($windowsize) {
case "small":
$width = 640;
$height = 480;
$width = 1024;
$height = 768;
break;
case "medium":
$width = 1024;
$width = 1366;
$height = 768;
break;
case "large":
@@ -979,6 +989,56 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
}
}
/**
* Convert page names to URLs for steps like 'When I am on the "[page name]" page'.
*
* You should override this as appropriate for your plugin. The method
* {@link behat_navigation::resolve_core_page_url()} is a good example.
*
* Your overridden method should document the recognised page types with
* a table like this:
*
* Recognised page names are:
* | Page | Description |
*
* @param string $page name of the page, with the component name removed e.g. 'Admin notification'.
* @return moodle_url the corresponding URL.
* @throws Exception with a meaningful error message if the specified page cannot be found.
*/
protected function resolve_page_url(string $page): moodle_url {
throw new Exception('Component "' . get_class($this) .
'" does not support the generic \'When I am on the "' . $page .
'" page\' navigation step.');
}
/**
* Convert page names to URLs for steps like 'When I am on the "[identifier]" "[page type]" page'.
*
* A typical example might be:
* When I am on the "Test quiz" "mod_quiz > Responses report" page
* which would cause this method in behat_mod_quiz to be called with
* arguments 'Responses report', 'Test quiz'.
*
* You should override this as appropriate for your plugin. The method
* {@link behat_navigation::resolve_core_page_instance_url()} is a good example.
*
* Your overridden method should document the recognised page types with
* a table like this:
*
* Recognised page names are:
* | Type | identifier meaning | Description |
*
* @param string $type identifies which type of page this is, e.g. 'Attempt review'.
* @param string $identifier identifies the particular page, e.g. 'Test quiz > student > Attempt 1'.
* @return moodle_url the corresponding URL.
* @throws Exception with a meaningful error message if the specified page cannot be found.
*/
protected function resolve_page_instance_url(string $type, string $identifier): moodle_url {
throw new Exception('Component "' . get_class($this) .
'" does not support the generic \'When I am on the "' . $identifier .
'" "' . $type . '" page\' navigation step.');
}
/**
* Gets the required timeout in seconds.
*
@@ -1028,4 +1088,58 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
public static function get_extended_timeout() : int {
return self::get_real_timeout(10);
}
/**
* Return a list of the exact named selectors for the component.
*
* Named selectors are what make Behat steps like
* Then I should see "Useful text" in the "General" "fieldset"
* work. Here, "fieldset" is the named selector, and "General" is the locator.
*
* If you override this method in your plugin (e.g. mod_mymod), to define
* new selectors specific to your plugin. For example, if you returned
* new behat_component_named_selector('Thingy',
* [".//some/xpath//img[contains(@alt, %locator%)]/.."])
* then
* Then I should see "Useful text" in the "Whatever" "mod_mymod > Thingy"
* would work.
*
* This method should return a list of {@link behat_component_named_selector} and
* the docs on that class explain how it works.
*
* @return behat_component_named_selector[]
*/
public static function get_exact_named_selectors(): array {
return [];
}
/**
* Return a list of the partial named selectors for the component.
*
* Like the exact named selectors above, but the locator only
* needs to match part of the text. For example, the standard
* "button" is a partial selector, so:
* When I click "Save" "button"
* will activate "Save changes".
*
* @return behat_component_named_selector[]
*/
public static function get_partial_named_selectors(): array {
return [];
}
/**
* Return a list of the Mink named replacements for the component.
*
* Named replacements allow you to define parts of an xpath that can be reused multiple times, or in multiple
* xpaths.
*
* This method should return a list of {@link behat_component_named_replacement} and the docs on that class explain
* how it works.
*
* @return behat_component_named_replacement[]
*/
public static function get_named_replacements(): array {
return [];
}
}
@@ -90,6 +90,9 @@ class behat_context_helper {
* @return behat_base
*/
public static function get($classname) {
if (self::$environment->hasContextClass($classname)) {
return self::$environment->getContext($classname);
}
$suitename = self::$environment->getSuite()->getName();
// If default suite, then get the default theme name.
@@ -114,6 +117,16 @@ class behat_context_helper {
return self::$environment->getContext($classname);
}
/**
* Return whether there is a context of the specified classname.
*
* @param string $classname
* @return bool
*/
public static function has_context(string $classname): bool {
return self::$environment->hasContextClass($classname);
}
/**
* Translates string to XPath literal.
*
+43 -24
View File
@@ -23,10 +23,12 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/named_selector.php');
require_once(__DIR__ . '/exact_named_selector.php');
require_once(__DIR__ . '/partial_named_selector.php');
use Behat\Mink\Exception\ExpectationException as ExpectationException;
use Behat\Mink\Element\Element;
/**
* Moodle selectors manager.
@@ -47,32 +49,13 @@ class behat_selectors {
* @return array Contains the selector and the locator expected by Mink.
*/
public static function get_behat_selector($selectortype, $element, Behat\Mink\Session $session) {
// Note: This function is not deprecated, but not the recommended way of doing things.
$normalised = $session->normalise_selector($selectortype, $element, $session->getPage());
$selector = $normalised['selector'];
$locator = $normalised['locator'];
// CSS and XPath selectors locator is one single argument.
if ($selectortype == 'css_element' || $selectortype == 'xpath_element') {
$selector = str_replace('_element', '', $selectortype);
$locator = $element;
} else {
// Named selectors uses arrays as locators including the type of named selector.
$allowedselectors = self::get_allowed_selectors();
if (!isset($allowedselectors[$selectortype])) {
throw new ExpectationException('The "' . $selectortype . '" selector not registered.', $session);
}
$locator = array($allowedselectors[$selectortype], behat_context_helper::escape($element));
// Get the selector which should be used.
$allowedpartialselectors = behat_partial_named_selector::get_allowed_selectors();
$allowedexactselectors = behat_exact_named_selector::get_allowed_selectors();
if (isset($allowedpartialselectors[$selectortype])) {
$selector = 'named_partial';
} else if (isset($allowedexactselectors[$selectortype])) {
$selector = 'named_exact';
} else {
throw new ExpectationException('The "' . $selectortype . '" selector not registered.', $session);
}
}
return array($selector, $locator);
return [$selector, $locator];
}
/**
@@ -98,4 +81,40 @@ class behat_selectors {
behat_exact_named_selector::get_allowed_text_selectors()
);
}
/**
* Normalise the selector and locator for a named partial.
*
* @param string $selector The selector name
* @param string $locator The value to normalise
* @return array
*/
public static function normalise_named_selector(string $selector, string $locator): array {
return [
$selector,
behat_context_helper::escape($locator),
];
}
/**
* Transform the selector for a field.
*
* @param string $label The label to find
* @param Element $container The container to look within
* @return array The selector, locator, and container to search within
*/
public static function transform_find_for_field(behat_base $context, string $label, Element $container): array {
$hasfieldset = strpos($label, '>');
if (false !== $hasfieldset) {
list($containerlabel, $label) = explode(">", $label, 2);
$container = $context->find_fieldset(trim($containerlabel), $container);
$label = trim($label);
}
return [
'selector' => 'named_partial',
'locator' => self::normalise_named_selector('field', $label),
'container' => $container,
];
}
}
@@ -0,0 +1,99 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* A class for recording the definition of Mink replacements.
*
* @package core
* @category test
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* A class for recording the definition of Mink replacements for use in Mink selectors.
*
* These are comprised of a source string, and a replacement.
*
* During use the source string is converted from the string to be in the format:
*
* %[component]/[string]%
*
* For example:
*
* %mod_forum/title%
*
* Mink replacements are used in xpath translation to translate regularly used items such as title.
* Here is an example from the upstream Mink project:
*
* '%tagTextMatch%' => 'contains(normalize-space(string(.)), %locator%)'
*
* And can be used in an xpath:
*
* .//label[%tagTextMatch%]
*
* This would be expanded to:
*
* .//label[contains(normalize-space(string(.)), %locator%)]
*
* Replacements can also be used in other replacements, as long as that replacement is defined later.
*
* '%linkMatch%' => '(%idMatch% or %tagTextMatch% or %titleMatch% or %relMatch%)'
*
* @package core
* @category test
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_component_named_replacement {
/** @var string */
protected $from;
/** @var string */
protected $to;
/**
* Create the replacement.
*
* @param string $from this is the old selector that should no longer be used.
* For example 'group_message'.
* @param string $to this is the new equivalent that should be used instead.
* For example 'core_message > Message'.
*/
public function __construct(string $from, string $to) {
$this->from = $from;
$this->to = $to;
}
/**
* Get the 'from' part of the replacement, formatted for the component.
*
* @param string $component
* @return string
*/
public function get_from(string $component): string {
return "%{$component}/{$this->from}%";
}
/**
* Get the 'to' part of the replacement.
*
* @return string Target xpath
*/
public function get_to(): string {
return $this->to;
}
}
@@ -0,0 +1,124 @@
<?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/>.
/**
* Class representing a named selector that can be used in Behat tests.
*
* @package core
* @category test
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Class representing a named selector that can be used in Behat tests.
*
* Named selectors are what make Behat steps like
* Then I should see "Useful text" in the "General" "fieldset"
* Here, "fieldset" is the named selector, and "General" is the locator.
*
* Selectors can either be exact, in which case the locator needs to
* match exactly, or can be partial, for example the way
* When I click "Save" "button"
* will trigger a "Save changes" button.
*
* Instances of this class get returned by the get_exact_named_selectors()
* and get_partial_named_selectors() methods in classes like behat_mod_mymod.
* The code that makes the magic work is in the trait behat_named_selector
* used by both behat_exact_named_selector and behat_partial_named_selector.
*
* @package core
* @category test
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_component_named_selector {
/** @var string */
protected $alias;
/** @var array List of xpaths */
protected $xpaths;
/** @var string */
protected $istextselector;
/**
* Create the selector definition.
*
* As an example, if you define
* new behat_component_named_selector('Message',
* [".//*[@data-conversation-id]//img[contains(@alt, %locator%)]/.."])
* in get_partial_named_selectors in behat_message in
* message/tests/behat/behat_message.php, then steps like
* When "Group 1" "core_message > Message" should exist
* will work.
*
* Text selectors are things that contain other things (e.g. some particular text), e.g.
* Then I can see "Some text" in the "Whatever" "text_selector"
* whereas non-text selectors are atomic things, like
* When I click the "Whatever" "widget".
*
* @param string $alias The 'friendly' name of the thing. This will be prefixed with the component name.
* For example, if the mod_mymod plugin, says 'Thingy', then "mod_mymod > Thingy" becomes a selector.
* @param array $xpaths A list of xpaths one or more XPaths that the selector gets transformed into.
* @param bool $istextselector Whether this selector can also be used as a text selector.
*/
public function __construct(string $alias, array $xpaths, bool $istextselector = true) {
$this->alias = $alias;
$this->xpaths = $xpaths;
$this->istextselector = $istextselector;
}
/**
* Whether this is a text selector.
*
* @return bool
*/
public function is_text_selector(): bool {
return $this->istextselector;
}
/**
* Get the name of the selector.
* This is a back-end feature and contains a namespaced md5 of the human-readable name.
*
* @param string $component
* @return string
*/
public function get_name(string $component): string {
return implode('_', [$component, md5($this->alias)]);
}
/**
* Get the alias of the selector.
* This is the human-readable name that you would typically interact with.
*
* @param string $component
* @return string
*/
public function get_alias(string $component): string {
return implode(" > ", [$component, $this->alias]);;
}
/**
* Get the list of combined xpaths.
*
* @return string The list of xpaths combined with the xpath | (OR) operator
*/
public function get_combined_xpath(): string {
return implode(' | ', $this->xpaths);
}
}
@@ -32,6 +32,9 @@
*/
class behat_exact_named_selector extends \Behat\Mink\Selector\ExactNamedSelector {
// Use the named selector trait.
use behat_named_selector;
/**
* Creates selector instance.
*/
@@ -63,6 +66,9 @@ class behat_exact_named_selector extends \Behat\Mink\Selector\ExactNamedSelector
'text_exact' => 'text',
);
/** @var List of deprecated selectors */
protected static $deprecatedselectors = [];
/**
* Allowed selectors getter.
*
+113
View File
@@ -0,0 +1,113 @@
<?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/>.
/**
* Moodle-specific common functions for named selectors.
*
* @package core
* @category test
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Common functions for named selectors.
*
* This has to be a trait, because we need this in both the classes
* behat_exact_named_selector and behat_partial_named_selector, and
* those classes have to be subclasses of \Behat\Mink\Selector\ExactNamedSelector
* and \Behat\Mink\Selector\PartialNamedSelector. This trait is a way achieve
* that without duplciated code.
*
* @package core
* @category test
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
trait behat_named_selector {
/**
* Registers new XPath selector with specified name.
*
* @param string $component
* @param behat_component_named_selector $selector
*/
public function register_component_selector(string $component, behat_component_named_selector $selector) {
$alias = $selector->get_alias($component);
$name = $selector->get_name($component);
static::$allowedselectors[$alias] = $name;
if ($selector->is_text_selector()) {
static::$allowedtextselectors[$alias] = $name;
}
// We must use Reflection here. The replacements property is private and cannot be accessed otherwise.
// This is due to an API limitation in Mink.
$rc = new \ReflectionClass(\Behat\Mink\Selector\NamedSelector::class);
$r = $rc->getProperty('replacements');
$r->setAccessible(true);
$replacements = $r->getValue($this);
$selectorxpath = strtr($selector->get_combined_xpath(), $replacements);
parent::registerNamedXpath($name, $selectorxpath);
}
/**
* Registers new XPath selector with specified name.
*
* @param string $component
* @param behat_component_named_replacement $replacement
*/
public function register_replacement(string $component, behat_component_named_replacement $replacement) {
// We must use Reflection here. The replacements property is private and cannot be accessed otherwise.
// This is due to an API limitation in Mink.
$rc = new \ReflectionClass(\Behat\Mink\Selector\NamedSelector::class);
$r = $rc->getProperty('replacements');
$r->setAccessible(true);
$existing = $r->getValue($this);
$from = $replacement->get_from($component);
if (isset($existing[$from])) {
throw new \coding_exception("A named replacement already exists in the partial named selector for '{$from}'. " .
"Replacement names must be unique, and should be namespaced to the component");
}
$translatedto = strtr($replacement->get_to(), $existing);
$this->registerReplacement($from, $translatedto);
}
/**
* Check whether the specified selector has been deprecated and marked for replacement.
*
* @param string $selector
* @return bool
*/
public static function is_deprecated_selector(string $selector): bool {
return array_key_exists($selector, static::$deprecatedselectors);
}
/**
* Fetch the replacement name of a deprecated selector if any exists.
*
* @param string $selector
* @return string
*/
public static function get_deprecated_replacement(string $selector) {
return static::$deprecatedselectors[$selector];
}
}
@@ -33,6 +33,9 @@
*/
class behat_partial_named_selector extends \Behat\Mink\Selector\PartialNamedSelector {
// Use the named selector trait.
use behat_named_selector;
/**
* Creates selector instance.
*/
@@ -74,6 +77,7 @@ class behat_partial_named_selector extends \Behat\Mink\Selector\PartialNamedSele
'table_row' => 'table_row',
'xpath_element' => 'xpath_element',
'form_row' => 'form_row',
'autocomplete' => 'autocomplete',
);
/**
@@ -111,6 +115,7 @@ class behat_partial_named_selector extends \Behat\Mink\Selector\PartialNamedSele
'form_row' => 'form_row',
'autocomplete_selection' => 'autocomplete_selection',
'autocomplete_suggestions' => 'autocomplete_suggestions',
'autocomplete' => 'autocomplete',
);
/**
@@ -198,6 +203,9 @@ XPATH
XPATH
, 'autocomplete_suggestions' => <<<XPATH
.//ul[contains(concat(' ', normalize-space(@class), ' '), concat(' ', 'form-autocomplete-suggestions', ' '))]/li[@role='option'][contains(normalize-space(.), %locator%)]
XPATH
, 'autocomplete' => <<<XPATH
.//descendant::input[@id = //label[contains(normalize-space(string(.)), %locator%)]/@for]/ancestor::*[@data-fieldtype = 'autocomplete']
XPATH
);
@@ -243,6 +251,10 @@ XPATH
],
];
/** @var List of deprecated selectors */
protected static $deprecatedselectors = [
];
/**
* Allowed selectors getter.
*
+37
View File
@@ -31,6 +31,9 @@ require_once(__DIR__ . '/behat_command.php');
require_once(__DIR__ . '/behat_config_manager.php');
require_once(__DIR__ . '/../../filelib.php');
require_once(__DIR__ . '/../../clilib.php');
use Behat\Mink\Session;
/**
* Init/reset utilities for Behat database and dataroot
@@ -373,4 +376,38 @@ class behat_util extends testing_util {
// $CFG values from the old run. @see set_config.
initialise_cfg();
}
/**
* Pause execution immediately.
*
* @param Session $session
* @param string $message The message to show when pausing.
* This will be passed through cli_ansi_format so appropriate ANSI formatting and features are available.
*/
public static function pause(Session $session, string $message) {
$posixexists = function_exists('posix_isatty');
// Make sure this step is only used with interactive terminal (if detected).
if ($posixexists && !@posix_isatty(STDOUT)) {
throw new ExpectationException('Break point should only be used with interactive terminal.', $session);
}
// Save the cursor position, ring the bell, and add a new line.
fwrite(STDOUT, cli_ansi_format("<cursor:save><bell><newline>"));
// Output the formatted message and reset colour back to normal.
$formattedmessage = cli_ansi_format("{$message}<colour:normal>");
fwrite(STDOUT, $formattedmessage);
// Wait for input.
fread(STDIN, 1024);
// Move the cursor back up to the previous position, then restore the original position stored earlier, and move
// it back down again.
fwrite(STDOUT, cli_ansi_format("<cursor:up><cursor:up><cursor:restore><cursor:down><cursor:down>"));
// Add any extra lines back if the provided message was spread over multiple lines.
$linecount = count(explode("\n", $formattedmessage));
fwrite(STDOUT, str_repeat(cli_ansi_format("<cursor:down>"), $linecount - 1));
}
}
@@ -45,7 +45,7 @@ class behat_form_autocomplete extends behat_form_text {
*/
public function set_value($value) {
if (!$this->running_javascript()) {
throw new coding_exception('Setting the valid of an autocomplete field requires javascript.');
throw new coding_exception('Setting the value of an autocomplete field requires javascript.');
}
// Set the value of the autocomplete's input.
+77
View File
@@ -0,0 +1,77 @@
<?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/>.
/**
* Custom Moodle engine for mustache.
*
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
/**
* Custom Moodle engine for mustache.
*
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mustache_engine extends \Mustache_Engine {
/**
* @var mustache_helper_collection
*/
private $helpers;
/**
* @var string[] Names of helpers that aren't allowed to be called within other helpers.
*/
private $blacklistednestedhelpers = [];
/**
* Mustache engine constructor.
*
* This provides an additional option to the parent \Mustache_Engine implementation:
* $options = [
* // A list of helpers (by name) to prevent from executing within the rendering
* // of other helpers.
* 'blacklistednestedhelpers' => ['js']
* ];
* @param array $options [description]
*/
public function __construct(array $options = []) {
if (isset($options['blacklistednestedhelpers'])) {
$this->blacklistednestedhelpers = $options['blacklistednestedhelpers'];
}
parent::__construct($options);
}
/**
* Get the current set of Mustache helpers.
*
* @see Mustache_Engine::setHelpers
*
* @return \Mustache_HelperCollection
*/
public function getHelpers()
{
if (!isset($this->helpers)) {
$this->helpers = new mustache_helper_collection(null, $this->blacklistednestedhelpers);
}
return $this->helpers;
}
}
@@ -0,0 +1,176 @@
<?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/>.
/**
* Custom Moodle helper collection for mustache.
*
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
/**
* Custom Moodle helper collection for mustache.
*
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mustache_helper_collection extends \Mustache_HelperCollection {
/**
* @var string[] Names of helpers that aren't allowed to be called within other helpers.
*/
private $blacklistednestedhelpers = [];
/**
* Helper Collection constructor.
*
* Optionally accepts an array (or Traversable) of `$name => $helper` pairs.
*
* @throws \Mustache_Exception_InvalidArgumentException if the $helpers argument isn't an array or Traversable
*
* @param array|\Traversable $helpers (default: null)
* @param string[] $blacklistednestedhelpers Names of helpers that aren't allowed to be called within other helpers.
*/
public function __construct($helpers = null, array $blacklistednestedhelpers = []) {
$this->blacklistednestedhelpers = $blacklistednestedhelpers;
parent::__construct($helpers);
}
/**
* Add a helper to this collection.
*
* This function has overridden the parent implementation to provide blacklist
* functionality for certain helpers to prevent them being called from within
* other helpers. This is because the JavaScript helper can be used in a
* security exploit if it can be nested.
*
* The function will wrap callable helpers in an anonymous function that strips
* out the blacklisted helpers from the source string before giving it to the
* helper function. This prevents the blacklisted helper functions from being
* called by nested render functions from within other helpers.
*
* @see \Mustache_HelperCollection::add()
* @param string $name
* @param mixed $helper
*/
public function add($name, $helper)
{
$blacklist = $this->blacklistednestedhelpers;
if (is_callable($helper) && !empty($blacklist)) {
$helper = function($source, \Mustache_LambdaHelper $lambdahelper) use ($helper, $blacklist) {
// Temporarily override the blacklisted helpers to return nothing
// so that they can't be executed from within other helpers.
$disabledhelpers = $this->disable_helpers($blacklist);
// Call the original function with the modified sources.
$result = call_user_func($helper, $source, $lambdahelper);
// Restore the original blacklisted helper implementations now
// that this helper has finished executing so that the rest of
// the rendering process continues to work correctly.
$this->restore_helpers($disabledhelpers);
// Lastly parse the returned string to strip out any unwanted helper
// tags that were added through variable substitution (or other means).
// This is done because a secondary render is called on the result
// of a helper function if it still includes mustache tags. See
// the section function of Mustache_Compiler for details.
return $this->strip_blacklisted_helpers($blacklist, $result);
};
}
parent::add($name, $helper);
}
/**
* Disable a list of helpers (by name) by changing their implementation to
* simply return an empty string.
*
* @param string[] $names List of helper names to disable
* @return \Closure[] The original helper functions indexed by name
*/
private function disable_helpers($names) {
$disabledhelpers = [];
foreach ($names as $name) {
if ($this->has($name)) {
$function = $this->get($name);
// Null out the helper. Must call parent::add here to avoid
// a recursion problem.
parent::add($name, function() {
return '';
});
$disabledhelpers[$name] = $function;
}
}
return $disabledhelpers;
}
/**
* Restore the original helper implementations. Typically used after disabling
* a helper.
*
* @param \Closure[] $helpers The helper functions indexed by name
*/
private function restore_helpers($helpers) {
foreach ($helpers as $name => $function) {
// Restore the helper functions. Must call parent::add here to avoid
// a recursion problem.
parent::add($name, $function);
}
}
/**
* Parse the given string and remove any reference to blacklisted helpers.
*
* E.g.
* $blacklist = ['js'];
* $string = "core, move, {{#js}} some nasty JS hack {{/js}}"
* result: "core, move, {{}}"
*
* @param string[] $blacklist List of helper names to strip
* @param string $string String to parse
* @return string Parsed string
*/
public function strip_blacklisted_helpers($blacklist, $string) {
$starttoken = \Mustache_Tokenizer::T_SECTION;
$endtoken = \Mustache_Tokenizer::T_END_SECTION;
if ($endtoken == '/') {
$endtoken = '\/';
}
$regexes = array_map(function($name) use ($starttoken, $endtoken) {
// We only strip out the name of the helper (excluding delimiters)
// the user is able to change the delimeters on a per template
// basis so they may not be curly braces.
return '/\s*' . $starttoken . '\s*'. $name . '\W+.*' . $endtoken . '\s*' . $name . '\s*/';
}, $blacklist);
// This will strip out unwanted helpers from the $source string
// before providing it to the original helper function.
// E.g.
// Before:
// "core, move, {{#js}} some nasty JS hack {{/js}}"
// After:
// "core, move, {{}}"
return preg_replace_callback($regexes, function() {
return '';
}, $string);
}
}
+56
View File
@@ -227,3 +227,59 @@ function cli_logo($padding=2, $return=false) {
cli_write($logo);
}
}
/**
* Substitute cursor, colour, and bell placeholders in a CLI output to ANSI escape characters when ANSI is available.
*
* @param string $message
* @return string
*/
function cli_ansi_format(string $message): string {
global $CFG;
$replacements = [
"<newline>" => "\n",
"<bell>" => "\007",
// Cursor movement: https://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x361.html.
"<cursor:save>" => "\033[s",
"<cursor:restore>" => "\033[u",
"<cursor:up>" => "\033[1A",
"<cursor:down>" => "\033[1B",
"<cursor:forward>" => "\033[1C",
"<cursor:back>" => "\033[1D",
];
$colours = [
'normal' => '0;0',
'black' => '0;30',
'darkGray' => '1;30',
'blue' => '0;34',
'lightBlue' => '1;34',
'green' => '0;32',
'lightGreen' => '1;32',
'cyan' => '0;36',
'lightCyan' => '1;36',
'red' => '0;31',
'lightRed' => '1;31',
'purple' => '0;35',
'lightPurple' => '1;35',
'brown' => '0;33',
'yellow' => '1;33',
'lightYellow' => '0;93',
'lightGray' => '0;37',
'white' => '1;37',
];
foreach ($colours as $colour => $code) {
$replacements["<colour:{$colour}>"] = "\033[{$code}m";
}
// Windows don't support ANSI code by default, but does if ANSICON is available.
$isansicon = getenv('ANSICON');
if (($CFG->ostype === 'WINDOWS') && empty($isansicon)) {
return str_replace(array_keys($replacements), '', $message);
}
return str_replace(array_keys($replacements), array_values($replacements), $message);
}
@@ -22,7 +22,7 @@ Feature: Tinymce with enable/disable function.
@javascript
Scenario: Check disable Tinymce editor.
When I click on "option[value=1]" "css_element" in the "select#id_mycontrol" "css_element"
When I set the field "My control" to "Disable"
Then the "class" attribute of "a#id_myeditor_pdw_toggle" "css_element" should contain "mceButtonDisabled"
And the "class" attribute of "table#id_myeditor_formatselect" "css_element" should contain "mceListBoxDisabled"
And the "class" attribute of "a#id_myeditor_bold" "css_element" should contain "mceButtonDisabled"
@@ -39,7 +39,7 @@ Feature: Tinymce with enable/disable function.
@javascript
Scenario: Check enable Tinymce editor.
When I click on "option[value=0]" "css_element" in the "select#id_mycontrol" "css_element"
When I set the field "My control" to "Enable"
Then the "class" attribute of "a#id_myeditor_pdw_toggle" "css_element" should contain "mceButtonEnabled"
And the "class" attribute of "table#id_myeditor_formatselect" "css_element" should contain "mceListBoxEnabled"
And the "class" attribute of "a#id_myeditor_bold" "css_element" should contain "mceButtonEnabled"
+11
View File
@@ -1628,3 +1628,14 @@ function restrict_php_version_72(&$result) {
function restrict_php_version_73(&$result) {
return restrict_php_version($result, '7.3');
}
/**
* Check if the current PHP version is greater than or equal to
* PHP version 7.4.
*
* @param object $result an environment_results instance
* @return bool result of version check
*/
function restrict_php_version_74(&$result) {
return restrict_php_version($result, '7.4');
}
+1 -1
View File
@@ -38,7 +38,7 @@ class processor implements \core_analytics\classifier, \core_analytics\regresso
/**
* The required version of the python package that performs all calculations.
*/
const REQUIRED_PIP_PACKAGE_VERSION = '0.0.5';
const REQUIRED_PIP_PACKAGE_VERSION = '1.0.1';
/**
* The path to the Python bin.

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