Compare commits

..

295 Commits

Author SHA1 Message Date
Eloy Lafuente (stronk7) 237f4ccada Moodle release 3.10.4 2021-05-08 16:40:42 +02:00
Eloy Lafuente (stronk7) f674b77d34 weekly release 3.10.3+ 2021-05-07 16:54:50 +02:00
Eloy Lafuente (stronk7) 9215f9d294 NOBUG: Fixed SVG browser compatibility 2021-05-07 16:54:50 +02:00
Eloy Lafuente (stronk7) b72be5f142 Merge branch 'install_310_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_310_STABLE 2021-05-07 16:54:49 +02:00
abgreeve 5ed11926a6 Merge branch 'MDL-68747-310' of https://github.com/dcai/moodle into MOODLE_310_STABLE 2021-05-07 11:45:45 +08:00
Mathew May e4274d80a4 MDL-70622 mod_lti: Tighten redirect param typing 2021-05-07 11:45:45 +08:00
Adam Olley 87883bb43c MDL-70622 mod_lti: Prevent xss on lti 1.3 authentication script
Without this, people can craft URLs that other users might use not realising
what they do - and as a XSS vulnerability, it could do any number of things the
clicking-user has access to do on the site.

Change-Id: I82adc71e8706d8929011b4b24523d5b62b8ccea1
2021-05-07 11:45:34 +08:00
Jun Pataleta cd83b3732e Merge branch 'MDL-71395-310-2' of git://github.com/marinaglancy/moodle into MOODLE_310_STABLE 2021-05-07 10:30:39 +08:00
abgreeve c6ec73b12e Merge branch 'MDL-70181_310' of https://github.com/stronk7/moodle into MOODLE_310_STABLE 2021-05-07 10:22:28 +08:00
Víctor Déniz 3c23c78500 Merge branch 'MDL-71303-310' of https://github.com/StudiUM/moodle into MOODLE_310_STABLE 2021-05-06 23:34:33 +01:00
Eloy Lafuente (stronk7) 1b22f24666 Merge branch 'MDL-70049-310_count_submissions' of https://github.com/agrowe/moodle into MOODLE_310_STABLE 2021-05-06 23:27:20 +02:00
Eloy Lafuente (stronk7) 0656fb0d19 Merge branch 'MDL-70772-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-05-06 23:20:13 +02:00
Eloy Lafuente (stronk7) d13532e364 Merge branch 'MDL-71313_310' of https://github.com/timhunt/moodle into MOODLE_310_STABLE 2021-05-06 23:15:06 +02:00
Eloy Lafuente (stronk7) 0e077d9b68 Merge branch 'MDL-71343-m310' of https://github.com/rjnl/moodle into MOODLE_310_STABLE 2021-05-06 23:07:46 +02:00
Sara Arjona 35fbf2b4af MDL-70110 mod_assign: make phpunit test DB order agnostic 2021-05-06 19:38:45 +02:00
Sara Arjona 3938b91363 Merge branch 'MDL-71491-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-05-06 18:42:32 +02:00
Marina Glancy d87d0973f5 MDL-71395 core: fix bug in google lib wrapper
because of changes in PHP8 the '' == 0 is no longer true and the error in the google api wrapper was discovered
2021-05-06 18:39:10 +02:00
Sara Arjona c5298a3d9a Merge branch 'MDL-71420_310' of https://github.com/stronk7/moodle into MOODLE_310_STABLE 2021-05-06 17:58:29 +02:00
Marina Glancy 83dda7ae04 MDL-71395 block_tag_youtube: sometimes exception may have empty message 2021-05-06 17:49:14 +02:00
Víctor Déniz 2561b61d81 Merge branch 'MDL-71402-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-05-06 16:47:02 +01:00
Paul Holden 119c201a10 MDL-71491 repository_flickr: return more file data.
See also 643b0cca.
2021-05-06 16:36:35 +01:00
Sara Arjona 3cdb182a70 Merge branch 'MDL-71268-310' of git://github.com/lucaboesch/moodle into MOODLE_310_STABLE 2021-05-06 17:11:31 +02:00
Paul Holden 5e6e118a2e MDL-71402 availability: fix group restriction button in mod form.
* The button to "Add group/grouping access restriction" under
   common module settings should only be present if corresponding
   availability plugins are enabled; and
 * Prevent the same button behaving as a submit button, which
   intercepted the user hitting return in the form and added a
   restriction without deliberate action taken by the user.
2021-05-06 14:20:32 +01:00
Sara Arjona cd0b5a63fa Merge branch 'MDL-70110-310' of https://github.com/septatrix/moodle into MOODLE_310_STABLE 2021-05-06 13:16:13 +02:00
Víctor Déniz afc3b1dd3d Merge branch 'MDL-71434-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-05-06 11:15:18 +01:00
Sara Arjona d1983c3ce1 Merge branch 'MDL-71390-310' of git://github.com/marinaglancy/moodle into MOODLE_310_STABLE 2021-05-06 08:07:40 +02:00
Adrian Greeve 0094d8e66e Merge branch 'MDL-70786-310' of git://github.com/lucaboesch/moodle into MOODLE_310_STABLE 2021-05-06 10:05:02 +08:00
AMOS bot b34246ca19 Automatically generated installer lang files 2021-05-06 00:07:32 +00:00
Juan Leyva 5e29bd67e6 MDL-71513 mobile: Fix last access visibility 2021-05-05 22:49:07 +01:00
Eloy Lafuente (stronk7) ad7c296dc9 MDL-71420 environment: Lower sodium requirement to recommended 2021-05-05 23:06:18 +02:00
Sara Arjona 9aad1581bc Merge branch 'MDL-71517-310' of https://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-05-05 12:17:43 +02:00
Sara Arjona 56882a76d1 Merge branch 'MDL-71187-310' of git://github.com/lucaboesch/moodle into MOODLE_310_STABLE 2021-05-05 12:14:13 +02:00
Víctor Déniz 14c8b8bd56 Merge branch 'MDL-71460-310' of git://github.com/peterRd/moodle into MOODLE_310_STABLE 2021-05-05 10:43:08 +01:00
Luca Bösch e66bf8a5c1 MDL-71187 quizaccess_seb: Change "Launch Safe Exam Browser" to a href.
In order to allow for correct seb:// or sebs:// calls without browser
warnings of insecure links, it is not possible to send a get request
with an attached cmid parameter to the unknown seb:// or sebs://
URL via a form button.
We've got to use a <a href> link outside a form to circumvent
browsers warning of an insecure link and call Safe Exam Browser
correctly.
2021-05-05 10:54:34 +02:00
Leon Stringer 01aee79df0 MDL-70181 database: Quote database object names
Table names and database name now enclosed in backticks.
admin/cli/mysql_collation.php failed if $CFG->prefix was blank with
MySQL 8.0 because table 'groups' conflicted with a new reserved word.

Note that this quotes both mysql_collation.php and mysql_compressed_rows.php
but only the former was mal-functioning.

The case of mysql_compressed_rows.php is a little bit special because
not all tables are processed, only those having big rows. And the groups
table is not one of them. In any case, better add the quotes there for
any future case.

Also, when testing this patch https://tracker.moodle.org/browse/MDL-71512
was discovered and will need to be fixed to make core 100% compliant
with MySQL 8.0 and MariaDB 10.6 and up.
2021-05-05 10:16:54 +02:00
Peter Dias e3b4f6d3fd MDL-71460 hub: Use checkboxes instead of dropdowns
Change the yes/no dropdowns to a checkbox. Defaults to unchecked.
2021-05-05 10:27:28 +08:00
Víctor Déniz 73d36ebafa Merge branch 'MDL-70864-310-zip_packer_windows' of git://github.com/mudrd8mz/moodle into MOODLE_310_STABLE 2021-05-05 00:03:40 +01:00
Eloy Lafuente (stronk7) d73d9dd99f Merge branch 'MDL-71425-310-enfix' of git://github.com/mudrd8mz/moodle into MOODLE_310_STABLE 2021-05-04 23:39:50 +02:00
Paul Holden 301f78c282 MDL-71359 mod_forum: empty forums shouldn't export anything. 2021-05-04 17:22:47 +02:00
Sara Arjona 373c263482 MDL-71408 h5plib_v124: Add required changes after lib upgrade
- The mbstring extension is required since Moodle 3.9 onwards so
the upgrading steps have been removed because they are not
required any more.
- The pull-request for fixing some minor PHP7.4 problems has been
integrated so this patch is not required.
2021-05-04 17:22:47 +02:00
Sara Arjona ac7cd6c603 MDL-71408 h5plib_v124: Upgrade H5P core library to 1.24.02 2021-05-04 17:22:47 +02:00
Huong Nguyen 62b4ebab51 MDL-70720 Quiz: Improve capability checks when fetching users' best grades 2021-05-04 17:22:47 +02:00
Shamim Rezaie abbda5d025 MDL-69028 repository: Put a rate limit on draft file uploads 2021-05-04 17:22:47 +02:00
Paul Holden 0debb3cac0 MDL-71130 quiz_grading: escape user idnumber on display. 2021-05-04 17:22:47 +02:00
David Mudrák 9f4404e80d MDL-70804 mnet: Use proper DML method to get records from the table
This improves the code and avoids the risk of SQL injection through the
malicious XML-RPC request from the MNet peer.
2021-05-04 17:22:47 +02:00
Sara Arjona d9af87d70a Merge branch 'MDL-71274-310' of git://github.com/aanabit/moodle into MOODLE_310_STABLE 2021-05-04 17:10:48 +02:00
David Mudrák 804f1f5ac7 MDL-71425 lang: Fix reworded strings in tests 2021-05-04 16:56:09 +02:00
Helen Foster 23f0d09ec7 MDL-71425 lang: Import fixed English strings (en_fix) 2021-05-04 16:48:21 +02:00
Paul Holden 641b678d4b MDL-71517 mod_data: remove invalid form custom data access.
On PHP7.4, trying to access array offsets of NULL triggers a notice.
2021-05-04 15:35:46 +01:00
Luca Bösch 5183181f94 MDL-70786 report_loglive: respect fullname setting. 2021-05-04 14:19:17 +02:00
Luca Bösch b74abdbea1 MDL-70786 report_log: respect fullname setting. 2021-05-04 14:19:17 +02:00
Luca Bösch a11c2cb391 MDL-70786 report_completion: respect fullname setting. 2021-05-04 14:19:17 +02:00
Luca Bösch 204748856a MDL-70786 report_competency: respect fullname setting. 2021-05-04 14:19:17 +02:00
Luca Bösch 8962bd76c3 MDL-70786 report_progress: respect fullname setting. 2021-05-04 14:19:17 +02:00
Luca Bösch 8a07e2f53d MDL-71268 graphics: Use new Moodle logo for RSS feeds by Moodle 2021-05-04 14:15:29 +02:00
Sara Arjona 3de329785d weekly release 3.10.3+ 2021-05-04 13:27:55 +02:00
Sara Arjona 63a4a88dd1 Merge branch 'install_310_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_310_STABLE 2021-05-04 13:27:55 +02:00
Adrian Greeve 4f46b8449b Merge branch 'MDL-66673-MOODLE_310_STABLE' of https://github.com/ScottVerbeek/moodle into MOODLE_310_STABLE 2021-05-04 15:47:33 +08:00
AMOS bot 72c300facf Automatically generated installer lang files 2021-05-04 00:07:31 +00:00
Eloy Lafuente (stronk7) c96074aafd Merge branch 'MDL-71463-310' of https://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-05-03 19:01:01 +02:00
David Mudrák 7f507d94d9 MDL-70864 files: Fix zip_packer extracting files with trailing dots
File and folder names cannot end with dots on Windows. So replace the
trailing dots with underscore consistently with how some other zip tools
(such as 7-zip) handle this case.
2021-05-03 18:14:17 +02:00
Marina Glancy 6211bc0be5 MDL-71390 core: raise max_input_vars requirement for Moodle 3.11 2021-05-03 13:25:36 +02:00
Adrian Greeve 6dc0b43072 Merge branch 'MDL-71440-310' of git://github.com/mihailges/moodle into MOODLE_310_STABLE 2021-05-03 16:04:36 +08:00
Mihail Geshoski 502076cde6 MDL-71440 mod_assign: New capability for viewing the submission summary
Creates a new capability mod/assign:viewownsubmissionsummary which will
be required in order to display the submission summary to the user in
the assignment page.
2021-05-03 15:32:21 +08:00
Jake Dallimore 79368aaa6a Merge branch 'MDL-52724-310' of git://github.com/merrill-oakland/moodle into MOODLE_310_STABLE 2021-05-03 13:25:36 +08:00
Víctor Déniz 5431063238 Merge branch 'MDL-69956-310' of https://github.com/dcai/moodle into MOODLE_310_STABLE 2021-05-01 13:43:19 +01:00
Víctor Déniz 36764c36c0 weekly release 3.10.3+ 2021-04-30 12:54:29 +01:00
Rajneel Totaram f902e75bae MDL-71343 core_h5p: Convert all line-endings to UNIX format 2021-04-30 23:45:21 +12:00
Andrew Nicols 8fd5eb7be5 MDL-71343 core_h5p: Add unit test for helper::parse_js_array
Signed-off-by: Rajneel Totaram <rjnlfj@yahoo.com>
2021-04-30 23:40:49 +12:00
Dongsheng Cai 8399b45fd0 MDL-69956 forum: throw exception when advanced grading is not ready 2021-04-30 14:59:36 +10:00
Adrian Greeve dc783d3d5e Merge branch 'MDL-71481-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-30 11:19:46 +08:00
Jun Pataleta aaaf3abc4d Merge branch 'MDL-70947-310' of git://github.com/HuongNV13/moodle into MOODLE_310_STABLE 2021-04-30 10:34:24 +08:00
Sara Arjona 9a36fe6886 Merge branch 'MDL-60331-310_take3' of git://github.com/lucaboesch/moodle into MOODLE_310_STABLE 2021-04-29 17:49:55 +02:00
Paul Holden 1b8ccba1f2 MDL-71463 dml: escape square brackets for SQL Server LIKE operator. 2021-04-29 13:49:28 +01:00
Amaia Anabitarte 723ffea626 MDL-71274 analytics: Don't generate insight for hidden courses
Hidden courses can be used for training
but we do not want to generate insights for them
because students do not have access to hidden courses.
This was fixed in MDL-66806 for "Students at risk" model.
Fixed for "Students who have not accessed the course recently" in this issue.
2021-04-29 11:13:02 +02:00
Paul Holden 0c57ffc2b5 MDL-71481 repository_flickr_public: return more file data.
A HTTP HEAD request is required to get the image filesize.
The remaining data can be obtained from existing calls to
the Flickr API.
2021-04-29 09:49:16 +01:00
Huong Nguyen f3ae9f9d45 MDL-70947 File upload: Upload protection
Interacts badly with double-submit protection, and not all uploads protected
2021-04-29 15:32:12 +07:00
Sara Arjona 3bffbfabb5 Merge branch 'MDL-71016-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-29 09:21:52 +02:00
Jun Pataleta 3afeaed3ff Merge branch 'MDL-70863-310' of git://github.com/jleyva/moodle into MOODLE_310_STABLE 2021-04-29 15:05:15 +08:00
Andrew Nicols c05862dc9a Merge branch 'MDL-56609_310' of https://github.com/stronk7/moodle into MOODLE_310_STABLE 2021-04-29 09:41:59 +08:00
Víctor Déniz 9cd78a8b4f Merge branch 'MDL-71200-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-28 20:53:49 +01:00
Paul Holden 8f630db213 MDL-71016 gradeexport_xml: ensure user/grade idnumbers are encoded. 2021-04-28 19:03:56 +01:00
Luca Bösch 689d10b02b MDL-60331 questions: prevent double escaping question categories. 2021-04-28 17:32:27 +02:00
Sara Arjona 71f40d0018 Merge branch 'MDL-70979-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-28 13:06:18 +02:00
Sara Arjona b9f9758f5e Merge branch 'MDL-71127-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-28 12:58:28 +02:00
Sara Arjona 9e19f3c1f5 Merge branch 'MDL-70763-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-28 12:36:36 +02:00
Septatrix 8f0daf43b7 MDL-70110 assign: add group submission attemptnumber test 2021-04-28 11:50:24 +02:00
Sara Arjona fa72038987 Merge branch 'MDL-71244-310' of git://github.com/lucaboesch/moodle into MOODLE_310_STABLE 2021-04-28 09:17:39 +02:00
Andrew Nicols 6679c41c89 Merge branch 'MDL-71168-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-28 10:50:19 +08:00
Andrew Nicols 1ed97439dd MDL-70909 mod_h5pactivity: Whitespace fix 2021-04-28 10:33:29 +08:00
Andrew Nicols 17625bd5b2 Merge branch 'MDL-70909-310' of git://github.com/ferranrecio/moodle into MOODLE_310_STABLE 2021-04-28 10:26:41 +08:00
Andrew Nicols 475ea093b4 Merge branch 'MDL-71386-310' of git://github.com/NoelDeMartin/moodle into MOODLE_310_STABLE 2021-04-28 10:15:54 +08:00
Eloy Lafuente (stronk7) 40d45ede41 MDL-56609 phpunit: Fix bogus expectation on DST changes day
Current test works ok every day of the year but the day of
DST begin changes (last Sunday of March currently).

That day, 01:00 Europe/London simply doesn't exist, because
the time is moved forward by 60 minutes.

Hence the expectation for the next cron execution is not 1 "normal"
day (24h) but 1 less hour.

Note this only happens for the current test on DST begins, the
opposite change (DST ends), happening the last Sunday of October
is not affected, because, at all effects, 01:00 is a perfectly normal
and existing hour.
2021-04-27 18:31:01 +02:00
Luca Bösch 6651b3f940 MDL-71244 enrol_manual: Add bootstrap classes to buttons 2021-04-27 17:26:24 +02:00
Ferran Recio e1ac84a458 MDL-70909 mod_h5pactivity: fix reports on freeze context 2021-04-27 15:35:39 +02:00
Sara Arjona ff833a08de weekly release 3.10.3+ 2021-04-27 15:14:58 +02:00
Adrian Greeve d426ee3ccc Merge branch 'MDL-71329-310' of https://github.com/Chocolate-lightning/moodle into MOODLE_310_STABLE 2021-04-27 09:35:52 +08:00
Víctor Déniz 813338182c Merge branch 'MDL-65687-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-27 01:13:18 +01:00
Víctor Déniz 5e7f937481 Merge branch 'MDL-71003-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-27 00:37:00 +01:00
Sara Arjona 1bbfbd7444 Merge branch 'MDL-70917-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-26 15:18:12 +02:00
Sara Arjona 0f308b0df1 Merge branch 'MDL-71416-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-26 12:35:50 +02:00
Eloy Lafuente (stronk7) fea40706b1 Merge branch 'MDL-70446-m310' of https://github.com/sammarshallou/moodle into MOODLE_310_STABLE 2021-04-26 10:45:16 +02:00
Paul Holden eb60ece090 MDL-71434 mod_data: add form control class to template textarea. 2021-04-25 18:35:04 +01:00
Tim Schroeder 062dc30ffd MDL-70110 assign: fixed ws get_submissions for groups 2021-04-24 12:11:15 +02:00
Paul Holden a834862ce4 MDL-71416 report_log: user context header should show users name.
Co-Authored-By: Luca Bösch <luca.boesch@bfh.ch>
2021-04-23 20:24:34 +01:00
Sara Arjona e148629f48 weekly release 3.10.3+ 2021-04-23 10:47:03 +02:00
Mathew May 8e750158a0 MDL-71329 tool_moodlenet: Check to see if we get errors before curl 2021-04-23 15:49:55 +08:00
Jun Pataleta 78ea26ad7b Merge branch 'MDL-71171_310' of git://github.com/mdjnelson/moodle into MOODLE_310_STABLE 2021-04-23 12:40:47 +08:00
Mark Nelson b58a1b9e34 MDL-71171 customfield_text: ensure we set defaults properly 2021-04-23 12:37:38 +08:00
Andrew Nicols 298a70eba1 Merge branch 'MOODLE_310_STABLE-MDL-71376' of https://github.com/rjnl/moodle into MOODLE_310_STABLE 2021-04-23 11:53:24 +08:00
Andrew Nicols 0e301e88a5 Merge branch 'MDL-70970-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-23 11:39:04 +08:00
ScottVerbeek 4b9b0839b6 MDL-66673 groups: Stop notice when messaging system is disabled 2021-04-23 08:54:38 +10:00
Ferran Recio d7d94cf30f MDL-70909 behat: context freeze and not freeze steps 2021-04-22 15:51:36 +02:00
Sara Arjona 72b6d0be84 Merge branch 'MDL-71136_310' of https://github.com/TomoTsuyuki/moodle into MOODLE_310_STABLE 2021-04-22 13:56:49 +02:00
Sara Arjona fa2d1ab5d3 Merge branch 'MDL-71400-310' of git://github.com/mihailges/moodle into MOODLE_310_STABLE 2021-04-22 13:16:38 +02:00
Ferran Recio 78aabd1e28 MDL-70909 core: add cannotmatchanyrows to capabilities join 2021-04-22 13:10:37 +02:00
Jun Pataleta 72e7b87b92 Merge branch 'MDL-71170_310' of git://github.com/mdjnelson/moodle into MOODLE_310_STABLE 2021-04-22 18:55:12 +08:00
Noel De Martin 3a83f57043 MDL-71386 behat: Support for 3.9.5+ mobile app 2021-04-22 12:16:01 +02:00
Paul Holden 2a706c3e5c MDL-71200 mod_folder: ensure "Download folder" option is backed up. 2021-04-22 10:29:12 +01:00
Paul Holden faaa25b5cc MDL-70979 search: validate external user search parameters.
Clean return value in unit test for same.
2021-04-22 10:22:57 +01:00
Mihail Geshoski 6b5a1b02fe MDL-71400 assignfeedback_offline: Fix message after worksheet upload 2021-04-22 13:08:52 +08:00
Jun Pataleta 96ddfecda3 Merge branch 'MDL-71156-310' of git://github.com/lameze/moodle into MOODLE_310_STABLE 2021-04-22 11:53:39 +08:00
Simey Lameze 94259f8a7d MDL-71156 core: machinery to recover orphaned calendar events
The machinery to fix orphaned calendar events that were broken by MDL-67494.

The solution consists of:

1) Upgrade step that checks if this site has executed the problematic upgrade steps and
   if positive, it will schedule a new run for calendar_fix_orphaned_events adhoc task.

2) Adhoc task that will self-spawn calling the recovery machinery, running until
   all the orphaned calendar events are fixed. It also sets the maximum runtime of
   60 seconds. It is also possible to override that number by specifing the desired
   number setting the ->calendareventsmaxseconds in your config.php

3) CLI script that will look for all the calendar events which userids
   where broken by a wrong upgrade step, affecting to Moodle 3.9.5
   and up.

   It performs checks to both:
     a) Detect if the site was affected (ran the wrong upgrade step).
     b) Look for orphaned calendar events, categorising them as:
       - standard: site / category / course / group / user events
       - subscription: events created via subscriptions.
       - action: normal action events, created to show common important dates.
       - override: user and group override events, particular, that some activities support.
       - custom: other events, not being any of the above, common or particular.

   By specifying it (--fix) try to recover as many broken events (missing userid) as
   possible. Standard, subscription, action, override events in core are fully supported but
   override or custom events should be fixed by each plugin as far as there isn't any standard
   API (plugin-wise) to launch a rebuild of the calendar events.

4) Unit tests and helper functions to generate calendar events. We have decided to
   keep the tests simple, testing only true and false and not using data generators because
   the code is purely to recover the calendar events and won't turn into an API or something
   and also due to the urgency of this issue.
   The helpers have been created in calendar/tests/helpers.php since there are no data generators
   for calendar.
2021-04-22 10:43:37 +08:00
Andrew Nicols 855e7e9ede Merge branch 'MDL-70412-310' of git://github.com/aanabit/moodle into MOODLE_310_STABLE 2021-04-22 10:07:57 +08:00
Neill Magill e6725b8cbb MDL-64336 assign: Add missing PHP doc 2021-04-21 17:21:19 +02:00
Neill Magill bd80f68c11 MDL-64336 assign: Teachers should be able to see all submissions
Before this change a teacher would be able to see users listed if:

* They have an active enrolment and can submit
* They have an an inactive enrolment for a role that can submit

After this change they will additonally be able to see users listed:

* That have an active enrolment and have submitted
* That have an inactive enrolment and have submitted

This means that if an assignment has it's context frozen all users
that have made some form of submission will still be listed.

It will also apply if the submission capability is removed from a
user.

If a user's enrolment is deleted they will not be listed.

The submission and grading counts have also been updated so
they will reflect the new rules.
2021-04-21 17:15:54 +02:00
Neill Magill dbcfda9641 MDL-64336 assign: Submissions should be visible while frozen
Before this change if a student visited an assignment that is
frozen they would only see the title and description even if
they had made a submission to it.

After the change they will be able to see the status of their
submission and any feedback and grades they have recived.

It will also make the Moodle app recognise that submission
should not happen because the assignment is frozen.

Tests based on ones created by Andrew Nicols
2021-04-21 17:15:45 +02:00
Sara Arjona b62526d0ad Merge branch 'MDL-62244_310_label_view' of https://github.com/sheesania/moodle into MOODLE_310_STABLE 2021-04-20 17:45:06 +02:00
Sara Arjona d9293c77bc weekly release 3.10.3+ 2021-04-20 15:51:19 +02:00
Sara Arjona 6f9415fea0 Merge branch 'install_310_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_310_STABLE 2021-04-20 15:51:18 +02:00
Paul Holden 647020f584 MDL-70970 quizaccess_seb: consistent property array sorting by key.
In PHP8.0 using `ksort` was producing incorrect results by sorting
keys differing only in case in the wrong order. This change makes
sorting consistent between PHP versions.

Co-Authored-By: Tim Hunt <T.J.Hunt@open.ac.uk>
2021-04-20 14:36:19 +01:00
Rajneel Totaram afc20e7caa MDL-71376 mod_quiz: fix undefined object reference 2021-04-20 20:13:40 +12:00
Jun Pataleta 6d82a01438 Merge branch 'MDL-71364_310' of https://github.com/stronk7/moodle into MOODLE_310_STABLE 2021-04-20 09:23:43 +08:00
AMOS bot 3e47cdda94 Automatically generated installer lang files 2021-04-20 00:07:27 +00:00
Víctor Déniz 7ccaf08d5a Merge branch 'MDL-71087-310' of https://github.com/NashTechOpenUniversity/moodle into MOODLE_310_STABLE 2021-04-19 23:45:02 +01:00
Eloy Lafuente (stronk7) 5d309ef2a8 Merge branch 'MDL-69256-310' of git://github.com/andrewnicols/moodle into MOODLE_310_STABLE 2021-04-20 00:05:05 +02:00
Víctor Déniz ce023afec9 Merge branch 'MOODLE_310_STABLE_fix_gradeimport_csv' of https://github.com/intrajp/moodle into MOODLE_310_STABLE 2021-04-19 22:49:09 +01:00
Dongsheng Cai fdb9a664f6 MDL-68747 mod_quiz: force LTR for quiz grading overview chart 2021-04-19 21:06:37 +10:00
Sara Arjona 79d705df64 Merge branch 'MDL-71365_310' of https://github.com/stronk7/moodle into MOODLE_310_STABLE 2021-04-19 10:18:39 +02:00
Paul Holden 73e9259c10 MDL-71127 mod_quiz: fix thrown exception for invalid question columns. 2021-04-19 09:07:13 +01:00
Paul Holden 785cf4fb22 MDL-71168 report_participation: fix select all users checkbox. 2021-04-19 09:05:13 +01:00
Sara Arjona c30923d77d Merge branch 'MDL-66475-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-19 09:49:23 +02:00
Sara Arjona ea388d74ab Merge branch 'MDL-65553-310' of git://github.com/aanabit/moodle into MOODLE_310_STABLE 2021-04-19 08:53:45 +02:00
AMOS bot 61d1af0818 Automatically generated installer lang files 2021-04-18 00:07:25 +00:00
Eloy Lafuente (stronk7) 93960101e9 MDL-71365 grunt: fix ignores missing LF @ EOF 2021-04-18 00:44:06 +02:00
Eloy Lafuente (stronk7) 26e8b4a0fa MDL-71364 behat: fix randomness causes by "now" advancing
Some recent tests do set a date time element
to ##now## or tomorrow and, immediately after that
the look if, effectively, ##now## and #tomorrow#
have been set (with minutes resolution).

Problem is that, between the field is set and the field
is verified, it can happen that the time advances to
next minute (from H:M:59 to H:M+1:00) and then the
assertion fails.

To avoid this, we could have lowered the resolution to be
hours... but that doesn't solve the problem just makes it
to happen less often.

So, instead of that... we are setting the 2 now and tomorrow
cases to be "today noon" and "tomorrow noon" (12:00:00) so
we ensure they won't be ever in the risk of jumping of minute.
2021-04-17 12:33:32 +02:00
Eloy Lafuente (stronk7) a1523774bc Merge branch 'MDL-70099-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-17 09:40:37 +02:00
Sara Arjona 7c1feb97b2 weekly release 3.10.3+ 2021-04-16 17:03:33 +02:00
Sara Arjona 26e00f4640 Merge branch 'install_310_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_310_STABLE 2021-04-16 17:03:32 +02:00
Andrew Nicols bf4038a971 Merge branch 'MOODLE_310_MDL-71233' of https://github.com/golenkovm/moodle into MOODLE_310_STABLE 2021-04-16 12:08:51 +08:00
Andrew Nicols 10c4a67760 Merge branch 'MDL-60490-missing_close_div-310' of https://github.com/leonstr/moodle into MOODLE_310_STABLE 2021-04-16 11:54:13 +08:00
Andrew Nicols def3f4a074 Merge branch 'MDL-69415-310' of git://github.com/sarjona/moodle into MOODLE_310_STABLE 2021-04-16 11:27:06 +08:00
Amaia Anabitarte 71613b1055 MDL-70412 groups: Improving create_group data generator
Increasing the size of the name for generated groups
to ensure we don't have problems with ordering.
2021-04-15 16:57:23 +02:00
Amaia Anabitarte db374d6a80 MDL-65553 core_analytics: Don't allow to flag predictions several times
Even if a prediction is hidden from the report once is flagged,
it can be flagged several times if the user visits detailed view via URL.
We remove the checkbox to select a prediction and flag it
once it has already been flagged.
2021-04-15 16:39:09 +02:00
Jake Dallimore 6958a078a6 Merge branch 'MDL-71338-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-15 14:42:44 +08:00
Jake Dallimore 5566bfe436 Merge branch 'MDL-71119-310' of git://github.com/sarjona/moodle into MOODLE_310_STABLE 2021-04-15 08:32:29 +08:00
AMOS bot 40cdfc58fc Automatically generated installer lang files 2021-04-15 00:07:34 +00:00
Paul Holden 416b25fc77 MDL-71338 tool_usertours: correct Content-Type on tour export.
Replace existing code with standard file serving mechanism, in
turn correcting the previously incorrect Content-Type header.
2021-04-14 21:12:02 +01:00
Eloy Lafuente (stronk7) ae88411798 Merge branch 'MDL-70616-310-fixed' of https://github.com/izendegi/moodle into MOODLE_310_STABLE 2021-04-14 12:45:23 +02:00
Iñigo Zendegi 909c977e00 MDL-70616 gradingform_rubric: Filters not applied to rubric name 2021-04-14 11:51:11 +02:00
Sara Arjona 3306411314 MDL-71119 core_badges: Add more information to errors
Some errors raised when calling external services were ignored.
Displaying this information helps to debug and find the real reasons
why some action, like connecting to an external backpack, can't be
done.
2021-04-14 11:04:06 +02:00
Sara Arjona 4203a0389e MDL-71119 core_badges: Use basicauth and remove urlencode
Basic authorization header is required to get token. Besides, client
id and secret shouldn't call urlencode.
2021-04-14 11:01:27 +02:00
Sara Arjona 2d308243f4 MDL-71119 core_badges: Initialise scopes param in backpack-connect.php
The scopes parameter should be passed when creating the OAuth2 badges
client. As it is an optional parameter, when it's empty, it will
be initilised with the supported scopes for the backpack issuer.

It will happen, for instance, when a call is done to oauth2callback.php
because, as defined in RFC6749, OAuth2 authorization response only
supports code and state.
2021-04-14 11:01:27 +02:00
Paul Holden 6a7baaced0 MDL-70772 mod_forum: check for valid usernode before making active. 2021-04-14 09:20:20 +01:00
Andrew Nicols 0c611e0a13 Merge branch 'MDL-71154_310' of https://github.com/stronk7/moodle into MOODLE_310_STABLE 2021-04-14 12:11:21 +08:00
AMOS bot 905511e816 Automatically generated installer lang files 2021-04-14 00:07:28 +00:00
Eloy Lafuente (stronk7) d883536e6f MDL-71154 ci: Prevent upstream branches and tags to be built
Both for travis and GHA:

- master and MOODLE_XXX_STABLE branches
- any vX.Y.Z tag

won't lead to a build anymore.
2021-04-13 16:55:50 +02:00
Eloy Lafuente (stronk7) 653d6f0229 weekly release 3.10.3+ 2021-04-13 10:26:39 +02:00
Eloy Lafuente (stronk7) 4ab41d6090 Merge branch 'install_310_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_310_STABLE 2021-04-13 10:26:38 +02:00
Eloy Lafuente (stronk7) c50d68cfd9 Merge branch 'MDL-71059-310' of git://github.com/abias/moodle into MOODLE_310_STABLE 2021-04-13 00:42:34 +02:00
Tim Hunt 33d6929e1f MDL-71313 repositories: warn if get_instances context is wrong type 2021-04-12 20:35:02 +01:00
Sara Arjona f4a604e4e2 Merge branch 'MDL-68716-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-12 17:24:46 +02:00
Sara Arjona 0ecc30eb85 Merge branch 'MDL-71264_310' of https://github.com/stronk7/moodle into MOODLE_310_STABLE 2021-04-12 16:40:16 +02:00
Sara Arjona 4de37cf603 Merge branch 'MDL-70595-310' of git://github.com/mickhawkins/moodle into MOODLE_310_STABLE 2021-04-12 16:01:40 +02:00
Sara Arjona dda7890857 Merge branch 'MDL-71107-10' of git://github.com/aanabit/moodle into MOODLE_310_STABLE 2021-04-12 12:41:16 +02:00
Eloy Lafuente (stronk7) b470f7b246 Merge branch 'MDL-69102-310' of https://github.com/martygilbert/moodle into MOODLE_310_STABLE 2021-04-12 00:02:05 +02:00
Eloy Lafuente (stronk7) f5087ef548 MDL-69102 phpunit: Add test to cover ALL cohort criteria
That way we can check that the expectations are
working ok for every database. Previously only the
ANY case was being tested.

Also, convert them to course badges and check, via
review_all_criteria() that the SQL structures returned
by award_criteria_cohort->get_completed_criteria_sql()
doesn't fail and return the expected counters.
2021-04-10 12:38:46 -04:00
Issam Taboubi 7e146f752a MDL-71303 core: HTML not escaped in block skip link 2021-04-09 17:31:27 -04:00
AMOS bot ece30526dd Automatically generated installer lang files 2021-04-09 00:07:31 +00:00
Eric Merrill a3fa31fa21 MDL-52724 editor_atto: Scrub atto html for invalid ol, ul, and li tags
Try to correct broken ul/ol/li tags, as they have an outsized impact
on course layout. Uses basic regex and loops to track open and closed
tags.

Also adds a deep clean option to the HTML cleaner, that runs less
frequent, more intensive cleanings. This is because normally _cleanHTML
gets called after each keystroke, which could cause problems with
large content on weak systems.

Behat changes are a fix for setting multiline strings in Atto, and
creating a multiline match step.
2021-04-08 15:30:50 -04:00
Eloy Lafuente (stronk7) 06b3ac6ca1 Merge branch 'MDL-70802-310' of git://github.com/andrewnicols/moodle into MOODLE_310_STABLE 2021-04-08 18:37:39 +02:00
Víctor Déniz 4c8c9a4c4b Merge branch 'MDL-71116-310' of git://github.com/sarjona/moodle into MOODLE_310_STABLE 2021-04-08 01:05:55 +01:00
Amaia Anabitarte 538c8eddb5 MDL-71107 core_contentbank: Behat test. Authoring when copying a course 2021-04-07 10:03:37 +02:00
Amaia Anabitarte fafed9dc90 MDL-71107 core_contentbank: Fill content author when copying a course
'usercreated' and 'usermodified' fields can not be always mapped.
We are filling those fields with 'old' users when working on the same
site, or with current user when working on a different instance.
2021-04-07 10:03:37 +02:00
Alex Rowe 1611b30b1c MDL-70049 assign: count_submissions_need_grading call when can't grade 2021-04-07 08:57:05 +10:00
Eloy Lafuente (stronk7) 69455d835c Merge branch 'MDL-70959-310' of git://github.com/junpataleta/moodle into MOODLE_310_STABLE 2021-04-06 22:29:07 +02:00
Eloy Lafuente (stronk7) c33060d008 Merge branch 'MDL-68635-310' of https://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-06 22:23:42 +02:00
Víctor Déniz 10f934e300 Merge branch 'MDL-71128-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-04-06 19:51:02 +01:00
Michael Hawkins 9d63efed82 MDL-70595 core: Updated security.txt expiry 2021-04-06 13:13:45 +08:00
AMOS bot da5111e321 Automatically generated installer lang files 2021-04-06 00:07:37 +00:00
Paul Holden ebfe1d5945 MDL-70099 enrol: increase accuracy of current enrolments by date.
By rounding the current time it was possible that the most recently
created user enrolments (e.g. self enrolments) were being excluded.

This would manifest itself in a user being enrolled on a course,
but it not appearing under "My courses" navigation or on their own
Dashboard until the rounded time had caught up with the current
time.
2021-04-05 19:32:15 +01:00
Paul Holden 359cc7e787 MDL-71003 user: fix alignment of autocomplete participant filters. 2021-04-04 23:21:25 +01:00
Paul Holden 981784000d MDL-70917 tool_behat: fix profile/replace option comparison.
The `isset` call always returned true for the 'replace' option,
because even if not specified it receives a non-null value. The
`strpos` call now does a strict equality check rather than
greater-than-or-equal (which always returns true).
2021-04-04 23:18:37 +01:00
Paul Holden 542a7490f2 MDL-70763 core: export preferences for correct user.
Ensure we are using the ID of the given user rather than falling
back to that of the current user during privacy export.
2021-04-04 23:15:43 +01:00
Paul Holden e44ae4df58 MDL-70763 quiz_overview: export preferences for correct user. 2021-04-04 23:15:43 +01:00
Paul Holden e89432fb19 MDL-70763 gradereport_grader: export preferences for correct user. 2021-04-04 23:15:43 +01:00
Paul Holden 3e30dffa31 MDL-66475 task: fix random timing failures in logger cleanup test. 2021-04-04 23:12:38 +01:00
AMOS bot e084f4c0b4 Automatically generated installer lang files 2021-04-04 00:07:33 +00:00
Eloy Lafuente (stronk7) 215851c4c0 MDL-71264 behat: Change date(time) element update order
This includes 2 change to the order date(time) elements are filled,
each one addressing one type of problem, where current order is
problematic and can lead to unexpected dates.

1) Changing date, when current month only has X days and target
   month has more than X days. Example, being 1 April, change
   the date to 31 May.
     This is solved by changing the order of introduction
     from current D => M => Y to Y => M => D.
2) Changing date, when target month only has X days and current
   month has more that X days. Example, being 31 March, change
   the date to 28 Feb.
     This is solved by always setting the D to 1, before the
     Y => M => D sequence commented @ 1) begins.
2021-04-03 18:10:04 +02:00
Eloy Lafuente (stronk7) f751383a15 MDL-71264 behat: New feature covering all day/month/year date changes
Because of the order that dates and datetimes are filled by behat
sometimes there are some intermediate results that are impossible
and then the form (javascript) automatically reacts and fixes the
date, ultimate leading to a different date that the one we wanted
to set with Behat.

This is noticeable when switching between months (with some day
being the last day of the month) and the 2 months have a different
number of days. For example April date => March date (or the opposite).

This test covers all the critical changes (day, month and year),
back and forth. All times are Perth/Australia.
2021-04-03 10:59:45 +02:00
AMOS bot ab1207e8eb Automatically generated installer lang files 2021-04-02 00:07:25 +00:00
Marty Gilbert 5ef3241ae3 MDL-69102 badge: Fixes broken WHERE clause
When buildilng the 'WHERE' clause, the SQL generated
a query that selected any user who was a member of the
**last** group, not all of the groups.

I believe the query has to be re-worked so that it only
returns users who are in ALL of the groups. This can
be done by a GROUP BY and HAVING.
2021-04-01 14:51:21 -04:00
Alison Blomenberg eebc6ba6a8 MDL-62244 Resource: label view redirects to label, not course 2021-04-01 11:54:48 -06:00
Leon Stringer 6cd746cb4d MDL-60490 tool_lp: Missing div close
Mustache template had opening <div> with missing </div>.
2021-04-01 12:18:13 +01:00
sam marshall 6877043d8c MDL-70446 search_solr: File indexing can fail due to multipart upload
Due to Solr bug SOLR-15039, uploading files for indexing can fail if
it uses multipart upload. This changes it to use direct binary upload.

Unfortunately, the direct binary version in PHP curl only supports a
string, so we have to load the file into memory. I added extra code to
restrict the size of files indexed to (memory limit - 100MB), which is
usually 284MB unless configured differently because cron runs under
MEMORY_EXTRA.
2021-04-01 11:40:36 +01:00
AMOS bot 2c249dbee1 Automatically generated installer lang files 2021-04-01 00:07:27 +00:00
Juan Leyva 6c14348b41 MDL-70863 mod_forum: Allow NULL in timecreated 2021-03-31 17:49:58 +02:00
Víctor Déniz c7b4cf91f8 weekly release 3.10.3+ 2021-03-30 15:37:42 +01:00
Víctor Déniz 3bc49b14bf Merge branch 'install_310_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_310_STABLE 2021-03-30 15:37:41 +01:00
Paul Holden f4e28ca6c2 MDL-65687 message: increase confirmation dialog zindex.
Make sure that confirmation dialogs (such as block/unblock user,
delete conversation) are shown with prominence in the messaging
drawer.
2021-03-30 10:50:48 +01:00
Tomo Tsuyuki ed1ce9cf9b MDL-71136 backup: Fix moodle_exception path. 2021-03-30 10:56:09 +11:00
Mikhail Golenkov 31530a2730 MDL-71233 backup: Fix Moodle version for MDL-69418 2021-03-30 10:45:14 +11:00
Paul Holden 82babe08ea MDL-68635 tool_cohortroles: observe user initial table filter. 2021-03-29 14:12:17 +01:00
Paul Holden 76e39a9c2a MDL-68716 mod_forum: extra column sorting options in privacy export.
Accounts for extra sort options that were added in bc23cbaf.
2021-03-29 14:05:37 +01:00
Eloy Lafuente (stronk7) f41360dd50 Merge branch 'MDL-70963-310' of git://github.com/sarjona/moodle into MOODLE_310_STABLE 2021-03-29 12:39:56 +02:00
Andrew Nicols 838190a43a Merge branch 'MDL-71004_310' of https://github.com/stronk7/moodle into MOODLE_310_STABLE 2021-03-29 11:54:27 +08:00
AMOS bot e2b34c37bd Automatically generated installer lang files 2021-03-29 00:07:26 +00:00
AMOS bot 250afa688f Automatically generated installer lang files 2021-03-28 00:07:23 +00:00
Víctor Déniz 325915ef34 Merge branch 'MDL-70537-m310' of https://github.com/sammarshallou/moodle into MOODLE_310_STABLE 2021-03-26 20:15:29 +00:00
Sara Arjona fec16e68c9 MDL-70963 core_badges: Use expected JSON format for OBv2.1
This patch adds some changes to guarantee that OpenBadges 2.1 (OBv2.1)
will return always the same JSON format that OBv2.0 (because the only
difference between both versions it's the BadgeConnect API).
2021-03-26 09:02:18 +01:00
Shintaro Fujiwara c906a33e1e MDL-69304 gradeimport_csv: Validate if the user id is numerical. 2021-03-26 10:31:40 +09:00
Víctor Déniz e8a5efe835 Merge branch 'MDL-71194-310' of git://github.com/NoelDeMartin/moodle into MOODLE_310_STABLE 2021-03-25 15:15:09 +00:00
Noel De Martin aa07315267 MDL-71194 behat: Fix app tests 2021-03-25 13:51:01 +01:00
Andrew Nicols d4de6e594d Merge branch 'MDL-70980-310' of git://github.com/ferranrecio/moodle into MOODLE_310_STABLE 2021-03-25 08:07:41 +08:00
AMOS bot 14138237ee Automatically generated installer lang files 2021-03-25 00:07:27 +00:00
Eloy Lafuente (stronk7) ef7fcf4261 Moodle release 3.10.3 2021-03-24 20:13:09 +01:00
Eloy Lafuente (stronk7) 0e74a6c0c5 MDL-71004 git: Ignore moodle-plugin-ci.phar in any directory 2021-03-24 20:10:34 +01:00
Víctor Déniz fa85c3b806 weekly release 3.10.2+ 2021-03-24 14:21:00 +00:00
Eloy Lafuente (stronk7) 8518f6307f Merge branch 'MDL-71182-310-2' of git://github.com/lameze/moodle into MOODLE_310_STABLE 2021-03-24 10:50:23 +01:00
Simey Lameze 0c799ad2e1 MDL-71182 calendar: add logic for category events in get_context
This commit adds the missing logic for category events in
get_context() method. Without this fix, users will get errors
if they have category events with userid set to 0.
2021-03-24 17:34:31 +08:00
Andrew Nicols 06c9de6b05 MDL-70802 javascript: Normalise component consistently in templates 2021-03-24 11:18:33 +08:00
Simey Lameze 7a09d64104 MDL-71182 calendar: Revert "Merge branch 'MDL-67494-310' of git://github.com/lameze/moodle into MOODLE_310_STABLE"
This reverts commit 5a407772c7, reversing
changes made to 82f2fe02e6.
2021-03-24 09:22:04 +08:00
Víctor Déniz 5f7136f17c Merge branch 'MDL-71022-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-03-23 23:01:31 +00:00
Víctor Déniz 3278f5ca50 Merge branch 'MDL-70727-310' of https://github.com/NashTechOpenUniversity/moodle into MOODLE_310_STABLE 2021-03-23 18:24:19 +00:00
Andrew Nicols 43520e3eaa Merge branch 'MDL-71039-310' of git://github.com/marinaglancy/moodle into MOODLE_310_STABLE 2021-03-23 11:18:39 +08:00
Andrew Nicols 107244cf20 Merge branch 'MDL-69036-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-03-23 09:54:42 +08:00
Andrew Nicols 7cb384756e Merge branch 'MDL-67870-310' of git://github.com/mihailges/moodle into MOODLE_310_STABLE 2021-03-23 09:24:54 +08:00
Andrew Nicols a8d18f88a1 MDL-69256 tests: Fix random slow test failures in completion cron 2021-03-23 08:08:29 +08:00
Mark Nelson a87f5b6e8c MDL-71170 customfield_text: use correct error string 2021-03-22 21:56:48 +08:00
Sara Arjona 9e52a6a0e1 MDL-69415 core_h5p: prepend H5P libraries in autoloader
Core and mod_hvp are using the same namespace for some H5P
libraries. This is causing some random errors.
In order to get the expected behaviour in Moodle core, this patch
prepends H5P libraries in order to guarantee they are loaded first.
Plugins using same libraries will need to use a different namespace
if they overlap and a different version of these libraries should
be used.
2021-03-22 12:41:02 +01:00
Ferran Recio e21872130d MDL-70980 mod_h5pactivity: fix review mode 2021-03-22 10:58:00 +01:00
Mihail Geshoski 65aceea7ed MDL-67870 backup: Skip value change if setting is locked by hierarchy
The given condition in the if statement did not take into account cases
when the setting is locked by hierarchy. Settings which are locked by
hierarchy should should keep their current values which were inherited
from the parent.
2021-03-22 15:33:47 +08:00
Thinh Pham 3567998cfd MDL-71087 File picker: Focus lost after 'Create folder' 2021-03-22 14:30:43 +07:00
Chris Brown 61d4fcc812 MDL-71139 trademark: Improve the trademark messaging 2021-03-22 12:00:28 +08:00
Sara Arjona 0820e24c18 MDL-71116 core_badges: Backpack URLs with more than 50 chars
Backpack URLs (web and API) can have up to 255. However, the form
for creating/editing them, doesn't allow URLs with more than 50
characters. This patch align limitation to DB value (255).
2021-03-19 15:09:07 +01:00
Eloy Lafuente (stronk7) 2b260526fc weekly release 3.10.2+ 2021-03-19 14:06:33 +01:00
Eloy Lafuente (stronk7) 1d38f7c3eb Merge branch 'install_310_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_310_STABLE 2021-03-19 14:06:31 +01:00
Jun Pataleta 35f368a1b4 MDL-70959 completion: Fix completion_info::get_data() unit tests
* Fixed inverted $sameuser test data.
* Fixed caching expectation check. Caching only relies on whether the
user accessing the completion data is the same user or not.
* Fixed checking for the caching of other modules. Should have been
checking cm ID and not instance ID.
* Additional test case when whole course parameter is passed as
true, but the requesting user is different from the target user.
2021-03-19 11:50:14 +08:00
sam marshall 31aedd4c1c MDL-70537 Availability: Avoid recursive calls to $cm->name
These recursive calls didn't work in PHP 7.3 and below, but in PHP
7.4 they also cause a fatal error which means if you have invalid
availability data, the whole page might die.
2021-03-18 15:56:28 +00:00
Jun Pataleta 348d2c66df Merge branch 'MDL-70992-310' of https://github.com/NashTechOpenUniversity/moodle into MOODLE_310_STABLE 2021-03-18 12:23:27 +08:00
Adrian Greeve cdc875c25c Merge branch 'MDL-70924-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-03-18 11:51:59 +08:00
Adrian Greeve a3c38be96b Merge branch 'MDL-71048-310' of https://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-03-18 11:10:13 +08:00
Adrian Greeve 2f1b88412d Merge branch 'MDL-70977-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-03-18 10:27:24 +08:00
Adrian Greeve a688fc9d6c Merge branch 'MDL-71068-protectusername-confirmed-MOODLE_310_STABLE' of https://github.com/brendanheywood/moodle into MOODLE_310_STABLE 2021-03-18 09:46:21 +08:00
AMOS bot 401c241450 Automatically generated installer lang files 2021-03-18 00:07:25 +00:00
Brendan Heywood 6b7850290b MDL-71068 login: Fix edge cases with $CFG->protectusernames 2021-03-17 17:39:43 +11:00
sangnguyen ba0a487685 MDL-70992 Folder: Unnecessary tab stops to the left of files 2021-03-17 09:37:21 +07:00
Paul Holden 147a25d87f MDL-71128 mod_quiz: escape question idnumber on display. 2021-03-16 20:28:48 +00:00
Sara Arjona 0e462101e0 Merge branch 'MDL-44613-integration-310' of git://github.com/mihailges/moodle into MOODLE_310_STABLE 2021-03-16 08:27:03 +01:00
Mihail Geshoski fae4570571 MDL-44613 mod_assign: Fix behat failure in classic
The link to the calendar page is missing in the navigation block in
classic which causes a behat failure in the 'Set availibility dates
for an assignment' feature. The fix includes adding the calendar block
to the Course page which can be used to navigate to the calendar page.
2021-03-16 11:32:36 +08:00
Adrian Greeve 096b28dee9 Merge branch 'MDL-70780-310' of git://github.com/andrewnicols/moodle into MOODLE_310_STABLE 2021-03-16 11:24:29 +08:00
Sara Arjona 4a4912ecbf Merge branch 'MDL-44613-310-1' of git://github.com/mihailges/moodle into MOODLE_310_STABLE 2021-03-15 15:53:58 +01:00
Paul Holden 8c276d003c MDL-70977 mod_forum: fix empty equality check updating post content.
Using the external method for updating posts, the check for empty
subject/message content wasn't correct (disallowed the string '0'),
in addition to being impossible to set a posts message format
property to FORMAT_MOODLE (integer 0).
2021-03-15 12:49:06 +00:00
Paul Holden 23fd19658d MDL-70924 course: ensure required fields present when getting courses. 2021-03-15 12:46:49 +00:00
Mihail Geshoski 2551709dbd MDL-44613 mod_assign: Automate testing of the assignment availability 2021-03-15 20:01:36 +08:00
Mihail Geshoski 9d2f8acfb4 MDL-44613 tool_behat: Update datetime format usages to strftime
Updates all existing datetime format usages to strftime in the
'time to timestamp' transformation in tool_behat behat tests.
2021-03-15 20:01:36 +08:00
Mihail Geshoski 6484e5ae81 MDL-44613 core_message: Update datetime format usages to strftime
Updates all existing datetime format usages to strftime in the
'time to timestamp' transformation in core_message behat tests.
2021-03-15 20:01:36 +08:00
Mihail Geshoski 7726160ef3 MDL-44613 behat: strftime format in time to timestamp transformation
Modifies the 'string time to timestamp' behat trasformation to use
userdate() instead of date(). The userdate() method is generally used
throught Moodle for constructing formatted date strings and this change
will provide more consistency and prevent any potential behat failures.
Therefore, if the date format is defined in the given trasformation, it
has to be strftime compatible. Example:
'I should see "##tomorrow noon##%A, %d %B %Y, %I:%M %p##"'
2021-03-15 20:01:36 +08:00
Adrian Greeve 936f22755c Merge branch 'MDL-70306-310' of git://github.com/peterRd/moodle into MOODLE_310_STABLE 2021-03-15 11:14:51 +08:00
Peter Dias d199b23598 MDL-70306 auth: Update the thirdpartylib.xml 2021-03-15 11:10:23 +08:00
AMOS bot f2ff9d8256 Automatically generated installer lang files 2021-03-13 00:07:27 +00:00
Jun Pataleta 7eb9ddc5ae MDL-70959 completion: Fix unit tests for get_data()
The unit tests for completion_info::get_data() does not make a lot of
sense with mocking being incorrectly used and the actual functionality
is not being properly tested. I have rewritten the test to use actual
cm_info instances and data providers for better coverage.
2021-03-12 13:34:58 +08:00
Jun Pataleta 3afc0edaf9 MDL-70959 completion: Documentation improvements
* Fix typo in $cm parameter's type in PHPDoc
* Improve the comment to better explain the logic why fetching the
completion data for the whole course can only be done when caching is
used.
Even if $wholecourse is set to true, there's no point in fetching the
completion data for all the course modules unless we're caching the
results.
2021-03-12 13:34:55 +08:00
Sara Arjona 6b804f9e1f weekly release 3.10.2+ 2021-03-11 17:18:50 +01:00
Sara Arjona 201734b465 Merge branch 'install_310_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_310_STABLE 2021-03-11 17:18:49 +01:00
Adrian Greeve cb8a282e05 Merge branch 'MDL-66025-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-03-11 11:40:39 +08:00
Adrian Greeve 3f1a70c171 Merge branch 'MDL-71026-310' of git://github.com/ilyatregubov/moodle into MOODLE_310_STABLE 2021-03-11 11:02:44 +08:00
Eloy Lafuente (stronk7) dc9f2d2509 Merge branch 'MDL-70726-3.10' of https://github.com/NashTechOpenUniversity/moodle into MOODLE_310_STABLE 2021-03-11 00:11:52 +01:00
Eloy Lafuente (stronk7) acd3b26df0 Merge branch 'MDL-70789-310' of git://github.com/paulholden/moodle into MOODLE_310_STABLE 2021-03-10 23:52:54 +01:00
Eloy Lafuente (stronk7) d5e880ca73 Merge branch 'MDL-70967-310' of git://github.com/marinaglancy/moodle into MOODLE_310_STABLE 2021-03-10 23:35:08 +01:00
AMOS bot f1e7a22672 Automatically generated installer lang files 2021-03-10 00:07:21 +00:00
Paul Holden 2730c1a987 MDL-69036 theme_boost: consistent table row hovering for links.
When hovering over table rows with the `dimmed_text` class we
should consistently set the text/link colour.

Co-Authored-By: Mikel Martín <mikel@moodle.com>
2021-03-09 16:22:42 +00:00
Andrew Nicols 3f87850bc2 MDL-70780 customfield: Address random behat fails
A number of behat fails have been creeping in with the customfield code
as behat is moving onto the next step before the current step finishes.

This change introduces some additional pending JS tracking to prevent
this.
2021-03-09 07:52:24 +08:00
AMOS bot a7e720be5f Automatically generated installer lang files 2021-03-07 00:07:20 +00:00
Alexander Bias e1fcbc9d3d MDL-71059 repository_contentbank: set default returntype
The content bank was designed for making easier to reuse content.
So "Link to the file" should be the default option when using the
contentbank repository, in order to let teachers modify their
content in the content bank and update automatically it everywhere
is used.
2021-03-05 11:34:38 +01:00
Amaia Anabitarte f8b584f09a MDL-70726 behat: Fixing existing fail tests
Some of the current behat tests were wrong but not failing because of the bug in 'should not exist' step.
Once the bug is fixed, we must fix also wrong behats.
2021-03-05 14:08:55 +07:00
Paul Holden 627fcc0ebc MDL-71048 lang: fix null equality check of lang string args. 2021-03-04 19:43:55 +00:00
Marina Glancy 37ff88f18d MDL-71039 core_form: support valuehtmlcallback in autocomplete inline 2021-03-04 13:39:51 +01:00
Thinh Pham bbdd616d5c MDL-70727 Behat: function scrollIntoView not working in the app 2021-03-04 13:37:13 +07:00
Ilya Tregubov f81bc6488d MDL-71026 user: Fix incorrect get_string call. 2021-03-03 11:44:23 +02:00
Paul Holden 7bbab5d400 MDL-71022 gradereport_history: prevent double escaping of users.
The user identity fields are already escaped by the AJAX endpoint
that returns them, we don't need to do it again at the client.
2021-03-02 09:14:40 +00:00
Thong Bui 1de403d9b7 MDL-70726 behat: should_not_exist function does not work correctly 2021-03-01 17:50:36 +07:00
Marina Glancy 6578994426 MDL-70967 enrol_meta: fix error in unittest 2021-02-22 18:06:43 +01:00
Paul Holden 7b4f2014c3 MDL-66025 user: require message content in bulk action.
Also import missing 'core/notification' dependency.
2021-02-14 20:10:35 +00:00
Paul Holden fd80c7a6b7 MDL-70789 theme_boost: fix double click on drawer hiding content.
If the drawer toggle button was clicked in quick succession to close
and then re-open, then the delayed Aria.hide method would be called
which hid the drawer content when it finished opening.
2021-02-11 21:45:51 +00:00
319 changed files with 6671 additions and 1352 deletions
+1 -1
View File
@@ -92,4 +92,4 @@ theme/boost/amd/src/bootstrap/tab.js
theme/boost/amd/src/bootstrap/toast.js
theme/boost/amd/src/bootstrap/tooltip.js
theme/boost/amd/src/bootstrap/util.js
theme/boost/scss/fontawesome/
theme/boost/scss/fontawesome/
+7 -1
View File
@@ -1,6 +1,12 @@
name: Core
on: [push]
on:
push:
branches-ignore:
- master
- MOODLE_[0-9]+_STABLE
tags-ignore:
- v[0-9]+.[0-9]+.[0-9]+*
env:
php: 7.4
+1
View File
@@ -46,3 +46,4 @@ composer.phar
atlassian-ide-plugin.xml
/node_modules/
/.vscode/
moodle-plugin-ci.phar
+1 -1
View File
@@ -93,4 +93,4 @@ theme/boost/amd/src/bootstrap/tab.js
theme/boost/amd/src/bootstrap/toast.js
theme/boost/amd/src/bootstrap/tooltip.js
theme/boost/amd/src/bootstrap/util.js
theme/boost/scss/fontawesome/
theme/boost/scss/fontawesome/
+6
View File
@@ -19,6 +19,12 @@ services:
addons:
postgresql: "9.6"
branches:
except:
- master
- /MOODLE_[0-9]+_STABLE/
- /^v[0-9]+\.[0-9]+\.[0-9]+.*/
jobs:
# Enable fast finish.
# This will fail the build if a single job fails (except those in allow_failures).
+2 -2
View File
@@ -424,7 +424,7 @@ module.exports = function(grunt) {
'*/**/yui/src/*/meta/',
'*/**/build/',
].concat(thirdPartyPaths);
grunt.file.write('.eslintignore', eslintIgnores.join('\n'));
grunt.file.write('.eslintignore', eslintIgnores.join('\n') + '\n');
// Generate .stylelintignore.
const stylelintIgnores = [
@@ -433,7 +433,7 @@ module.exports = function(grunt) {
'theme/boost/style/moodle.css',
'theme/classic/style/moodle.css',
].concat(thirdPartyPaths);
grunt.file.write('.stylelintignore', stylelintIgnores.join('\n'));
grunt.file.write('.stylelintignore', stylelintIgnores.join('\n') + '\n');
};
/**
+1 -1
View File
@@ -2,7 +2,7 @@ MOODLE TRADEMARKS POLICY
========================
The word "Moodle" is trademarked in many countries around the world. The word
was originally an acronym: Modular Object-Oriented Dynamic Learning
was originally coined as an acronym: Modular Object-Oriented Dynamic Learning
Environment. The trademark is owned by Martin Dougiamas, Founder and Director
of Moodle.
+138
View File
@@ -0,0 +1,138 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Fix orphaned calendar events that were broken by MDL-67494.
*
* This script will look for all the calendar events which userids
* where broken by a wrong upgrade step, affecting to Moodle 3.9.5
* and up.
*
* It performs checks to both:
* a) Detect if the site was affected (ran the wrong upgrade step).
* b) Look for orphaned calendar events, categorising them as:
* - standard: site / category / course / group / user events
* - subscription: events created via subscriptions.
* - action: normal action events, created to show common important dates.
* - override: user and group override events, particular, that some activities support.
* - custom: other events, not being any of the above, common or particular.
* By specifying it (--fix) try to recover as many broken events (missing userid) as
* possible. Standard, subscription, action, override events in core are fully supported but
* override or custom events should be fixed by each plugin as far as there isn't any standard
* API (plugin-wise) to launch a rebuild of the calendar events.
*
* @package core
* @copyright 2021 onwards Simey Lameze <simey@moodle.com>
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require_once(__DIR__ . '/../../config.php');
require_once($CFG->libdir . "/clilib.php");
require_once($CFG->libdir . '/db/upgradelib.php');
// Supported options.
$long = ['fix' => false, 'help' => false];
$short = ['f' => 'fix', 'h' => 'help'];
// CLI options.
[$options, $unrecognized] = cli_get_params($long, $short);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
if ($options['help']) {
$help = <<<EOT
Fix orphaned calendar events.
This script detects calendar events that have had their
userid lost. By default it will perform various checks
and report them, showing the site status in an easy way.
Also, optionally (--fix), it wil try to recover as many
lost userids as possible from different sources. Note that
this script aims to process well-know events in core,
leaving custom events in 3rd part plugins mostly unmodified
because there isn't any consistent way to regenerate them.
For more details: https://tracker.moodle.org/browse/MDL-71156
Options:
-h, --help Print out this help.
-f, --fix Fix the orphaned calendar events in the DB.
If not specified only check and report problems to STDERR.
Usage:
- Only report: \$ sudo -u www-data /usr/bin/php admin/cli/fix_orphaned_calendar_events.php
- Report and fix: \$ sudo -u www-data /usr/bin/php admin/cli/fix_orphaned_calendar_events.php -f
EOT;
cli_writeln($help);
die;
}
// Check various usual pre-requisites.
if (empty($CFG->version)) {
cli_error('Database is not yet installed.');
}
$admin = get_admin();
if (!$admin) {
cli_error('Error: No admin account was found.');
}
if (moodle_needs_upgrading()) {
cli_error('Moodle upgrade pending, script execution suspended.');
}
// Do everything as admin by default.
\core\session\manager::set_user($admin);
// Report current site status.
cli_heading('Checking the site status');
$needsfix = upgrade_calendar_site_status();
// Report current calendar events status.
cli_heading('Checking the calendar events status');
$info = upgrade_calendar_events_status();
$hasbadevents = $info['total']->bad > 0 || $info['total']->bad != $info['other']->bad;
$needsfix = $needsfix || $hasbadevents;
// If, selected, fix as many calendar events as possible.
if ($options['fix']) {
// If the report has told us that the fix was not needed... ask for confirmation!
if (!$needsfix) {
cli_writeln("This site DOES NOT NEED to run the calendar events fix.");
$input = cli_input('Are you completely sure that you want to run the fix? (y/N)', 'N', ['y', 'Y', 'n', 'N']);
if (strtolower($input) != 'y') {
exit(0);
}
cli_writeln("");
}
cli_heading('Fixing as many as possible calendar events');
upgrade_calendar_events_fix_remaining($info);
// Report current (after fix) calendar events status.
cli_heading('Checking the calendar events status (after fix)');
upgrade_calendar_events_status();
} else if ($needsfix) {
// Fix option was not provided but problem events have been found. Notify the user and provide info how to fix these events.
cli_writeln("This site NEEDS to run the calendar events fix!");
cli_writeln("To fix the calendar events, re-run this script with the --fix option.");
}
+8 -8
View File
@@ -119,7 +119,7 @@ if (!empty($options['collation'])) {
if ($dbcollation->value !== $collation || $dbcharset->value !== $charset) {
// Try to convert the DB.
echo "Converting database to '$collation' for $CFG->wwwroot:\n";
$sql = "ALTER DATABASE $CFG->dbname DEFAULT CHARACTER SET $charset DEFAULT COLLATE = $collation";
$sql = "ALTER DATABASE `$CFG->dbname` DEFAULT CHARACTER SET $charset DEFAULT COLLATE = $collation";
try {
$DB->change_database_structure($sql);
} catch (exception $e) {
@@ -146,7 +146,7 @@ if (!empty($options['collation'])) {
} else {
try {
$DB->change_database_structure("ALTER TABLE $table->name CONVERT TO CHARACTER SET $charset COLLATE $collation");
$DB->change_database_structure("ALTER TABLE `$table->name` CONVERT TO CHARACTER SET $charset COLLATE $collation");
echo "CONVERTED\n";
$converted++;
} catch (ddl_exception $e) {
@@ -163,7 +163,7 @@ if (!empty($options['collation'])) {
}
}
$sql = "SHOW FULL COLUMNS FROM $table->name WHERE collation IS NOT NULL";
$sql = "SHOW FULL COLUMNS FROM `$table->name` WHERE collation IS NOT NULL";
$rs2 = $DB->get_recordset_sql($sql);
foreach ($rs2 as $column) {
$column = (object)array_change_key_case((array)$column, CASE_LOWER);
@@ -181,7 +181,7 @@ if (!empty($options['collation'])) {
$notnull = ($column->null === 'NO') ? 'NOT NULL' : 'NULL';
$default = (!is_null($column->default) and $column->default !== '') ? "DEFAULT '$column->default'" : '';
// primary, unique and inc are not supported for texts
$sql = "ALTER TABLE $table->name
$sql = "ALTER TABLE `$table->name`
MODIFY COLUMN $column->field $column->type
CHARACTER SET $charset
COLLATE $collation $notnull $default";
@@ -192,11 +192,11 @@ if (!empty($options['collation'])) {
$default = !is_null($column->default) ? "DEFAULT '$column->default'" : '';
if ($rowformat != '') {
$sql = "ALTER TABLE $table->name $rowformat";
$sql = "ALTER TABLE `$table->name` $rowformat";
$DB->change_database_structure($sql);
}
$sql = "ALTER TABLE $table->name
$sql = "ALTER TABLE `$table->name`
MODIFY COLUMN $column->field $column->type
CHARACTER SET $charset
COLLATE $collation $notnull $default";
@@ -315,8 +315,8 @@ function mysql_set_row_format($tablename, $charset, $collation, $engine) {
if ($rs->row_format == 'Compact' || $rs->row_format == 'Redundant') {
$rowformat = $DB->get_row_format_sql($engine, $collation);
// Try to convert to compressed format and then try updating the collation again.
$DB->change_database_structure("ALTER TABLE $tablename $rowformat");
$DB->change_database_structure("ALTER TABLE $tablename CONVERT TO CHARACTER SET $charset COLLATE $collation");
$DB->change_database_structure("ALTER TABLE `$tablename` $rowformat");
$DB->change_database_structure("ALTER TABLE `$tablename` CONVERT TO CHARACTER SET $charset COLLATE $collation");
} else {
// Row format may not be the problem. Can not diagnose problem. Send fail reply.
return false;
+2 -2
View File
@@ -158,7 +158,7 @@ if (!empty($options['info'])) {
}
foreach ($fixtables as $table) {
$DB->change_database_structure("ALTER TABLE {$prefix}$table ROW_FORMAT=Compressed");
$DB->change_database_structure("ALTER TABLE `{$prefix}$table` ROW_FORMAT=Compressed");
echo str_pad($prefix . $table, 32, ' ', STR_PAD_RIGHT) . " ... Compressed\n";
}
@@ -189,7 +189,7 @@ if (!empty($options['info'])) {
echo "SET GLOBAL innodb_file_per_table=1;\n";
echo "SET GLOBAL innodb_file_format=Barracuda;\n";
foreach ($fixtables as $table) {
echo "ALTER TABLE {$prefix}$table ROW_FORMAT=Compressed;\n";
echo "ALTER TABLE `{$prefix}$table` ROW_FORMAT=Compressed;\n";
}
echo "\n";
exit(0);
+3 -1
View File
@@ -3447,7 +3447,7 @@
</PHP_EXTENSION>
<PHP_EXTENSION name="hash" level="required"/>
<PHP_EXTENSION name="fileinfo" level="required"/>
<PHP_EXTENSION name="sodium" level="required"/>
<PHP_EXTENSION name="sodium" level="optional"/>
</PHP_EXTENSIONS>
<PHP_SETTINGS>
<PHP_SETTING name="memory_limit" value="96M" level="required">
@@ -3527,6 +3527,8 @@
<ON_CHECK message="sixtyfourbitswarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_max_input_vars" level="optional">
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
</COMPATIBILITY_MATRIX>
+5 -4
View File
@@ -45,7 +45,7 @@ list($options, $unrecognised) = cli_get_params(
array(
'stop-on-failure' => 0,
'verbose' => false,
'replace' => false,
'replace' => '',
'help' => false,
'tags' => '',
'profile' => '',
@@ -70,7 +70,7 @@ $help = "
Behat utilities to run behat tests in parallel
Usage:
php run.php [--BEHAT_OPTION=\"value\"] [--feature=\"value\"] [--replace] [--fromrun=value --torun=value] [--help]
php run.php [--BEHAT_OPTION=\"value\"] [--feature=\"value\"] [--replace=\"{run}\"] [--fromrun=value --torun=value] [--help]
Options:
--BEHAT_OPTION Any combination of behat option specified in http://behat.readthedocs.org/en/v2.5/guides/6.cli.html
@@ -144,9 +144,10 @@ $extraopts = $unrecognised;
if ($options['profile']) {
$profile = $options['profile'];
// If profile passed is not set, then exit.
// If profile passed is not set, then exit (note we skip if the 'replace' option is found within the 'profile' value).
if (!isset($CFG->behat_config[$profile]) && !isset($CFG->behat_profiles[$profile]) &&
!(isset($options['replace']) && (strpos($options['profile'], $options['replace']) >= 0 ))) {
!($options['replace'] && (strpos($profile, (string) $options['replace']) !== false))) {
echo "Invalid profile passed: " . $profile . PHP_EOL;
exit(1);
}
@@ -21,5 +21,5 @@ Feature: Transform date time string arguments
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I follow "Test assignment name"
And I should see "##yesterday##l, j F Y##"
And I should see "##yesterday##%A, %d %B %Y##"
And I log out
@@ -189,7 +189,12 @@ class cohort_role_assignments_table extends table_sql {
FROM {tool_cohortroles} uca
JOIN {user} u ON u.id = uca.userid
JOIN {cohort} c ON c.id = uca.cohortid";
$params = array();
// Check if any additional filtering is required.
[$sqlwhere, $params] = $this->get_sql_where();
if ($sqlwhere) {
$sql .= " WHERE {$sqlwhere}";
}
// Add order by if needed.
if (!$count && $sqlsort = $this->get_sql_sort()) {
+2 -2
View File
@@ -86,8 +86,8 @@ if ($removeid) {
$title = get_string('existingcohortroles', 'tool_cohortroles');
echo $output->heading($title);
$url = new moodle_url('/admin/tool/cohortroles/index.php');
$table = new tool_cohortroles\output\cohort_role_assignments_table(uniqid(), $url);
$table = new tool_cohortroles\output\cohort_role_assignments_table('cohort-role-assignments', $pageurl);
echo $table->out(50, true);
echo $output->spacer();
@@ -37,67 +37,68 @@
This template does not have an example context because it includes ajax functionality.
}}
<div data-region="user-competency-full-info" data-node="user-competency" data-competencyid="{{usercompetency.competencyid}}" data-userid="{{usercompetency.userid}}" data-region-id="{{uniqid}}">
<div data-region="competency-summary">
{{#competency}}
{{> tool_lp/competency_summary }}
{{/competency}}
<dl>
{{#usercompetency}}
<dt>{{#str}}reviewstatus, tool_lp{{/str}}</dt>
<dd data-region="user-competency-status">{{statusname}}
<div data-region="competency-summary">
{{#competency}}
{{> tool_lp/competency_summary }}
{{/competency}}
<dl>
{{#usercompetency}}
<dt>{{#str}}reviewstatus, tool_lp{{/str}}</dt>
<dd data-region="user-competency-status">{{statusname}}
{{#isstatusinreview}}
- {{reviewer.fullname}}
{{/isstatusinreview}}
{{#isstatusinreview}}
- {{reviewer.fullname}}
{{/isstatusinreview}}
{{#isrequestreviewallowed}}<button class="btn btn-secondary" data-action="request-review">{{#str}}requestreview, tool_lp{{/str}}</button>{{/isrequestreviewallowed}}
{{#iscancelreviewrequestallowed}}<button class="btn btn-secondary" data-action="cancel-review-request">{{#str}}cancelreviewrequest, tool_lp{{/str}}</button>{{/iscancelreviewrequestallowed}}
{{#isstartreviewallowed}}<button class="btn btn-secondary" data-action="start-review">{{#str}}startreview, tool_lp{{/str}}</button>{{/isstartreviewallowed}}
{{#isstopreviewallowed}}<button class="btn btn-secondary" data-action="stop-review">{{#str}}stopreview, tool_lp{{/str}}</button>{{/isstopreviewallowed}}
</dd>
<dt>{{#str}}proficient, tool_lp{{/str}}</dt>
<dd>
<span class="badge {{#proficiency}}badge-success{{/proficiency}}{{^proficiency}}badge-danger{{/proficiency}} float-left">
{{proficiencyname}}
</span>
</dd>
<dt>{{#str}}rating, tool_lp{{/str}}</dt>
<dd>{{gradename}}
{{#cangrade}}
<button class="btn btn-secondary" id="rate_{{uniqid}}">{{#str}}rate, tool_lp{{/str}}</button>
{{/cangrade}}
</dd>
{{#js}}
require(['jquery', 'tool_lp/grade_user_competency_inline', 'tool_lp/user_competency_info', 'tool_lp/user_competency_workflow'], function($, mod, info, UserCompWorkflow) {
{{#isrequestreviewallowed}}<button class="btn btn-secondary" data-action="request-review">{{#str}}requestreview, tool_lp{{/str}}</button>{{/isrequestreviewallowed}}
{{#iscancelreviewrequestallowed}}<button class="btn btn-secondary" data-action="cancel-review-request">{{#str}}cancelreviewrequest, tool_lp{{/str}}</button>{{/iscancelreviewrequestallowed}}
{{#isstartreviewallowed}}<button class="btn btn-secondary" data-action="start-review">{{#str}}startreview, tool_lp{{/str}}</button>{{/isstartreviewallowed}}
{{#isstopreviewallowed}}<button class="btn btn-secondary" data-action="stop-review">{{#str}}stopreview, tool_lp{{/str}}</button>{{/isstopreviewallowed}}
</dd>
<dt>{{#str}}proficient, tool_lp{{/str}}</dt>
<dd>
<span class="badge {{#proficiency}}badge-success{{/proficiency}}{{^proficiency}}badge-danger{{/proficiency}} float-left">
{{proficiencyname}}
</span>
</dd>
<dt>{{#str}}rating, tool_lp{{/str}}</dt>
<dd>{{gradename}}
{{#cangrade}}
<button class="btn btn-secondary" id="rate_{{uniqid}}">{{#str}}rate, tool_lp{{/str}}</button>
{{/cangrade}}
</dd>
{{#js}}
require(['jquery', 'tool_lp/grade_user_competency_inline', 'tool_lp/user_competency_info', 'tool_lp/user_competency_workflow'], function($, mod, info, UserCompWorkflow) {
var competencyElement = $('[data-region-id="{{uniqid}}"]');
var infoReloader = new info(competencyElement, '{{competency.competency.id}}', '{{user.id}}');
var competencyElement = $('[data-region-id="{{uniqid}}"]');
var infoReloader = new info(competencyElement, '{{competency.competency.id}}', '{{user.id}}');
var ucw = new UserCompWorkflow();
ucw.registerEvents('[data-region="user-competency-status"]');
ucw.on('status-changed', infoReloader.reload.bind(infoReloader));
ucw.on('error-occured', infoReloader.reload.bind(infoReloader));
var ucw = new UserCompWorkflow();
ucw.registerEvents('[data-region="user-competency-status"]');
ucw.on('status-changed', infoReloader.reload.bind(infoReloader));
ucw.on('error-occured', infoReloader.reload.bind(infoReloader));
var inlineGrader = new mod('#rate_{{uniqid}}', '{{competency.scaleid}}', '{{competency.competency.id}}', '{{user.id}}', '{{plan.id}}', '', '{{#str}}chooserating, tool_lp{{/str}}');
inlineGrader.on('competencyupdated', infoReloader.reload.bind(infoReloader));
});
{{/js}}
{{/usercompetency}}
</dl>
{{#commentarea}}
{{#canpostorhascomments}}
{{>tool_lp/comment_area}}
{{/canpostorhascomments}}
{{/commentarea}}
<dl data-region="evidence-listing">
<dt>{{#str}}evidence, tool_lp{{/str}}</dt>
<dd>
{{#evidence}}
{{> tool_lp/evidence_summary }}
{{/evidence}}
{{^evidence}}
<p>{{#str}}noevidence, tool_lp{{/str}}</p>
{{/evidence}}
</dd>
</dl>
var inlineGrader = new mod('#rate_{{uniqid}}', '{{competency.scaleid}}', '{{competency.competency.id}}', '{{user.id}}', '{{plan.id}}', '', '{{#str}}chooserating, tool_lp{{/str}}');
inlineGrader.on('competencyupdated', infoReloader.reload.bind(infoReloader));
});
{{/js}}
{{/usercompetency}}
</dl>
{{#commentarea}}
{{#canpostorhascomments}}
{{>tool_lp/comment_area}}
{{/canpostorhascomments}}
{{/commentarea}}
<dl data-region="evidence-listing">
<dt>{{#str}}evidence, tool_lp{{/str}}</dt>
<dd>
{{#evidence}}
{{> tool_lp/evidence_summary }}
{{/evidence}}
{{^evidence}}
<p>{{#str}}noevidence, tool_lp{{/str}}</p>
{{/evidence}}
</dd>
</dl>
</div>
</div>
+24 -22
View File
@@ -170,32 +170,34 @@ function tool_mobile_myprofile_navigation(\core_user\output\myprofile\tree $tree
}
// Check if the user is using the app, encouraging him to use it otherwise.
$usertoken = tool_mobile_get_token($user->id);
$mobilestrconnected = null;
$mobilelastaccess = null;
if ($iscurrentuser || is_siteadmin()) {
$usertoken = tool_mobile_get_token($user->id);
$mobilestrconnected = null;
$mobilelastaccess = null;
if ($usertoken) {
$mobilestrconnected = get_string('lastsiteaccess');
if ($usertoken->lastaccess) {
$mobilelastaccess = userdate($usertoken->lastaccess) . "&nbsp; (" . format_time(time() - $usertoken->lastaccess) . ")";
// Logout link.
$validtoken = empty($usertoken->validuntil) || time() < $usertoken->validuntil;
if ($iscurrentuser && $validtoken) {
$url = new moodle_url('/'.$CFG->admin.'/tool/mobile/logout.php', ['sesskey' => sesskey()]);
$logoutlink = html_writer::link($url, get_string('logout'));
$mobilelastaccess .= "&nbsp; ($logoutlink)";
if ($usertoken) {
$mobilestrconnected = get_string('lastsiteaccess');
if ($usertoken->lastaccess) {
$mobilelastaccess = userdate($usertoken->lastaccess) . "&nbsp; (" . format_time(time() - $usertoken->lastaccess) . ")";
// Logout link.
$validtoken = empty($usertoken->validuntil) || time() < $usertoken->validuntil;
if ($iscurrentuser && $validtoken) {
$url = new moodle_url('/'.$CFG->admin.'/tool/mobile/logout.php', ['sesskey' => sesskey()]);
$logoutlink = html_writer::link($url, get_string('logout'));
$mobilelastaccess .= "&nbsp; ($logoutlink)";
}
} else {
// We should not reach this point.
$mobilelastaccess = get_string("never");
}
} else {
// We should not reach this point.
$mobilelastaccess = get_string("never");
} else if ($url = tool_mobile_create_app_download_url()) {
$mobilestrconnected = get_string('mobileappenabled', 'tool_mobile', $url->out());
}
} else if ($url = tool_mobile_create_app_download_url()) {
$mobilestrconnected = get_string('mobileappenabled', 'tool_mobile', $url->out());
}
if ($mobilestrconnected) {
$newnodes[] = new core_user\output\myprofile\node('mobile', 'mobileappnode', $mobilestrconnected, null, null,
$mobilelastaccess);
if ($mobilestrconnected) {
$newnodes[] = new core_user\output\myprofile\node('mobile', 'mobileappnode', $mobilestrconnected, null, null,
$mobilelastaccess);
}
}
// Add nodes, if any.
@@ -310,11 +310,10 @@ class profile_manager {
'CURLOPT_HEADER' => 0,
];
$content = $curl->get($url, null, $options);
$errno = $curl->get_errno();
$info = $curl->get_info();
// The base cURL seems fine, let's press on.
if (!$errno) {
if (!$curl->get_errno() && !$curl->error) {
// WebFinger gave us a 404 back so the user has no droids here.
if ($info['http_code'] >= 400) {
if ($info['http_code'] === 404) {
@@ -132,12 +132,12 @@ Feature: Upload users
# Boost check.
And I log in as "jonest"
And I am on "Maths" course homepage
And "Turn editing on" "button" should not exist
And "Calendar" "link" should be visible
And I log out
# Classic check.
And I log in as "reznor"
And I am on "Maths" course homepage
And "Turn editing on" "button" should exist
And "Calendar" "link" should not be visible
@javascript
Scenario: Upload users setting their user theme when allowuserthemes is false
+1 -11
View File
@@ -438,17 +438,7 @@ class manager {
$filename = 'tour_export_' . $tour->get_id() . '_' . time() . '.json';
// Force download.
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . 'GMT');
header('Pragma: no-cache');
header('Accept-Ranges: none');
header('Content-disposition: attachment; filename=' . $filename);
header('Content-length: ' . strlen($exportstring));
header('Content-type: text/calendar; charset=utf-8');
echo $exportstring;
die;
send_file($exportstring, $filename, 0, 0, true, true);
}
/**
@@ -76,10 +76,10 @@ Feature: Apply accessibility to a tour
And "input[tabindex],button[tabindex]" "css_element" should exist
When I click on "Next" "button"
Then "input[aria-describedby^='tour-step-tool_usertours'],button[aria-describedby^='tour-step-tool_usertours']" "css_element" should not exist
And "input[tabindex],button[tabindex]" "css_element" should not exist
And "input[tabindex]:not([tabindex='-1']),button[tabindex]:not([tabindex='-1'])" "css_element" should not exist
When I click on "Previous" "button"
Then "input[aria-describedby^='tour-step-tool_usertours'],button[aria-describedby^='tour-step-tool_usertours']" "css_element" should exist
And "input[tabindex],button[tabindex]" "css_element" should exist
When I click on "End tour" "button"
Then "input[aria-describedby^='tour-step-tool_usertours'],button[aria-describedby^='tour-step-tool_usertours']" "css_element" should not exist
And "input[tabindex],button[tabindex]" "css_element" should not exist
And "input[tabindex]:not([tabindex='0']),button[tabindex]:not([tabindex='0'])" "css_element" should not exist
+27
View File
@@ -178,6 +178,33 @@ class prediction {
\core\event\prediction_action_started::create($eventdata)->trigger();
}
/**
* Get the executed actions.
*
* Actions could be filtered by actionname.
*
* @param array $actionnamefilter Limit the results obtained to this list of action names.
* @param int $userid the user id. Current user by default.
* @return array of actions.
*/
public function get_executed_actions(array $actionnamefilter = null, int $userid = 0): array {
global $USER, $DB;
$conditions[] = "predictionid = :predictionid";
$params['predictionid'] = $this->get_prediction_data()->id;
if (!$userid) {
$userid = $USER->id;
}
$conditions[] = "userid = :userid";
$params['userid'] = $userid;
if ($actionnamefilter) {
list($actionsql, $actionparams) = $DB->get_in_or_equal($actionnamefilter, SQL_PARAMS_NAMED);
$conditions[] = "actionname $actionsql";
$params = $params + $actionparams;
}
return $DB->get_records_select('analytics_prediction_actions', implode(' AND ', $conditions), $params);
}
/**
* format_calculations
*
@@ -112,6 +112,86 @@ class analytics_prediction_actions_testcase extends advanced_testcase {
$this->assertEquals(2, $DB->count_records('analytics_prediction_actions'));
}
/**
* Data provider for test_get_executed_actions.
*
* @return array
*/
public function execute_actions_provider(): array {
return [
'Empty actions with no filter' => [
[],
[],
0
],
'Empty actions with filter' => [
[],
[\core_analytics\prediction::ACTION_FIXED],
0
],
'Multiple actions with no filter' => [
[
\core_analytics\prediction::ACTION_FIXED,
\core_analytics\prediction::ACTION_FIXED,
\core_analytics\prediction::ACTION_INCORRECTLY_FLAGGED
],
[],
3
],
'Multiple actions applying filter' => [
[
\core_analytics\prediction::ACTION_FIXED,
\core_analytics\prediction::ACTION_FIXED,
\core_analytics\prediction::ACTION_INCORRECTLY_FLAGGED
],
[\core_analytics\prediction::ACTION_FIXED],
2
],
'Multiple actions not applying filter' => [
[
\core_analytics\prediction::ACTION_FIXED,
\core_analytics\prediction::ACTION_FIXED,
\core_analytics\prediction::ACTION_INCORRECTLY_FLAGGED
],
[\core_analytics\prediction::ACTION_NOT_APPLICABLE],
0
],
'Multiple actions with multiple filter' => [
[
\core_analytics\prediction::ACTION_FIXED,
\core_analytics\prediction::ACTION_FIXED,
\core_analytics\prediction::ACTION_INCORRECTLY_FLAGGED
],
[\core_analytics\prediction::ACTION_FIXED, \core_analytics\prediction::ACTION_INCORRECTLY_FLAGGED],
3
],
];
}
/**
* Tests for get_executed_actions() function.
*
* @dataProvider execute_actions_provider
* @param array $actionstoexecute An array of actions to execute
* @param array $actionnamefilter Actions to filter
* @param int $returned Number of actions returned
*
* @covers \core_analytics\prediction::get_executed_actions
*/
public function test_get_executed_actions(array $actionstoexecute, array $actionnamefilter, int $returned) {
$this->setUser($this->teacher2);
list($ignored, $predictions) = $this->model->get_predictions($this->context, true);
$prediction = reset($predictions);
$target = $this->model->get_target();
foreach($actionstoexecute as $action) {
$prediction->action_executed($action, $target);
}
$filteredactions = $prediction->get_executed_actions($actionnamefilter);
$this->assertCount($returned, $filteredactions);
}
/**
* test_get_predictions
*/
+5
View File
@@ -1,6 +1,11 @@
This files describes API changes in analytics sub system,
information provided here is intended especially for developers.
=== 3.10.4 ===
* A new function get_executed_actions() has been added to \core_analytics\prediction class
to get all (or filtered by action name) executed actions of a prediction
=== 3.8 ===
* "Time-splitting method" have been replaced by "Analysis interval" for the language strings that are
+1 -1
View File
@@ -1,3 +1,3 @@
Description of phpCAS 1.3.8 library import
* downloaded from http://downloads.jasig.org/cas-clients/php/current/
* downloaded source code from https://github.com/apereo/phpCAS/releases
+1 -1
View File
@@ -4,7 +4,7 @@
<location>CAS</location>
<name>CAS</name>
<license>Apache</license>
<version>1.3.7</version>
<version>1.3.8</version>
<licenseversion>2.0</licenseversion>
</library>
</libraries>
+1 -1
View File
@@ -45,7 +45,7 @@ $string['auth_dbname'] = 'Name of the database itself. Leave empty if using an O
$string['auth_dbname_key'] = 'DB name';
$string['auth_dbpass'] = 'Password matching the above username';
$string['auth_dbpass_key'] = 'Password';
$string['auth_dbpasstype'] = '<p>Specify the format that the password field is using.</p> <p>Use \'internal\' if you want the external database to manage usernames and email addresses, but Moodle to manage passwords. If you use \'internal\', you <i>must</i> provide a populated email address field in the external database, and you must execute both admin/cron.php and auth/db/cli/sync_users.php regularly. Moodle will send an email to new users with a temporary password.</p>';
$string['auth_dbpasstype'] = '<p>Specify the format that the password field is using.</p> <p>Use \'internal\' if you want the external database to manage usernames and email addresses, but Moodle to manage passwords. If you use \'internal\', you must provide a populated email address field in the external database, and you must enable the \auth_db\task\sync_users scheduled task. Moodle will send an email to new users with a temporary password.</p>';
$string['auth_dbpasstype_key'] = 'Password format';
$string['auth_dbreviveduser'] = 'Revived user {$a->name} id {$a->id}';
$string['auth_dbrevivedusererror'] = 'Error reviving user {$a}';
+1 -1
View File
@@ -75,7 +75,7 @@ $string['auth_ldap_passwdexpire_settings'] = 'LDAP password expiry settings';
$string['auth_ldap_preventpassindb'] = 'Select yes to prevent passwords from being stored in Moodle\'s DB.';
$string['auth_ldap_preventpassindb_key'] = 'Prevent password caching';
$string['auth_ldap_rolecontext'] = '{$a->localname} context';
$string['auth_ldap_rolecontext_help'] = 'LDAP context used to select for <i>{$a->localname}</i> mapping. Separate multiple groups with \';\'. Usually something like "cn={$a->shortname},ou=staff,o=myorg".';
$string['auth_ldap_rolecontext_help'] = 'LDAP context used to select for <i>{$a->localname}</i> mapping. Separate multiple groups with \';\'. Usually something like "cn={$a->shortname},ou=first-ou-with-role-groups,o=myorg; cn={$a->shortname},ou=second-ou-with-role-groups,o=myorg".';
$string['auth_ldap_search_sub'] = 'Search users from subcontexts.';
$string['auth_ldap_search_sub_key'] = 'Search subcontexts';
$string['auth_ldap_server_settings'] = 'LDAP server settings';
+1 -3
View File
@@ -710,9 +710,7 @@ class auth_plugin_mnet extends auth_plugin_base {
foreach($superArray as $subArray) {
$subArray = array_values($subArray);
$instring = "('".implode("', '",$subArray)."')";
$query = "select id, session_id, username from {mnet_session} where username in $instring";
$results = $DB->get_records_sql($query);
$results = $DB->get_records_list('mnet_session', 'username', $subArray, '', 'id, session_id, username');
if ($results == false) {
// We seem to have a username that breaks our query:
+6 -6
View File
@@ -290,9 +290,9 @@ abstract class info {
*/
protected function warn_about_invalid_availability(\coding_exception $e) {
$name = $this->get_thing_name();
// If it occurs while building modinfo based on somebody calling $cm->name,
// we can't get $cm->name, and this line will cause a warning.
$htmlname = @$this->format_info($name, $this->course);
$htmlname = $this->format_info($name, $this->course);
// Because we call format_info here, likely in the middle of building dynamic data for the
// activity, there could be a chance that the name might not be available.
if ($htmlname === '') {
// So instead use the numbers (cmid) from the tag.
$htmlname = preg_replace('~[^0-9]~', '', $name);
@@ -739,11 +739,11 @@ abstract class info {
$info = preg_replace_callback('~<AVAILABILITY_CMNAME_([0-9]+)/>~',
function($matches) use($modinfo, $context) {
$cm = $modinfo->get_cm($matches[1]);
if ($cm->has_view() and $cm->uservisible) {
if ($cm->has_view() and $cm->get_user_visible()) {
// Help student by providing a link to the module which is preventing availability.
return \html_writer::link($cm->url, format_string($cm->name, true, array('context' => $context)));
return \html_writer::link($cm->get_url(), format_string($cm->get_name(), true, ['context' => $context]));
} else {
return format_string($cm->name, true, array('context' => $context));
return format_string($cm->get_name(), true, ['context' => $context]);
}
}, $info);
+29
View File
@@ -511,4 +511,33 @@ class info_testcase extends advanced_testcase {
sort($result);
$this->assertEquals($expected, $result);
}
/**
* Tests the info_module class when involved in a recursive call to $cm->name.
*/
public function test_info_recursive_name_call() {
global $DB;
$this->resetAfterTest();
// Create a course and page.
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$page1 = $generator->create_module('page', ['course' => $course->id, 'name' => 'Page1']);
// Set invalid availability.
$DB->set_field('course_modules', 'availability', 'not valid', ['id' => $page1->cmid]);
// Get the cm_info object.
$this->setAdminUser();
$modinfo = get_fast_modinfo($course);
$cm1 = $modinfo->get_cm($page1->cmid);
// At this point we will generate dynamic data for $cm1, which will cause the debugging
// call below.
$this->assertEquals('Page1', $cm1->name);
$this->assertDebuggingCalled('Error processing availability data for ' .
'&lsquo;Page1&rsquo;: Invalid availability text');
}
}
@@ -198,8 +198,11 @@ M.core_availability.form = {
// If the groupmode and grouping id aren't set, disable it.
var groupmode = Y.one('#id_groupmode');
var groupingid = Y.one('#id_groupingid');
if ((!groupmode || Number(groupmode.get('value')) === 0) &&
(!groupingid || Number(groupingid.get('value')) === 0)) {
var groupavailability = Number(this.restrictByGroup.getData('groupavailability')) === 1;
var groupingavailability = Number(this.restrictByGroup.getData('groupingavailability')) === 1;
if ((!groupmode || Number(groupmode.get('value')) === 0 || !groupavailability) &&
(!groupingid || Number(groupingid.get('value')) === 0 || !groupingavailability)) {
this.restrictByGroup.set('disabled', true);
return;
}
@@ -220,22 +223,28 @@ M.core_availability.form = {
e.preventDefault();
// Add the condition.
var groupmode = Y.one('#id_groupmode');
var groupingid = Y.one('#id_groupingid');
var groupavailability = Number(this.restrictByGroup.getData('groupavailability')) === 1;
var groupingavailability = Number(this.restrictByGroup.getData('groupingavailability')) === 1;
var newChild;
if (groupingid && Number(groupingid.get('value')) !== 0) {
if (groupingid && Number(groupingid.get('value')) !== 0 && groupingavailability) {
// Add a grouping restriction if one is specified.
newChild = new M.core_availability.Item(
{type: 'grouping', id: Number(groupingid.get('value'))}, true);
} else {
} else if (groupmode && groupavailability) {
// Otherwise just add a group restriction.
newChild = new M.core_availability.Item({type: 'group'}, true);
}
// Refresh HTML.
this.rootList.addChild(newChild);
this.update();
this.rootList.renumber();
this.rootList.updateHtml();
if (newChild !== null) {
this.rootList.addChild(newChild);
this.update();
this.rootList.renumber();
this.rootList.updateHtml();
}
}
};
File diff suppressed because one or more lines are too long
@@ -198,8 +198,11 @@ M.core_availability.form = {
// If the groupmode and grouping id aren't set, disable it.
var groupmode = Y.one('#id_groupmode');
var groupingid = Y.one('#id_groupingid');
if ((!groupmode || Number(groupmode.get('value')) === 0) &&
(!groupingid || Number(groupingid.get('value')) === 0)) {
var groupavailability = Number(this.restrictByGroup.getData('groupavailability')) === 1;
var groupingavailability = Number(this.restrictByGroup.getData('groupingavailability')) === 1;
if ((!groupmode || Number(groupmode.get('value')) === 0 || !groupavailability) &&
(!groupingid || Number(groupingid.get('value')) === 0 || !groupingavailability)) {
this.restrictByGroup.set('disabled', true);
return;
}
@@ -220,22 +223,28 @@ M.core_availability.form = {
e.preventDefault();
// Add the condition.
var groupmode = Y.one('#id_groupmode');
var groupingid = Y.one('#id_groupingid');
var groupavailability = Number(this.restrictByGroup.getData('groupavailability')) === 1;
var groupingavailability = Number(this.restrictByGroup.getData('groupingavailability')) === 1;
var newChild;
if (groupingid && Number(groupingid.get('value')) !== 0) {
if (groupingid && Number(groupingid.get('value')) !== 0 && groupingavailability) {
// Add a grouping restriction if one is specified.
newChild = new M.core_availability.Item(
{type: 'grouping', id: Number(groupingid.get('value'))}, true);
} else {
} else if (groupmode && groupavailability) {
// Otherwise just add a group restriction.
newChild = new M.core_availability.Item({type: 'group'}, true);
}
// Refresh HTML.
this.rootList.addChild(newChild);
this.update();
this.rootList.renumber();
this.rootList.updateHtml();
if (newChild !== null) {
this.rootList.addChild(newChild);
this.update();
this.rootList.renumber();
this.rootList.updateHtml();
}
}
};
+17 -8
View File
@@ -196,8 +196,11 @@ M.core_availability.form = {
// If the groupmode and grouping id aren't set, disable it.
var groupmode = Y.one('#id_groupmode');
var groupingid = Y.one('#id_groupingid');
if ((!groupmode || Number(groupmode.get('value')) === 0) &&
(!groupingid || Number(groupingid.get('value')) === 0)) {
var groupavailability = Number(this.restrictByGroup.getData('groupavailability')) === 1;
var groupingavailability = Number(this.restrictByGroup.getData('groupingavailability')) === 1;
if ((!groupmode || Number(groupmode.get('value')) === 0 || !groupavailability) &&
(!groupingid || Number(groupingid.get('value')) === 0 || !groupingavailability)) {
this.restrictByGroup.set('disabled', true);
return;
}
@@ -218,22 +221,28 @@ M.core_availability.form = {
e.preventDefault();
// Add the condition.
var groupmode = Y.one('#id_groupmode');
var groupingid = Y.one('#id_groupingid');
var groupavailability = Number(this.restrictByGroup.getData('groupavailability')) === 1;
var groupingavailability = Number(this.restrictByGroup.getData('groupingavailability')) === 1;
var newChild;
if (groupingid && Number(groupingid.get('value')) !== 0) {
if (groupingid && Number(groupingid.get('value')) !== 0 && groupingavailability) {
// Add a grouping restriction if one is specified.
newChild = new M.core_availability.Item(
{type: 'grouping', id: Number(groupingid.get('value'))}, true);
} else {
} else if (groupmode && groupavailability) {
// Otherwise just add a group restriction.
newChild = new M.core_availability.Item({type: 'group'}, true);
}
// Refresh HTML.
this.rootList.addChild(newChild);
this.update();
this.rootList.renumber();
this.rootList.updateHtml();
if (newChild !== null) {
this.rootList.addChild(newChild);
this.update();
this.rootList.renumber();
this.rootList.updateHtml();
}
}
};
+23 -2
View File
@@ -4055,9 +4055,30 @@ class restore_contentbankcontent_structure_step extends restore_structure_step {
$exists = $DB->record_exists('contentbank_content', $params);
if (!$exists) {
$params['configdata'] = $data->configdata;
$params['usercreated'] = $this->get_mappingid('user', $data->usercreated);
$params['usermodified'] = $this->get_mappingid('user', $data->usermodified);
$params['timemodified'] = time();
// Trying to map users. Users cannot always be mapped, e.g. when copying.
$params['usercreated'] = $this->get_mappingid('user', $data->usercreated);
if (!$params['usercreated']) {
// Leave the content creator unchanged when we are restoring the same site.
// Otherwise use current user id.
if ($this->task->is_samesite()) {
$params['usercreated'] = $data->usercreated;
} else {
$params['usercreated'] = $this->task->get_userid();
}
}
$params['usermodified'] = $this->get_mappingid('user', $data->usermodified);
if (!$params['usermodified']) {
// Leave the content modifier unchanged when we are restoring the same site.
// Otherwise use current user id.
if ($this->task->is_samesite()) {
$params['usermodified'] = $data->usermodified;
} else {
$params['usermodified'] = $this->task->get_userid();
}
}
$newitemid = $DB->insert_record('contentbank_content', $params);
$this->set_mapping('contentbank_content', $oldid, $newitemid, true);
}
+67
View File
@@ -278,6 +278,73 @@ class core_backup_automated_backup_testcase extends advanced_testcase {
$this->assertTrue($skipped);
$this->expectOutputRegex("/Skipping $course->fullname \(Not modified since previous backup\)/");
}
/**
* Test the task completes when coureid is missing.
*/
public function test_task_complete_when_courseid_is_missing() {
global $DB;
$admin = get_admin();
$classobject = $this->backupcronautomatedhelper->return_this();
// Create this backup course.
$backupcourse = new stdClass;
$backupcourse->courseid = $this->course->id;
$backupcourse->laststatus = backup_cron_automated_helper::BACKUP_STATUS_NOTYETRUN;
$DB->insert_record('backup_courses', $backupcourse);
$backupcourse = $DB->get_record('backup_courses', ['courseid' => $this->course->id]);
// Create a backup task.
$method = new ReflectionMethod('\backup_cron_automated_helper', 'push_course_backup_adhoc_task');
$method->setAccessible(true); // Allow accessing of private method.
$method->invokeArgs($classobject, [$backupcourse, $admin]);
// Delete course for this test.
delete_course($this->course->id, false);
$task = core\task\manager::get_next_adhoc_task(time());
ob_start();
$task->execute();
$output = ob_get_clean();
$this->assertStringContainsString('Invalid course id: ' . $this->course->id . ', task aborted.', $output);
core\task\manager::adhoc_task_complete($task);
}
/**
* Test the task completes when backup course is missing.
*/
public function test_task_complete_when_backup_course_is_missing() {
global $DB;
$admin = get_admin();
$classobject = $this->backupcronautomatedhelper->return_this();
// Create this backup course.
$backupcourse = new stdClass;
$backupcourse->courseid = $this->course->id;
$backupcourse->laststatus = backup_cron_automated_helper::BACKUP_STATUS_NOTYETRUN;
$DB->insert_record('backup_courses', $backupcourse);
$backupcourse = $DB->get_record('backup_courses', ['courseid' => $this->course->id]);
// Create a backup task.
$method = new ReflectionMethod('\backup_cron_automated_helper', 'push_course_backup_adhoc_task');
$method->setAccessible(true); // Allow accessing of private method.
$method->invokeArgs($classobject, [$backupcourse, $admin]);
// Delete backup course for this test.
$DB->delete_records('backup_courses', ['courseid' => $this->course->id]);
$task = core\task\manager::get_next_adhoc_task(time());
ob_start();
$task->execute();
$output = ob_get_clean();
$this->assertStringContainsString('Automated backup for course: ' . $this->course->fullname . ' encounters an error.',
$output);
core\task\manager::adhoc_task_complete($task);
}
}
/**
+1 -1
View File
@@ -1,7 +1,7 @@
This files describes API changes in /backup/*,
information provided here is intended especially for developers.
=== 4.0 ===
=== 3.10 ===
* Local plugins can now hook into a backup and restore process of grade items by
using define_grade_item_plugin_structure method (See MDL-69418).
+3 -1
View File
@@ -116,7 +116,9 @@ class backup_ui_stage_initial extends backup_ui_stage {
if (isset($data->$name) && $data->$name != $setting->get_value()) {
$setting->set_value($data->$name);
$changes++;
} else if (!isset($data->$name) && $setting->get_ui_type() == backup_setting::UI_HTML_CHECKBOX && $setting->get_value()) {
} else if (!isset($data->$name) && $setting->get_value() &&
$setting->get_ui_type() == backup_setting::UI_HTML_CHECKBOX &&
$setting->get_status() !== backup_setting::LOCKED_BY_HIERARCHY) {
$setting->set_value(0);
$changes++;
}
+1 -1
View File
@@ -52,7 +52,7 @@ if (!is_null($action)) {
} else { // Revoked badge.
header("HTTP/1.0 410 Gone");
$assertion = array();
if ($obversion == OPEN_BADGES_V2) {
if ($obversion >= OPEN_BADGES_V2) {
$assertionurl = new moodle_url('/badges/assertion.php', array('b' => $hash));
$assertion['id'] = $assertionurl->out();
}
+4
View File
@@ -43,6 +43,10 @@ if ($persistedissuer) {
$returnurl = new moodle_url('/badges/backpack-connect.php',
['action' => 'authorization', 'sesskey' => sesskey(), 'backpackid' => $backpackid]);
// If scope is not passed as parameter, use the issuer supported scopes.
if (empty($scope)) {
$scope = $issuer->get('scopessupported');
}
$client = new core_badges\oauth2\client($issuer, $returnurl, $scope, $externalbackpack);
if ($client) {
if (!$client->is_logged_in()) {
+2 -2
View File
@@ -128,7 +128,7 @@ class core_badges_assertion {
$email = empty($this->_data->backpackemail) ? $this->_data->email : $this->_data->backpackemail;
$assertionurl = new moodle_url('/badges/assertion.php', array('b' => $hash, 'obversion' => $this->_obversion));
if ($this->_obversion == OPEN_BADGES_V2) {
if ($this->_obversion >= OPEN_BADGES_V2) {
$classurl = new moodle_url('/badges/badge_json.php', array('id' => $this->get_badge_id()));
} else {
$classurl = new moodle_url('/badges/assertion.php', array('b' => $hash, 'action' => 1));
@@ -325,7 +325,7 @@ class core_badges_assertion {
*/
protected function embed_data_badge_version2 (&$json, $type = OPEN_BADGES_V2_TYPE_ASSERTION) {
// Specification Version 2.0.
if ($this->_obversion == OPEN_BADGES_V2) {
if ($this->_obversion >= OPEN_BADGES_V2) {
$badge = new badge($this->_data->id);
if (empty($this->_data->courseid)) {
$context = context_system::instance();
+2 -2
View File
@@ -56,12 +56,12 @@ class external_backpack extends \moodleform {
$mform->addElement('text', 'backpackapiurl', get_string('backpackapiurl', 'core_badges'));
$mform->setType('backpackapiurl', PARAM_URL);
$mform->addRule('backpackapiurl', null, 'required', null, 'client');
$mform->addRule('backpackapiurl', get_string('maximumchars', '', 255), 'maxlength', 50, 'client');
$mform->addRule('backpackapiurl', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addElement('text', 'backpackweburl', get_string('backpackweburl', 'core_badges'));
$mform->setType('backpackweburl', PARAM_URL);
$mform->addRule('backpackweburl', null, 'required', null, 'client');
$mform->addRule('backpackweburl', get_string('maximumchars', '', 255), 'maxlength', 50, 'client');
$mform->addRule('backpackweburl', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$apiversions = badges_get_badge_api_versions();
$mform->addElement('select', 'apiversion', get_string('apiversion', 'core_badges'), $apiversions);
+6 -3
View File
@@ -222,6 +222,7 @@ class client extends \core\oauth2\client {
$callbackurl = self::callback_url();
if ($granttype == 'authorization_code') {
$this->basicauth = true;
$params = array('code' => $code,
'grant_type' => $granttype,
'redirect_uri' => $callbackurl->out(false),
@@ -236,7 +237,7 @@ class client extends \core\oauth2\client {
);
}
if ($this->basicauth) {
$idsecret = urlencode($this->clientid) . ':' . urlencode($this->clientsecret);
$idsecret = $this->clientid . ':' . $this->clientsecret;
$this->setHeader('Authorization: Basic ' . base64_encode($idsecret));
} else {
$params['client_id'] = $this->clientid;
@@ -244,11 +245,13 @@ class client extends \core\oauth2\client {
}
// Requests can either use http GET or POST.
$response = $this->post($this->token_url(), $this->build_post_data($params));
$r = json_decode($response);
if ($this->info['http_code'] !== 200) {
throw new moodle_exception('Could not upgrade oauth token');
$debuginfo = !empty($this->error) ? $this->error : $response;
throw new moodle_exception('oauth2refreshtokenerror', 'core_error', '', $this->info['http_code'], $debuginfo);
}
$r = json_decode($response);
if (is_null($r)) {
throw new moodle_exception("Could not decode JSON token response");
}
+13 -3
View File
@@ -240,13 +240,23 @@ class award_criteria_cohort extends award_criteria {
return array($join, $where, $params);
} else {
// User is a member of ALL of the specified cohorts.
$join = " LEFT JOIN {cohort_members} cm ON cm.userid = u.id";
$join = ' LEFT JOIN {cohort_members} cm ON cm.userid = u.id';
$where = ' AND (';
$i = 0;
foreach ($this->params as $param) {
$i++;
$where = ' AND cm.cohortid = :cohortid'.$i;
if ($i == 0) {
$where .= 'cm.cohortid = :cohortid'.$i;
} else {
$where .= ' OR cm.cohortid = :cohortid'.$i;
}
$params['cohortid'.$i] = $param['cohort'];
$i++;
}
$where .= ')
GROUP BY u.id, bi.badgeid
HAVING COUNT(cm.cohortid) = :cohortcount';
$params['cohortcount'] = $i;
return array($join, $where, $params);
}
}
+110 -8
View File
@@ -624,35 +624,127 @@ class core_badges_badgeslib_testcase extends advanced_testcase {
}
/**
* Test badges observer when cohort_member_added event is fired.
* Test badges observer when cohort_member_added event is fired and user required to belong to any cohort.
*
* @covers award_criteria_cohort
*/
public function test_badges_observer_cohort_criteria_review() {
public function test_badges_observer_any_cohort_criteria_review() {
global $CFG;
require_once("$CFG->dirroot/cohort/lib.php");
$cohort = $this->getDataGenerator()->create_cohort();
$cohort1 = $this->getDataGenerator()->create_cohort();
$cohort2 = $this->getDataGenerator()->create_cohort();
$this->preventResetByRollback(); // Messaging is not compatible with transactions.
$badge = new badge($this->badgeid);
$this->assertFalse($badge->is_issued($this->user->id));
$this->assertSame(0, $badge->review_all_criteria()); // Verify award_criteria_cohort->get_completed_criteria_sql().
// Set up the badge criteria.
$criteriaoverall = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_OVERALL, 'badgeid' => $badge->id));
$criteriaoverall->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY));
$criteriaoverall1 = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_COHORT, 'badgeid' => $badge->id));
$criteriaoverall1->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY, 'cohort_cohorts' => array('0' => $cohort->id)));
// Make the badge active.
$criteriaoverall1->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY,
'cohort_cohorts' => array('0' => $cohort1->id, '1' => $cohort2->id)));
$badge->set_status(BADGE_STATUS_ACTIVE);
// Reload it to contain criteria.
$badge = new badge($this->badgeid);
$this->assertFalse($badge->is_issued($this->user->id));
$this->assertSame(0, $badge->review_all_criteria()); // Verify award_criteria_cohort->get_completed_criteria_sql().
// Add the user to the cohort.
cohort_add_member($cohort->id, $this->user->id);
cohort_add_member($cohort2->id, $this->user->id);
$this->assertDebuggingCalled();
// Verify that the badge was awarded.
$this->assertDebuggingCalled();
$this->assertTrue($badge->is_issued($this->user->id));
// As the badge has been awarded to user because core_badges_observer been called when the member has been added to the
// cohort, there are no other users that can award this badge.
$this->assertSame(0, $badge->review_all_criteria()); // Verify award_criteria_cohort->get_completed_criteria_sql().
}
/**
* Test badges observer when cohort_member_added event is fired and user required to belong to multiple (all) cohorts.
*
* @covers award_criteria_cohort
*/
public function test_badges_observer_all_cohort_criteria_review() {
global $CFG;
require_once("$CFG->dirroot/cohort/lib.php");
$cohort1 = $this->getDataGenerator()->create_cohort();
$cohort2 = $this->getDataGenerator()->create_cohort();
$cohort3 = $this->getDataGenerator()->create_cohort();
// Add user2 to cohort1 and cohort3.
$user2 = $this->getDataGenerator()->create_user();
cohort_add_member($cohort3->id, $user2->id);
cohort_add_member($cohort1->id, $user2->id);
// Add user3 to cohort1, cohort2 and cohort3.
$user3 = $this->getDataGenerator()->create_user();
cohort_add_member($cohort1->id, $user3->id);
cohort_add_member($cohort2->id, $user3->id);
cohort_add_member($cohort3->id, $user3->id);
$this->preventResetByRollback(); // Messaging is not compatible with transactions.
// Cohort criteria are used in site badges.
$badge = new badge($this->badgeid);
$this->assertFalse($badge->is_issued($this->user->id));
$this->assertSame(0, $badge->review_all_criteria()); // Verify award_criteria_cohort->get_completed_criteria_sql().
// Set up the badge criteria.
$criteriaoverall = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_OVERALL, 'badgeid' => $badge->id));
$criteriaoverall->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY));
$criteriaoverall1 = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_COHORT, 'badgeid' => $badge->id));
$criteriaoverall1->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ALL,
'cohort_cohorts' => array('0' => $cohort1->id, '1' => $cohort2->id, '2' => $cohort3->id)));
$badge->set_status(BADGE_STATUS_ACTIVE);
// Reload it to contain criteria.
$badge = new badge($this->badgeid);
// Verify that the badge was not awarded yet (ALL cohorts are needed and review_all_criteria has to be called).
$this->assertFalse($badge->is_issued($this->user->id));
$this->assertFalse($badge->is_issued($user2->id));
$this->assertFalse($badge->is_issued($user3->id));
// Verify that after calling review_all_criteria, users with the criteria (user3) award the badge instantly.
$this->assertSame(1, $badge->review_all_criteria()); // Verify award_criteria_cohort->get_completed_criteria_sql().
$this->assertFalse($badge->is_issued($this->user->id));
$this->assertFalse($badge->is_issued($user2->id));
$this->assertTrue($badge->is_issued($user3->id));
$this->assertDebuggingCalled();
// Add the user to the cohort1.
cohort_add_member($cohort1->id, $this->user->id);
// Verify that the badge was not awarded yet (ALL cohorts are needed).
$this->assertFalse($badge->is_issued($this->user->id));
$this->assertSame(0, $badge->review_all_criteria()); // Verify award_criteria_cohort->get_completed_criteria_sql().
// Add the user to the cohort3.
cohort_add_member($cohort3->id, $this->user->id);
// Verify that the badge was not awarded yet (ALL cohorts are needed).
$this->assertFalse($badge->is_issued($this->user->id));
$this->assertSame(0, $badge->review_all_criteria()); // Verify award_criteria_cohort->get_completed_criteria_sql().
// Add user to cohort2.
cohort_add_member($cohort2->id, $this->user->id);
$this->assertDebuggingCalled();
// Verify that the badge was awarded (ALL cohorts).
$this->assertTrue($badge->is_issued($this->user->id));
// As the badge has been awarded to user because core_badges_observer been called when the member has been added to the
// cohort, there are no other users that can award this badge.
$this->assertSame(0, $badge->review_all_criteria()); // Verify award_criteria_cohort->get_completed_criteria_sql().
}
/**
@@ -698,6 +790,16 @@ class core_badges_badgeslib_testcase extends advanced_testcase {
$this->assertStringMatchesFormat($testassertion2->badge, json_encode($assertion2->get_badge_assertion()));
$this->assertStringMatchesFormat($testassertion2->class, json_encode($assertion2->get_badge_class()));
$this->assertStringMatchesFormat($testassertion2->issuer, json_encode($assertion2->get_issuer()));
// Test Openbadge specification version 2.1. It has the same format as OBv2.0.
// Get assertion version 2.1.
$award = reset($awards);
$assertion2 = new core_badges_assertion($award->uniquehash, OPEN_BADGES_V2P1);
// Make sure JSON strings have the same structure.
$this->assertStringMatchesFormat($testassertion2->badge, json_encode($assertion2->get_badge_assertion()));
$this->assertStringMatchesFormat($testassertion2->class, json_encode($assertion2->get_badge_class()));
$this->assertStringMatchesFormat($testassertion2->issuer, json_encode($assertion2->get_issuer()));
}
/**
+6 -6
View File
@@ -120,7 +120,7 @@ Feature: Backpack badges
And I click on "Delete" "button" in the "Delete site backpack" "dialogue"
Then I should see "The site backpack has been deleted."
And I should not see "https://dc.imsglobal.org"
And "Delete" "button" should not exist
And "Delete" "button" should not be visible
@javascript
Scenario: Add a new site backpack with authentication details checkbox
@@ -131,27 +131,27 @@ Feature: Backpack badges
And I set the field "backpackapiurl" to "http://backpackapiurl.cat"
And I set the field "backpackweburl" to "http://backpackweburl.cat"
And I set the field "apiversion" to "2.1"
Then "Include authentication details with the backpack" "checkbox" should not exist
Then "Include authentication details with the backpack" "checkbox" should not be visible
And I should not see "Badge issuer email address"
And I should not see "Badge issuer password"
And I set the field "apiversion" to "1"
And "Include authentication details with the backpack" "checkbox" should exist
And "Include authentication details with the backpack" "checkbox" should be visible
And I click on "includeauthdetails" "checkbox"
And I should see "Badge issuer email address"
And I should not see "Badge issuer password"
And I set the field "apiversion" to "2"
And "Include authentication details with the backpack" "checkbox" should exist
And "Include authentication details with the backpack" "checkbox" should be visible
And I should see "Badge issuer email address"
And I should see "Badge issuer password"
And I set the field "backpackemail" to "test@test.com"
And I set the field "password" to "123456"
And I press "Save changes"
And I click on "Edit" "link" in the "http://backpackweburl.cat" "table_row"
And "input[name=includeauthdetails][type=checkbox][value=1]" "css_element" should exist
And the field "Include authentication details with the backpack" matches value "1"
And I click on "includeauthdetails" "checkbox"
And I press "Save changes"
And I click on "Edit" "link" in the "http://backpackweburl.cat" "table_row"
And "input[name=includeauthdetails][type=checkbox][value=1]" "css_element" should not exist
And the field "Include authentication details with the backpack" matches value "0"
And I click on "includeauthdetails" "checkbox"
And I should not see "test@test.com"
And I log out
@@ -23,7 +23,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['otherusers'] = 'Other Users ({$a})';
$string['otherusers'] = 'Other users ({$a})';
$string['onlinestatushiding_desc'] = 'If enabled, users have the option to hide their online status from other users.';
$string['configtimetosee'] = 'Number of minutes determining the period of inactivity after which a user is no longer considered to be online.';
$string['onlinestatushiding'] = 'Online status hiding';
@@ -37,7 +37,7 @@ Feature: The online users block allow you to see who is currently online on fron
When I log in as "student1"
And I am on site homepage
Then I should not see "Admin User" in the "Online users" "block"
And I should see "Other Users (1)" in the "Online users" "block"
And I should see "Other users (1)" in the "Online users" "block"
And I should see "Student 1" in the "Online users" "block"
And I should see "Student 2" in the "Online users" "block"
And I should see "3 online users" in the "Online users" "block"
@@ -79,7 +79,7 @@ Feature: The online users block allow you to see who is currently online on fron
And I am on site homepage
Then I should see "2 online user" in the "Online users" "block"
And I should not see "Admin" in the "Online users" "block"
And I should see "Other Users (1)" in the "Online users" "block"
And I should see "Other users (1)" in the "Online users" "block"
And I should see "Student 2" in the "Online users" "block"
And I should not see "Student 1" in the "Online users" "block"
And I log out
@@ -94,7 +94,7 @@ Feature: The online users block allow you to see who is currently online on fron
And I am on site homepage
Then I should see "3 online users" in the "Online users" "block"
And I should not see "Admin" in the "Online users" "block"
And I should see "Other Users (1)" in the "Online users" "block"
And I should see "Other users (1)" in the "Online users" "block"
And I should see "Student 2" in the "Online users" "block"
And I should see "Student 1" in the "Online users" "block"
@@ -138,7 +138,7 @@ Feature: The online users block allow you to see who is currently online on fron
And I am on site homepage
And I should see "2 online user" in the "Online users" "block"
And I should not see "Admin" in the "Online users" "block"
And I should see "Other Users (1)" in the "Online users" "block"
And I should see "Other users (1)" in the "Online users" "block"
And I should see "Student 2" in the "Online users" "block"
And I should not see "Student 1" in the "Online users" "block"
And I log out
@@ -148,6 +148,6 @@ Feature: The online users block allow you to see who is currently online on fron
When I am on site homepage
Then I should see "3 online users" in the "Online users" "block"
And I should not see "Admin" in the "Online users" "block"
And I should see "Other Users (1)" in the "Online users" "block"
And I should see "Other users (1)" in the "Online users" "block"
And I should see "Student 2" in the "Online users" "block"
And I should see "Student 1" in the "Online users" "block"
+3 -1
View File
@@ -42,19 +42,21 @@ class block_tag_youtube_edit_form extends block_edit_form {
// Category setting.
$categorychoices = ['0' => get_string('anycategory', 'block_tag_youtube')];
$hascategoryerror = false;
$categoryerror = '';
try {
// Get all video categories through an API call and add them to the category list.
$categorychoices += $this->block->get_categories();
} catch (Exception $e) {
$hascategoryerror = true;
$categoryerror = $e->getMessage();
}
$mform->addElement('select', 'config_category', get_string('category', 'block_tag_youtube'),
$categorychoices);
$mform->setDefault('config_category', 0);
if ($categoryerror) {
if ($hascategoryerror) {
$notification = $OUTPUT->notification(get_string('categoryerror', 'block_tag_youtube', $categoryerror),
'error');
$mform->addElement('static', 'config_category_error', '', $notification);
+135 -30
View File
@@ -97,18 +97,26 @@ class provider implements
public static function get_contexts_for_userid(int $userid) : contextlist {
$contextlist = new contextlist();
// Only events belonging to that user will be exported.
// Calendar Events can exist at Site, Course Category, Course, Course Group, User, or Course Modules contexts.
$params = [
'usercontext' => CONTEXT_USER,
'modulecontext' => CONTEXT_MODULE,
'cuserid' => $userid,
'muserid' => $userid,
'sitecontext' => CONTEXT_SYSTEM,
'categorycontext' => CONTEXT_COURSECAT,
'coursecontext' => CONTEXT_COURSE,
'groupcontext' => CONTEXT_COURSE,
'usercontext' => CONTEXT_USER,
'cuserid' => $userid,
'modulecontext' => CONTEXT_MODULE,
'muserid' => $userid
];
// Get contexts of Calendar Events for the owner.
$sql = "SELECT ctx.id
FROM {context} ctx
JOIN {event} e ON
(e.eventtype = 'site' AND ctx.contextlevel = :sitecontext) OR
(e.categoryid = ctx.instanceid AND e.eventtype = 'category' AND ctx.contextlevel = :categorycontext) OR
(e.courseid = ctx.instanceid AND e.eventtype = 'course' AND ctx.contextlevel = :coursecontext) OR
(e.courseid = ctx.instanceid AND e.eventtype = 'group' AND ctx.contextlevel = :groupcontext) OR
(e.userid = ctx.instanceid AND e.eventtype = 'user' AND ctx.contextlevel = :usercontext)
WHERE e.userid = :cuserid
UNION
@@ -120,8 +128,12 @@ class provider implements
WHERE e.userid = :muserid";
$contextlist->add_from_sql($sql, $params);
// Only export subscriptions belonging to that user.
// Calendar Subscriptions can exist at Site, Course Category, Course, Course Group, or User contexts.
$params = [
'sitecontext' => CONTEXT_SYSTEM,
'categorycontext' => CONTEXT_COURSECAT,
'coursecontext' => CONTEXT_COURSE,
'groupcontext' => CONTEXT_COURSE,
'usercontext' => CONTEXT_USER,
'userid' => $userid
];
@@ -130,6 +142,10 @@ class provider implements
$sql = "SELECT ctx.id
FROM {context} ctx
JOIN {event_subscriptions} s ON
(s.eventtype = 'site' AND ctx.contextlevel = :sitecontext) OR
(s.categoryid = ctx.instanceid AND s.eventtype = 'category' AND ctx.contextlevel = :categorycontext) OR
(s.courseid = ctx.instanceid AND s.eventtype = 'course' AND ctx.contextlevel = :coursecontext) OR
(s.courseid = ctx.instanceid AND s.eventtype = 'group' AND ctx.contextlevel = :groupcontext) OR
(s.userid = ctx.instanceid AND s.eventtype = 'user' AND ctx.contextlevel = :usercontext)
WHERE s.userid = :userid";
$contextlist->add_from_sql($sql, $params);
@@ -148,7 +164,8 @@ class provider implements
$context = $userlist->get_context();
// Calendar Events can exist at user (CONTEXT_USER), or Course Modules (CONTEXT_MODULE) contexts.
// Calendar Events can exist at Site (CONTEXT_SYSTEM), Course Category (CONTEXT_COURSECAT),
// Course and Course Group (CONTEXT_COURSE), User (CONTEXT_USER), or Course Modules (CONTEXT_MODULE) contexts.
if ($context->contextlevel == CONTEXT_MODULE) {
$params = ['cmid' => $context->instanceid];
@@ -159,21 +176,43 @@ class provider implements
WHERE cm.id = :cmid";
$userlist->add_from_sql('userid', $sql, $params);
} else if ($context->contextlevel == CONTEXT_USER) {
$params = ['instanceid' => $context->instanceid];
} else if ($context->contextlevel == CONTEXT_SYSTEM) {
// Get contexts of Calendar Events for the owner.
$sql = "SELECT userid FROM {event} WHERE eventtype = 'site'";
$userlist->add_from_sql('userid', $sql, []);
// Get contexts for Calendar Subscriptions for the owner.
$sql = "SELECT userid FROM {event_subscriptions} WHERE eventtype = 'site'";
$userlist->add_from_sql('userid', $sql, []);
} else if (in_array($context->contextlevel, [CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_USER])) {
$eventfields = [
CONTEXT_COURSECAT => 'categoryid',
CONTEXT_COURSE => 'courseid',
CONTEXT_USER => 'userid'
];
$eventfield = $eventfields[$context->contextlevel];
$eventtypes = [
CONTEXT_COURSECAT => 'category',
CONTEXT_COURSE => ['course' , 'group'],
CONTEXT_USER => 'user'
];
list($eventtypesql, $eventtypeparams) = $DB->get_in_or_equal($eventtypes[$context->contextlevel], SQL_PARAMS_NAMED);
$params = $eventtypeparams + ['instanceid' => $context->instanceid];
// Get contexts of Calendar Events for the owner.
$sql = "SELECT userid
FROM {event}
WHERE eventtype = 'user'
AND userid = :instanceid";
WHERE eventtype $eventtypesql
AND $eventfield = :instanceid";
$userlist->add_from_sql('userid', $sql, $params);
// Get contexts for Calendar Subscriptions for the owner.
$sql = "SELECT userid
FROM {event_subscriptions}
WHERE eventtype = 'user'
AND userid = :instanceid";
WHERE eventtype $eventtypesql
AND $eventfield = :instanceid";
$userlist->add_from_sql('userid', $sql, $params);
}
}
@@ -237,6 +276,9 @@ class provider implements
$userids = $userlist->get_userids();
$allowedcontexts = [
CONTEXT_SYSTEM,
CONTEXT_COURSECAT,
CONTEXT_COURSE,
CONTEXT_MODULE,
CONTEXT_USER
];
@@ -407,8 +449,9 @@ class provider implements
protected static function get_calendar_event_ids_by_context(\context $context, $userids = array()) {
global $DB;
// Only events can exist at user and course module contexts can be exported.
if (!in_array($context->contextlevel, [CONTEXT_USER, CONTEXT_MODULE])) {
// Calendar Events can exist at Site (CONTEXT_SYSTEM), Course Category (CONTEXT_COURSECAT),
// Course and Course Group (CONTEXT_COURSE), User (CONTEXT_USER), or Course Modules (CONTEXT_MODULE) contexts.
if (!in_array($context->contextlevel, [CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_USER, CONTEXT_MODULE])) {
return [];
}
@@ -428,16 +471,35 @@ class provider implements
JOIN {modules} m ON m.id = cm.module
JOIN {event} e ON e.modulename = m.name AND e.courseid = cm.course AND e.instance = cm.instance
WHERE cm.id = :cmid
AND e.userid <> 0
$whereusersql";
} else {
$params = ['instanceid' => $context->instanceid];
} else if ($context->contextlevel == CONTEXT_SYSTEM) { // Site events.
$params = [];
$sql = "SELECT DISTINCT e.id AS eventid
FROM {event} e
WHERE e.eventtype = 'site'
$whereusersql";
} else { // The rest.
$eventfields = [
CONTEXT_COURSECAT => 'categoryid',
CONTEXT_COURSE => 'courseid',
CONTEXT_USER => 'userid'
];
$eventfield = $eventfields[$context->contextlevel];
$eventtypes = [
CONTEXT_COURSECAT => 'category',
CONTEXT_COURSE => ['course' , 'group'],
CONTEXT_USER => 'user'
];
list($eventtypesql, $eventtypeparams) = $DB->get_in_or_equal($eventtypes[$context->contextlevel], SQL_PARAMS_NAMED);
$params = $eventtypeparams + ['instanceid' => $context->instanceid];
// Get Calendar Events for the specified Moodle context.
$sql = "SELECT DISTINCT e.id AS eventid
FROM {event} e
WHERE e.eventtype = 'user'
AND e.userid = :instanceid
WHERE e.eventtype $eventtypesql
AND e.{$eventfield} = :instanceid
$whereusersql";
}
@@ -458,8 +520,9 @@ class provider implements
protected static function get_calendar_subscription_ids_by_context(\context $context, $userids = array()) {
global $DB;
// Only own user event subscriptions can be exported.
if ($context->contextlevel != CONTEXT_USER) {
// Calendar Subscriptions can exist at Site (CONTEXT_SYSTEM), Course Category (CONTEXT_COURSECAT),
// Course and Course Group (CONTEXT_COURSE), or User (CONTEXT_USER) contexts.
if (!in_array($context->contextlevel, [CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_USER])) {
return [];
}
@@ -469,14 +532,40 @@ class provider implements
list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
$whereusersql = "AND s.userid {$usersql}";
}
$params = ['instanceid' => $context->instanceid];
// Get Calendar Subscriptions for the specified context.
$sql = "SELECT DISTINCT s.id AS subscriptionid
FROM {event_subscriptions} s
WHERE s.eventtype = 'user'
AND s.userid = :instanceid
$whereusersql";
if ($context->contextlevel == CONTEXT_SYSTEM) {
$params = [];
// Get Calendar Subscriptions for the system context.
$sql = "SELECT DISTINCT s.id AS subscriptionid
FROM {event_subscriptions} s
WHERE s.eventtype = 'site'
$whereusersql";
} else {
$eventfields = [
CONTEXT_COURSECAT => 'categoryid',
CONTEXT_COURSE => 'courseid',
CONTEXT_USER => 'userid'
];
$eventfield = $eventfields[$context->contextlevel];
$eventtypes = [
CONTEXT_COURSECAT => 'category',
CONTEXT_COURSE => ['course' , 'group'],
CONTEXT_USER => 'user'
];
list($eventtypesql, $eventtypeparams) = $DB->get_in_or_equal($eventtypes[$context->contextlevel], SQL_PARAMS_NAMED);
$params = $eventtypeparams + ['instanceid' => $context->instanceid];
// Get Calendar Subscriptions for the specified context.
$sql = "SELECT DISTINCT s.id AS subscriptionid
FROM {event_subscriptions} s
WHERE s.eventtype $eventtypesql
AND s.{$eventfield} = :instanceid
$whereusersql";
}
$params += $userparams;
return $DB->get_records_sql($sql, $params);
@@ -498,8 +587,12 @@ class provider implements
list($contextsql1, $contextparams1) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
list($contextsql2, $contextparams2) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
// Only User and Course Modules contexts belonging to the user can be exported.
// Calendar Events can exist at Site, Course Category, Course, Course Group, User, or Course Modules contexts.
$params = [
'sitecontext' => CONTEXT_SYSTEM,
'categorycontext' => CONTEXT_COURSECAT,
'coursecontext' => CONTEXT_COURSE,
'groupcontext' => CONTEXT_COURSE,
'usercontext' => CONTEXT_USER,
'cuserid' => $userid,
'modulecontext' => CONTEXT_MODULE,
@@ -522,6 +615,10 @@ class provider implements
ctx.id AS ctxid
FROM {context} ctx
INNER JOIN {event} e ON
(e.eventtype = 'site' AND ctx.contextlevel = :sitecontext) OR
(e.categoryid = ctx.instanceid AND e.eventtype = 'category' AND ctx.contextlevel = :categorycontext) OR
(e.courseid = ctx.instanceid AND e.eventtype = 'course' AND ctx.contextlevel = :coursecontext) OR
(e.courseid = ctx.instanceid AND e.eventtype = 'group' AND ctx.contextlevel = :groupcontext) OR
(e.userid = ctx.instanceid AND e.eventtype = 'user' AND ctx.contextlevel = :usercontext)
WHERE e.userid = :cuserid
AND ctx.id {$contextsql1}
@@ -557,6 +654,10 @@ class provider implements
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
$params = [
'sitecontext' => CONTEXT_SYSTEM,
'categorycontext' => CONTEXT_COURSECAT,
'coursecontext' => CONTEXT_COURSE,
'groupcontext' => CONTEXT_COURSE,
'usercontext' => CONTEXT_USER,
'userid' => $user->id
];
@@ -571,6 +672,10 @@ class provider implements
s.eventtype as eventtype
FROM {context} c
INNER JOIN {event_subscriptions} s ON
(s.eventtype = 'site' AND c.contextlevel = :sitecontext) OR
(s.categoryid = c.instanceid AND s.eventtype = 'category' AND c.contextlevel = :categorycontext) OR
(s.courseid = c.instanceid AND s.eventtype = 'course' AND c.contextlevel = :coursecontext) OR
(s.courseid = c.instanceid AND s.eventtype = 'group' AND c.contextlevel = :groupcontext) OR
(s.userid = c.instanceid AND s.eventtype = 'user' AND c.contextlevel = :usercontext)
WHERE s.userid = :userid
AND c.id {$contextsql}";
+40 -91
View File
@@ -235,18 +235,9 @@ class calendar_event {
$data->eventtype = 'user';
}
// Only user and user override type of events should record the user id.
// For all other event types we set userid to 0 as they are considered shared events.
// Default to the current user.
if (empty($data->userid)) {
if ($this->allow_custom_userid($data)) {
$data->userid = $USER->id;
} else {
$data->userid = 0;
}
}
if (empty($data->courseid) && $data->eventtype == 'site') {
$data->courseid = SITEID;
$data->userid = $USER->id;
}
if (!empty($data->timeduration) && is_array($data->timeduration)) {
@@ -324,28 +315,6 @@ class calendar_event {
return !empty($this->properties->{$key});
}
/**
* Decide whether user id should be stored on the event or not.
*
* Only user events and user overrides type of events should retain user id.
*
* @param stdClass $data The event data object.
* @return bool
*/
protected function allow_custom_userid(stdClass $data): bool {
if ($data->eventtype === 'user') {
return true;
}
$isactionevent = !empty($data->type) && $data->type == CALENDAR_EVENT_TYPE_ACTION;
$isuseroverride = !empty($data->priority) && $data->priority == CALENDAR_EVENT_USER_OVERRIDE_PRIORITY;
if ($isactionevent && $isuseroverride) {
return true;
}
return false;
}
/**
* Calculate the context value needed for an event.
*
@@ -520,18 +489,49 @@ class calendar_event {
}
if ($usingeditor) {
switch ($this->properties->eventtype) {
case 'user':
$this->properties->courseid = 0;
$this->properties->course = 0;
$this->properties->groupid = 0;
$this->properties->userid = $USER->id;
break;
case 'site':
$this->properties->courseid = SITEID;
$this->properties->course = SITEID;
$this->properties->groupid = 0;
$this->properties->userid = $USER->id;
break;
case 'course':
$this->properties->groupid = 0;
$this->properties->userid = $USER->id;
break;
case 'category':
$this->properties->groupid = 0;
$this->properties->category = 0;
$this->properties->userid = $USER->id;
break;
case 'group':
$this->properties->userid = $USER->id;
break;
default:
// We should NEVER get here, but just incase we do lets fail gracefully.
$usingeditor = false;
break;
}
// If we are actually using the editor, we recalculate the context because some default values
// were set when calculate_context() was called from the constructor.
$this->properties->context = $this->calculate_context();
$this->editorcontext = $this->get_context();
if ($usingeditor) {
$this->properties->context = $this->calculate_context();
$this->editorcontext = $this->get_context();
}
$editor = $this->properties->description;
$this->properties->format = $this->properties->description['format'];
$this->properties->description = $this->properties->description['text'];
}
$this->set_default_event_ids();
// Insert the event into the database.
$this->properties->id = $DB->insert_record('event', $this->properties);
@@ -676,8 +676,6 @@ class calendar_event {
$event->trigger();
}
} else {
$this->set_default_event_ids();
$DB->update_record('event', $this->properties);
$event = self::load($this->properties->id);
$this->properties = $event->properties();
@@ -872,49 +870,6 @@ class calendar_event {
return $properties;
}
/**
* Set the default event ids.
*/
protected function set_default_event_ids(): void {
global $USER;
$userid = (!empty($this->properties->userid)) ? $this->properties->userid : $USER->id;
switch ($this->properties->eventtype) {
case 'user':
$this->properties->courseid = 0;
$this->properties->course = 0;
$this->properties->groupid = 0;
$this->properties->userid = $userid;
break;
case 'site':
$this->properties->courseid = SITEID;
$this->properties->course = SITEID;
$this->properties->groupid = 0;
$this->properties->userid = 0;
break;
case 'course':
$this->properties->groupid = 0;
$this->properties->userid = 0;
break;
case 'category':
$this->properties->groupid = 0;
$this->properties->category = 0;
$this->properties->userid = 0;
break;
case 'group':
$this->properties->userid = 0;
break;
}
// Only user overrides type of events should store user's id.
$isactionevent = !empty($this->properties->type) && $this->properties->type == CALENDAR_EVENT_TYPE_ACTION;
$isuseroverride = $isactionevent && !empty($this->properties->priority) &&
$this->properties->priority == CALENDAR_EVENT_USER_OVERRIDE_PRIORITY;
if ($isuseroverride) {
$this->properties->userid = $userid;
}
}
/**
* Toggles the visibility of an event
*
@@ -2442,9 +2397,7 @@ function calendar_get_default_courses($courseid = null, $fields = '*', $canmanag
$prefixedfields = array_map(function($value) {
return 'c.' . trim(strtolower($value));
}, $fieldlist);
if (!in_array('c.visible', $prefixedfields) && !in_array('c.*', $prefixedfields)) {
$prefixedfields[] = 'c.visible';
}
$courses = get_courses('all', 'c.shortname', implode(',', $prefixedfields));
} else {
$courses = enrol_get_users_courses($userid, true, $fields);
@@ -2821,9 +2774,7 @@ function calendar_add_subscription($sub) {
// User events.
$sub->courseid = 0;
}
// Only subscriptions for user type should store user id, shared events (course,site...) should have userid set to 0.
$sub->userid = ($sub->eventtype == 'user') ? $USER->id : 0;
$sub->userid = $USER->id;
// File subscriptions never update.
if (empty($sub->url)) {
@@ -2952,7 +2903,7 @@ function calendar_add_icalendar_event($event, $unused = null, $subscriptionid, $
// We should never do anything with an event without a subscription reference.
$sub = calendar_get_subscription($subscriptionid);
$eventrecord->subscriptionid = $subscriptionid;
$eventrecord->userid = ($sub->eventtype == 'user') ? $sub->userid : 0;
$eventrecord->userid = $sub->userid;
$eventrecord->groupid = $sub->groupid;
$eventrecord->courseid = $sub->courseid;
$eventrecord->categoryid = $sub->categoryid;
@@ -3311,8 +3262,6 @@ function calendar_get_calendar_context($subscription) {
// Determine context based on calendar type.
if ($subscription->eventtype === 'site') {
$context = \context_course::instance(SITEID);
} else if ($subscription->eventtype === 'category') {
$context = \context_coursecat::instance($subscription->categoryid);
} else if ($subscription->eventtype === 'group' || $subscription->eventtype === 'course') {
$context = \context_course::instance($subscription->courseid);
} else {
+28 -28
View File
@@ -608,8 +608,8 @@ class core_calendar_event_vault_testcase extends advanced_testcase {
for ($i = 1; $i < 6; $i++) {
create_event([
'name' => sprintf('Event %d', $i),
'eventtype' => 'due',
'userid' => 0,
'eventtype' => 'user',
'userid' => $user->id,
'timesort' => $i,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'courseid' => $course1->id,
@@ -619,8 +619,8 @@ class core_calendar_event_vault_testcase extends advanced_testcase {
for ($i = 6; $i < 12; $i++) {
create_event([
'name' => sprintf('Event %d', $i),
'eventtype' => 'due',
'userid' => 0,
'eventtype' => 'user',
'userid' => $user->id,
'timesort' => $i,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'courseid' => $course2->id,
@@ -663,8 +663,8 @@ class core_calendar_event_vault_testcase extends advanced_testcase {
for ($i = 1; $i < 6; $i++) {
create_event([
'name' => sprintf('Event %d', $i),
'eventtype' => 'due',
'userid' => 0,
'eventtype' => 'user',
'userid' => $user->id,
'timesort' => $i,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'courseid' => $course1->id,
@@ -674,8 +674,8 @@ class core_calendar_event_vault_testcase extends advanced_testcase {
for ($i = 6; $i < 12; $i++) {
create_event([
'name' => sprintf('Event %d', $i),
'eventtype' => 'due',
'userid' => 0,
'eventtype' => 'user',
'userid' => $user->id,
'timesort' => $i,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'courseid' => $course2->id,
@@ -719,8 +719,8 @@ class core_calendar_event_vault_testcase extends advanced_testcase {
for ($i = 1; $i < 6; $i++) {
create_event([
'name' => sprintf('Event %d', $i),
'eventtype' => 'due',
'userid' => 0,
'eventtype' => 'user',
'userid' => $user->id,
'timesort' => $i,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'courseid' => $course1->id,
@@ -730,8 +730,8 @@ class core_calendar_event_vault_testcase extends advanced_testcase {
for ($i = 6; $i < 12; $i++) {
create_event([
'name' => sprintf('Event %d', $i),
'eventtype' => 'due',
'userid' => 0,
'eventtype' => 'user',
'userid' => $user->id,
'timesort' => $i,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'courseid' => $course2->id,
@@ -773,8 +773,8 @@ class core_calendar_event_vault_testcase extends advanced_testcase {
for ($i = 1; $i < 21; $i++) {
$records[] = create_event([
'name' => sprintf('Event %d', $i),
'eventtype' => 'due',
'userid' => 0,
'eventtype' => 'user',
'userid' => $user->id,
'timesort' => $i,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'courseid' => $course1->id,
@@ -784,8 +784,8 @@ class core_calendar_event_vault_testcase extends advanced_testcase {
for ($i = 21; $i < 41; $i++) {
$records[] = create_event([
'name' => sprintf('Event %d', $i),
'eventtype' => 'due',
'userid' => 0,
'eventtype' => 'user',
'userid' => $user->id,
'timesort' => $i,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'courseid' => $course2->id,
@@ -830,8 +830,8 @@ class core_calendar_event_vault_testcase extends advanced_testcase {
for ($i = 1; $i < 41; $i++) {
create_event([
'name' => sprintf('Event %d', $i),
'eventtype' => 'due',
'userid' => 0,
'eventtype' => 'user',
'userid' => $user->id,
'timesort' => $i,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'courseid' => $course1->id,
@@ -901,8 +901,8 @@ class core_calendar_event_vault_testcase extends advanced_testcase {
for ($i = 1; $i < 21; $i++) {
create_event([
'name' => sprintf('Event %d', $i),
'eventtype' => 'due',
'userid' => 0,
'eventtype' => 'user',
'userid' => $user->id,
'timesort' => $i,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'courseid' => $course1->id,
@@ -912,8 +912,8 @@ class core_calendar_event_vault_testcase extends advanced_testcase {
for ($i = 21; $i < 41; $i++) {
create_event([
'name' => sprintf('Event %d', $i),
'eventtype' => 'due',
'userid' => 0,
'eventtype' => 'user',
'userid' => $user->id,
'timesort' => $i,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'courseid' => $course2->id,
@@ -979,8 +979,8 @@ class core_calendar_event_vault_testcase extends advanced_testcase {
for ($i = 1; $i < 11; $i++) {
$records[] = create_event([
'name' => sprintf('1 event %d', $i),
'eventtype' => 'due',
'userid' => 0,
'eventtype' => 'user',
'userid' => $user->id,
'timesort' => $i,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'courseid' => $course1->id,
@@ -990,8 +990,8 @@ class core_calendar_event_vault_testcase extends advanced_testcase {
for ($i = 1; $i < 11; $i++) {
$records[] = create_event([
'name' => sprintf('2 event %d', $i),
'eventtype' => 'due',
'userid' => 0,
'eventtype' => 'user',
'userid' => $user->id,
'timesort' => $i,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'courseid' => $course1->id,
@@ -1002,8 +1002,8 @@ class core_calendar_event_vault_testcase extends advanced_testcase {
for ($i = 1; $i < 11; $i++) {
$records[] = create_event([
'name' => sprintf('3 event %d', $i),
'eventtype' => 'due',
'userid' => 0,
'eventtype' => 'user',
'userid' => $user->id,
'timesort' => $i,
'type' => CALENDAR_EVENT_TYPE_ACTION,
'courseid' => $course2->id,
+35 -39
View File
@@ -96,6 +96,13 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
} else {
$prop->repeat = 1;
}
if (empty($prop->userid)) {
if (!empty($userid)) {
$prop->userid = $userid;
} else {
$prop->userid = 0;
}
}
if (!isset($prop->courseid)) {
$prop->courseid = $SITE->id;
}
@@ -383,54 +390,44 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
$this->setUser($user);
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
// There should be a single user event.
$this->assertEquals(1, count($events['events']));
// There should be only two events (course and group).
$this->assertEquals(2, count($events['warnings']));
$this->assertEquals(2, count($events['events'])); // site, user.
$this->assertEquals(2, count($events['warnings'])); // course, group.
$role = $DB->get_record('role', array('shortname' => 'student'));
$this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
// There should be a site and both course events.
$this->assertEquals(3, count($events['events']));
// There should be a warning related to group event.
$this->assertEquals(1, count($events['warnings']));
$this->assertEquals(4, count($events['events'])); // site, user, both course events.
$this->assertEquals(1, count($events['warnings'])); // group.
$options = array ('siteevents' => true, 'userevents' => true, 'timeend' => time() + HOURSECS);
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
// There should be a site and a course event.
$this->assertEquals(2, count($events['events']));
// There should be a warning related to group event.
$this->assertEquals(1, count($events['warnings']));
$this->assertEquals(3, count($events['events'])); // site, user, one course event.
$this->assertEquals(1, count($events['warnings'])); // group.
groups_add_member($group, $user);
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
// There should be a site, group, one course event.
$this->assertEquals(3, count($events['events']));
$this->assertEquals(4, count($events['events'])); // site, user, group, one course event.
$this->assertEquals(0, count($events['warnings']));
$paramevents = array ('courseids' => array($course->id), 'groupids' => array($group->id));
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
// There should be only site, course and group events.
$this->assertEquals(3, count($events['events']));
$this->assertEquals(4, count($events['events'])); // site, user, group, one course event.
$this->assertEquals(0, count($events['warnings']));
$paramevents = array ('groupids' => array($group->id, 23));
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
// There should be only site and group events.
$this->assertEquals(2, count($events['events']));
$this->assertEquals(3, count($events['events'])); // site, user, group.
$this->assertEquals(1, count($events['warnings']));
$paramevents = array ('courseids' => array(23));
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
// There should be a single site event.
$this->assertEquals(1, count($events['events']));
$this->assertEquals(2, count($events['events'])); // site, user.
$this->assertEquals(1, count($events['warnings']));
$paramevents = array ();
@@ -471,7 +468,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
$this->assertCount(4, $events['events']);
$this->assertCount(5, $events['events']);
// Hide the assignment.
set_coursemodule_visible($assign->cmid, 0);
@@ -482,7 +479,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
// Expect one less.
$this->assertCount(3, $events['events']);
$this->assertCount(4, $events['events']);
// Create some category events.
$this->setAdminUser();
@@ -841,7 +838,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
$now = time();
// Create two events - one for everybody in the course and one only for the first student.
$event1 = $this->create_calendar_event('Base event', 0, 'due', 0, $now + DAYSECS, $params + ['courseid' => $course->id]);
$event2 = $this->create_calendar_event('User event', 0, 'user', 0, $now + 2 * DAYSECS, $params + ['courseid' => 0]);
$event2 = $this->create_calendar_event('User event', $user->id, 'due', 0, $now + 2*DAYSECS, $params + ['courseid' => 0]);
// Retrieve course events for the second student - only one "Base event" is returned.
$this->setUser($user2);
@@ -853,13 +850,14 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
$this->assertEquals(0, count($events['warnings']));
$this->assertEquals('Base event', $events['events'][0]['name']);
// Retrieve events for the first student.
// Retrieve events for the first student - both events are returned.
$this->setUser($user);
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
$this->assertEquals(1, count($events['events']));
$this->assertEquals(2, count($events['events']));
$this->assertEquals(0, count($events['warnings']));
$this->assertEquals('Base event', $events['events'][0]['name']);
$this->assertEquals('User event', $events['events'][1]['name']);
// Retrieve events by id as a teacher, 'User event' should be returned since teacher has access to this course.
$this->setUser($teacher);
@@ -1722,7 +1720,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
$timedurationuntil->add($interval);
$formdata = [
'id' => 0,
'userid' => 0,
'userid' => $user->id,
'modulename' => '',
'instance' => 0,
'visible' => 1,
@@ -1783,7 +1781,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
$timedurationuntil->add($interval);
$formdata = [
'id' => 0,
'userid' => 0,
'userid' => $user->id,
'modulename' => '',
'instance' => 0,
'visible' => 1,
@@ -1829,7 +1827,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
);
$event = $result['event'];
$this->assertEquals(0, $event['userid']);
$this->assertEquals($user->id, $event['userid']);
$this->assertEquals($formdata['eventtype'], $event['eventtype']);
$this->assertEquals($formdata['name'], $event['name']);
}
@@ -1849,7 +1847,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
$timedurationuntil->add($interval);
$formdata = [
'id' => 0,
'userid' => 0,
'userid' => $user->id,
'modulename' => '',
'instance' => 0,
'visible' => 1,
@@ -1960,7 +1958,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
);
$event = $result['event'];
$this->assertEquals(0, $event['userid']);
$this->assertEquals($user->id, $event['userid']);
$this->assertEquals($formdata['eventtype'], $event['eventtype']);
$this->assertEquals($formdata['name'], $event['name']);
$this->assertEquals($formdata['courseid'], $event['course']['id']);
@@ -2048,7 +2046,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
$timedurationuntil->add($interval);
$formdata = [
'id' => 0,
'userid' => 0,
'userid' => $user->id,
'modulename' => '',
'instance' => 0,
'visible' => 1,
@@ -2164,7 +2162,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
);
$event = $result['event'];
$this->assertEquals(0, $event['userid']);
$this->assertEquals($user->id, $event['userid']);
$this->assertEquals($formdata['eventtype'], $event['eventtype']);
$this->assertEquals($formdata['name'], $event['name']);
$this->assertEquals($group->id, $event['groupid']);
@@ -2187,7 +2185,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
$timedurationuntil->add($interval);
$formdata = [
'id' => 0,
'userid' => 0,
'userid' => $user->id,
'modulename' => '',
'instance' => 0,
'visible' => 1,
@@ -2238,7 +2236,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
);
$event = $result['event'];
$this->assertEquals(0, $event['userid']);
$this->assertEquals($user->id, $event['userid']);
$this->assertEquals($formdata['eventtype'], $event['eventtype']);
$this->assertEquals($formdata['name'], $event['name']);
$this->assertEquals($group->id, $event['groupid']);
@@ -2261,7 +2259,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
$timedurationuntil->add($interval);
$formdata = [
'id' => 0,
'userid' => 0,
'userid' => $user->id,
'modulename' => '',
'instance' => 0,
'visible' => 1,
@@ -2311,7 +2309,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
);
$event = $result['event'];
$this->assertEquals(0, $event['userid']);
$this->assertEquals($user->id, $event['userid']);
$this->assertEquals($formdata['eventtype'], $event['eventtype']);
$this->assertEquals($formdata['name'], $event['name']);
$this->assertEquals($group->id, $event['groupid']);
@@ -2583,10 +2581,8 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
$category = $generator->create_category(['visible' => 0]);
$name = 'Category Event (category: ' . $category->id . ')';
$record = new stdClass();
$record->eventtype = 'category';
$record->categoryid = $category->id;
$record->userid = 0;
$categoryevent = $this->create_calendar_event($name, 0, 'category', 0, time(), $record);
$categoryevent = $this->create_calendar_event($name, $USER->id, 'category', 0, time(), $record);
$events = [
'eventids' => [$categoryevent->id]
+96
View File
@@ -66,6 +66,102 @@ function create_event($properties) {
return $event->create($record);
}
/**
* Helper function to create a x number of events for each event type.
*
* @param int $quantity The quantity of events to be created.
* @return array List of created events.
*/
function create_standard_events(int $quantity): array {
$types = ['site', 'category', 'course', 'group', 'user'];
$events = [];
foreach ($types as $eventtype) {
// Create five events of each event type.
for ($i = 0; $i < $quantity; $i++) {
$events[] = create_event(['eventtype' => $eventtype]);
}
}
return $events;
}
/**
* Helper function to create an action event.
*
* @param array $data The event data.
* @return bool|calendar_event
*/
function create_action_event(array $data) {
global $CFG;
require_once($CFG->dirroot . '/calendar/lib.php');
if (!isset($data['modulename']) || !isset($data['instance'])) {
throw new coding_exception('Module and instance should be specified when creating an action event.');
}
$isuseroverride = isset($data->priority) && $data->priority == CALENDAR_EVENT_USER_OVERRIDE_PRIORITY;
if ($isuseroverride) {
if (!in_array($data['modulename'], ['assign', 'lesson', 'quiz'])) {
throw new coding_exception('Only assign, lesson and quiz modules supports overrides');
}
}
$event = array_merge($data, [
'eventtype' => isset($data['eventtype']) ? $data['eventtype'] : 'open',
'courseid' => isset($data['courseid']) ? $data['courseid'] : 0,
'instance' => $data['instance'],
'modulename' => $data['modulename'],
'type' => CALENDAR_EVENT_TYPE_ACTION,
]);
return create_event($event);
}
/**
* Helper function to create an user override calendar event.
*
* @param string $modulename The modulename.
* @param int $instanceid The instance id.
* @param int $userid The user id.
* @return calendar_event|false
*/
function create_user_override_event(string $modulename, int $instanceid, int $userid) {
if (!isset($userid)) {
throw new coding_exception('Must specify userid when creating a user override.');
}
return create_action_event([
'modulename' => $modulename,
'instance' => $instanceid,
'userid' => $userid,
'priority' => CALENDAR_EVENT_USER_OVERRIDE_PRIORITY,
]);
}
/**
* Helper function to create an group override calendar event.
*
* @param string $modulename The modulename.
* @param int $instanceid The instance id.
* @param int $courseid The course id.
* @param int $groupid The group id.
* @return calendar_event|false
*/
function create_group_override_event(string $modulename, int $instanceid, int $courseid, int $groupid) {
if (!isset($groupid)) {
throw new coding_exception('Must specify groupid when creating a group override.');
}
return create_action_event([
'groupid' => $groupid,
'courseid' => $courseid,
'modulename' => $modulename,
'instance' => $instanceid,
]);
}
/**
* A test factory that will create action events.
*
+6 -6
View File
@@ -154,8 +154,8 @@ class core_calendar_local_api_testcase extends advanced_testcase {
'courseid' => $course->id,
'modulename' => 'assign',
'instance' => $moduleinstance->id,
'userid' => 0,
'eventtype' => 'due',
'userid' => 1,
'eventtype' => 'user',
'repeats' => 0,
'timestart' => 1,
];
@@ -205,8 +205,8 @@ class core_calendar_local_api_testcase extends advanced_testcase {
'courseid' => $course->id,
'modulename' => 'assign',
'instance' => $moduleinstance->id,
'userid' => 0,
'eventtype' => 'due',
'userid' => 1,
'eventtype' => 'user',
'repeats' => 0,
'timestart' => 1,
];
@@ -257,8 +257,8 @@ class core_calendar_local_api_testcase extends advanced_testcase {
'courseid' => $course->id,
'modulename' => 'assign',
'instance' => $moduleinstance->id,
'userid' => 0,
'eventtype' => 'due',
'userid' => 1,
'eventtype' => 'user',
'repeats' => 0,
'timestart' => 1,
];
+90 -103
View File
@@ -80,8 +80,8 @@ class core_calendar_privacy_testcase extends provider_testcase {
$course3context = context_course::instance($course3->id);
// Add Category Calendar Events for Category.
$this->create_test_standard_calendar_event('category', 0, time(), '', $category->id);
$this->create_test_standard_calendar_event('category', 0, time(), '', $category->id);
$this->create_test_standard_calendar_event('category', $user->id, time(), '', $category->id);
$this->create_test_standard_calendar_event('category', $user->id, time(), '', $category->id);
// Add User Calendar Events for User.
$this->create_test_standard_calendar_event('user', $user->id, time(), '');
@@ -89,43 +89,38 @@ class core_calendar_privacy_testcase extends provider_testcase {
$this->create_test_standard_calendar_event('user', $user->id, time(), '', 0, $course2->id);
// Add a Course Calendar Event for Course 1.
$this->create_test_standard_calendar_event('course', 0, time(), '', 0, $course1->id);
$this->create_test_standard_calendar_event('course', $user->id, time(), '', 0, $course1->id);
// Add a Course Assignment Action Calendar Event for Course 2.
$assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
$generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
$params['course'] = $course2->id;
$params['assignsubmission_onlinetext_enabled'] = 1;
$assign1 = $assigngenerator->create_instance($params);
$assign1cm = get_coursemodule_from_instance('assign', $assign1->id);
$assign1context = context_module::instance($assign1cm->id);
$this->create_test_action_calendar_event('duedate', $course2->id, $assign1->id, 'assign', 0, time());
$this->create_test_action_calendar_event('gradingduedate', $course2->id, $assign1->id, 'assign', 0, time());
// Add a due date event similar to a user override in another assign instance.
$assign2 = $assigngenerator->create_instance($params);
$assign2cm = get_coursemodule_from_instance('assign', $assign2->id);
$assign2context = context_module::instance($assign2cm->id);
$this->create_test_action_calendar_event('duedate', $course2->id, $assign2->id, 'assign', $user->id, time());
$this->create_test_action_calendar_event('gradingduedate', $course2->id, $assign2->id, 'assign', $user->id, time());
$instance = $generator->create_instance($params);
$cm = get_coursemodule_from_instance('assign', $instance->id);
$modulecontext = context_module::instance($cm->id);
$assign = new assign($modulecontext, $cm, $course2);
$this->create_test_action_calendar_event('duedate', $course2->id, $instance->id, 'assign', $user->id, time());
$this->create_test_action_calendar_event('gradingduedate', $course2->id, $instance->id, 'assign', $user->id, time());
// Add a Calendar Subscription and Group Calendar Event to Course 3.
$this->create_test_calendar_subscription('course', 'https://calendar.google.com/', 0, 0, $course3->id);
$this->create_test_standard_calendar_event('group', 0, time(), '', 0, $course3->id, $course3group->id);
$this->create_test_calendar_subscription('course', 'https://calendar.google.com/', $user->id, 0, $course3->id);
$this->create_test_standard_calendar_event('group', $user->id, time(), '', 0, $course3->id, $course3group->id);
// The user will be in these contexts.
$usercontextids = [
$usercontext->id,
$assign2context->id,
$categorycontext->id,
$course1context->id,
$modulecontext->id,
$course3context->id
];
// Retrieve the user's context ids.
$contextids = provider::get_contexts_for_userid($user->id);
// Check the user context list and retrieved user context lists contains the same number of records.
$this->assertCount(count($usercontextids), $contextids->get_contextids());
$this->assertEquals(count($usercontextids), count($contextids->get_contextids()));
// There should be no difference between the contexts.
$this->assertEmpty(array_diff($usercontextids, $contextids->get_contextids()));
// Check that the module context for the assignment without user override event is not included.
$this->assertNotContains($assign1context->id, $contextids->get_contextids());
}
/**
@@ -155,12 +150,12 @@ class core_calendar_privacy_testcase extends provider_testcase {
$event1 = $this->create_test_standard_calendar_event('user', $user->id, time(), '');
// Add Category Calendar Events for Category.
$event2 = $this->create_test_standard_calendar_event('category', 0, time(), '', $category->id);
$event2 = $this->create_test_standard_calendar_event('category', $user->id, time(), '', $category->id);
// Add two Course Calendar Event for Course 1 and set the same time (1 day a head).
$time = strtotime('+1 day', time());
$event3 = $this->create_test_standard_calendar_event('course', 0, $time, 'ABC', 0, $course1->id);
$event4 = $this->create_test_standard_calendar_event('course', 0, $time, 'DEF', 0, $course1->id);
$event3 = $this->create_test_standard_calendar_event('course', $user->id, $time, 'ABC', 0, $course1->id);
$event4 = $this->create_test_standard_calendar_event('course', $user->id, $time, 'DEF', 0, $course1->id);
// Add a Course Assignment Action Calendar Event for Course 2.
$generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
@@ -174,7 +169,7 @@ class core_calendar_privacy_testcase extends provider_testcase {
// Add a Calendar Subscription and Group Calendar Event to Course 3.
$subscription1 = $this->create_test_calendar_subscription('course', 'https://calendar.google.com/', $user->id, 0, $course3->id);
$event6 = $this->create_test_standard_calendar_event('group', 0, time(), '', 0, $course3->id, $course3group->id);
$event6 = $this->create_test_standard_calendar_event('group', $user->id, time(), '', 0, $course3->id, $course3group->id);
// Retrieve the user's context ids.
$contextlist = provider::get_contexts_for_userid($user->id);
@@ -325,16 +320,16 @@ class core_calendar_privacy_testcase extends provider_testcase {
// Add a Course Calendar Event by User 1 for Course 1 and Course 2.
$this->setUser($user1);
$this->create_test_standard_calendar_event('course', 0, time(), '', 0, $course1->id);
$this->create_test_standard_calendar_event('course', 0, time(), '', 0, $course2->id);
$this->create_test_standard_calendar_event('course', $user1->id, time(), '', 0, $course1->id);
$this->create_test_standard_calendar_event('course', $user1->id, time(), '', 0, $course2->id);
// Add a Calendar Subscription by User 1 for Course 1.
$this->create_test_calendar_subscription('course', 'https://calendar.google.com/', $user1->id, 0, $course1->id);
// Add a Course Calendar Event by User 2 for Course 1 and Course 2.
$this->setUser($user2);
$this->create_test_standard_calendar_event('course', 0, time(), '', 0, $course1->id);
$this->create_test_standard_calendar_event('course', 0, time(), '', 0, $course2->id);
$this->create_test_standard_calendar_event('course', $user2->id, time(), '', 0, $course1->id);
$this->create_test_standard_calendar_event('course', $user2->id, time(), '', 0, $course2->id);
// Add a Calendar Subscription by User 2 for Course 2.
$this->create_test_calendar_subscription('course', 'https://calendar.google.com/', $user2->id, 0, $course2->id);
@@ -347,23 +342,23 @@ class core_calendar_privacy_testcase extends provider_testcase {
$cm = get_coursemodule_from_instance('assign', $instance->id);
$modulecontext = context_module::instance($cm->id);
$assign = new assign($modulecontext, $cm, $course2);
$this->create_test_action_calendar_event('duedate', $course2->id, $instance->id, 'assign', 0, time());
$this->create_test_action_calendar_event('duedate', $course2->id, $instance->id, 'assign', $user2->id, time());
$this->create_test_action_calendar_event('gradingduedate', $course2->id, $instance->id, 'assign', $user2->id, time());
// Delete all Calendar Events for all Users by Context for Course 1.
provider::delete_data_for_all_users_in_context($course1context);
// The course1 events should still exist, since it belongs to the course and not to the user who created them.
// Verify all Calendar Events for Course 1 were deleted.
$events = $DB->get_records('event', array('courseid' => $course1->id));
$this->assertCount(2, $events);
// Course1 subscription should still exist, since it belongs to the course and not to the user who created them.
$this->assertCount(0, $events);
// Verify all Calendar Subscriptions for Course 1 were deleted.
$subscriptions = $DB->get_records('event_subscriptions', array('courseid' => $course1->id));
$this->assertCount(1, $subscriptions);
$this->assertCount(0, $subscriptions);
// The course2 events should still exist, since it belongs to the course and not to the user who created them.
// Verify all Calendar Events for Course 2 exists still.
$events = $DB->get_records('event', array('courseid' => $course2->id));
$this->assertCount(4, $events);
// Course2 subscription should still exist, since it belongs to the course and not to the user who created them.
// Verify all Calendar Subscriptions for Course 2 exists still.
$subscriptions = $DB->get_records('event_subscriptions', array('courseid' => $course2->id));
$this->assertCount(1, $subscriptions);
@@ -372,10 +367,10 @@ class core_calendar_privacy_testcase extends provider_testcase {
// Verify all Calendar Events for Course 2 context were deleted.
$events = $DB->get_records('event', array('courseid' => $course2->id, 'modulename' => '0'));
$this->assertCount(2, $events);
$this->assertCount(0, $events);
// Verify all Calendar Subscriptions for Course 2 were deleted.
$subscriptions = $DB->get_records('event_subscriptions', array('courseid' => $course2->id));
$this->assertCount(1, $subscriptions);
$this->assertCount(0, $subscriptions);
// Verify all Calendar Events for the assignment exists still.
$events = $DB->get_records('event', array('modulename' => 'assign'));
@@ -384,9 +379,9 @@ class core_calendar_privacy_testcase extends provider_testcase {
// Delete all Calendar Events for all Users by Context for the assignment.
provider::delete_data_for_all_users_in_context($modulecontext);
// Verify that the action event still exists since it is not a user override.
// Verify all Calendar Events for the assignment context were deleted.
$events = $DB->get_records('event', array('modulename' => 'assign'));
$this->assertCount(1, $events);
$this->assertCount(0, $events);
}
/**
@@ -406,28 +401,25 @@ class core_calendar_privacy_testcase extends provider_testcase {
$course1 = $this->getDataGenerator()->create_course();
$course2 = $this->getDataGenerator()->create_course();
// Add 6 events for user 1 for various contexts.
// Add 5 Calendar Events for User 1 for various contexts.
$this->setUser($user1);
$this->create_test_standard_calendar_event('user', $user1->id, time(), '');
$this->create_test_standard_calendar_event('user', $user1->id, time(), '');
$this->create_test_standard_calendar_event('site', 0, time(), '', 0, 1);
$this->create_test_standard_calendar_event('category', 0, time(), '', $category->id);
$this->create_test_standard_calendar_event('course', 0, time(), '', 0, $course1->id);
$this->create_test_standard_calendar_event('course', 0, time(), '', 0, $course2->id);
$this->create_test_standard_calendar_event('site', $user1->id, time(), '', 0, 1);
$this->create_test_standard_calendar_event('category', $user1->id, time(), '', $category->id);
$this->create_test_standard_calendar_event('course', $user1->id, time(), '', 0, $course1->id);
$this->create_test_standard_calendar_event('course', $user1->id, time(), '', 0, $course2->id);
// Add two subscription for user 1 at course and user contexts.
$this->create_test_calendar_subscription('course', 'https://calendar.google.com/', 0, 0, $course2->id);
$this->create_test_calendar_subscription('user', 'https://calendar.google.com/', $user1->id);
// Add 1 Calendar Subscription for User 1 at course context.
$this->create_test_calendar_subscription('course', 'https://calendar.google.com/', $user1->id, 0, $course2->id);
// Add four events for user 2 for various contexts.
// Add 3 Calendar Events for User 2 for various contexts.
$this->setUser($user2);
$this->create_test_standard_calendar_event('user', $user2->id, time(), '');
$this->create_test_standard_calendar_event('user', $user2->id, time(), '');
$this->create_test_standard_calendar_event('category', 0, time(), '', $category->id);
$this->create_test_standard_calendar_event('course', 0, time(), '', 0, $course1->id);
$this->create_test_standard_calendar_event('category', $user2->id, time(), '', $category->id);
$this->create_test_standard_calendar_event('course', $user2->id, time(), '', 0, $course1->id);
// Add a calendar Subscription for User 2 at course context.
$this->create_test_calendar_subscription('user', 'https://calendar.google.com/', $user2->id, 0, $course2->id);
// Add 1 Calendar Subscription for User 2 at course context.
$this->create_test_calendar_subscription('course', 'https://calendar.google.com/', $user2->id, 0, $course2->id);
// Retrieve the user's context ids.
$contextlist = provider::get_contexts_for_userid($user1->id);
@@ -436,31 +428,17 @@ class core_calendar_privacy_testcase extends provider_testcase {
// Delete all Calendar data for User 1.
provider::delete_data_for_user($approvedcontextlist);
// All user events created by user1 should have been removed.
// Test all Calendar Events and Subscriptions for User 1 equals zero.
$events = $DB->get_records('event', ['userid' => $user1->id]);
$this->assertCount(0, $events);
// The subscription created by user1 should have been deleted.
$eventsubscriptions = $DB->get_records('event_subscriptions', ['userid' => $user1->id]);
$this->assertCount(0, $eventsubscriptions);
// Test all calendar events and subscriptions for user2 still exists and matches the same number created.
// Test all Calendar Events and Subscriptions for User 2 still exists and matches the same number created.
$events = $DB->get_records('event', ['userid' => $user2->id]);
$this->assertCount(2, $events);
// The subscription created by user2 should have not been deleted.
$this->assertCount(3, $events);
$eventsubscriptions = $DB->get_records('event_subscriptions', ['userid' => $user2->id]);
$this->assertCount(1, $eventsubscriptions);
// There should be only six "shared" events created (userid equal zero).
// One site, one category and two course events that were created by user1.
// One category and one course event created by user2.
$events = $DB->get_records('event', ['userid' => 0]);
$this->assertCount(6, $events);
// There should be eight event created in total.
// One site, one category and two course events that were created by user1.
// One category, one course and two user events created by user2.
$events = $DB->get_records('event');
$this->assertCount(8, $events);
}
/**
@@ -496,10 +474,10 @@ class core_calendar_privacy_testcase extends provider_testcase {
// Add Category Calendar Events for Category.
$this->setUser($user1);
$this->create_test_standard_calendar_event('category', 0, time(), '',
$this->create_test_standard_calendar_event('category', $user1->id, time(), '',
$category->id);
$this->setUser($user2);
$this->create_test_standard_calendar_event('category', 0, time(), '',
$this->create_test_standard_calendar_event('category', $user2->id, time(), '',
$category->id);
// Add User Calendar Events for user1 and user2.
@@ -515,10 +493,10 @@ class core_calendar_privacy_testcase extends provider_testcase {
// Add a Course Calendar Events for Course 1.
$this->setUser($user1);
$this->create_test_standard_calendar_event('course', 0, time(), '',
$this->create_test_standard_calendar_event('course', $user1->id, time(), '',
0, $course1->id);
$this->setUser($user2);
$this->create_test_standard_calendar_event('course', 0, time(), '',
$this->create_test_standard_calendar_event('course', $user2->id, time(), '',
0, $course1->id);
// Add a Course Assignment Action Calendar Event for Course 2.
@@ -536,7 +514,7 @@ class core_calendar_privacy_testcase extends provider_testcase {
'assign', $user2->id, time());
// Add a Calendar Subscription and Group Calendar Event to Course 3.
$this->create_test_standard_calendar_event('group', 0, time(), '', 0,
$this->create_test_standard_calendar_event('group', $user2->id, time(), '', 0,
$course3->id, $course3group->id);
$this->setUser($user3);
$this->create_test_calendar_subscription('course', 'https://calendar.google.com/', $user3->id,
@@ -552,22 +530,28 @@ class core_calendar_privacy_testcase extends provider_testcase {
provider::get_users_in_context($userlist2);
$this->assertCount(1, $userlist2);
$this->assertTrue(in_array($user2->id, $userlist2->get_userids()));
// The user list for course1context should not return any users.
// The user list for course1context should return user1 and user2.
$userlist3 = new \core_privacy\local\request\userlist($course1context, $component);
provider::get_users_in_context($userlist3);
$this->assertCount(0, $userlist3);
$this->assertCount(2, $userlist3);
$this->assertTrue(in_array($user1->id, $userlist3->get_userids()));
$this->assertTrue(in_array($user2->id, $userlist3->get_userids()));
// The user list for course2context should not return any users.
$userlist4 = new \core_privacy\local\request\userlist($course2context, $component);
provider::get_users_in_context($userlist4);
$this->assertCount(0, $userlist4);
// The user list for course3context should not return any users.
// The user list for course3context should return user2 and user3.
$userlist5 = new \core_privacy\local\request\userlist($course3context, $component);
provider::get_users_in_context($userlist5);
$this->assertCount(0, $userlist5);
// The user list for categorycontext should not return any users.
$this->assertCount(2, $userlist5);
$this->assertTrue(in_array($user2->id, $userlist5->get_userids()));
$this->assertTrue(in_array($user3->id, $userlist5->get_userids()));
// The user list for categorycontext should return user1 and user2.
$userlist6 = new \core_privacy\local\request\userlist($categorycontext, $component);
provider::get_users_in_context($userlist6);
$this->assertCount(0, $userlist6);
$this->assertCount(2, $userlist6);
$this->assertTrue(in_array($user1->id, $userlist6->get_userids()));
$this->assertTrue(in_array($user2->id, $userlist6->get_userids()));
// The user list for modulecontext should return user2.
$userlist7 = new \core_privacy\local\request\userlist($modulecontext, $component);
provider::get_users_in_context($userlist7);
@@ -612,10 +596,10 @@ class core_calendar_privacy_testcase extends provider_testcase {
// Add Category Calendar Events for Category.
$this->setUser($user1);
$this->create_test_standard_calendar_event('category', 0, time(), '',
$this->create_test_standard_calendar_event('category', $user1->id, time(), '',
$category->id);
$this->setUser($user2);
$this->create_test_standard_calendar_event('category', 0, time(), '',
$this->create_test_standard_calendar_event('category', $user2->id, time(), '',
$category->id);
// Add User Calendar Events for user1 and user2.
@@ -631,10 +615,10 @@ class core_calendar_privacy_testcase extends provider_testcase {
// Add a Course Calendar Events for Course 1.
$this->setUser($user1);
$this->create_test_standard_calendar_event('course', 0, time(), '',
$this->create_test_standard_calendar_event('course', $user1->id, time(), '',
0, $course1->id);
$this->setUser($user2);
$this->create_test_standard_calendar_event('course', 0, time(), '',
$this->create_test_standard_calendar_event('course', $user2->id, time(), '',
0, $course1->id);
// Add a Course Assignment Action Calendar Event for Course 2.
@@ -652,7 +636,7 @@ class core_calendar_privacy_testcase extends provider_testcase {
'assign', $user2->id, time());
// Add a Calendar Subscription and Group Calendar Event to Course 3.
$this->create_test_standard_calendar_event('group', 0, time(), '', 0,
$this->create_test_standard_calendar_event('group', $user2->id, time(), '', 0,
$course3->id, $course3group->id);
$this->setUser($user3);
$this->create_test_calendar_subscription('course', 'https://calendar.google.com/', $user3->id,
@@ -666,22 +650,22 @@ class core_calendar_privacy_testcase extends provider_testcase {
$userlist2 = new \core_privacy\local\request\userlist($usercontext2, $component);
provider::get_users_in_context($userlist2);
$this->assertCount(1, $userlist2);
// The user list for course1context should not return any users.
// The user list for course1context should return user1 and user2.
$userlist3 = new \core_privacy\local\request\userlist($course1context, $component);
provider::get_users_in_context($userlist3);
$this->assertCount(0, $userlist3);
$this->assertCount(2, $userlist3);
// The user list for course2context should not return any users.
$userlist4 = new \core_privacy\local\request\userlist($course2context, $component);
provider::get_users_in_context($userlist4);
$this->assertCount(0, $userlist4);
// The user list for course3context should not return any users.
// The user list for course3context should return user2 and user3.
$userlist5 = new \core_privacy\local\request\userlist($course3context, $component);
provider::get_users_in_context($userlist5);
$this->assertCount(0, $userlist5);
// The user list for categorycontext should not return any users.
$this->assertCount(2, $userlist5);
// The user list for categorycontext should return user1 and user2.
$userlist6 = new \core_privacy\local\request\userlist($categorycontext, $component);
provider::get_users_in_context($userlist6);
$this->assertCount(0, $userlist6);
$this->assertCount(2, $userlist6);
// The user list for modulecontext should return user2.
$userlist7 = new \core_privacy\local\request\userlist($modulecontext, $component);
provider::get_users_in_context($userlist7);
@@ -709,25 +693,28 @@ class core_calendar_privacy_testcase extends provider_testcase {
$approvedlist2 = new approved_userlist($course1context, $component, []);
// Delete using delete_data_for_user.
provider::delete_data_for_users($approvedlist2);
// The user list for course1context should not return any users.
// The user list for course1context should return user1 and user2.
$userlist3 = new \core_privacy\local\request\userlist($course1context, $component);
provider::get_users_in_context($userlist3);
$this->assertCount(0, $userlist3);
$this->assertCount(2, $userlist3);
$this->assertTrue(in_array($user1->id, $userlist3->get_userids()));
$this->assertTrue(in_array($user2->id, $userlist3->get_userids()));
// Convert $userlist3 into an approved_contextlist.
// Pass the ID of user1 as a value for the approved user list.
$approvedlist2 = new approved_userlist($course1context, $component, [$user1->id]);
// Delete using delete_data_for_user.
provider::delete_data_for_users($approvedlist2);
// The user list for course1context should not return any users.
// The user list for course1context should return user2.
$userlist3 = new \core_privacy\local\request\userlist($course1context, $component);
provider::get_users_in_context($userlist3);
$this->assertCount(0, $userlist3);
$this->assertCount(1, $userlist3);
$this->assertTrue(in_array($user2->id, $userlist3->get_userids()));
// The user list for course3context should not return any users.
// The user list for course3context should still return user2 and user3.
$userlist5 = new \core_privacy\local\request\userlist($course3context, $component);
provider::get_users_in_context($userlist5);
$this->assertCount(0, $userlist5);
$this->assertCount(2, $userlist5);
// Convert $userlist6 into an approved_contextlist.
$approvedlist3 = new approved_userlist($categorycontext, $component, $userlist6->get_userids());
@@ -775,7 +762,7 @@ class core_calendar_privacy_testcase extends provider_testcase {
'categoryid' => $categoryid,
'courseid' => $courseid,
'groupid' => $groupid,
'userid' => ($eventtype == 'user') ? $userid : 0,
'userid' => $userid,
'modulename' => 0,
'instance' => 0,
'eventtype' => $eventtype,
@@ -835,7 +822,7 @@ class core_calendar_privacy_testcase extends provider_testcase {
'categoryid' => $categoryid,
'courseid' => $courseid,
'groupid' => $groupid,
'userid' => ($eventtype == 'user') ? $userid : 0,
'userid' => $userid,
'eventtype' => $eventtype
];
@@ -44,4 +44,4 @@ Feature: Manage H5P content from the content bank
And I expand "Site pages" node
And I click on "Content bank" "link"
When I follow "filltheblanks.h5p"
Then "region-main-settings-menu" "region" should not exist
Then "Rename" "link" should not exist in the "region-main-settings-menu" "region"
@@ -43,25 +43,25 @@ Feature: Replace H5P file from an existing content requires special capabilities
Scenario: Teacher cannot replace another user's H5P files
When I click on "admincontent" "link"
Then "region-main-settings-menu" "region" should not exist
Then "Replace with file" "link" should not exist in the "region-main-settings-menu" "region"
Scenario: Teacher cannot replace a content without having upload capability
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/contentbank:upload | Prevent | editingteacher | Course | C1 |
When I click on "teachercontent" "link"
Then "region-main-settings-menu" "region" should not exist
Then "Replace with file" "link" should not exist in the "region-main-settings-menu" "region"
Scenario: Teacher cannot replace a content without having the H5P upload capability
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| contenttype/h5p:upload | Prevent | editingteacher | Course | C1 |
When I click on "teachercontent" "link"
Then "region-main-settings-menu" "region" should not exist
Then "Replace with file" "link" should not exist in the "region-main-settings-menu" "region"
Scenario: Teacher cannot replace a content without having the manage own content capability
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/contentbank:manageowncontent | Prevent | editingteacher | Course | C1 |
When I click on "teachercontent" "link"
Then "region-main-settings-menu" "region" should not exist
Then "Replace with file" "link" should not exist in the "region-main-settings-menu" "region"
@@ -58,7 +58,7 @@ Feature: Delete H5P file from the content bank
And I click on "Content bank" "link" in the "Navigation" "block"
And I should see "filltheblanks.h5p"
And I follow "filltheblanks.h5p"
Then ".header-actions-container" "css_element" should not exist
Then "Delete" "link" should not exist in the "region-main-settings-menu" "region"
And I click on "Content bank" "link"
And I click on "Upload" "link"
And I click on "Choose a file..." "button"
@@ -159,3 +159,37 @@ Feature: Content bank use editor feature
And I click on "Content bank" "link"
And I follow "filltheblanks.h5p"
Then "Edit" "link" should not exist in the "region-main" "region"
Scenario: Teachers keep their content authoring in copied courses
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
And the following "contentbank content" exist:
| contextlevel | reference | contenttype | user | contentname | filepath |
| Course | C1 | contenttype_h5p | admin | filltheblanks.h5p | /h5p/tests/fixtures/filltheblanks.h5p |
| Course | C1 | contenttype_h5p | teacher1 | ipsums.h5p | /h5p/tests/fixtures/ipsums.h5p |
And I am on "Course 1" course homepage
And I navigate to "Copy course" in current page administration
And I set the following fields to these values:
| Course full name | Copy |
| Course short name | Copy |
| Teacher | 1 |
When I press "Copy and view"
And I trigger cron
And I am on homepage
And I log out
And I log in as "teacher1"
And I am on "Copy" course homepage
And I expand "Site pages" node
And I click on "Content bank" "link"
And I follow "ipsums.h5p"
Then "Edit" "link" should exist in the "region-main" "region"
And I click on "Content bank" "link"
And I follow "filltheblanks.h5p"
Then "Edit" "link" should not exist in the "region-main" "region"
+11 -5
View File
@@ -24,16 +24,18 @@ Feature: Search content in the content bank
And I expand "Site pages" node
And I click on "Content bank" "link"
And I should see "santjordi.h5p"
And "Clear search input" "button" should not exist
And "Clear search input" "button" should not be visible
And I should not see "items found"
When I set the field "Search" to "book"
Then "Clear search input" "button" should exist
# Waiting for the animation to show the button to finish.
And I wait "1" seconds
Then "Clear search input" "button" should be visible
And I should see "3 items found"
And I should see "SantJordi_book"
And I should see "mathsbook.h5p"
And I should see "historybook.h5p"
And I set the field "Search" to "sant"
And "Clear search input" "button" should exist
And "Clear search input" "button" should be visible
And I should see "5 items found"
And I set the field "Search" to "santjordi"
And I should see "4 items found"
@@ -42,10 +44,14 @@ Feature: Search content in the content bank
And I should see "SantJordi_book"
And I should see "Dragon_santjordi.h5p"
And I click on "Clear search input" "button"
And "Clear search input" "button" should not exist
# Waiting for the animation to hide the button to finish.
And I wait "1" seconds
And "Clear search input" "button" should not be visible
And I should not see "items found"
And I set the field "Search" to ".h5p"
And "Clear search input" "button" should exist
# Waiting for the animation to show the button to finish.
And I wait "1" seconds
And "Clear search input" "button" should be visible
And I should see "7 items found"
And I set the field "Search" to "friend"
And I should see "0 items found"
@@ -103,6 +103,10 @@ class no_recent_accesses extends course_enrolments {
return get_string('nocoursestudents', 'course');
}
if (!$fortraining && !$course->get_course_data()->visible) {
return get_string('hiddenfromstudents');
}
if ($course->get_end() && $course->get_end() < $course->get_start()) {
return get_string('errorendbeforestart', 'course');
}
+24 -25
View File
@@ -57,8 +57,6 @@ M.course_dndupload = {
// The selector identifying the list of modules within a section (note changing this may require
// changes to the get_mods_element function)
modslistselector: 'ul.section',
// Original onbeforeunload event.
originalUnloadEvent: null,
/**
* Initalise the drag and drop upload interface
@@ -765,7 +763,10 @@ M.course_dndupload = {
xhr.onreadystatechange = function() {
if (xhr.readyState == 1) {
this.originalUnloadEvent = window.onbeforeunload;
self.reportUploadDirtyState(true);
// Trigger form upload start events.
require(['core_form/events'], function(FormEvent) {
FormEvent.triggerUploadStarted(section.get('id'));
});
}
if (xhr.readyState == 4) {
if (xhr.status == 200) {
@@ -793,7 +794,10 @@ M.course_dndupload = {
} else {
new M.core.alert({message: M.util.get_string('servererror', 'moodle')});
}
self.reportUploadDirtyState(false);
// Trigger form upload complete events.
require(['core_form/events'], function(FormEvent) {
FormEvent.triggerUploadCompleted(section.get('id'));
});
}
};
@@ -1025,7 +1029,10 @@ M.course_dndupload = {
xhr.onreadystatechange = function() {
if (xhr.readyState == 1) {
this.originalUnloadEvent = window.onbeforeunload;
self.reportUploadDirtyState(true);
// Trigger form upload start events.
require(['core_form/events'], function(FormEvent) {
FormEvent.triggerUploadStarted(section.get('id'));
});
}
if (xhr.readyState == 4) {
if (xhr.status == 200) {
@@ -1043,13 +1050,24 @@ M.course_dndupload = {
} else {
// Error - remove the dummy element
resel.parent.removeChild(resel.li);
// Trigger form upload complete events.
require(['core_form/events'], function(FormEvent) {
FormEvent.triggerUploadCompleted(section.get('id'));
});
new M.core.alert({message: result.error});
}
}
} else {
// Trigger form upload complete events.
require(['core_form/events'], function(FormEvent) {
FormEvent.triggerUploadCompleted(section.get('id'));
});
new M.core.alert({message: M.util.get_string('servererror', 'moodle')});
}
self.reportUploadDirtyState(false);
// Trigger form upload complete events.
require(['core_form/events'], function(FormEvent) {
FormEvent.triggerUploadCompleted(section.get('id'));
});
}
};
@@ -1083,24 +1101,5 @@ M.course_dndupload = {
if (M.core.actionmenu && M.core.actionmenu.newDOMNode) {
M.core.actionmenu.newDOMNode(node);
}
},
/**
* Set the event to prevent user navigate away when upload progress still running.
*
* @param {bool} enable true if upload progress is running, false otherwise
*/
reportUploadDirtyState: function(enable) {
if (!enable) {
window.onbeforeunload = this.originalUnloadEvent;
} else {
window.onbeforeunload = function(e) {
var warningMessage = M.util.get_string('changesmadereallygoaway', 'moodle');
if (e) {
e.returnValue = warningMessage;
}
return warningMessage;
};
}
}
};
+16 -3
View File
@@ -621,10 +621,23 @@ abstract class moodleform_mod extends moodleform {
if (!empty($CFG->enableavailability)) {
// Add special button to end of previous section if groups/groupings
// are enabled.
if ($this->_features->groups || $this->_features->groupings) {
$availabilityplugins = \core\plugininfo\availability::get_enabled_plugins();
$groupavailability = $this->_features->groups && array_key_exists('group', $availabilityplugins);
$groupingavailability = $this->_features->groupings && array_key_exists('grouping', $availabilityplugins);
if ($groupavailability || $groupingavailability) {
// When creating the button, we need to set type=button to prevent it behaving as a submit.
$mform->addElement('static', 'restrictgroupbutton', '',
html_writer::tag('button', get_string('restrictbygroup', 'availability'),
array('id' => 'restrictbygroup', 'disabled' => 'disabled', 'class' => 'btn btn-secondary')));
html_writer::tag('button', get_string('restrictbygroup', 'availability'), [
'id' => 'restrictbygroup',
'type' => 'button',
'disabled' => 'disabled',
'class' => 'btn btn-secondary',
'data-groupavailability' => $groupavailability,
'data-groupingavailability' => $groupingavailability
])
);
}
// Availability field. This is just a textarea; the user interface
+9 -7
View File
@@ -51,8 +51,8 @@ Feature: Display and choose from the available activities in course
And I should see "The assignment activity module enables a teacher to communicate tasks, collect work and provide grades and feedback." in the "help" "core_course > Activity chooser screen"
And I should see "Back" in the "help" "core_course > Activity chooser screen"
When I click on "Back" "button" in the "help" "core_course > Activity chooser screen"
Then "modules" "core_course > Activity chooser screen" should exist
And "help" "core_course > Activity chooser screen" should not exist
Then "modules" "core_course > Activity chooser screen" should be visible
And "help" "core_course > Activity chooser screen" should not be visible
And "Back" "button" should not exist in the "modules" "core_course > Activity chooser screen"
And I should not see "The assignment activity module enables a teacher to communicate tasks, collect work and provide grades and feedback." in the "Add an activity or resource" "dialogue"
@@ -123,24 +123,26 @@ Feature: Display and choose from the available activities in course
And I should see "Lesson" in the "Add an activity or resource" "dialogue"
When I set the field "search" to ""
And I should not see "1 results found" in the "Add an activity or resource" "dialogue"
Then ".searchresultscontainer" "css_element" should not exist
Then ".searchresultscontainer" "css_element" should not be visible
And ".optionscontainer" "css_element" should exist
Scenario: Teacher can not see a "clear" button if a search query is not entered in the activity chooser search bar
When I click on "Add an activity or resource" "button" in the "Topic 1" "section"
Then "Clear search input" "button" should not exist
Then "Clear search input" "button" should not be visible
Scenario: Teacher can see a "clear" button after entering a search query in the activity chooser search bar
Given I click on "Add an activity or resource" "button" in the "Topic 1" "section"
When I set the field "search" to "Search query"
Then "Clear search input" "button" should not exist
Then "Clear search input" "button" should not be visible
Scenario: Teacher can not see a "clear" button if the search query is removed in the activity chooser search bar
Given I click on "Add an activity or resource" "button" in the "Topic 1" "section"
And I set the field "search" to "Search query"
And "Clear search input" "button" should exist
When I set the field "search" to ""
Then "Clear search input" "button" should not exist
# Waiting for the animation to hide the button to finish.
And I wait "1" seconds
Then "Clear search input" "button" should not be visible
Scenario: Teacher can instantly remove the search query from the activity search bar by clicking on the "clear" button
Given I click on "Add an activity or resource" "button" in the "Topic 1" "section"
@@ -148,7 +150,7 @@ Feature: Display and choose from the available activities in course
And I should see "results found" in the "Add an activity or resource" "dialogue"
When I click on "Clear search input" "button"
Then I should not see "Search query"
And ".searchresultscontainer" "css_element" should not exist
And ".searchresultscontainer" "css_element" should not be visible
And ".optionscontainer" "css_element" should exist
Scenario: Teacher gets the base case for the Activity Chooser tab mode
+1 -1
View File
@@ -1,2 +1,2 @@
define ("core_customfield/form",["jquery","core/str","core/notification","core/ajax","core/templates","core/sortable_list","core/inplace_editable"],function(a,b,c,d,e,f){var g=function(f,g,h,i,j){b.get_strings([{key:"confirm"},{key:"confirmdelete"+g,component:"core_customfield"},{key:"yes"},{key:"no"}]).done(function(b){c.confirm(b[0],b[1],b[2],b[3],function(){var b="field"===g?"core_customfield_delete_field":"core_customfield_delete_category";d.call([{methodname:b,args:{id:f}},{methodname:"core_customfield_reload_template",args:{component:h,area:i,itemid:j}}])[1].then(function(a){return e.render("core_customfield/list",a)}).then(function(b,c){e.replaceNode(a("[data-region=\"list-page\"]"),b,c);return null}).fail(c.exception)})}).fail(c.exception)},h=function(b,f,g){var h=d.call([{methodname:"core_customfield_create_category",args:{component:b,area:f,itemid:g}},{methodname:"core_customfield_reload_template",args:{component:b,area:f,itemid:g}}]),i;h[0].then(function(a){i=a;return null}).fail(c.exception);h[1].then(function(a){return e.render("core_customfield/list",a)}).then(function(b,c){e.replaceNode(a("[data-region=\"list-page\"]"),b,c);window.location.href="#category-"+i;return null}).fail(c.exception)};return{init:function init(){var e=a("#customfield_catlist"),i=e.attr("data-component"),j=e.attr("data-area"),k=e.attr("data-itemid");a("[data-role=deletefield]").on("click",function(b){g(a(this).attr("data-id"),"field",i,j,k);b.preventDefault()});a("[data-role=deletecategory]").on("click",function(b){g(a(this).attr("data-id"),"category",i,j,k);b.preventDefault()});a("[data-role=addnewcategory]").on("click",function(){h(i,j,k)});var l=function(a){return a.closest("[data-category-id]").find("[data-inplaceeditable][data-itemtype=category][data-component=core_customfield]").attr("data-value")},m=new f(a("#customfield_catlist .categorieslist"),{moveHandlerSelector:".movecategory [data-drag-type=move]"});m.getElementName=function(b){return a.Deferred().resolve(l(b))};a("[data-category-id]").on("sortablelist-drop",function(a,b){if(b.positionChanged){var e=d.call([{methodname:"core_customfield_move_category",args:{id:b.element.data("category-id"),beforeid:b.targetNextElement.data("category-id")}}]);e[0].fail(c.exception)}a.stopPropagation()});var n=new f(a("#customfield_catlist .fieldslist tbody"),{moveHandlerSelector:".movefield [data-drag-type=move]"});n.getDestinationName=function(c,d){if(!d.length){return b.get_string("totopofcategory","customfield",l(c))}else if(d.attr("data-field-name")){return b.get_string("afterfield","customfield",d.attr("data-field-name"))}else{return a.Deferred().resolve("")}};a("[data-field-name]").on("sortablelist-drop",function(a,b){a.stopPropagation();if(b.positionChanged){var e=d.call([{methodname:"core_customfield_move_field",args:{id:b.element.data("field-id"),beforeid:b.targetNextElement.data("field-id"),categoryid:+b.targetList.closest("[data-category-id]").attr("data-category-id")}}]);e[0].fail(c.exception)}}).on("sortablelist-drag",function(d){d.stopPropagation();b.get_string("therearenofields","core_customfield").then(function(b){a("#customfield_catlist .categorieslist").children().each(function(){var c=a(this).find(a(".field")),d=a(this).find(a(".nofields"));if(!c.length&&!d.length){a(this).find("tbody").append("<tr class=\"nofields\"><td colspan=\"5\">"+b+"</td></tr>")}if(c.length&&d.length){d.remove()}});return null}).fail(c.exception)});a("[data-category-id], [data-field-name]").on("sortablelist-dragstart",function(b,c){setTimeout(function(){a(".sortable-list-is-dragged").width(c.element.width())},501)})}}});
define ("core_customfield/form",["jquery","core/str","core/notification","core/ajax","core/templates","core/sortable_list","core/pending","core/inplace_editable"],function(a,b,c,d,e,f,g){var h=function(f,h,i,j,k){var l=new g("core_customfield/form:confirmDelete");b.get_strings([{key:"confirm"},{key:"confirmdelete"+h,component:"core_customfield"},{key:"yes"},{key:"no"}]).then(function(b){c.confirm(b[0],b[1],b[2],b[3],function(){var b=new g("core_customfield/form:confirmDelete"),l="field"===h?"core_customfield_delete_field":"core_customfield_delete_category";d.call([{methodname:l,args:{id:f}},{methodname:"core_customfield_reload_template",args:{component:i,area:j,itemid:k}}])[1].then(function(a){return e.render("core_customfield/list",a)}).then(function(b,c){e.replaceNode(a("[data-region=\"list-page\"]"),b,c);return null}).then(b.resolve).catch(c.exception)})}).then(l.resolve).catch(c.exception)},i=function(b,f,h){var i=new g("core_customfield/form:confirmDelete"),j=d.call([{methodname:"core_customfield_create_category",args:{component:b,area:f,itemid:h}},{methodname:"core_customfield_reload_template",args:{component:b,area:f,itemid:h}}]),k;j[0].then(function(a){k=a;return null}).catch(c.exception);j[1].then(function(a){return e.render("core_customfield/list",a)}).then(function(b,c){e.replaceNode(a("[data-region=\"list-page\"]"),b,c);window.location.href="#category-"+k;return null}).catch(c.exception);Promise.all(j).then(i.resolve).catch()};return{init:function init(){var e=a("#customfield_catlist"),j=e.attr("data-component"),k=e.attr("data-area"),l=e.attr("data-itemid");a("[data-role=deletefield]").on("click",function(b){h(a(this).attr("data-id"),"field",j,k,l);b.preventDefault()});a("[data-role=deletecategory]").on("click",function(b){h(a(this).attr("data-id"),"category",j,k,l);b.preventDefault()});a("[data-role=addnewcategory]").on("click",function(){i(j,k,l)});var m=function(a){return a.closest("[data-category-id]").find("[data-inplaceeditable][data-itemtype=category][data-component=core_customfield]").attr("data-value")},n=new f(a("#customfield_catlist .categorieslist"),{moveHandlerSelector:".movecategory [data-drag-type=move]"});n.getElementName=function(b){return a.Deferred().resolve(m(b))};a("[data-category-id]").on("sortablelist-drop",function(a,b){if(b.positionChanged){var e=new g("core_customfield/form:categoryid:on:sortablelist-drop");d.call([{methodname:"core_customfield_move_category",args:{id:b.element.data("category-id"),beforeid:b.targetNextElement.data("category-id")}}])[0].then(e.resolve).catch(c.exception)}a.stopPropagation()});var o=new f(a("#customfield_catlist .fieldslist tbody"),{moveHandlerSelector:".movefield [data-drag-type=move]"});o.getDestinationName=function(c,d){if(!d.length){return b.get_string("totopofcategory","customfield",m(c))}else if(d.attr("data-field-name")){return b.get_string("afterfield","customfield",d.attr("data-field-name"))}else{return a.Deferred().resolve("")}};a("[data-field-name]").on("sortablelist-drop",function(a,b){a.stopPropagation();if(b.positionChanged){var e=new g("core_customfield/form:fieldname:on:sortablelist-drop");d.call([{methodname:"core_customfield_move_field",args:{id:b.element.data("field-id"),beforeid:b.targetNextElement.data("field-id"),categoryid:+b.targetList.closest("[data-category-id]").attr("data-category-id")}}])[0].then(e.resolve).catch(c.exception)}});a("[data-field-name]").on("sortablelist-drag",function(d){var e=new g("core_customfield/form:fieldname:on:sortablelist-drag");d.stopPropagation();b.get_string("therearenofields","core_customfield").then(function(b){a("#customfield_catlist .categorieslist").children().each(function(){var c=a(this).find(a(".field")),d=a(this).find(a(".nofields"));if(!c.length&&!d.length){a(this).find("tbody").append("<tr class=\"nofields\"><td colspan=\"5\">"+b+"</td></tr>")}if(c.length&&d.length){d.remove()}});return null}).then(e.resolve).catch(c.exception)});a("[data-category-id], [data-field-name]").on("sortablelist-dragstart",function(b,c){setTimeout(function(){a(".sortable-list-is-dragged").width(c.element.width())},501)})}}});
//# sourceMappingURL=form.min.js.map
File diff suppressed because one or more lines are too long
+66 -27
View File
@@ -21,9 +21,16 @@
* @copyright 2018 Toni Barbera
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/str', 'core/notification', 'core/ajax', 'core/templates', 'core/sortable_list', 'core/inplace_editable'],
function(
$, Str, Notification, Ajax, Templates, SortableList) {
define([
'jquery',
'core/str',
'core/notification',
'core/ajax',
'core/templates',
'core/sortable_list',
'core/pending',
'core/inplace_editable',
], function($, Str, Notification, Ajax, Templates, SortableList, Pending) {
/**
* Display confirmation dialogue
@@ -35,25 +42,36 @@ define(['jquery', 'core/str', 'core/notification', 'core/ajax', 'core/templates'
* @param {Number} itemid
*/
var confirmDelete = function(id, type, component, area, itemid) {
var pendingPromise = new Pending('core_customfield/form:confirmDelete');
Str.get_strings([
{'key': 'confirm'},
{'key': 'confirmdelete' + type, component: 'core_customfield'},
{'key': 'yes'},
{'key': 'no'},
]).done(function(s) {
])
.then(function(s) {
Notification.confirm(s[0], s[1], s[2], s[3], function() {
var pendingDeletePromise = new Pending('core_customfield/form:confirmDelete');
var func = (type === 'field') ? 'core_customfield_delete_field' : 'core_customfield_delete_category';
Ajax.call([
{methodname: func, args: {id: id}},
{methodname: 'core_customfield_reload_template', args: {component: component, area: area, itemid: itemid}}
])[1].then(function(response) {
])[1]
.then(function(response) {
return Templates.render('core_customfield/list', response);
}).then(function(html, js) {
})
.then(function(html, js) {
Templates.replaceNode($('[data-region="list-page"]'), html, js);
return null;
}).fail(Notification.exception);
})
.then(pendingDeletePromise.resolve)
.catch(Notification.exception);
});
}).fail(Notification.exception);
return;
})
.then(pendingPromise.resolve)
.catch(Notification.exception);
};
/**
@@ -64,24 +82,31 @@ define(['jquery', 'core/str', 'core/notification', 'core/ajax', 'core/templates'
* @param {Number} itemid
*/
var createNewCategory = function(component, area, itemid) {
var pendingPromise = new Pending('core_customfield/form:confirmDelete');
var promises = Ajax.call([
{methodname: 'core_customfield_create_category', args: {component: component, area: area, itemid: itemid}},
{methodname: 'core_customfield_reload_template', args: {component: component, area: area, itemid: itemid}}
]),
categoryid;
{methodname: 'core_customfield_create_category', args: {component: component, area: area, itemid: itemid}},
{methodname: 'core_customfield_reload_template', args: {component: component, area: area, itemid: itemid}}
]);
var categoryid;
promises[0].then(function(response) {
categoryid = response;
return null;
}).fail(Notification.exception);
}).catch(Notification.exception);
promises[1].then(function(response) {
return Templates.render('core_customfield/list', response);
}).then(function(html, js) {
})
.then(function(html, js) {
Templates.replaceNode($('[data-region="list-page"]'), html, js);
window.location.href = '#category-' + categoryid;
return null;
}).fail(Notification.exception);
})
.catch(Notification.exception);
Promise.all(promises)
.then(pendingPromise.resolve)
.catch();
};
return {
@@ -89,18 +114,21 @@ define(['jquery', 'core/str', 'core/notification', 'core/ajax', 'core/templates'
* Initialise the custom fields manager
*/
init: function() {
var mainlist = $('#customfield_catlist'),
component = mainlist.attr('data-component'),
area = mainlist.attr('data-area'),
itemid = mainlist.attr('data-itemid');
var mainlist = $('#customfield_catlist');
var component = mainlist.attr('data-component');
var area = mainlist.attr('data-area');
var itemid = mainlist.attr('data-itemid');
$("[data-role=deletefield]").on('click', function(e) {
confirmDelete($(this).attr('data-id'), 'field', component, area, itemid);
e.preventDefault();
});
$("[data-role=deletecategory]").on('click', function(e) {
confirmDelete($(this).attr('data-id'), 'category', component, area, itemid);
e.preventDefault();
});
$('[data-role=addnewcategory]').on('click', function() {
createNewCategory(component, area, itemid);
});
@@ -124,7 +152,8 @@ define(['jquery', 'core/str', 'core/notification', 'core/ajax', 'core/templates'
$('[data-category-id]').on('sortablelist-drop', function(evt, info) {
if (info.positionChanged) {
var promises = Ajax.call([
var pendingPromise = new Pending('core_customfield/form:categoryid:on:sortablelist-drop');
Ajax.call([
{
methodname: 'core_customfield_move_category',
args: {
@@ -133,8 +162,9 @@ define(['jquery', 'core/str', 'core/notification', 'core/ajax', 'core/templates'
}
},
]);
promises[0].fail(Notification.exception);
])[0]
.then(pendingPromise.resolve)
.catch(Notification.exception);
}
evt.stopPropagation(); // Important for nested lists to prevent multiple targets.
});
@@ -158,7 +188,8 @@ define(['jquery', 'core/str', 'core/notification', 'core/ajax', 'core/templates'
$('[data-field-name]').on('sortablelist-drop', function(evt, info) {
evt.stopPropagation(); // Important for nested lists to prevent multiple targets.
if (info.positionChanged) {
var promises = Ajax.call([
var pendingPromise = new Pending('core_customfield/form:fieldname:on:sortablelist-drop');
Ajax.call([
{
methodname: 'core_customfield_move_field',
args: {
@@ -167,11 +198,17 @@ define(['jquery', 'core/str', 'core/notification', 'core/ajax', 'core/templates'
categoryid: Number(info.targetList.closest('[data-category-id]').attr('data-category-id'))
},
},
]);
promises[0].fail(Notification.exception);
])[0]
.then(pendingPromise.resolve)
.catch(Notification.exception);
}
}).on('sortablelist-drag', function(evt) {
});
$('[data-field-name]').on('sortablelist-drag', function(evt) {
var pendingPromise = new Pending('core_customfield/form:fieldname:on:sortablelist-drag');
evt.stopPropagation(); // Important for nested lists to prevent multiple targets.
// Refreshing fields tables.
Str.get_string('therearenofields', 'core_customfield').then(function(s) {
$('#customfield_catlist .categorieslist').children().each(function() {
@@ -187,7 +224,9 @@ define(['jquery', 'core/str', 'core/notification', 'core/ajax', 'core/templates'
}
});
return null;
}).fail(Notification.exception);
})
.then(pendingPromise.resolve)
.catch(Notification.exception);
});
$('[data-category-id], [data-field-name]').on('sortablelist-dragstart',
@@ -55,12 +55,16 @@ class field_controller extends \core_customfield\field_controller {
$mform->addElement('text', 'configdata[displaysize]', get_string('displaysize', 'customfield_text'), ['size' => 6]);
$mform->setType('configdata[displaysize]', PARAM_INT);
$mform->setDefault('configdata[displaysize]', 50);
if (!$this->get_configdata_property('displaysize')) {
$mform->setDefault('configdata[displaysize]', 50);
}
$mform->addRule('configdata[displaysize]', null, 'numeric', null, 'client');
$mform->addElement('text', 'configdata[maxlength]', get_string('maxlength', 'customfield_text'), ['size' => 6]);
$mform->setType('configdata[maxlength]', PARAM_INT);
$mform->setDefault('configdata[maxlength]', 1333);
if (!$this->get_configdata_property('maxlength')) {
$mform->setDefault('configdata[maxlength]', 1333);
}
$mform->addRule('configdata[maxlength]', null, 'numeric', null, 'client');
$mform->addElement('selectyesno', 'configdata[ispassword]', get_string('ispassword', 'customfield_text'));
@@ -113,7 +117,7 @@ class field_controller extends \core_customfield\field_controller {
$errors['configdata[link]'] = get_string('errorconfiglinkplaceholder', 'customfield_text');
} else if (!validateUrlSyntax(str_replace('$$', 'XYZ', $link), 's+H?S?F-E-u-P-a?I?p?f?q?r?')) {
// This validation is more strict than PARAM_URL - it requires the protocol and it must be either http or https.
$errors['configdata[link]'] = get_string('errorconfigdisplaysize', 'customfield_text');
$errors['configdata[link]'] = get_string('errorconfiglinksyntax', 'customfield_text');
}
}
}
+6 -2
View File
@@ -188,7 +188,9 @@ $removeenabled = $canunenrol ? '' : 'disabled="disabled"';
</td>
<td id="buttonscell">
<div id="addcontrols">
<input name="add" <?php echo $addenabled; ?> id="add" type="submit" value="<?php echo $OUTPUT->larrow().'&nbsp;'.get_string('add'); ?>" title="<?php print_string('add'); ?>" /><br />
<input class="btn btn-secondary" name="add" <?php echo $addenabled; ?> id="add" type="submit"
value="<?php echo $OUTPUT->larrow() . '&nbsp;' . get_string('add'); ?>"
title="<?php print_string('add'); ?>" /><br />
<div class="enroloptions">
@@ -205,7 +207,9 @@ $removeenabled = $canunenrol ? '' : 'disabled="disabled"';
</div>
<div id="removecontrols">
<input name="remove" id="remove" <?php echo $removeenabled; ?> type="submit" value="<?php echo get_string('remove').'&nbsp;'.$OUTPUT->rarrow(); ?>" title="<?php print_string('remove'); ?>" />
<input class="btn btn-secondary" name="remove" id="remove" <?php echo $removeenabled; ?> type="submit"
value="<?php echo get_string('remove') . '&nbsp;' . $OUTPUT->rarrow(); ?>"
title="<?php print_string('remove'); ?>" />
</div>
</td>
<td id="potentialcell">
+2 -1
View File
@@ -1055,7 +1055,8 @@ class enrol_meta_plugin_testcase extends advanced_testcase {
$this->assertArrayHasKey('customint2', $errors);
// Test with valid data.
$validgroup = reset($groups);
reset($groups);
$validgroup = key($groups);
$data = [
'customint1' => [$metacourse2->id],
'customint2' => $validgroup
@@ -98,6 +98,7 @@ Feature: Users can auto-enrol themself in courses where self enrolment is allowe
And I log in as "student1"
And I am on "Course 1" course homepage
And I press "Enrol me"
And I should see "You are enrolled in the course"
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
@@ -118,6 +119,7 @@ Feature: Users can auto-enrol themself in courses where self enrolment is allowe
And I log in as "student1"
And I am on "Course 1" course homepage
And I press "Enrol me"
And I should see "You are enrolled in the course"
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
+126
View File
@@ -1036,6 +1036,59 @@ class core_enrollib_testcase extends advanced_testcase {
$this->assertEquals([$course1->id, $course2->id, $course3->id, $course4->id], array_keys($courses));
}
/**
* Data provider for {@see test_enrol_get_my_courses_by_time}
*
* @return array
*/
public function enrol_get_my_courses_by_time_provider(): array {
return [
'No start or end time' =>
[null, null, true],
'Start time now, no end time' =>
[0, null, true],
'Start time now, end time in the future' =>
[0, MINSECS, true],
'Start time in the past, no end time' =>
[-MINSECS, null, true],
'Start time in the past, end time in the future' =>
[-MINSECS, MINSECS, true],
'Start time in the past, end time in the past' =>
[-DAYSECS, -HOURSECS, false],
'Start time in the future' =>
[MINSECS, null, false],
];
}
/**
* Test that expected course enrolments are returned when they have timestart / timeend specified
*
* @param int|null $timestartoffset Null for 0, otherwise offset from current time
* @param int|null $timeendoffset Null for 0, otherwise offset from current time
* @param bool $expectreturn
*
* @dataProvider enrol_get_my_courses_by_time_provider
*/
public function test_enrol_get_my_courses_by_time(?int $timestartoffset, ?int $timeendoffset, bool $expectreturn): void {
$this->resetAfterTest();
$time = time();
$timestart = $timestartoffset === null ? 0 : $time + $timestartoffset;
$timeend = $timeendoffset === null ? 0 : $time + $timeendoffset;
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_and_enrol($course, 'student', null, 'manual', $timestart, $timeend);
$this->setUser($user);
$courses = enrol_get_my_courses();
if ($expectreturn) {
$this->assertCount(1, $courses);
$this->assertEquals($course->id, reset($courses)->id);
} else {
$this->assertEmpty($courses);
}
}
/**
* test_course_users
*
@@ -1362,4 +1415,77 @@ class core_enrollib_testcase extends advanced_testcase {
$durationinday = $duration / DAYSECS;
$this->assertEquals(9, $durationinday);
}
/**
* Test get_enrolled_with_capabilities_join cannotmatchanyrows attribute.
*
* @dataProvider get_enrolled_with_capabilities_join_cannotmatchanyrows_data()
* @param string $capability the tested capability
* @param bool $useprohibit if the capability must be assigned to prohibit
* @param int $expectedmatch expected cannotmatchanyrows value
* @param int $expectedcount expceted count value
*/
public function test_get_enrolled_with_capabilities_join_cannotmatchanyrows(
string $capability,
bool $useprohibit,
int $expectedmatch,
int $expectedcount
) {
global $DB, $CFG;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$context = context_course::instance($course->id);
$roleid = $CFG->defaultuserroleid;
// Override capability if necessary.
if ($useprohibit && $capability) {
assign_capability($capability, CAP_PROHIBIT, $roleid, $context);
}
// Check if we must enrol or not.
$this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
$join = get_enrolled_with_capabilities_join($context, '', $capability);
// Execute query.
$sql = "SELECT COUNT(DISTINCT u.id)
FROM {user} u {$join->joins}
WHERE {$join->wheres}";
$countrecords = $DB->count_records_sql($sql, $join->params);
// Validate cannotmatchanyrows.
$this->assertEquals($expectedmatch, $join->cannotmatchanyrows);
$this->assertEquals($expectedcount, $countrecords);
}
/**
* Data provider for test_get_enrolled_with_capabilities_join_cannotmatchanyrows
*
* @return @array of testing scenarios
*/
public function get_enrolled_with_capabilities_join_cannotmatchanyrows_data() {
return [
'no prohibits, no capability' => [
'capability' => '',
'useprohibit' => false,
'expectedmatch' => 0,
'expectedcount' => 1,
],
'no prohibits with capability' => [
'capability' => 'moodle/course:manageactivities',
'useprohibit' => false,
'expectedmatch' => 0,
'expectedcount' => 1,
],
'prohibits with capability' => [
'capability' => 'moodle/course:manageactivities',
'useprohibit' => true,
'expectedmatch' => 1,
'expectedcount' => 0,
],
];
}
}
+14 -2
View File
@@ -23,6 +23,16 @@ class grade_export_xml extends grade_export {
public $plugin = 'xml';
public $updatedgradesonly = false; // default to export ALL grades
/**
* Ensure we produce correctly formed XML content by encoding idnumbers appropriately
*
* @param string $idnumber
* @return string
*/
private static function xml_export_idnumber(string $idnumber): string {
return htmlspecialchars($idnumber, ENT_QUOTES | ENT_XML1);
}
/**
* To be implemented by child classes
* @param boolean $feedback
@@ -84,9 +94,11 @@ class grade_export_xml extends grade_export {
}
// only need id number
fwrite($handle, "\t\t<assignment>{$grade_item->idnumber}</assignment>\n");
$gradeitemidnumber = self::xml_export_idnumber($grade_item->idnumber);
fwrite($handle, "\t\t<assignment>{$gradeitemidnumber}</assignment>\n");
// this column should be customizable to use either student id, idnumber, uesrname or email.
fwrite($handle, "\t\t<student>{$user->idnumber}</student>\n");
$useridnumber = self::xml_export_idnumber($user->idnumber);
fwrite($handle, "\t\t<student>{$useridnumber}</student>\n");
// Format and display the grade in the selected display type (real, letter, percentage).
if (is_array($this->displaytype)) {
// Grades display type came from the return of export_bulk_export_data() on grade publishing.
+13 -5
View File
@@ -12,10 +12,12 @@ Feature: I need to export grades as xml
| username | firstname | lastname | email | idnumber |
| teacher1 | Teacher | 1 | teacher1@example.com | t1 |
| student1 | Student | 1 | student1@example.com | s1 |
| student2 | Student | 2 | student2@example.com | 'Bill'&"Ben"<tag>Hello</tag> |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "activities" exist:
| activity | course | idnumber | name | intro |
| assign | C1 | a1 | Test assignment name | Submit something! |
@@ -24,15 +26,21 @@ Feature: I need to export grades as xml
And I navigate to "View > Grader report" in the course gradebook
And I turn editing mode on
And I give the grade "80.00" to the user "Student 1" for the grade item "Test assignment name"
And I give the grade "42.00" to the user "Student 2" for the grade item "Test assignment name"
And I press "Save changes"
@javascript
Scenario: Export grades as text
Scenario: Export grades as XML
When I navigate to "Export > XML file" in the course gradebook
And I expand all fieldsets
And I set the field "Grade export decimal places" to "1"
And I press "Download"
Then I should see "s1"
And I should see "a1"
And I should see "80.0"
And I should not see "80.00"
Then I should see "s1" in the "//results//result[1]//student" "xpath_element"
And I should see "a1" in the "//results//result[1]//assignment" "xpath_element"
And I should see "80.0" in the "//results//result[1]//score" "xpath_element"
And I should not see "80.00" in the "//results//result[1]//score" "xpath_element"
# Ensure we have the encoded ID number of student 2.
And I should see "'Bill'&\"Ben\"<tag>Hello</tag>" in the "//results//result[2]//student" "xpath_element"
And I should see "a1" in the "//results//result[2]//assignment" "xpath_element"
And I should see "42.0" in the "//results//result[2]//score" "xpath_element"
And I should not see "42.00" in the "//results//result[2]//score" "xpath_element"
@@ -156,6 +156,9 @@ class fetch extends external_api {
$hasgrade = $gradeitem->user_has_grade($gradeduser);
$grade = $gradeitem->get_grade_for_user($gradeduser, $USER);
$instance = $gradeitem->get_advanced_grading_instance($USER, $grade);
if (!$instance) {
throw new moodle_exception('error:gradingunavailable', 'grading');
}
$controller = $instance->get_controller();
$definition = $controller->get_definition();
$fillings = $instance->get_guide_filling();
@@ -149,6 +149,10 @@ class fetch extends external_api {
$hasgrade = $gradeitem->user_has_grade($gradeduser);
$grade = $gradeitem->get_grade_for_user($gradeduser, $USER);
$instance = $gradeitem->get_advanced_grading_instance($USER, $grade);
if (!$instance) {
throw new moodle_exception('error:gradingunavailable', 'grading');
}
$controller = $instance->get_controller();
$definition = $controller->get_definition();
$fillings = $instance->get_rubric_filling();
+1 -1
View File
@@ -231,7 +231,7 @@ if (!empty($method)) {
} else {
$tag = html_writer::tag('span', get_string('statusdraft', 'core_grading'), array('class' => 'status draft'));
}
echo $output->heading(s($definition->name) . ' ' . $tag, 3, 'definition-name');
echo $output->heading(format_string($definition->name) . ' ' . $tag, 3, 'definition-name');
echo $output->box($controller->render_preview($PAGE), 'definition-preview');
}
}
+4
View File
@@ -240,6 +240,10 @@ class gradeimport_csv_load_data {
$select = "{$field} = :{$field}";
}
// Validate if the user id value is numerical.
if ($field === 'id' && !is_numeric($value)) {
$errorkey = 'usermappingerror';
}
// Make sure the record exists and that there's only one matching record found.
$user = $DB->get_record_select('user', $select, array($userfields['field'] => $value), '*', MUST_EXIST);
} catch (dml_missing_record_exception $missingex) {
@@ -87,7 +87,7 @@ class provider implements
* @param int $userid The userid of the user whose data is to be exported.
*/
public static function export_user_preferences(int $userid) {
$preferences = get_user_preferences();
$preferences = get_user_preferences(null, null, $userid);
foreach ($preferences as $name => $value) {
$prefname = null;
$prefdescription = null;
+6 -2
View File
@@ -63,14 +63,18 @@ class gradereport_grader_privacy_testcase extends \core_privacy\tests\provider_t
* These preferences can be set on each course, but the value is shared in the whole site.
*/
public function test_export_user_preferences_single() {
// Add some user preferences.
// Create test user, add some preferences.
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
set_user_preference('grade_report_showcalculations', 1, $user);
set_user_preference('grade_report_meanselection', GRADE_REPORT_MEAN_GRADED, $user);
set_user_preference('grade_report_studentsperpage', 50, $user);
// Validate exported data.
// Switch to admin user (so we can validate preferences of our test user are still exported).
$this->setAdminUser();
// Validate exported data for our test user.
provider::export_user_preferences($user->id);
$context = context_user::instance($user->id);
$writer = writer::with_context($context);
@@ -397,7 +397,7 @@ Y.namespace('M.gradereport_history').UserSelector = Y.extend(USERSELECTOR, M.cor
'<div class="{{CSS.FULLNAME}}">' +
'<label for="{{checkboxId}}">{{fullname}}</label>' +
'</div>' +
'<div id="{{extraFieldsId}}" class="{{CSS.EXTRAFIELDS}}">{{extrafields}}</div>' +
'<div id="{{extraFieldsId}}" class="{{CSS.EXTRAFIELDS}}">{{{extrafields}}}</div>' +
'</div>' +
'</div>'
);
File diff suppressed because one or more lines are too long
@@ -397,7 +397,7 @@ Y.namespace('M.gradereport_history').UserSelector = Y.extend(USERSELECTOR, M.cor
'<div class="{{CSS.FULLNAME}}">' +
'<label for="{{checkboxId}}">{{fullname}}</label>' +
'</div>' +
'<div id="{{extraFieldsId}}" class="{{CSS.EXTRAFIELDS}}">{{extrafields}}</div>' +
'<div id="{{extraFieldsId}}" class="{{CSS.EXTRAFIELDS}}">{{{extrafields}}}</div>' +
'</div>' +
'</div>'
);
@@ -395,7 +395,7 @@ Y.namespace('M.gradereport_history').UserSelector = Y.extend(USERSELECTOR, M.cor
'<div class="{{CSS.FULLNAME}}">' +
'<label for="{{checkboxId}}">{{fullname}}</label>' +
'</div>' +
'<div id="{{extraFieldsId}}" class="{{CSS.EXTRAFIELDS}}">{{extrafields}}</div>' +
'<div id="{{extraFieldsId}}" class="{{CSS.EXTRAFIELDS}}">{{{extrafields}}}</div>' +
'</div>' +
'</div>'
);
@@ -13,4 +13,4 @@ Feature: We can use the user report
And I am on "Course 1" course homepage
And I navigate to "View > User report" in the course gradebook
And I select "All users (0)" from the "Select all or one user" singleselect
Then I should see "No students enrolled in this course yet"
Then I should see "There are no students enrolled in this course."
+1 -1
View File
@@ -232,7 +232,7 @@ if ($editform->is_cancelled()) {
$newgroup = new stdClass();
$newgroup->courseid = $data->courseid;
$newgroup->name = $group['name'];
$newgroup->enablemessaging = $data->enablemessaging;
$newgroup->enablemessaging = $data->enablemessaging ?? 0;
$groupid = groups_create_group($newgroup);
$createdgroups[] = $groupid;
foreach($group['members'] as $user) {
+2
View File
@@ -421,6 +421,8 @@ class helper {
* @return array The JS array converted to PHP array.
*/
public static function parse_js_array(string $jscontent): array {
// Convert all line-endings to UNIX format first.
$jscontent = str_replace(array("\r\n", "\r"), "\n", $jscontent);
$jsarray = preg_split('/,\n\s+/', substr($jscontent, 0, -1));
$jsarray = preg_replace('~{?\\n~', '', $jsarray);
+3 -1
View File
@@ -79,7 +79,9 @@ abstract class handler {
* Register the H5P autoloader.
*/
public static function register(): void {
spl_autoload_register([static::class, 'autoload']);
// Prepend H5P libraries in order to guarantee they are loaded first. Plugins using same libraries will need to use a
// different namespace if they want to use a different version.
spl_autoload_register([static::class, 'autoload'], true, true);
}
/**
Binary file not shown.
@@ -5,52 +5,52 @@
<json>
<![CDATA[
{
"fontFamily": "h5p-core-21",
"fontFamily": "h5p-core",
"description": "Font generated by IcoMoon.",
"majorVersion": 1,
"minorVersion": 1,
"version": "Version 1.1",
"fontId": "h5p-core-21",
"psName": "h5p-core-21",
"fontId": "h5p-core",
"psName": "h5p-core",
"subFamily": "Regular",
"fullName": "h5p-core-21"
"fullName": "h5p-core"
}
]]>
</json>
</metadata>
<defs>
<font id="h5p-core-21" horiz-adv-x="1024">
<font id="h5p-core" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#xe565;" glyph-name="arrow-down" data-tags="arrow-down" d="M234 389.669h556l-278-278z" />
<glyph unicode="&#xe566;" glyph-name="arrow-left" data-tags="arrow-left" d="M381-11.331v524l262-262z" />
<glyph unicode="&#xe58e;" glyph-name="colapse" data-tags="colapse" d="M512 447.336l256-256-60-60-196 196-196-196-60 60z" />
<glyph unicode="&#xe58f;" glyph-name="expand" data-tags="expand" d="M708 423.336l60-60-256-256-256 256 60 60 196-196z" />
<glyph unicode="&#xe600;" glyph-name="move" data-tags="move" d="M386.662 725.063h71.27v-71.27h-71.27v71.27zM566.067 725.063h71.27v-71.27h-71.27v71.27zM386.662 568.8h71.27v-71.27h-71.27v71.27zM566.067 568.8h71.27v-71.27h-71.27v71.27zM386.662 412.435h71.27v-71.27h-71.27v71.27zM566.067 412.435h71.27v-71.27h-71.27v71.27zM386.662 256.173h71.27v-71.27h-71.27v71.27zM566.067 256.173h71.27v-71.27h-71.27v71.27zM386.662 99.808h71.27v-71.27h-71.27v71.27zM566.067 99.808h71.27v-71.27h-71.27v71.27zM386.662-56.454h71.27v-71.27h-71.27v71.27zM566.067-56.454h71.27v-71.27h-71.27v71.27z" />
<glyph unicode="&#xe601;" glyph-name="check-mark" data-tags="check-mark" d="M454.299 245.924l-116.917 116.917-84.781-84.707 201.696-201.697 317.097 317.097-84.781 84.706z" />
<glyph unicode="&#xe888;" glyph-name="arrow-up-circle" data-tags="arrow-up-circle" d="M512 606.057c-148.616 0-264.722-120.75-260.077-269.367 0-125.395 88.241-232.212 208.991-255.434v213.636h-92.885c-13.933 0-13.933 9.288-9.288 18.577l139.327 171.838c4.645 9.288 13.933 9.288 23.221 4.645 0 0 4.645-4.645 4.645-4.645l139.327-171.838c9.288-9.288 4.645-18.577-9.288-18.577h-92.885v-213.636c143.972 32.51 232.212 171.838 199.703 315.808-23.221 120.75-130.039 204.347-250.789 208.991z" />
<glyph unicode="&#xe889;" glyph-name="info-circle" data-tags="info-circle" d="M512 601.601c-144.077 0-260.266-116.191-260.266-260.266s116.191-260.266 260.266-260.266 260.266 116.191 260.266 260.266v0c0 139.429-116.191 255.619-260.266 260.266zM470.171 550.478h88.305v-69.714h-88.305v69.714zM600.305 160.078h-181.257v51.123h51.123v162.666h-51.123v51.123h139.429v-218.438h46.477l-4.648-46.477z" />
<glyph unicode="&#xe88a;" glyph-name="search" data-tags="search" d="M772.098 125.51l-110.68 110.68c71.943 99.612 49.806 243.494-49.806 315.437s-243.494 44.27-315.437-55.339c-71.943-99.612-49.806-243.494 49.806-315.437 77.475-55.339 182.623-55.339 260.098 0l110.68-110.68c5.533-5.533 11.068-5.533 16.601 0 0 0 0 0 0 0l33.205 33.205c11.068 5.533 11.068 16.601 5.533 22.137 0 0 0 0 0 0zM478.795 202.985c-88.544 0-160.486 71.943-160.486 160.486s71.943 160.486 160.486 160.486 160.486-71.943 160.486-160.486-71.943-160.486-160.486-160.486v0z" />
<glyph unicode="&#xe88c;" glyph-name="fullscreen" data-tags="fullscreen" d="M368.55 490.521c5.737 5.737 0 5.737-5.737 5.737l-103.284 11.476c-5.737 5.737-11.476 0-11.476-5.737l11.476-109.021c0-5.737 5.737-5.737 5.737-5.737l103.284 103.284zM293.959 427.403l63.118-63.118c5.737-5.737 11.476-5.737 17.213 0l22.953 22.953c5.737 5.737 5.737 11.476 0 17.213l-63.118 57.379-40.166-34.429zM787.42 387.237c5.737-5.737 5.737 0 5.737 5.737l11.476 109.021c0 5.737-5.737 11.476-11.476 11.476l-109.021-11.476c-5.737 0-5.737-5.737-5.737-5.737l109.021-109.021zM724.305 461.832l-63.118-63.118c-5.737-5.737-5.737-11.476 0-17.213l22.953-22.953c5.737-5.737 11.476-5.737 17.213 0l63.118 63.118-40.166 40.166zM689.876 180.672c-5.737-5.737 0-5.737 5.737-5.737l109.021-11.476c5.737 0 11.476 5.737 11.476 11.476l-17.213 103.284c0 5.737-5.737 5.737-5.737 5.737l-103.284-103.284zM758.731 249.527l-63.118 63.118c-5.737 5.737-11.476 5.737-17.213 0l-22.953-22.953c-5.737-5.737-5.737-11.476 0-17.213l63.118-63.118 40.166 40.166zM265.269 283.956c-5.737 5.737-5.737 0-5.737-5.737l-11.476-109.021c0-5.737 5.737-11.476 11.476-11.476l109.021 11.476c5.737 0 5.737 5.737 5.737 5.737l-109.021 109.021zM334.124 209.362l63.118 63.118c5.737 5.737 5.737 11.476 0 17.213l-22.953 22.953c-5.737 5.737-11.476 5.737-17.213 0l-63.118-63.118 40.166-40.166zM161.985 593.805v-499.201h722.979v499.201h-722.979zM844.799 134.77h-636.911v413.13h636.911v-413.13z" />
<glyph unicode="&#xe88e;" glyph-name="h5p" data-tags="h5p" d="M934.072 489.192c-22.319 16.738-50.216 27.897-89.273 27.897h-139.487v-66.954h-156.225l-11.159-55.795c11.159 5.579 27.897 11.159 39.057 11.159s22.319 0 33.476 0c33.476 0 66.954-11.159 89.273-33.476s33.476-50.216 33.476-83.692c0-22.319-5.579-44.635-16.738-66.954s-27.897-39.057-50.216-50.216c-5.579-5.579-16.738 0-22.319-11.159h117.17v133.908h66.954c44.635 0 78.113 11.159 100.43 27.897 22.319 22.319 33.476 50.216 33.476 83.692 0 39.057-11.159 66.954-27.897 83.692v0zM839.221 377.603c-11.159-5.579-22.319-11.159-44.635-11.159h-33.476v83.692h39.057c22.319 0 33.476-5.579 44.635-11.159 5.579-5.579 11.159-16.738 11.159-27.897 0-16.738-5.579-27.897-16.738-33.476v0zM565.826 338.546c-16.738 0-33.476-11.159-44.635-27.897l-94.851 16.738 44.635 195.281h-94.851v-150.646h-117.17v150.646h-111.589v-362.667h111.589v133.908h117.17v-133.908h139.487c-16.738 11.159-33.476 11.159-44.635 22.319s-22.319 22.319-27.897 33.476c-5.579 11.159-11.159 22.319-16.738 39.057l94.851 16.738c5.579-16.738 22.319-27.897 44.635-27.897 27.897 0 50.216 22.319 50.216 50.216 0 22.319-22.319 44.635-50.216 44.635v0z" />
<glyph unicode="&#xe88f;" glyph-name="rights-of-use" data-tags="rights-of-use" d="M899.611 329.519c0-5.907 0-5.907 0-5.907-23.631-23.631-47.261-35.448-76.799-41.355-11.813 0-23.631-5.907-35.448-5.907s-17.724 0-29.537 0c0 0-5.907 0-5.907 5.907-64.985 59.079-135.877 118.153-200.863 183.139 0 0-5.907 0-5.907 0-23.631-5.907-47.261-11.813-70.892-17.724s-53.168 0-76.799 11.813c-17.724 11.813-23.631 17.724-29.537 35.448-5.907 5.907 0 23.631 11.813 23.631 41.355 11.813 88.616 29.537 129.971 47.261 11.813 5.907 29.537 5.907 41.355 5.907 5.907 0 11.813-5.907 11.813-5.907 41.355-17.724 82.709-29.537 124.060-47.261 0 0 5.907 0 5.907 0 29.537 5.907 64.985 17.724 94.523 23.631 5.907 0 5.907 0 5.907 0l106.34-212.676zM291.12 335.429c17.724 11.813 35.448 5.907 53.168-11.813 11.813-11.813 11.813-29.537 5.907-47.261 17.724 5.907 35.448-5.907 41.355-17.724 11.813-17.724 5.907-35.448-5.907-47.261 5.907 0 11.813 0 17.724 0 11.813-5.907 23.631-11.813 29.537-29.537s0-29.537-5.907-35.448c-5.907-5.907-11.813-11.813-17.724-17.724s-11.813-11.813-17.724-17.724-35.448-17.724-53.168 0c-29.537 29.537-53.168 64.985-82.709 94.523-17.724 23.631-35.448 41.355-47.261 64.985-5.907 11.813-11.813 17.724-11.813 29.537 0 5.907 0 17.724 5.907 23.631 11.813 11.813 17.724 17.724 29.537 29.537 17.724 17.724 47.261 11.813 64.985-5.907-5.907 0-5.907-5.907-5.907-11.813v0zM438.811 128.66l29.537-29.537c17.724-17.724 47.261-11.813 59.079 5.907l-5.907 5.907c-23.631 23.631-47.261 47.261-70.892 70.892-5.907 5.907-5.907 5.907-5.907 11.813s5.907 5.907 11.813 11.813c5.907 0 11.813 0 11.813-5.907 11.813-11.813 29.537-29.537 47.261-47.261 11.813-11.813 29.537-29.537 47.261-47.261 5.907-11.813 17.724-11.813 29.537-11.813 11.813 5.907 23.631 11.813 29.537 23.631 0 5.907 0 5.907 0 5.907-41.355 41.355-88.616 82.709-129.971 129.971-5.907 5.907-5.907 5.907-5.907 11.813 0 11.813 11.813 11.813 23.631 5.907 0 0 5.907 0 5.907-5.907 41.355-41.355 88.616-88.616 129.971-129.971 5.907-5.907 5.907-5.907 5.907-5.907 17.724 0 35.448 17.724 35.448 35.448 0 5.907 0 5.907 0 5.907-47.261 47.261-100.429 100.429-147.691 147.691-5.907 5.907-5.907 5.907-5.907 11.813s5.907 11.813 5.907 11.813c5.907 0 11.813 0 11.813-5.907 5.907-5.907 5.907-5.907 11.813-11.813 35.448-35.448 70.892-70.892 106.34-106.34 11.813-11.813 23.631-23.631 29.537-29.537 0 0 5.907-5.907 5.907 0 23.631 5.907 35.448 29.537 29.537 53.168h35.448c0 0 0 0 0 0 0-5.907 0-17.724 0-23.631-5.907-29.537-23.631-47.261-53.168-59.079 0 0-5.907 0-5.907-5.907-11.813-29.537-35.448-53.168-64.985-53.168-5.907 0-5.907 0-5.907-5.907-17.724-35.448-59.079-47.261-88.616-35.448-5.907 0-11.813 5.907-11.813 5.907-5.907-5.907-11.813-11.813-23.631-17.724-29.537-11.813-59.079-5.907-76.799 11.813-11.813 11.813-17.724 17.724-29.537 29.537 17.724 23.631 23.631 29.537 29.537 41.355v0 0zM273.396 642.628c29.537-11.813 64.985-23.631 94.523-29.537 35.448-11.813 64.985-23.631 100.429-29.537 0 0 0 0 5.907 0-17.724-5.907-35.448-11.813-47.261-17.724 0 0-5.907 0-5.907 0-47.261 11.813-94.523 23.631-135.877 41.355-5.907 0-5.907 0-5.907 0l-76.799-183.139c0-11.813 5.907-17.724 11.813-23.631s5.907-5.907 5.907-11.813c-5.907-5.907-11.813-17.724-23.631-23.631-17.724 17.724-29.537 35.448-29.537 64.985l88.616 212.676c-5.907-11.813 5.907 5.907 17.724 0v0z" />
<glyph unicode="&#xe890;" glyph-name="delete-circle" data-tags="delete-circle" d="M512 601.601c-147.107 0-260.266-118.817-260.266-260.266s118.817-260.266 260.266-260.266 260.266 118.817 260.266 260.266-113.158 260.266-260.266 260.266zM653.449 262.123c5.659-5.659 5.659-16.973 0-28.29l-33.949-33.949c-5.659-5.659-16.973-5.659-28.29 0l-79.212 79.212-79.212-79.212c-5.659-5.659-16.973-5.659-28.29 0l-33.949 33.949c-5.659 5.659-5.659 16.973 0 28.29l84.871 79.212-79.212 79.212c-5.659 5.659-5.659 16.973 0 28.29l33.949 33.949c5.659 5.659 16.973 5.659 28.29 0l73.554-84.871 79.212 79.212c5.659 5.659 16.973 5.659 28.29 0l33.949-33.949c5.659-5.659 5.659-16.973 0-28.29l-79.212-73.554 79.212-79.212z" />
<glyph unicode="&#xe891;" glyph-name="window" data-tags="window" d="M203.936 461.136c-5.704-5.704 0-5.704 5.704-5.704l108.394-11.41c5.704 0 11.41 5.704 11.41 11.41l-17.114 102.687c0 5.704-5.704 5.704-5.704 5.704l-102.687-102.687zM272.395 523.891l-62.752 62.752c-5.704 5.704-11.41 5.704-17.114 0l-17.114-22.821c-5.704-5.704-5.704-11.41 0-17.114l62.752-62.752 34.228 39.935zM751.605 558.119c-5.704 5.704-5.704 0-5.704-5.704l-11.41-108.394c0-5.704 5.704-11.41 11.41-11.41l108.394 11.41c5.704 0 5.704 5.704 5.704 5.704l-108.394 108.394zM814.357 483.957l62.752 62.752c5.704 5.704 5.704 11.41 0 17.114l-22.821 22.821c-5.704 5.704-11.41 5.704-17.114 0l-62.752-62.752 39.935-39.935zM848.588 221.534c5.704 5.704 0 5.704-5.704 5.704l-102.687 17.114c-5.704 0-11.41-5.704-11.41-11.41l11.41-108.394c0-5.704 5.704-5.704 5.704-5.704l102.687 102.687zM780.129 158.779l62.752-62.752c5.704-5.704 11.41-5.704 17.114 0l22.821 22.821c5.704 5.704 5.704 11.41 0 17.114l-62.752 62.752-39.935-39.935zM300.919 124.551c5.704-5.704 5.704 0 5.704 5.704l11.41 108.394c0 5.704-5.704 11.41-11.41 11.41l-108.394-11.41c-5.704 0-5.704-5.704-5.704-5.704l108.394-108.394zM238.167 193.010l-62.752-62.752c-5.704-5.704-5.704-11.41 0-17.114l22.821-22.821c5.704-5.704 11.41-5.704 17.114 0l62.752 62.752-39.935 39.935zM352.264 466.843v-239.605h347.998v239.605h-347.998zM654.622 267.172h-262.424v154.032h262.424v-154.032z" />
<glyph unicode="&#xe892;" glyph-name="code" data-tags="code" d="M449.641 235.325c6.235-6.235 6.235-12.472 6.235-18.707v-62.359c0-6.235-6.235-6.235-6.235-6.235l-230.728 155.897c-6.235 6.235-6.235 12.472-6.235 18.707v49.886c0 6.235 6.235 12.472 6.235 18.707l230.728 155.897c6.235 6.235 6.235 0 6.235-6.235v-62.359c0-6.235-6.235-12.472-6.235-18.707l-162.134-112.245c-6.235-6.235-6.235-6.235 0-12.472l162.134-99.776zM736.493 341.335c6.235 6.235 6.235 6.235 0 12.472l-155.897 112.245c-6.235 6.235-6.235 12.472-6.235 18.707v62.359c0 6.235 6.235 6.235 6.235 6.235l230.728-155.897c6.235-6.235 6.235-12.472 6.235-18.707v-49.886c0-6.235-6.235-12.472-6.235-18.707l-230.728-155.897c-6.235-6.235-6.235 0-6.235 6.235v62.359c0 6.235 6.235 12.472 6.235 18.707l155.897 99.776z" />
<glyph unicode="&#xe893;" glyph-name="download" data-tags="download" d="M358.941 435.525c-11.773 0-17.66-5.887-5.887-17.66l153.059-188.382c5.887-11.773 23.547-11.773 29.433 0l153.059 188.382c5.887 11.773 5.887 17.66-5.887 17.66h-323.782zM576.756 423.751v135.399c0 11.773-11.773 23.547-23.547 23.547h-70.643c-11.773 0-23.547-11.773-23.547-23.547v-141.286h117.739zM653.286 288.352c-5.887 0-17.66-5.887-23.547-11.773l-76.53-94.19c-5.887-5.887-17.66-17.66-23.547-23.547 0 0-5.887-5.887-11.773-5.887s-17.66 11.773-17.66 11.773c-5.887 5.887-17.66 17.66-23.547 23.547l-76.53 94.19c-5.887 5.887-17.66 11.773-23.547 11.773h-123.626c-5.887 0-17.66-5.887-17.66-17.66v-141.286c0-5.887 5.887-17.66 17.66-17.66h529.824c5.887 0 17.66 5.887 17.66 17.66v141.286c0 5.887-5.887 17.66-17.66 17.66l-129.513-5.887zM305.958 176.502c-17.66 0-29.433 11.773-29.433 29.433s11.773 29.433 29.433 29.433c17.66 0 29.433-11.773 29.433-29.433s-11.773-29.433-29.433-29.433v0z" />
<glyph unicode="&#xe894;" glyph-name="delete" data-tags="delete" d="M620.266 341.335l134.045 134.045c10.311 10.311 10.311 30.934 0 41.245l-61.866 61.866c-10.311 10.311-30.934 10.311-41.245 0l-134.045-134.045-134.045 134.045c-10.311 10.311-30.934 10.311-41.245 0l-61.866-61.866c-10.311-10.311-10.311-30.934 0-41.245l134.045-134.045-134.045-134.045c-10.311-10.311-10.311-30.934 0-41.245l61.866-61.866c10.311-10.311 30.934-10.311 41.245 0l134.045 134.045 134.045-134.045c10.311-10.311 30.934-10.311 41.245 0l61.866 61.866c10.311 10.311 10.311 30.934 0 41.245l-134.045 134.045z" />
<glyph unicode="&#xe900;" glyph-name="edit-image" data-tags="edit-image" d="M300.237 621.639c69.018 23.142 133.325 14.234 189.133-33.28 56.627-48.128 77.619-110.592 63.181-183.808-2.355-12.186 0.307-19.456 8.704-27.853 93.901-93.389 156.774-156.467 250.47-250.163 5.427-5.427 10.957-10.854 15.667-16.896 39.424-50.278 16.794-124.006-44.237-142.029-36.966-10.957-68.403 0-95.334 27.034-95.642 96.051-160.973 160.973-256.614 257.024-6.963 6.963-12.8 8.909-22.63 6.758-117.76-26.317-229.171 60.826-231.731 181.453-0.614 26.419 3.584 52.326 15.974 77.926 34.816-34.816 68.506-67.789 101.274-101.786 10.445-10.752 20.992-15.36 36.045-15.36 14.643 0 25.19 3.891 34.816 14.848 10.752 12.39 23.040 23.347 34.611 35.021 14.336 14.438 14.336 46.080-0.205 60.518-35.123 35.226-70.349 70.349-106.598 106.496 3.891 2.253 5.632 3.482 7.475 4.096zM703.386 63.559c-0.41-24.269 20.685-45.466 44.851-45.158 23.757 0.41 44.032 20.992 43.93 44.544-0.102 23.757-20.275 44.032-44.237 44.134-23.859 0.307-44.237-19.661-44.544-43.52z" />
<glyph unicode="&#xe901;" glyph-name="hourglass" data-tags="hourglass" d="M733.286-10.579c-147.763 0-295.526 0-443.29 0 0 2.048 0.102 4.096 0 6.144-0.307 13.824-1.024 32.666-0.922 46.49 0.41 39.731 6.861 78.131 19.046 115.2 17.203 52.224 43.725 96.256 81.306 130.355 4.506 4.096 9.216 7.885 13.722 11.776-0.205 0.717-0.307 1.126-0.41 1.229-1.331 1.229-2.765 2.355-4.198 3.584-28.058 22.63-50.688 51.405-68.403 85.606-30.618 59.085-43.52 123.597-41.165 192.614 0.205 7.168 0.614 18.33 0.922 25.498 147.763 0 295.526 0 443.29 0 0.205-1.331 0.512-2.662 0.614-3.994 2.662-36.966 1.229-77.722-5.939-113.869-14.336-72.909-44.544-133.837-95.027-179.405-4.096-3.686-8.294-7.066-12.39-10.547 0.205-0.717 0.307-1.126 0.512-1.331 0.819-0.717 1.638-1.434 2.458-2.15 42.189-33.894 71.68-79.872 90.931-135.782 11.776-34.202 18.637-69.837 20.070-106.701 0.819-19.763-0.614-44.851-1.126-64.717zM687.309 32.634c0 6.554 0.205 12.493 0 18.432-1.331 37.581-7.27 74.138-19.866 108.749-17.92 49.562-45.568 88.269-88.678 108.646-2.458 1.126-2.97 3.072-2.97 5.837 0.102 16.691 0.102 33.485 0 50.176 0 3.994 1.331 5.427 4.096 6.963 9.114 5.325 18.432 10.24 26.829 16.896 29.696 23.552 49.152 56.934 62.362 95.744 10.342 30.413 15.77 62.259 17.818 94.822 0.614 9.114 0.102 18.227 0.102 27.546-116.634 0-233.574 0-351.027 0 0.307-8.704 0.614-16.998 1.024-25.395 1.946-37.274 8.499-73.216 21.504-107.213 18.125-47.104 45.261-83.558 86.528-103.117 2.253-1.024 2.867-2.662 2.867-5.427-0.102-17.203-0.102-34.509 0-51.712 0-3.072-1.024-4.506-3.277-5.632-5.632-2.867-11.366-5.734-16.691-9.216-34.304-22.733-56.832-57.754-71.987-100.147-12.493-35.123-18.125-71.987-19.661-109.875-0.205-5.325 0-10.752 0-16.282 117.35 0.205 234.189 0.205 351.027 0.205zM410.214 451.962c68.096 0 135.373 0 203.674 0-3.789-6.554-7.168-12.595-10.752-18.227-10.957-16.998-24.269-30.618-39.731-41.472s-27.75-25.293-32.768-46.592c-1.638-6.963-2.765-14.336-2.867-21.606-0.307-17.203-0.205-34.406 0.307-51.712 0.717-28.058 12.493-48.947 32.154-62.566 43.008-30.003 65.843-75.878 75.776-132.506 0.307-1.638 0.307-3.379 0.614-5.53-83.149 0-166.093 0-249.242 0 2.662 20.685 7.885 40.346 15.462 59.085 13.312 32.973 32.768 59.187 59.494 77.722 16.589 11.469 28.365 27.955 32.358 50.995 0.819 4.813 1.331 9.83 1.434 14.746 0.102 18.637 0.614 37.274-0.205 55.808-1.126 24.678-11.981 43.213-28.57 57.139-9.216 7.782-18.944 14.746-27.648 23.142-11.981 11.162-21.197 25.395-29.491 41.574z" />
<glyph unicode="&#xe902;" glyph-name="plus-icon" data-tags="plus-icon" d="M768 285.015c0-19.323-15.664-34.987-34.987-34.987h-151.040v-151.467c0-19.323-15.664-34.987-34.987-34.987h-69.547c-19.323 0-34.987 15.664-34.987 34.987v151.467h-151.467c-19.323 0-34.987 15.664-34.987 34.987v69.547c0 19.323 15.664 34.987 34.987 34.987h151.467v151.467c0 19.323 15.664 34.987 34.987 34.987h69.547c19.323 0 34.987-15.664 34.987-34.987v-151.467h151.467c19.323 0 34.987-15.664 34.987-34.987z" />
<glyph unicode="&#xe903;" glyph-name="video-upload-icon" data-tags="video-upload-icon" d="M384 328.535v-128c0-21.333 21.333-42.667 42.667-42.667h128c21.333 0 42.667 17.067 42.667 42.667v128c0 21.333-17.067 42.667-42.667 42.667h-128c-21.333 0-42.667-17.067-42.667-42.667zM785.067 499.201l-102.4 106.667c-12.8 12.8-38.4 21.333-55.467 21.333h-140.8l21.333-42.667h89.6v-136.533c0-17.067 12.8-29.867 29.867-29.867h140.8v-341.333h-426.667v328.533h-42.667v-341.333c0-17.067 12.8-29.867 29.867-29.867h448c17.067 0 29.867 12.8 29.867 29.867v384c4.267 17.067-8.533 38.4-21.333 51.2zM640 456.535v123.733c4.267 0 12.8-4.267 12.8-8.533l102.4-102.4c4.267-4.267 4.267-8.533 8.533-12.8h-123.733zM725.333 166.401v196.267c0 4.267-4.267 8.533-8.533 8.533s-8.533 0-12.8-4.267l-89.6-89.6v-29.867l89.6-89.6c8.533-4.267 8.533 0 12.8 0 4.267 4.267 8.533 4.267 8.533 8.533zM349.867 473.601v136.533l59.733-59.733c8.533-8.533 17.067-4.267 25.6 0l12.8 12.8c8.533 8.533 8.533 17.067 0 25.6l-115.2 106.667c-8.533 8.533-17.067 8.533-21.333 0l-115.2-110.933c-4.267-8.533-4.267-17.067 0-25.6l12.8-12.8c8.533-8.533 17.067-8.533 21.333 0l59.733 59.733v-136.533c0-8.533 8.533-17.067 17.067-17.067h25.6c0 0 17.067 8.533 17.067 21.333z" />
<glyph unicode="&#xe904;" glyph-name="play-icon" data-tags="play-icon" d="M392.533 597.335c81.067 46.933 187.733 46.933 273.067 0 42.667-25.6 72.533-55.467 98.133-98.133 72.533-128 29.867-294.4-98.133-371.2-128-72.533-294.4-29.867-371.2 98.133-46.933 81.067-46.933 187.733 0 273.067 21.333 42.667 55.467 72.533 98.133 98.133zM661.333 345.601c12.8 8.533 12.8 29.867 0 38.4l-192 110.933c-8.533 4.267-12.8 4.267-21.333 0s-12.8-12.8-12.8-21.333v-226.133c0-8.533 4.267-17.067 12.8-21.333s17.067-4.267 21.333 0l192 119.467z" />
<glyph unicode="&#xe905;" glyph-name="copy" data-tags="copy" d="M722.133 561.201h-247.733c-65.867 0-119.2-53.333-119.2-119.2v-288.533c0-65.867 53.333-119.2 119.2-119.2h247.867c65.867 0 119.2 53.333 119.2 119.2v288.533c-0.267 65.867-53.467 119.2-119.333 119.2zM778.533 156.534c0-31.333-25.067-56.4-56.4-56.4h-247.867c-31.333 0-56.4 25.067-56.4 56.4v285.467c0 31.333 25.067 56.4 56.4 56.4h247.733c31.333 0 56.4-25.067 56.4-56.4v-285.467zM245.2 326.001v288.533c0 31.333 25.067 56.4 56.4 56.4h247.733c31.333 0 56.4-25.067 56.4-56.4v-18.8h62.667v18.8c0 65.867-53.333 119.2-119.2 119.2h-247.467c-65.867 0-119.2-53.333-119.2-119.2v-288.533c0-65.867 53.333-119.2 119.2-119.2h18.8v62.667h-18.8c-31.467-3.067-56.533 22-56.533 56.533zM681.2 360.534h-163.067c-18.8 0-31.333 12.533-31.333 31.333s12.533 31.333 31.333 31.333h163.067c18.8 0 31.333-12.533 31.333-31.333s-15.6-31.333-31.333-31.333zM681.2 266.401h-163.067c-18.8 0-31.333 12.533-31.333 31.333s12.533 31.333 31.333 31.333h163.067c18.8 0 31.333-12.533 31.333-31.333s-15.6-31.333-31.333-31.333zM681.2 172.268h-163.067c-18.8 0-31.333 12.533-31.333 31.333s12.533 31.333 31.333 31.333h163.067c18.8 0 31.333-12.533 31.333-31.333s-15.6-31.333-31.333-31.333z" />
<glyph unicode="&#xe906;" glyph-name="examples-icon" data-tags="examples-icon" d="M213.333 166.402c89.6 38.4 183.467 68.267 273.067 17.067v281.6c-68.267 46.933-157.867 55.467-234.667 12.8l-38.4-311.467zM810.667 166.402l-42.667 315.733c-72.533 38.4-166.4 34.133-234.667-17.067v-285.867c93.867 51.2 187.733 21.333 277.333-12.8zM832 520.535c-51.2 29.867-110.933 46.933-170.667 55.467-51.2 0-102.4-8.533-149.333-29.867-46.933 21.333-98.133 29.867-149.333 29.867-59.733-4.267-119.467-25.6-170.667-55.467l-64-452.267c0 0 29.867-17.067 110.933 21.333 46.933 25.6 102.4 34.133 157.867 25.6 42.667-4.267 85.333-21.333 115.2-55.467v0c29.867 34.133 72.533 51.2 115.2 55.467 55.467 4.267 106.667-4.267 157.867-29.867 81.067-38.4 110.933-21.333 110.933-21.333l-64 456.533zM793.6 115.202c-42.667 21.333-89.6 34.133-140.8 34.133-8.533 0-21.333 0-29.867 0-42.667-4.267-81.067-17.067-115.2-42.667-34.133 25.6-72.533 38.4-115.2 42.667-12.8 0-21.333 0-34.133 0-46.933 0-93.867-8.533-136.533-29.867-21.333-12.8-46.933-21.333-72.533-25.6l64 413.867c46.933 25.6 98.133 38.4 149.333 42.667 46.933 0 93.867-8.533 136.533-25.6l12.8-8.533 12.8 4.267c42.667 17.067 89.6 25.6 136.533 25.6 51.2-4.267 102.4-17.067 149.333-42.667l59.733-418.133c-29.867 8.533-51.2 17.067-76.8 29.867z" />
<glyph unicode="&#xe907;" glyph-name="tutorials-icon" data-tags="tutorials-icon" d="M887.467 430.935l-375.467-110.933h-4.267l-217.6 68.267c-21.333-25.6-34.133-59.733-34.133-98.133 21.333-12.8 25.6-38.4 12.8-59.733-4.267-4.267-8.533-8.533-12.8-12.8l17.067-145.067c0-4.267 0-4.267-4.267-8.533 0 0 0 0-4.267 0h-64c-4.267 0-4.267 0-8.533 4.267 0 4.267-4.267 4.267-4.267 8.533l17.067 145.067c-12.8 8.533-17.067 21.333-17.067 34.133 0 17.067 8.533 29.867 21.333 38.4 0 38.4 12.8 76.8 34.133 110.933l-106.667 29.867c-8.533 4.267-8.533 8.533-8.533 17.067 0 4.267 4.267 4.267 4.267 4.267l375.467 119.467h4.267l375.467-123.733c4.267 0 8.533-4.267 8.533-8.533s-4.267-8.533-8.533-12.8zM725.333 234.669c4.267-46.933-93.867-85.333-213.333-85.333s-213.333 38.4-213.333 85.333l4.267 106.667 192-64c4.267 0 12.8 0 17.067 0s12.8 0 17.067 4.267l192 59.733 4.267-106.667z" />
<glyph unicode="&#xe908;" glyph-name="info-important-description" data-tags="info-important-description" d="M512 697.368c-188.5 0-341.3-152.8-341.3-341.3s152.8-341.4 341.3-341.4 341.3 152.8 341.3 341.3-152.8 341.4-341.3 341.4v0zM512 43.268c-172.7 0-312.7 140-312.7 312.7s140 312.7 312.7 312.7c172.7 0 312.7-140 312.7-312.7-0.2-172.6-140.1-312.5-312.7-312.7v0zM512 605.568c-137.9 0-249.6-111.8-249.6-249.6s111.7-249.6 249.6-249.6 249.6 111.8 249.6 249.6-111.8 249.6-249.6 249.6v0z" />
<glyph unicode="&#xe909;" glyph-name="icon-info" data-tags="icon-info" d="M467.2 459.522h87.467c0.028 0 0.062 0 0.095 0 6.056 0 11.499 2.629 15.248 6.808 3.979 4.15 6.419 9.769 6.419 15.957 0 0.097-0.001 0.194-0.002 0.29v70.385c0.001 0.082 0.002 0.179 0.002 0.276 0 6.188-2.44 11.806-6.409 15.946-3.759 4.19-9.201 6.819-15.257 6.819-0.033 0-0.067 0-0.1 0h-87.462c-0.028 0-0.062 0-0.095 0-6.056 0-11.499-2.629-15.248-6.808-3.979-4.15-6.419-9.769-6.419-15.957 0-0.097 0.001-0.194 0.002-0.29v-69.959c-0.001-0.082-0.002-0.179-0.002-0.276 0-6.188 2.44-11.806 6.409-15.946 3.715-4.373 9.2-7.159 15.338-7.245zM597.333 156.589h-22.187v209.92c0.001 0.082 0.002 0.179 0.002 0.276 0 6.188-2.44 11.806-6.409 15.946-3.759 4.19-9.201 6.819-15.257 6.819-0.033 0-0.067 0-0.1 0h-130.128c-0.028 0-0.062 0-0.095 0-6.056 0-11.499-2.629-15.248-6.808-3.979-4.15-6.419-9.769-6.419-15.957 0-0.097 0.001-0.194 0.002-0.29v-46.492c-0.001-0.082-0.002-0.179-0.002-0.276 0-6.188 2.44-11.806 6.409-15.946 3.759-4.19 9.201-6.819 15.257-6.819 0.033 0 0.067 0 0.1 0h22.182v-139.947h-22.187c-0.028 0-0.062 0-0.095 0-6.056 0-11.499-2.629-15.248-6.808-3.979-4.15-6.419-9.769-6.419-15.957 0-0.097 0.001-0.194 0.002-0.29v-46.492c-0.001-0.082-0.002-0.179-0.002-0.276 0-6.188 2.44-11.806 6.409-15.946 3.759-4.19 9.201-6.819 15.257-6.819 0.033 0 0.067 0 0.1 0h174.075c0.028 0 0.062 0 0.095 0 6.056 0 11.499 2.629 15.248 6.808 3.979 4.15 6.419 9.769 6.419 15.957 0 0.097-0.001 0.194-0.002 0.29v46.065c0.043 0.527 0.067 1.141 0.067 1.761 0 5.302-1.791 10.185-4.8 14.079-3.742 4.424-9.36 7.247-15.636 7.247-0.489 0-0.975-0.017-1.456-0.051z" />
<glyph unicode="&#xe90a;" glyph-name="paste" data-tags="paste" d="M394.402 702.4h-75.333c-65.867 0-119.2-53.333-119.2-119.2v-288.533c0-56.4 37.6-100.4 87.867-116v69.067c-15.733 9.467-25.067 25.067-25.067 47.067v288.4c0 31.333 25.067 56.4 56.4 56.4h131.733c0 0 0 0 3.2 3.2v0c0 31.333-28.267 59.6-59.6 59.6zM704.802 592.533c0 0-28.267 0-40.8 0-12.533 34.533-43.867 59.6-84.667 59.6s-69.067-25.067-81.6-59.6c-12.533 0-40.8 0-40.8 0-65.867 0-119.2-53.333-119.2-119.2v-288.533c0-65.867 53.333-119.2 119.2-119.2h247.867c65.867 0 119.2 53.333 119.2 119.2v285.467c3.2 65.867-53.2 122.267-119.2 122.267zM582.535 605.2c22 0 40.8-18.8 40.8-40.8s-18.8-40.8-40.8-40.8c-22 0-40.8 18.8-40.8 40.8s15.733 40.8 40.8 40.8zM764.402 181.733c0-31.333-25.067-56.4-56.4-56.4h-250.933c-31.333 0-56.4 25.067-56.4 56.4v288.533c0 18.8 9.467 37.6 25.067 47.067v0c0-43.867 34.533-78.4 78.4-78.4h160c43.867 0 78.4 34.533 78.4 78.4v0c12.533-9.467 22-28.267 22-47.067v-288.533z" />
<glyph unicode="&#xe565;" glyph-name="arrow-down" data-tags="arrow-down" d="M234 565.667h556l-278-278z" />
<glyph unicode="&#xe566;" glyph-name="arrow-left" data-tags="arrow-left" d="M381 164.667v524l262-262z" />
<glyph unicode="&#xe58e;" glyph-name="colapse" data-tags="colapse" d="M512 584.667l256-256-60-60-196 196-196-196-60 60z" />
<glyph unicode="&#xe58f;" glyph-name="expand" data-tags="expand" d="M708 584.667l60-60-256-256-256 256 60 60 196-196z" />
<glyph unicode="&#xe600;" glyph-name="move" data-tags="move" d="M386.662 853.061h71.27v-71.27h-71.27v71.27zM566.067 853.061h71.27v-71.27h-71.27v71.27zM386.662 696.798h71.27v-71.27h-71.27v71.27zM566.067 696.798h71.27v-71.27h-71.27v71.27zM386.662 540.433h71.27v-71.27h-71.27v71.27zM566.067 540.433h71.27v-71.27h-71.27v71.27zM386.662 384.171h71.27v-71.27h-71.27v71.27zM566.067 384.171h71.27v-71.27h-71.27v71.27zM386.662 227.806h71.27v-71.27h-71.27v71.27zM566.067 227.806h71.27v-71.27h-71.27v71.27zM386.662 71.544h71.27v-71.27h-71.27v71.27zM566.067 71.544h71.27v-71.27h-71.27v71.27z" />
<glyph unicode="&#xe601;" glyph-name="check-mark" data-tags="check-mark" d="M454.301 395.253l-116.917 116.917-84.781-84.707 201.696-201.697 317.097 317.097-84.781 84.706z" />
<glyph unicode="&#xe888;" glyph-name="arrow-up-circle" data-tags="arrow-up-circle" d="M513.344 689.068c-148.616 0-264.722-120.75-260.077-269.367 0-125.395 88.241-232.212 208.991-255.434v213.636h-92.885c-13.933 0-13.933 9.288-9.288 18.577l139.327 171.838c4.645 9.288 13.933 9.288 23.221 4.645 0 0 4.645-4.645 4.645-4.645l139.327-171.838c9.288-9.288 4.645-18.577-9.288-18.577h-92.885v-213.636c143.972 32.51 232.212 171.838 199.703 315.808-23.221 120.75-130.039 204.347-250.789 208.991z" />
<glyph unicode="&#xe889;" glyph-name="info-circle" data-tags="info-circle" d="M512 686.933c-144.077 0-260.266-116.191-260.266-260.266s116.191-260.266 260.266-260.266 260.266 116.191 260.266 260.266v0c0 139.429-116.191 255.619-260.266 260.266zM470.171 635.81h88.305v-69.714h-88.305v69.714zM600.305 245.41h-181.257v51.123h51.123v162.666h-51.123v51.123h139.429v-218.438h46.477l-4.648-46.477z" />
<glyph unicode="&#xe88a;" glyph-name="search" data-tags="search" d="M769.263 223.163l-110.68 110.68c71.943 99.612 49.806 243.494-49.806 315.437s-243.494 44.27-315.437-55.339c-71.943-99.612-49.806-243.494 49.806-315.437 77.475-55.339 182.623-55.339 260.098 0l110.68-110.68c5.533-5.533 11.068-5.533 16.601 0 0 0 0 0 0 0l33.205 33.205c11.068 5.533 11.068 16.601 5.533 22.137 0 0 0 0 0 0zM475.96 300.638c-88.544 0-160.486 71.943-160.486 160.486s71.943 160.486 160.486 160.486 160.486-71.943 160.486-160.486-71.943-160.486-160.486-160.486v0z" />
<glyph unicode="&#xe88c;" glyph-name="fullscreen" data-tags="fullscreen" d="M357.075 572.983c5.737 5.737 0 5.737-5.737 5.737l-103.284 11.476c-5.737 5.737-11.476 0-11.476-5.737l11.476-109.021c0-5.737 5.737-5.737 5.737-5.737l103.284 103.284zM282.484 509.865l63.118-63.118c5.737-5.737 11.476-5.737 17.213 0l22.953 22.953c5.737 5.737 5.737 11.476 0 17.213l-63.118 57.379-40.166-34.429zM775.945 469.699c5.737-5.737 5.737 0 5.737 5.737l11.476 109.021c0 5.737-5.737 11.476-11.476 11.476l-109.021-11.476c-5.737 0-5.737-5.737-5.737-5.737l109.021-109.021zM712.83 544.294l-63.118-63.118c-5.737-5.737-5.737-11.476 0-17.213l22.953-22.953c5.737-5.737 11.476-5.737 17.213 0l63.118 63.118-40.166 40.166zM678.401 263.134c-5.737-5.737 0-5.737 5.737-5.737l109.021-11.476c5.737 0 11.476 5.737 11.476 11.476l-17.213 103.284c0 5.737-5.737 5.737-5.737 5.737l-103.284-103.284zM747.256 331.989l-63.118 63.118c-5.737 5.737-11.476 5.737-17.213 0l-22.953-22.953c-5.737-5.737-5.737-11.476 0-17.213l63.118-63.118 40.166 40.166zM253.794 366.418c-5.737 5.737-5.737 0-5.737-5.737l-11.476-109.021c0-5.737 5.737-11.476 11.476-11.476l109.021 11.476c5.737 0 5.737 5.737 5.737 5.737l-109.021 109.021zM322.649 291.824l63.118 63.118c5.737 5.737 5.737 11.476 0 17.213l-22.953 22.953c-5.737 5.737-11.476 5.737-17.213 0l-63.118-63.118 40.166-40.166zM150.51 676.267v-499.201h722.979v499.201h-722.979zM833.324 217.232h-636.911v413.13h636.911v-413.13z" />
<glyph unicode="&#xe88e;" glyph-name="h5p" data-tags="h5p" d="M891.406 574.524c-22.319 16.738-50.216 27.897-89.273 27.897h-139.487v-66.954h-156.225l-11.159-55.795c11.159 5.579 27.897 11.159 39.057 11.159s22.319 0 33.476 0c33.476 0 66.954-11.159 89.273-33.476s33.476-50.216 33.476-83.692c0-22.319-5.579-44.635-16.738-66.954s-27.897-39.057-50.216-50.216c-5.579-5.579-16.738 0-22.319-11.159h117.17v133.908h66.954c44.635 0 78.113 11.159 100.43 27.897 22.319 22.319 33.476 50.216 33.476 83.692 0 39.057-11.159 66.954-27.897 83.692v0zM796.555 462.935c-11.159-5.579-22.319-11.159-44.635-11.159h-33.476v83.692h39.057c22.319 0 33.476-5.579 44.635-11.159 5.579-5.579 11.159-16.738 11.159-27.897 0-16.738-5.579-27.897-16.738-33.476v0zM523.16 423.878c-16.738 0-33.476-11.159-44.635-27.897l-94.851 16.738 44.635 195.281h-94.851v-150.646h-117.17v150.646h-111.589v-362.667h111.589v133.908h117.17v-133.908h139.487c-16.738 11.159-33.476 11.159-44.635 22.319s-22.319 22.319-27.897 33.476c-5.579 11.159-11.159 22.319-16.738 39.057l94.851 16.738c5.579-16.738 22.319-27.897 44.635-27.897 27.897 0 50.216 22.319 50.216 50.216 0 22.319-22.319 44.635-50.216 44.635v0z" />
<glyph unicode="&#xe88f;" glyph-name="rights-of-use" data-tags="rights-of-use" d="M878.277 414.35c0-5.907 0-5.907 0-5.907-23.631-23.631-47.261-35.448-76.799-41.355-11.813 0-23.631-5.907-35.448-5.907s-17.724 0-29.537 0c0 0-5.907 0-5.907 5.907-64.985 59.079-135.877 118.153-200.863 183.139 0 0-5.907 0-5.907 0-23.631-5.907-47.261-11.813-70.892-17.724s-53.168 0-76.799 11.813c-17.724 11.813-23.631 17.724-29.537 35.448-5.907 5.907 0 23.631 11.813 23.631 41.355 11.813 88.616 29.537 129.971 47.261 11.813 5.907 29.537 5.907 41.355 5.907 5.907 0 11.813-5.907 11.813-5.907 41.355-17.724 82.709-29.537 124.060-47.261 0 0 5.907 0 5.907 0 29.537 5.907 64.985 17.724 94.523 23.631 5.907 0 5.907 0 5.907 0l106.34-212.676zM269.786 420.26c17.724 11.813 35.448 5.907 53.168-11.813 11.813-11.813 11.813-29.537 5.907-47.261 17.724 5.907 35.448-5.907 41.355-17.724 11.813-17.724 5.907-35.448-5.907-47.261 5.907 0 11.813 0 17.724 0 11.813-5.907 23.631-11.813 29.537-29.537s0-29.537-5.907-35.448c-5.907-5.907-11.813-11.813-17.724-17.724s-11.813-11.813-17.724-17.724-35.448-17.724-53.168 0c-29.537 29.537-53.168 64.985-82.709 94.523-17.724 23.631-35.448 41.355-47.261 64.985-5.907 11.813-11.813 17.724-11.813 29.537 0 5.907 0 17.724 5.907 23.631 11.813 11.813 17.724 17.724 29.537 29.537 17.724 17.724 47.261 11.813 64.985-5.907-5.907 0-5.907-5.907-5.907-11.813v0zM417.477 213.491l29.537-29.537c17.724-17.724 47.261-11.813 59.079 5.907l-5.907 5.907c-23.631 23.631-47.261 47.261-70.892 70.892-5.907 5.907-5.907 5.907-5.907 11.813s5.907 5.907 11.813 11.813c5.907 0 11.813 0 11.813-5.907 11.813-11.813 29.537-29.537 47.261-47.261 11.813-11.813 29.537-29.537 47.261-47.261 5.907-11.813 17.724-11.813 29.537-11.813 11.813 5.907 23.631 11.813 29.537 23.631 0 5.907 0 5.907 0 5.907-41.355 41.355-88.616 82.709-129.971 129.971-5.907 5.907-5.907 5.907-5.907 11.813 0 11.813 11.813 11.813 23.631 5.907 0 0 5.907 0 5.907-5.907 41.355-41.355 88.616-88.616 129.971-129.971 5.907-5.907 5.907-5.907 5.907-5.907 17.724 0 35.448 17.724 35.448 35.448 0 5.907 0 5.907 0 5.907-47.261 47.261-100.429 100.429-147.691 147.691-5.907 5.907-5.907 5.907-5.907 11.813s5.907 11.813 5.907 11.813c5.907 0 11.813 0 11.813-5.907 5.907-5.907 5.907-5.907 11.813-11.813 35.448-35.448 70.892-70.892 106.34-106.34 11.813-11.813 23.631-23.631 29.537-29.537 0 0 5.907-5.907 5.907 0 23.631 5.907 35.448 29.537 29.537 53.168h35.448c0 0 0 0 0 0 0-5.907 0-17.724 0-23.631-5.907-29.537-23.631-47.261-53.168-59.079 0 0-5.907 0-5.907-5.907-11.813-29.537-35.448-53.168-64.985-53.168-5.907 0-5.907 0-5.907-5.907-17.724-35.448-59.079-47.261-88.616-35.448-5.907 0-11.813 5.907-11.813 5.907-5.907-5.907-11.813-11.813-23.631-17.724-29.537-11.813-59.079-5.907-76.799 11.813-11.813 11.813-17.724 17.724-29.537 29.537 17.724 23.631 23.631 29.537 29.537 41.355v0 0zM252.062 727.459c29.537-11.813 64.985-23.631 94.523-29.537 35.448-11.813 64.985-23.631 100.429-29.537 0 0 0 0 5.907 0-17.724-5.907-35.448-11.813-47.261-17.724 0 0-5.907 0-5.907 0-47.261 11.813-94.523 23.631-135.877 41.355-5.907 0-5.907 0-5.907 0l-76.799-183.139c0-11.813 5.907-17.724 11.813-23.631s5.907-5.907 5.907-11.813c-5.907-5.907-11.813-17.724-23.631-23.631-17.724 17.724-29.537 35.448-29.537 64.985l88.616 212.676c-5.907-11.813 5.907 5.907 17.724 0v0z" />
<glyph unicode="&#xe890;" glyph-name="delete-circle" data-tags="delete-circle" d="M512 686.933c-147.107 0-260.266-118.817-260.266-260.266s118.817-260.266 260.266-260.266 260.266 118.817 260.266 260.266-113.158 260.266-260.266 260.266zM653.449 347.455c5.659-5.659 5.659-16.973 0-28.29l-33.949-33.949c-5.659-5.659-16.973-5.659-28.29 0l-79.212 79.212-79.212-79.212c-5.659-5.659-16.973-5.659-28.29 0l-33.949 33.949c-5.659 5.659-5.659 16.973 0 28.29l84.871 79.212-79.212 79.212c-5.659 5.659-5.659 16.973 0 28.29l33.949 33.949c5.659 5.659 16.973 5.659 28.29 0l73.554-84.871 79.212 79.212c5.659 5.659 16.973 5.659 28.29 0l33.949-33.949c5.659-5.659 5.659-16.973 0-28.29l-79.212-73.554 79.212-79.212z" />
<glyph unicode="&#xe891;" glyph-name="window" data-tags="window" d="M186.821 549.319c-5.704-5.704 0-5.704 5.704-5.704l108.394-11.41c5.704 0 11.41 5.704 11.41 11.41l-17.114 102.687c0 5.704-5.704 5.704-5.704 5.704l-102.687-102.687zM255.28 612.074l-62.752 62.752c-5.704 5.704-11.41 5.704-17.114 0l-17.114-22.821c-5.704-5.704-5.704-11.41 0-17.114l62.752-62.752 34.228 39.935zM734.49 646.302c-5.704 5.704-5.704 0-5.704-5.704l-11.41-108.394c0-5.704 5.704-11.41 11.41-11.41l108.394 11.41c5.704 0 5.704 5.704 5.704 5.704l-108.394 108.394zM797.242 572.14l62.752 62.752c5.704 5.704 5.704 11.41 0 17.114l-22.821 22.821c-5.704 5.704-11.41 5.704-17.114 0l-62.752-62.752 39.935-39.935zM831.473 309.717c5.704 5.704 0 5.704-5.704 5.704l-102.687 17.114c-5.704 0-11.41-5.704-11.41-11.41l11.41-108.394c0-5.704 5.704-5.704 5.704-5.704l102.687 102.687zM763.014 246.962l62.752-62.752c5.704-5.704 11.41-5.704 17.114 0l22.821 22.821c5.704 5.704 5.704 11.41 0 17.114l-62.752 62.752-39.935-39.935zM283.804 212.734c5.704-5.704 5.704 0 5.704 5.704l11.41 108.394c0 5.704-5.704 11.41-11.41 11.41l-108.394-11.41c-5.704 0-5.704-5.704-5.704-5.704l108.394-108.394zM221.052 281.193l-62.752-62.752c-5.704-5.704-5.704-11.41 0-17.114l22.821-22.821c5.704-5.704 11.41-5.704 17.114 0l62.752 62.752-39.935 39.935zM335.149 555.026v-239.605h347.998v239.605h-347.998zM637.507 355.355h-262.424v154.032h262.424v-154.032z" />
<glyph unicode="&#xe892;" glyph-name="code" data-tags="code" d="M446.523 311.303c6.235-6.235 6.235-12.472 6.235-18.707v-62.359c0-6.235-6.235-6.235-6.235-6.235l-230.728 155.897c-6.235 6.235-6.235 12.472-6.235 18.707v49.886c0 6.235 6.235 12.472 6.235 18.707l230.728 155.897c6.235 6.235 6.235 0 6.235-6.235v-62.359c0-6.235-6.235-12.472-6.235-18.707l-162.134-112.245c-6.235-6.235-6.235-6.235 0-12.472l162.134-99.776zM733.375 417.313c6.235 6.235 6.235 6.235 0 12.472l-155.897 112.245c-6.235 6.235-6.235 12.472-6.235 18.707v62.359c0 6.235 6.235 6.235 6.235 6.235l230.728-155.897c6.235-6.235 6.235-12.472 6.235-18.707v-49.886c0-6.235-6.235-12.472-6.235-18.707l-230.728-155.897c-6.235-6.235-6.235 0-6.235 6.235v62.359c0 6.235 6.235 12.472 6.235 18.707l155.897 99.776z" />
<glyph unicode="&#xe893;" glyph-name="download" data-tags="download" d="M353.050 512.027c-11.773 0-17.66-5.887-5.887-17.66l153.059-188.382c5.887-11.773 23.547-11.773 29.433 0l153.059 188.382c5.887 11.773 5.887 17.66-5.887 17.66h-323.782zM570.865 500.253v135.399c0 11.773-11.773 23.547-23.547 23.547h-70.643c-11.773 0-23.547-11.773-23.547-23.547v-141.286h117.739zM647.395 364.854c-5.887 0-17.66-5.887-23.547-11.773l-76.53-94.19c-5.887-5.887-17.66-17.66-23.547-23.547 0 0-5.887-5.887-11.773-5.887s-17.66 11.773-17.66 11.773c-5.887 5.887-17.66 17.66-23.547 23.547l-76.53 94.19c-5.887 5.887-17.66 11.773-23.547 11.773h-123.626c-5.887 0-17.66-5.887-17.66-17.66v-141.286c0-5.887 5.887-17.66 17.66-17.66h529.824c5.887 0 17.66 5.887 17.66 17.66v141.286c0 5.887-5.887 17.66-17.66 17.66l-129.513-5.887zM300.067 253.004c-17.66 0-29.433 11.773-29.433 29.433s11.773 29.433 29.433 29.433c17.66 0 29.433-11.773 29.433-29.433s-11.773-29.433-29.433-29.433v0z" />
<glyph unicode="&#xe894;" glyph-name="delete" data-tags="delete" d="M615.111 426.667l134.045 134.045c10.311 10.311 10.311 30.934 0 41.245l-61.866 61.866c-10.311 10.311-30.934 10.311-41.245 0l-134.045-134.045-134.045 134.045c-10.311 10.311-30.934 10.311-41.245 0l-61.866-61.866c-10.311-10.311-10.311-30.934 0-41.245l134.045-134.045-134.045-134.045c-10.311-10.311-10.311-30.934 0-41.245l61.866-61.866c10.311-10.311 30.934-10.311 41.245 0l134.045 134.045 134.045-134.045c10.311-10.311 30.934-10.311 41.245 0l61.866 61.866c10.311 10.311 10.311 30.934 0 41.245l-134.045 134.045z" />
<glyph unicode="&#xe900;" glyph-name="edit-image" data-tags="edit-image" d="M300.336 749.664c69.018 23.142 133.325 14.234 189.133-33.28 56.627-48.128 77.619-110.592 63.181-183.808-2.355-12.186 0.307-19.456 8.704-27.853 93.901-93.389 156.774-156.467 250.47-250.163 5.427-5.427 10.957-10.854 15.667-16.896 39.424-50.278 16.794-124.006-44.237-142.029-36.966-10.957-68.403 0-95.334 27.034-95.642 96.051-160.973 160.973-256.614 257.024-6.963 6.963-12.8 8.909-22.63 6.758-117.76-26.317-229.171 60.826-231.731 181.453-0.614 26.419 3.584 52.326 15.974 77.926 34.816-34.816 68.506-67.789 101.274-101.786 10.445-10.752 20.992-15.36 36.045-15.36 14.643 0 25.19 3.891 34.816 14.848 10.752 12.39 23.040 23.347 34.611 35.021 14.336 14.438 14.336 46.080-0.205 60.518-35.123 35.226-70.349 70.349-106.598 106.496 3.891 2.253 5.632 3.482 7.475 4.096zM703.485 191.584c-0.41-24.269 20.685-45.466 44.851-45.158 23.757 0.41 44.032 20.992 43.93 44.544-0.102 23.757-20.275 44.032-44.237 44.134-23.859 0.307-44.237-19.661-44.544-43.52z" />
<glyph unicode="&#xe901;" glyph-name="hourglass" data-tags="hourglass" d="M733.414 117.419c-147.763 0-295.526 0-443.29 0 0 2.048 0.102 4.096 0 6.144-0.307 13.824-1.024 32.666-0.922 46.49 0.41 39.731 6.861 78.131 19.046 115.2 17.203 52.224 43.725 96.256 81.306 130.355 4.506 4.096 9.216 7.885 13.722 11.776-0.205 0.717-0.307 1.126-0.41 1.229-1.331 1.229-2.765 2.355-4.198 3.584-28.058 22.63-50.688 51.405-68.403 85.606-30.618 59.085-43.52 123.597-41.165 192.614 0.205 7.168 0.614 18.33 0.922 25.498 147.763 0 295.526 0 443.29 0 0.205-1.331 0.512-2.662 0.614-3.994 2.662-36.966 1.229-77.722-5.939-113.869-14.336-72.909-44.544-133.837-95.027-179.405-4.096-3.686-8.294-7.066-12.39-10.547 0.205-0.717 0.307-1.126 0.512-1.331 0.819-0.717 1.638-1.434 2.458-2.15 42.189-33.894 71.68-79.872 90.931-135.782 11.776-34.202 18.637-69.837 20.070-106.701 0.819-19.763-0.614-44.851-1.126-64.717zM687.437 160.632c0 6.554 0.205 12.493 0 18.432-1.331 37.581-7.27 74.138-19.866 108.749-17.92 49.562-45.568 88.269-88.678 108.646-2.458 1.126-2.97 3.072-2.97 5.837 0.102 16.691 0.102 33.485 0 50.176 0 3.994 1.331 5.427 4.096 6.963 9.114 5.325 18.432 10.24 26.829 16.896 29.696 23.552 49.152 56.934 62.362 95.744 10.342 30.413 15.77 62.259 17.818 94.822 0.614 9.114 0.102 18.227 0.102 27.546-116.634 0-233.574 0-351.027 0 0.307-8.704 0.614-16.998 1.024-25.395 1.946-37.274 8.499-73.216 21.504-107.213 18.125-47.104 45.261-83.558 86.528-103.117 2.253-1.024 2.867-2.662 2.867-5.427-0.102-17.203-0.102-34.509 0-51.712 0-3.072-1.024-4.506-3.277-5.632-5.632-2.867-11.366-5.734-16.691-9.216-34.304-22.733-56.832-57.754-71.987-100.147-12.493-35.123-18.125-71.987-19.661-109.875-0.205-5.325 0-10.752 0-16.282 117.35 0.205 234.189 0.205 351.027 0.205zM410.342 579.96c68.096 0 135.373 0 203.674 0-3.789-6.554-7.168-12.595-10.752-18.227-10.957-16.998-24.269-30.618-39.731-41.472s-27.75-25.293-32.768-46.592c-1.638-6.963-2.765-14.336-2.867-21.606-0.307-17.203-0.205-34.406 0.307-51.712 0.717-28.058 12.493-48.947 32.154-62.566 43.008-30.003 65.843-75.878 75.776-132.506 0.307-1.638 0.307-3.379 0.614-5.53-83.149 0-166.093 0-249.242 0 2.662 20.685 7.885 40.346 15.462 59.085 13.312 32.973 32.768 59.187 59.494 77.722 16.589 11.469 28.365 27.955 32.358 50.995 0.819 4.813 1.331 9.83 1.434 14.746 0.102 18.637 0.614 37.274-0.205 55.808-1.126 24.678-11.981 43.213-28.57 57.139-9.216 7.782-18.944 14.746-27.648 23.142-11.981 11.162-21.197 25.395-29.491 41.574z" />
<glyph unicode="&#xe902;" glyph-name="plus-icon" data-tags="plus-icon" d="M767.787 391.893c0-19.323-15.664-34.987-34.987-34.987h-151.040v-151.467c0-19.323-15.664-34.987-34.987-34.987h-69.547c-19.323 0-34.987 15.664-34.987 34.987v151.467h-151.467c-19.323 0-34.987 15.664-34.987 34.987v69.547c0 19.323 15.664 34.987 34.987 34.987h151.467v151.467c0 19.323 15.664 34.987 34.987 34.987h69.547c19.323 0 34.987-15.664 34.987-34.987v-151.467h151.467c19.323 0 34.987-15.664 34.987-34.987z" />
<glyph unicode="&#xe903;" glyph-name="video-upload-icon" data-tags="video-upload-icon" d="M395.848 387.201v-128c0-21.333 21.333-42.667 42.667-42.667h128c21.333 0 42.667 17.067 42.667 42.667v128c0 21.333-17.067 42.667-42.667 42.667h-128c-21.333 0-42.667-17.067-42.667-42.667zM796.915 557.867l-102.4 106.667c-12.8 12.8-38.4 21.333-55.467 21.333h-140.8l21.333-42.667h89.6v-136.533c0-17.067 12.8-29.867 29.867-29.867h140.8v-341.333h-426.667v328.533h-42.667v-341.333c0-17.067 12.8-29.867 29.867-29.867h448c17.067 0 29.867 12.8 29.867 29.867v384c4.267 17.067-8.533 38.4-21.333 51.2zM651.848 515.201v123.733c4.267 0 12.8-4.267 12.8-8.533l102.4-102.4c4.267-4.267 4.267-8.533 8.533-12.8h-123.733zM737.181 225.067v196.267c0 4.267-4.267 8.533-8.533 8.533s-8.533 0-12.8-4.267l-89.6-89.6v-29.867l89.6-89.6c8.533-4.267 8.533 0 12.8 0 4.267 4.267 8.533 4.267 8.533 8.533zM361.715 532.267v136.533l59.733-59.733c8.533-8.533 17.067-4.267 25.6 0l12.8 12.8c8.533 8.533 8.533 17.067 0 25.6l-115.2 106.667c-8.533 8.533-17.067 8.533-21.333 0l-115.2-110.933c-4.267-8.533-4.267-17.067 0-25.6l12.8-12.8c8.533-8.533 17.067-8.533 21.333 0l59.733 59.733v-136.533c0-8.533 8.533-17.067 17.067-17.067h25.6c0 0 17.067 8.533 17.067 21.333z" />
<glyph unicode="&#xe904;" glyph-name="play-icon" data-tags="play-icon" d="M375.444 661.357c81.067 46.933 187.733 46.933 273.067 0 42.667-25.6 72.533-55.467 98.133-98.133 72.533-128 29.867-294.4-98.133-371.2-128-72.533-294.4-29.867-371.2 98.133-46.933 81.067-46.933 187.733 0 273.067 21.333 42.667 55.467 72.533 98.133 98.133zM644.244 409.623c12.8 8.533 12.8 29.867 0 38.4l-192 110.933c-8.533 4.267-12.8 4.267-21.333 0s-12.8-12.8-12.8-21.333v-226.133c0-8.533 4.267-17.067 12.8-21.333s17.067-4.267 21.333 0l192 119.467z" />
<glyph unicode="&#xe905;" glyph-name="copy" data-tags="copy" d="M722.133 603.867h-247.733c-65.867 0-119.2-53.333-119.2-119.2v-288.533c0-65.867 53.333-119.2 119.2-119.2h247.867c65.867 0 119.2 53.333 119.2 119.2v288.533c-0.267 65.867-53.467 119.2-119.333 119.2zM778.533 199.2c0-31.333-25.067-56.4-56.4-56.4h-247.867c-31.333 0-56.4 25.067-56.4 56.4v285.467c0 31.333 25.067 56.4 56.4 56.4h247.733c31.333 0 56.4-25.067 56.4-56.4v-285.467zM245.2 368.667v288.533c0 31.333 25.067 56.4 56.4 56.4h247.733c31.333 0 56.4-25.067 56.4-56.4v-18.8h62.667v18.8c0 65.867-53.333 119.2-119.2 119.2h-247.467c-65.867 0-119.2-53.333-119.2-119.2v-288.533c0-65.867 53.333-119.2 119.2-119.2h18.8v62.667h-18.8c-31.467-3.067-56.533 22-56.533 56.533zM681.2 403.2h-163.067c-18.8 0-31.333 12.533-31.333 31.333s12.533 31.333 31.333 31.333h163.067c18.8 0 31.333-12.533 31.333-31.333s-15.6-31.333-31.333-31.333zM681.2 309.067h-163.067c-18.8 0-31.333 12.533-31.333 31.333s12.533 31.333 31.333 31.333h163.067c18.8 0 31.333-12.533 31.333-31.333s-15.6-31.333-31.333-31.333zM681.2 214.934h-163.067c-18.8 0-31.333 12.533-31.333 31.333s12.533 31.333 31.333 31.333h163.067c18.8 0 31.333-12.533 31.333-31.333s-15.6-31.333-31.333-31.333z" />
<glyph unicode="&#xe906;" glyph-name="examples-icon" data-tags="examples-icon" d="M213.333 275.201c89.6 38.4 183.467 68.267 273.067 17.067v281.6c-68.267 46.933-157.867 55.467-234.667 12.8l-38.4-311.467zM810.667 275.201l-42.667 315.733c-72.533 38.4-166.4 34.133-234.667-17.067v-285.867c93.867 51.2 187.733 21.333 277.333-12.8zM832 629.334c-51.2 29.867-110.933 46.933-170.667 55.467-51.2 0-102.4-8.533-149.333-29.867-46.933 21.333-98.133 29.867-149.333 29.867-59.733-4.267-119.467-25.6-170.667-55.467l-64-452.267c0 0 29.867-17.067 110.933 21.333 46.933 25.6 102.4 34.133 157.867 25.6 42.667-4.267 85.333-21.333 115.2-55.467v0c29.867 34.133 72.533 51.2 115.2 55.467 55.467 4.267 106.667-4.267 157.867-29.867 81.067-38.4 110.933-21.333 110.933-21.333l-64 456.533zM793.6 224.001c-42.667 21.333-89.6 34.133-140.8 34.133-8.533 0-21.333 0-29.867 0-42.667-4.267-81.067-17.067-115.2-42.667-34.133 25.6-72.533 38.4-115.2 42.667-12.8 0-21.333 0-34.133 0-46.933 0-93.867-8.533-136.533-29.867-21.333-12.8-46.933-21.333-72.533-25.6l64 413.867c46.933 25.6 98.133 38.4 149.333 42.667 46.933 0 93.867-8.533 136.533-25.6l12.8-8.533 12.8 4.267c42.667 17.067 89.6 25.6 136.533 25.6 51.2-4.267 102.4-17.067 149.333-42.667l59.733-418.133c-29.867 8.533-51.2 17.067-76.8 29.867z" />
<glyph unicode="&#xe907;" glyph-name="tutorials-icon" data-tags="tutorials-icon" d="M887.468 537.598l-375.467-110.933h-4.267l-217.6 68.267c-21.333-25.6-34.133-59.733-34.133-98.133 21.333-12.8 25.6-38.4 12.8-59.733-4.267-4.267-8.533-8.533-12.8-12.8l17.067-145.067c0-4.267 0-4.267-4.267-8.533 0 0 0 0-4.267 0h-64c-4.267 0-4.267 0-8.533 4.267 0 4.267-4.267 4.267-4.267 8.533l17.067 145.067c-12.8 8.533-17.067 21.333-17.067 34.133 0 17.067 8.533 29.867 21.333 38.4 0 38.4 12.8 76.8 34.133 110.933l-106.667 29.867c-8.533 4.267-8.533 8.533-8.533 17.067 0 4.267 4.267 4.267 4.267 4.267l375.467 119.467h4.267l375.467-123.733c4.267 0 8.533-4.267 8.533-8.533s-4.267-8.533-8.533-12.8zM725.334 341.332c4.267-46.933-93.867-85.333-213.333-85.333s-213.333 38.4-213.333 85.333l4.267 106.667 192-64c4.267 0 12.8 0 17.067 0s12.8 0 17.067 4.267l192 59.733 4.267-106.667z" />
<glyph unicode="&#xe908;" glyph-name="info-important-description" data-tags="info-important-description" d="M512 768.017c-188.5 0-341.3-152.8-341.3-341.3s152.8-341.4 341.3-341.4 341.3 152.8 341.3 341.3-152.8 341.4-341.3 341.4v0zM512 113.917c-172.7 0-312.7 140-312.7 312.7s140 312.7 312.7 312.7c172.7 0 312.7-140 312.7-312.7-0.2-172.6-140.1-312.5-312.7-312.7v0zM512 676.217c-137.9 0-249.6-111.8-249.6-249.6s111.7-249.6 249.6-249.6 249.6 111.8 249.6 249.6-111.8 249.6-249.6 249.6v0z" />
<glyph unicode="&#xe909;" glyph-name="icon-info" data-tags="icon-info" d="M468.875 565.974h87.467c0.028 0 0.062 0 0.095 0 6.056 0 11.499 2.629 15.248 6.808 3.979 4.15 6.419 9.769 6.419 15.957 0 0.097-0.001 0.194-0.002 0.29v70.385c0.001 0.082 0.002 0.179 0.002 0.276 0 6.188-2.44 11.806-6.409 15.946-3.759 4.19-9.201 6.819-15.257 6.819-0.033 0-0.067 0-0.1 0h-87.462c-0.028 0-0.062 0-0.095 0-6.056 0-11.499-2.629-15.248-6.808-3.979-4.15-6.419-9.769-6.419-15.957 0-0.097 0.001-0.194 0.002-0.29v-69.959c-0.001-0.082-0.002-0.179-0.002-0.276 0-6.188 2.44-11.806 6.409-15.946 3.715-4.373 9.2-7.159 15.338-7.245zM599.008 263.041h-22.187v209.92c0.001 0.082 0.002 0.179 0.002 0.276 0 6.188-2.44 11.806-6.409 15.946-3.759 4.19-9.201 6.819-15.257 6.819-0.033 0-0.067 0-0.1 0h-130.128c-0.028 0-0.062 0-0.095 0-6.056 0-11.499-2.629-15.248-6.808-3.979-4.15-6.419-9.769-6.419-15.957 0-0.097 0.001-0.194 0.002-0.29v-46.492c-0.001-0.082-0.002-0.179-0.002-0.276 0-6.188 2.44-11.806 6.409-15.946 3.759-4.19 9.201-6.819 15.257-6.819 0.033 0 0.067 0 0.1 0h22.182v-139.947h-22.187c-0.028 0-0.062 0-0.095 0-6.056 0-11.499-2.629-15.248-6.808-3.979-4.15-6.419-9.769-6.419-15.957 0-0.097 0.001-0.194 0.002-0.29v-46.492c-0.001-0.082-0.002-0.179-0.002-0.276 0-6.188 2.44-11.806 6.409-15.946 3.759-4.19 9.201-6.819 15.257-6.819 0.033 0 0.067 0 0.1 0h174.075c0.028 0 0.062 0 0.095 0 6.056 0 11.499 2.629 15.248 6.808 3.979 4.15 6.419 9.769 6.419 15.957 0 0.097-0.001 0.194-0.002 0.29v46.065c0.043 0.527 0.067 1.141 0.067 1.761 0 5.302-1.791 10.185-4.8 14.079-3.742 4.424-9.36 7.247-15.636 7.247-0.489 0-0.975-0.017-1.456-0.051z" />
<glyph unicode="&#xe90a;" glyph-name="paste" data-tags="paste" d="M394.402 745.067h-75.333c-65.867 0-119.2-53.333-119.2-119.2v-288.533c0-56.4 37.6-100.4 87.867-116v69.067c-15.733 9.467-25.067 25.067-25.067 47.067v288.4c0 31.333 25.067 56.4 56.4 56.4h131.733c0 0 0 0 3.2 3.2v0c0 31.333-28.267 59.6-59.6 59.6zM704.802 635.2c0 0-28.267 0-40.8 0-12.533 34.533-43.867 59.6-84.667 59.6s-69.067-25.067-81.6-59.6c-12.533 0-40.8 0-40.8 0-65.867 0-119.2-53.333-119.2-119.2v-288.533c0-65.867 53.333-119.2 119.2-119.2h247.867c65.867 0 119.2 53.333 119.2 119.2v285.467c3.2 65.867-53.2 122.267-119.2 122.267zM582.535 647.867c22 0 40.8-18.8 40.8-40.8s-18.8-40.8-40.8-40.8c-22 0-40.8 18.8-40.8 40.8s15.733 40.8 40.8 40.8zM764.402 224.4c0-31.333-25.067-56.4-56.4-56.4h-250.933c-31.333 0-56.4 25.067-56.4 56.4v288.533c0 18.8 9.467 37.6 25.067 47.067v0c0-43.867 34.533-78.4 78.4-78.4h160c43.867 0 78.4 34.533 78.4 78.4v0c12.533-9.467 22-28.267 22-47.067v-288.533z" />
<glyph unicode="&#xe90b;" glyph-name="reuse" data-tags="reuse" d="M734.974 624.751c-54.605 61.619-134.123 100.573-222.977 100.573-164.936 0-298.661-133.724-298.661-298.661h74.667c0 123.721 100.272 223.993 223.993 223.993 68.214 0 128.747-30.96 169.766-79.119l-70.213-70.213h199.109v199.109l-75.689-75.689zM511.999 202.671c-68.214 0-128.747 30.96-169.766 79.119l70.213 70.213h-199.109v-199.109l75.689 75.689c54.605-61.619 134.123-100.573 222.977-100.573 164.936 0 298.661 133.724 298.661 298.661h-74.667c0-123.721-100.272-223.993-223.993-223.993z" />
<glyph unicode="&#xe90c;" glyph-name="info-outlined" data-tags="info-outlined" d="M467.199 181.336h89.599v268.8h-89.599v-268.8zM512 853.335c-247.296 0-448.001-200.705-448.001-448.001s200.705-448.001 448.001-448.001 448.001 200.705 448.001 448.001-200.705 448.001-448.001 448.001zM512 46.936c-197.568 0-358.398 160.83-358.398 358.398s160.83 358.398 358.398 358.398 358.398-160.83 358.398-358.398-160.83-358.398-358.398-358.398zM467.199 539.734h89.599v89.599h-89.599v-89.599z" />
<glyph unicode="&#xe90d;" glyph-name="spinner" data-tags="spinner" d="M600 808.001c0-48.602-39.398-88-88-88s-88 39.398-88 88 39.398 88 88 88 88-39.398 88-88zM512 133.333c-48.602 0-88-39.398-88-88s39.398-88 88-88 88 39.398 88 88-39.398 88-88 88zM893.334 514.667c-48.602 0-88-39.398-88-88s39.398-88 88-88 88 39.398 88 88-39.398 88-88 88zM218.666 426.667c0 48.602-39.398 88-88 88s-88-39.398-88-88 39.398-88 88-88 88 39.398 88 88zM242.357 245.024c-48.602 0-88-39.398-88-88s39.398-88 88-88 88 39.398 88 88c0 48.6-39.4 88-88 88zM781.643 245.024c-48.602 0-88-39.398-88-88s39.398-88 88-88 88 39.398 88 88c0 48.6-39.398 88-88 88zM242.357 784.31c-48.602 0-88-39.398-88-88s39.398-88 88-88 88 39.398 88 88-39.4 88-88 88z" />

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.
Binary file not shown.
@@ -107,7 +107,7 @@ abstract class H5PMetadata {
switch ($config['type']) {
case 'text':
if ($value !== null && strlen($value) > $config['maxLength']) {
$value = \core_text::substr($value, 0, $config['maxLength']);
$value = mb_substr($value, 0, $config['maxLength']);
}
$types[] = '%s';
break;
+13 -17
View File
@@ -758,14 +758,11 @@ class H5PValidator {
unlink($tmpPath);
return FALSE;
}
// Moodle: the extension mbstring is optional.
/*
if (!extension_loaded('mbstring')) {
$this->h5pF->setErrorMessage($this->h5pF->t('The mbstring PHP extension is not loaded. H5P need this to function properly'), 'mbstring-unsupported');
unlink($tmpPath);
return FALSE;
}
*/
// Create a temporary dir to extract package in.
$tmpDir = $this->h5pF->getUploadedH5pFolderPath();
@@ -812,7 +809,7 @@ class H5PValidator {
}
$totalSize += $fileStat['size'];
$fileName = \core_text::strtolower($fileStat['name']);
$fileName = mb_strtolower($fileStat['name']);
if (preg_match('/(^[\._]|\/[\._])/', $fileName) !== 0) {
continue; // Skip any file or folder starting with a . or _
}
@@ -3318,15 +3315,12 @@ class H5PCore {
$setup->disable_hub = TRUE;
}
// Moodle: the extension mbstring is optional.
/*
if (!extension_loaded('mbstring')) {
$setup->errors[] = $this->h5pF->t(
'The mbstring PHP extension is not loaded. H5P needs this to function properly'
);
$setup->disable_hub = TRUE;
}
*/
// Check php version >= 5.2
$php_version = explode('.', phpversion());
@@ -3674,13 +3668,12 @@ class H5PContentValidator {
// Check if string is within allowed length
if (isset($semantics->maxLength)) {
// Moodle: the extension mbstring is optional.
/*
if (!extension_loaded('mbstring')) {
$this->h5pF->setErrorMessage($this->h5pF->t('The mbstring PHP extension is not loaded. H5P need this to function properly'), 'mbstring-unsupported');
}
*/
$text = \core_text::substr($text, 0, $semantics->maxLength);
else {
$text = mb_substr($text, 0, $semantics->maxLength);
}
}
// Check if string is according to optional regexp in semantics
@@ -3730,14 +3723,11 @@ class H5PContentValidator {
// file name, 2. testing against a returned error array that could
// never be more than 1 element long anyway, 3. recreating the regex
// for every file.
// Moodle: the extension mbstring is optional.
/*
if (!extension_loaded('mbstring')) {
$this->h5pF->setErrorMessage($this->h5pF->t('The mbstring PHP extension is not loaded. H5P need this to function properly'), 'mbstring-unsupported');
$valid = FALSE;
}
*/
if (!preg_match($wl_regex, \core_text::strtolower($file))) {
else if (!preg_match($wl_regex, mb_strtolower($file))) {
$this->h5pF->setErrorMessage($this->h5pF->t('File "%filename" not allowed. Only files with the following extensions are allowed: %files-allowed.', array('%filename' => $file, '%files-allowed' => $whitelist)), 'not-in-whitelist');
$valid = FALSE;
}
@@ -4279,7 +4269,7 @@ class H5PContentValidator {
return '&lt;';
}
if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9\-]+)([^>]*)>?|(<!--.*?-->)$%', $string, $matches)) {
if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9\-]+)\s*([^>]*)>?|(<!--.*?-->)$%', $string, $matches)) {
// Seriously malformed.
return '';
}
@@ -4341,7 +4331,13 @@ class H5PContentValidator {
// Attribute name, href for instance.
if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
$attrName = strtolower($match[1]);
$skip = ($attrName == 'style' || substr($attrName, 0, 2) == 'on');
$skip = (
$attrName == 'style' ||
substr($attrName, 0, 2) == 'on' ||
substr($attrName, 0, 1) == '-' ||
// Ignore long attributes to avoid unnecessary processing overhead.
strlen($attrName) > 96
);
$working = $mode = 1;
$attr = preg_replace('/^[-a-zA-Z]+/', '', $attr);
}
+13 -3
View File
@@ -1030,7 +1030,7 @@ H5P.t = function (key, vars, ns) {
H5P.Dialog = function (name, title, content, $element) {
/** @alias H5P.Dialog# */
var self = this;
var $dialog = H5P.jQuery('<div class="h5p-popup-dialog h5p-' + name + '-dialog">\
var $dialog = H5P.jQuery('<div class="h5p-popup-dialog h5p-' + name + '-dialog" role="dialog" tabindex="-1">\
<div class="h5p-inner">\
<h2>' + title + '</h2>\
<div class="h5p-scroll-content">' + content + '</div>\
@@ -1053,6 +1053,12 @@ H5P.Dialog = function (name, title, content, $element) {
.click(function () {
self.close();
})
.keypress(function (e) {
if (e.which === 13 || e.which === 32) {
self.close();
return false;
}
})
.end()
.find('a')
.click(function (e) {
@@ -1072,6 +1078,7 @@ H5P.Dialog = function (name, title, content, $element) {
$dialog.addClass('h5p-open'); // Fade in
// Triggering an event, in case something has to be done after dialog has been opened.
H5P.jQuery(self).trigger('dialog-opened', [$dialog]);
$dialog.focus();
}, 1);
};
@@ -1083,6 +1090,8 @@ H5P.Dialog = function (name, title, content, $element) {
setTimeout(function () {
$dialog.remove();
H5P.jQuery(self).trigger('dialog-closed', [$dialog]);
$element.attr('tabindex', '-1');
$element.focus();
}, 200);
};
};
@@ -1363,11 +1372,11 @@ H5P.openEmbedDialog = function ($element, embedCode, resizeCode, size, instance)
var $expander = H5P.jQuery(this);
var $content = $expander.next();
if ($content.is(':visible')) {
$expander.removeClass('h5p-open').text(H5P.t('showAdvanced'));
$expander.removeClass('h5p-open').text(H5P.t('showAdvanced')).attr('aria-expanded', 'true');
$content.hide();
}
else {
$expander.addClass('h5p-open').text(H5P.t('hideAdvanced'));
$expander.addClass('h5p-open').text(H5P.t('hideAdvanced')).attr('aria-expanded', 'false');
$content.show();
}
$dialog.find('.h5p-embed-code-container').each(function () {
@@ -1378,6 +1387,7 @@ H5P.openEmbedDialog = function ($element, embedCode, resizeCode, size, instance)
$dialog.find('.h5p-expander').click(expand).keypress(function (event) {
if (event.keyCode === 32) {
expand.apply(this);
return false;
}
});
}).on('dialog-closed', function () {
+7 -20
View File
@@ -14,7 +14,7 @@ Removed:
Added:
* readme_moodle.txt
Downloaded version: 1.24 release
Downloaded version: 1.24.2 release
Changes:
1. In order to allow the dependency path to be overridden by child H5PCore classes, a couple of minor changes have been added to the
@@ -31,33 +31,21 @@ https://github.com/h5p/h5p-php-library/compare/master...andrewnicols:libraryPath
Hopefully, when upgrading, these patch won't be needed because it will be included in the H5P library by default.
2. As the mbstring extension is optional in Moodle, the following changes have been hardcoded to the library:
2.1. Comment the following methods in h5p.classes.php file where the extension_loaded('mbstring') is called:
* isValidPackage
* checkSetupErrorMessage
* validateText
* validateContentFiles
2. Replace the $_SESSION references to $SESSION. That implies that the information is saved to backends, so only the Moodle one
should be used by core (core should be free from $_SESSION and always use $SESSION).
2.2. Change all the mb_uses straight to the core_text() alternatives. Version 1.24 has 3 ocurrences in h5p.classes.php
and 1 ocurrence in h5p-metadata.class.php.
3. Another PR has been sent to H5P library (https://github.com/h5p/h5p-php-library/pull/69) to fix some php74 minor problems. The same fix is being applied locally by MDL-67077. Once we import a new version, if it includes de fix, this won't be needed to reapply and can be removed.
4. Replace the $_SESSION references to $SESSION. That implies that the information is saved to backends, so only the Moodle one should be used by core (core should be free from $_SESSION and always use $SESSION).
h5p.classes.php file:
- Into hashToken method:
Declare the global $SESSION.
Change all the $_SESSION by $SESSION.
A script for testing this part can be found in MDL-68068
The point 2 from above won't be needed once the mbstring extension becomes mandatory in Moodle. A request has been
sent to MDL-65809.
5. Upgrade and patch JQuery library.
3. Upgrade and patch JQuery library.
Once https://github.com/h5p/h5p-php-library/issues/83 gets integrated in
H5P PHP Library (upgrading the JQuery version), this change won't be needed.
5.1. Prepare the patched JQuery 1.12.4 library following these steps:
3.1. Prepare the patched JQuery 1.12.4 library following these steps:
a) Download the uncompressed JQuery Core 1.12.4 from https://code.jquery.com/jquery-1.12.4.js
b) Add the patch in https://snyk.io/vuln/SNYK-JS-JQUERY-174006 to the downloaded file.
You'll need to replace this code (line 212):
@@ -75,15 +63,14 @@ H5P PHP Library (upgrading the JQuery version), this change won't be needed.
}
c) Minify the patched jquery-1-12.4.js.
5.2. Edit h5p/h5plib/v124/joubel/core/js/jquery.js and replace the JQuery piece of code
3.2. Edit h5p/h5plib/v124/joubel/core/js/jquery.js and replace the JQuery piece of code
(at the beginning of the file, above the comment "// Snap this specific version of jQuery into H5P. jQuery.noConflict will")
with the previous patched and minified JQuery version.
5.3. Remove the following comment in h5p/h5plib/v124/joubel/core/js/jquery.js:
3.3. Remove the following comment in h5p/h5plib/v124/joubel/core/js/jquery.js:
/**
* jQuery v1.9.1
*
* @member
*/
+26 -8
View File
@@ -202,7 +202,7 @@ div.h5p-fullscreen {
color: #999;
text-decoration: none;
outline: none;
line-height: 23px;
line-height: 22px;
}
.h5p-actions > .h5p-button:hover {
color: #666;
@@ -223,8 +223,8 @@ div.h5p-fullscreen {
.h5p-actions > .h5p-button:before {
font-family: 'H5P';
font-size: 20px;
line-height: 20px;
vertical-align: top;
line-height: 23px;
vertical-align: bottom;
padding-right: 0;
}
.h5p-actions > .h5p-button.h5p-export:before {
@@ -367,10 +367,6 @@ div.h5p-fullscreen {
}
.h5p-popup-dialog .h5p-close {
cursor: pointer;
}
.h5p-popup-dialog .h5p-close:after {
font-family: 'H5P';
content: "\e894";
font-size: 2em;
position: absolute;
right: 0;
@@ -383,6 +379,10 @@ div.h5p-fullscreen {
text-indent: -0.065em;
z-index: 3
}
.h5p-popup-dialog .h5p-close:after {
font-family: 'H5P';
content: "\e894";
}
.h5p-popup-dialog .h5p-close:hover:after,
.h5p-popup-dialog .h5p-close:focus:after {
color: #454545;
@@ -412,7 +412,6 @@ div.h5p-fullscreen {
.h5p-expander {
cursor: pointer;
font-size: 1.125em;
outline: none;
margin: 0.5em 0 0;
display: inline-block;
}
@@ -543,6 +542,25 @@ div.h5p-fullscreen {
opacity: 0;
visibility: hidden;
}
.h5p-content code,
.h5peditor code {
color: #3d3d3d;
background: #e0e0e0;
border-radius: 2px;
padding: 0 5px;
}
.h5p-content pre > code,
.h5peditor pre > code {
background-color: #fafafa;
padding: 5px;
display: block;
line-height: normal;
border: 1px solid #c7c7c7;
border-left-width: 4px;
max-width: 100%;
white-space: pre;
overflow: auto;
}
/* This is loaded as part of Core and not Editor since this needs to be outside the editor iframe */
+1 -1
View File
@@ -4,7 +4,7 @@
<location>joubel/core</location>
<name>h5p-php-library</name>
<license>GPL-3.0</license>
<version>1.24</version>
<version>1.24.2</version>
</library>
<library>
<location>joubel/editor</location>

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