Compare commits

...

273 Commits

Author SHA1 Message Date
Eloy Lafuente (stronk7) 5b78584fdb Moodle release 3.5.1 2018-07-07 06:54:07 +02:00
AMOS bot ce390c4725 Automatically generated installer lang files 2018-07-07 00:08:27 +00:00
Andrew Nicols 8675c2c908 weekly release 3.5+ 2018-07-06 14:05:15 +08:00
Andrew Nicols 70ffd8bb2b Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2018-07-06 14:05:13 +08:00
Jake Dallimore c2fea8ae43 Merge branch 'MDL-62670-35' of git://github.com/sarjona/moodle into MOODLE_35_STABLE 2018-07-05 09:26:05 +08:00
AMOS bot 0a520053cb Automatically generated installer lang files 2018-07-05 00:08:24 +00:00
Sara Arjona e154204728 MDL-62670 policy: viewall.php must display always all active policies
The viewall.php page is called from the mobile app to display the policies
to the users before agree them. That's why it must display all the active
policies (even for guests), to guarantee the users agree all current
policies from the app.
2018-07-04 12:18:52 +02:00
Sara Arjona 2d90a7d91a MDL-62670 policy: Remove footer link when there are not active policies 2018-07-04 12:18:52 +02:00
Tim Hunt 8ac7c623d3 MDL-62820 question import: properly escape output 2018-07-04 15:40:58 +08:00
Jake Dallimore caa9881c0d Merge branch 'wip-nolink-bug-35' of https://gitlab.di.unito.it/rabellino/moodle into MOODLE_35_STABLE 2018-07-04 14:38:13 +08:00
Jake Dallimore f0188d7b66 Merge branch 'MDL-41463-35' of https://github.com/lucaboesch/moodle into MOODLE_35_STABLE 2018-07-04 12:59:21 +08:00
Jake Dallimore ab94fee038 Merge branch 'MDL-44081-35' of git://github.com/zig-moodle/moodle into MOODLE_35_STABLE 2018-07-04 10:50:48 +08:00
Andrew Nicols 2d64d63373 MDL-62834 behat: Move include to function 2018-07-04 10:45:25 +08:00
Eloy Lafuente (stronk7) 6cb165d681 Merge branch 'wip-MDL-62526-35' of git://github.com/marinaglancy/moodle into MOODLE_35_STABLE 2018-07-04 04:19:24 +02:00
Adrian Greeve 55fc303a5f MDL-62616 tool_log: Add a setting to disable exporting logs. 2018-07-04 09:59:32 +08:00
Eloy Lafuente (stronk7) c453ea21e1 Merge branch 'MDL-62288_35' of https://bitbucket.org/ujiapps/moodle-uji into MOODLE_35_STABLE 2018-07-04 01:21:19 +02:00
Eloy Lafuente (stronk7) 24e4ec9d60 Merge branch 'MDL-62792_35' of https://github.com/timhunt/moodle into MOODLE_35_STABLE 2018-07-04 00:55:22 +02:00
Eloy Lafuente (stronk7) 25e9be8ea9 Merge branch 'MDL-62838_35' of git://github.com/timhunt/moodle into MOODLE_35_STABLE 2018-07-04 00:52:14 +02:00
Eloy Lafuente (stronk7) 9ffcaa7db7 Merge branch 'MDL-62834_35' of git://github.com/timhunt/moodle into MOODLE_35_STABLE 2018-07-04 00:46:40 +02:00
Eloy Lafuente (stronk7) 936d21d2dd Merge branch 'MDL-62211-35' of git://github.com/junpataleta/moodle into MOODLE_35_STABLE 2018-07-03 23:33:48 +02:00
David Monllao d20b4b2f23 Merge branch 'MDL-61296-35' of git://github.com/cescobedo/moodle into MOODLE_35_STABLE 2018-07-03 23:33:47 +02:00
David Monllao d587c4bd90 Merge branch 'MDL-62656-35' of git://github.com/mihailges/moodle into MOODLE_35_STABLE 2018-07-03 23:33:47 +02:00
David Monllao 3828033e98 Merge branch 'MDL-60014-35-catcherror-nopermission-annotatepdfcron' of https://github.com/Eva-Dv/moodle into MOODLE_35_STABLE 2018-07-03 23:33:46 +02:00
David Monllao 4892662238 Merge branch 'MDL-62691-35' of git://github.com/bmbrands/moodle into MOODLE_35_STABLE 2018-07-03 23:33:46 +02:00
David Monllao ccbd142a63 Merge branch 'MDL-62813-35' of git://github.com/lameze/moodle into MOODLE_35_STABLE 2018-07-03 23:33:45 +02:00
David Monllao 3b52f52351 Merge branch 'MDL-62654-35' of git://github.com/bmbrands/moodle into MOODLE_35_STABLE 2018-07-03 15:19:34 +02:00
David Monllao 173968b25d Merge branch 'MDL-32232-35' of https://github.com/lucaboesch/moodle into MOODLE_35_STABLE 2018-07-03 14:00:20 +02:00
David Mudrák 4cdd9e77dc MDL-62796 tool_policy: Fix a regression in Behat test
We now correctly show the popup with links to policies that apply to
anonymous visitors of the site. So the name of the guests only policy
became visible in this popup in more cases and it caused false failures.

The point of the lines affected by this patch is to make sure that the
policy does / does not appear in the list of policies to be accepted. So
I am making it more specific of where the text is / is not supposed to
be seen.
2018-07-03 11:05:18 +02:00
Jake Dallimore 483d8fefa8 Merge branch 'MDL-59261-35' of git://github.com/ryanwyllie/moodle into MOODLE_35_STABLE 2018-07-03 14:44:53 +08:00
Bas Brands 654eac4f13 MDL-59261 theme_boost: compiled css fallback 2018-07-03 14:14:05 +08:00
Michael Hawkins 86699bedb0 MDL-62767 theme: Remove old localcaches when clearing/updating theme cache 2018-07-03 14:00:40 +08:00
Michael Hawkins 914e26d916 MDL-62801 themes: Added missing filelib include 2018-07-03 13:08:33 +08:00
Marina Glancy ced6691934 MDL-62790 course: cap check in core_course_get_categories 2018-07-03 12:02:18 +08:00
Jake Dallimore ff1fbf2992 Merge branch 'MDL-62715-35' of git://github.com/andrewnicols/moodle into MOODLE_35_STABLE 2018-07-03 08:49:52 +08:00
Eloy Lafuente (stronk7) 49e435c00a Merge branch 'MDL-62763-35-enfix' of git://github.com/mudrd8mz/moodle into MOODLE_35_STABLE 2018-07-02 20:09:15 +02:00
Eloy Lafuente (stronk7) b67ae5a0ad Merge branch 'MDL-62621-35-ixcomment' of git://github.com/mudrd8mz/moodle into MOODLE_35_STABLE 2018-07-02 20:02:25 +02:00
Helen Foster b2b1bb2d29 MDL-62763 lang: Merge English strings from the en_fix language pack
Significant string changes:

* various strings in tool_dataprivacy and tool_policy - 'Data Protection
  Officer' changed to 'privacy officer'
* idnumbermod_help,core - mentioning resource and custom reporting
2018-07-02 13:36:29 +02:00
David Monllao 5a13884a0f Merge branch 'wip-MDL-62796-35' of git://github.com/marinaglancy/moodle into MOODLE_35_STABLE 2018-07-02 12:21:52 +02:00
David Monllao d42328e223 Merge branch 'MDL-62713-m35' of https://github.com/sammarshallou/moodle into MOODLE_35_STABLE 2018-07-02 10:42:18 +02:00
Jake Dallimore 3879b520b7 Merge branch 'MDL-62801-35' of git://github.com/mickhawkins/moodle into MOODLE_35_STABLE 2018-07-02 16:19:10 +08:00
David Mudrák 6a3cffd3c6 MDL-62621 comments: Add missing indexes on comments table
The composite index over the fields contextid, commentarea and itemid
helps the comments API to load all comments for the given area
effectively.

The index over the userid field helps the privacy API to load all
comments by the given user.
2018-07-02 10:13:00 +02:00
Mihail Geshoski 40790755ce MDL-62656 course: Align elements in course and category management 2018-07-02 08:25:34 +08:00
AMOS bot 310a1f0895 Automatically generated installer lang files 2018-06-30 00:08:23 +00:00
Tim Hunt ed3468e40b MDL-62838 filters: dont use statics inside functions. Use MUC 2018-06-29 17:25:13 +01:00
Tim Hunt 9bedb84419 MDL-62834 behat: new step for enabling filters 2018-06-29 11:27:46 +01:00
sam marshall c843630aee MDL-62713 core_search: Cron error indexing sections in some cases 2018-06-29 10:52:53 +01:00
Michael Hawkins 233847bdb7 MDL-62801 themes: Remove old mustache caches when new one generated 2018-06-29 10:50:21 +08:00
Jun Pataleta f29d518ba8 MDL-62211 tool_dataprivacy: Request filtering and pagination
* Make data requests page use table_sql to make it sortable
* Also, add an autocomplete element that enables filtering by
  status and request type.
2018-06-29 10:32:25 +08:00
Zig Tan 749c7cc5a9 MDL-44081 assign: Fix Grader annotation button accessibility ordering 2018-06-29 09:28:45 +08:00
Luca Bösch bb3ce79c7e MDL-41463 course: Reset remove groups section advanced toggle. 2018-06-28 23:05:40 +02:00
Luca Bösch 4b52a5b2c6 MDL-32232 lesson: changing a page type chooser option for consistency 2018-06-28 23:05:29 +02:00
Sergio Rabellino af344834e1 MDL-61403 filter: Fix for the nolink tag ignored
Fix for the nolink tag ignored if there are some other attributes defined in the same span
2018-06-28 11:01:01 +02:00
Simey Lameze cdc9e56198 MDL-62813 calendar: add behat test for form validation 2018-06-28 14:12:18 +08:00
Simey Lameze 0e0fd72d98 MDL-62813 calendar: fix dmlreadexception on event form 2018-06-28 14:12:18 +08:00
Jake Dallimore 543005a342 weekly release 3.5+ 2018-06-28 14:03:04 +08:00
Marina Glancy 5c33665a74 MDL-62526 admin: ask privacy policy agreement for moodle.net 2018-06-28 11:44:38 +08:00
Eloy Lafuente (stronk7) 362518ef34 Merge branch 'MDL-62325-35' of git://github.com/rezaies/moodle into MOODLE_35_STABLE 2018-06-28 02:06:03 +02:00
Eloy Lafuente (stronk7) ff246f2260 Merge branch 'wip-MDL-62755-35' of git://github.com/abgreeve/moodle into MOODLE_35_STABLE 2018-06-28 01:56:37 +02:00
Eloy Lafuente (stronk7) fc1b13ede9 Merge branch 'MDL-61889-35' of git://github.com/junpataleta/moodle into MOODLE_35_STABLE 2018-06-28 01:49:31 +02:00
Eloy Lafuente (stronk7) 458b0fb900 Merge branch 'MDL-62747_35' of https://github.com/timhunt/moodle into MOODLE_35_STABLE 2018-06-28 00:32:34 +02:00
Matteo Scaramuccia 5df53c769e MDL-51080 env: warn when running MariaDB w/ wrong dbtype 2018-06-28 00:15:28 +02:00
Jake Dallimore 380a437428 Merge branch 'MDL-61732-35' of https://github.com/Dagefoerde/moodle into MOODLE_35_STABLE 2018-06-27 12:18:13 +08:00
Eloy Lafuente (stronk7) 5f6539e379 Merge branch 'MDL-45088-35' of https://github.com/lucaboesch/moodle into MOODLE_35_STABLE 2018-06-27 03:23:22 +02:00
Eloy Lafuente (stronk7) b44ddd4a7f Merge branch 'MDL-62703-35' of git://github.com/bmbrands/moodle into MOODLE_35_STABLE 2018-06-27 03:17:45 +02:00
Eloy Lafuente (stronk7) 833e2398ef Merge branch 'MDL-62471_35' of git://github.com/dmonllao/moodle into MOODLE_35_STABLE 2018-06-27 02:41:53 +02:00
Eloy Lafuente (stronk7) df9443db71 Merge branch 'MDL-62735-35-refactor' of https://github.com/snake/moodle into MOODLE_35_STABLE 2018-06-26 10:44:24 +02:00
Jake Dallimore 4d23edfb37 Merge branch 'MDL-62698_35' of git://github.com/timhunt/moodle into MOODLE_35_STABLE 2018-06-26 15:30:32 +08:00
Juan Segarra Montesinos 11489b20a5 MDL-62288 mod_glossary: Add links to glossary entries for all users 2018-06-26 08:41:21 +02:00
Jun Pataleta b3483e7ede MDL-61889 tool_dataprivacy: Fix CI errors 2018-06-26 13:21:51 +08:00
Marina Glancy dd05e0d29e MDL-62796 tool_policy: show popup for users who are not logged in 2018-06-26 12:33:24 +08:00
Eloy Lafuente (stronk7) 1b37fe78f9 Merge branch 'MDL-59047-35' of git://github.com/bmbrands/moodle into MOODLE_35_STABLE 2018-06-26 03:05:27 +02:00
Eloy Lafuente (stronk7) 82f21031a3 Merge branch 'MDL-62730-35' of https://github.com/snake/moodle into MOODLE_35_STABLE 2018-06-26 02:54:41 +02:00
Eloy Lafuente (stronk7) 6545e50f62 Merge branch 'MDL-51419-35' of https://github.com/snake/moodle into MOODLE_35_STABLE 2018-06-26 02:41:59 +02:00
Eva Davitasvili 3d7630a158 MDL-60014 assign: Annotate PDF - catch no permission error 2018-06-25 14:10:41 -04:00
Tim Hunt 2588126d66 MDL-62792 filters: fix some bad PHPdocs 2018-06-25 15:52:33 +01:00
Tim Hunt de71bef7cb MDL-62792 filter_mathjaxloader: don't use statics for once-only items 2018-06-25 15:52:31 +01:00
Tim Hunt e310f10818 MDL-62698 quiz_overview: new index should not be unique 2018-06-25 15:51:03 +01:00
Shamim Rezaie 9b6938f99b MDL-62325 enrol_paypal: Added some keys and indexes to enrol_paypal.
- Added these keys to the enrol_paypal db: courseid, userid, instanceid
- Added these indexes to the enrol_paypal db: business, receiver_email
2018-06-25 12:34:43 +10:00
Adrian Greeve 9c42327dd1 MDL-62755 mod_assign: Update to unit tests.
Overrides are now checked to be exported properly.
2018-06-25 09:50:50 +08:00
Adrian Greeve 28631a1455 MDL-62755 mod_assign: Fix override export problem.
The cutoffdate and allowsubmissionsfromdate were not
being exported.
2018-06-25 09:50:50 +08:00
Christian Glahn aa7deee9cc MDL-61732 filters: Have emoticons respect nolink
Also extracts static emoticon cache to a field in order to increases
testability of filter_emoticon, because we are now able to reset the
static emoticon cache before tests, particularly for observing the
response to changing $CFG->emoticons.
2018-06-22 10:29:53 +02:00
Jake Dallimore 009a9ca500 MDL-51419 core_files: Support for utf8 in file names in MS Edge 2018-06-22 10:29:56 +08:00
Jake Dallimore 7de4759799 MDL-62730 themes: Fix calendar icon alignment for datetime in Boost 2018-06-22 10:17:04 +08:00
Jake Dallimore 33433bb8a1 MDL-62735 core_search: searchareas page now respects indexing support 2018-06-22 10:14:34 +08:00
Luca Bösch 9efe4b84ce MDL-45088 xmldb: Fix table alignment 2018-06-21 19:37:26 +02:00
Eloy Lafuente (stronk7) 8793e120b9 weekly release 3.5+ 2018-06-21 14:01:11 +02:00
Eloy Lafuente (stronk7) 43f742e1e5 Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2018-06-21 14:01:09 +02:00
Andrew Nicols bea7b33840 MDL-62715 mod_assign: Bump submissions back when grading in tests 2018-06-21 08:41:09 +08:00
Jun Pataleta 4900f375d7 Merge branch 'MDL-62017-35' of https://github.com/lucaboesch/moodle into MOODLE_35_STABLE 2018-06-20 19:40:22 +08:00
David Monllao a94b1fed30 Merge branch 'MDL-61482-35' of git://github.com/zig-moodle/moodle into MOODLE_35_STABLE 2018-06-20 13:29:28 +02:00
David Monllao 9337d85eb3 MDL-62471 tool_dataprivacy: Test compatible with 32 bits OS 2018-06-20 12:59:15 +02:00
cescobedo 2a8f28520d MDL-61296 auth_ldap: remove PHP notices when field does not exist. 2018-06-20 11:43:28 +02:00
Luca Bösch e972b9e909 MDL-62017 tool_customlang: format language customisation buttons. 2018-06-20 10:54:13 +02:00
Andrew Nicols 74c35a9133 Merge branch 'wip-MDL-62391-35' of git://github.com/abgreeve/moodle into MOODLE_35_STABLE 2018-06-20 09:40:16 +08:00
Adrian Greeve b4b5124c73 MDL-62391 tool_dataprivacy: Added more detail about data requests.
This adds the detail about who requested the data request to the
tables and data request information page.
2018-06-20 09:01:16 +08:00
Andrew Nicols af963ad2ae Merge branch 'MOODLE_35_STABLE_MDL-62705_results_order_no_context' of https://github.com/mattporritt/moodle into MOODLE_35_STABLE 2018-06-20 08:36:51 +08:00
Eloy Lafuente (stronk7) 2341e8695e Merge branch 'MDL-62014-35' of git://github.com/nashtechdev01/moodle into MOODLE_35_STABLE 2018-06-20 00:57:28 +02:00
Eloy Lafuente (stronk7) f55516fd46 Merge branch 'MDL-36424-35' of https://github.com/lucaboesch/moodle into MOODLE_35_STABLE 2018-06-19 19:20:37 +02:00
Eloy Lafuente (stronk7) 36e337b176 Merge branch 'wip-MDL-61786-35' of git://github.com/abgreeve/moodle into MOODLE_35_STABLE 2018-06-19 19:16:39 +02:00
Eloy Lafuente (stronk7) 23e9fea9a5 Merge branch 'MDL-62358-35' of https://github.com/HuongNV13/moodle into MOODLE_35_STABLE 2018-06-19 19:08:52 +02:00
Eloy Lafuente (stronk7) 1408aa8bc8 Merge branch 'MDL-44791-35' of https://github.com/lucaboesch/moodle into MOODLE_35_STABLE 2018-06-19 18:59:50 +02:00
Eloy Lafuente (stronk7) 2f86cad04e Merge branch 'MDL-62449-35' of git://github.com/sarjona/moodle into MOODLE_35_STABLE 2018-06-19 18:52:31 +02:00
Zig Tan fd77e38fc9 MDL-61482 mod_assign: Clean html in offline grading CSV 2018-06-19 23:30:11 +08:00
Tim Hunt 0191113d24 MDL-62747 accesslib: rdef caching should handle roles with no caps 2018-06-19 16:26:21 +01:00
Bas Brands 8bc0bbf1ee MDL-62691 Theme boost: editpdf hide dropdown close btn 2018-06-19 16:57:38 +02:00
Bas Brands 1dc4c34d07 MDL-62703 Themes: remove hover styles in course listings 2018-06-19 16:32:31 +02:00
Bas Brands 5339d26259 MDL-59047 question type: short answer boost styles 2018-06-19 14:34:33 +02:00
David Monllao bc7a379947 Merge branch 'MDL-62588-35' of git://github.com/rezaies/moodle into MOODLE_35_STABLE 2018-06-19 12:53:19 +02:00
Jun Pataleta e9553d973a Merge branch 'MDL-61894-35' of https://github.com/lucaboesch/moodle into MOODLE_35_STABLE 2018-06-19 17:57:13 +08:00
David Monllao 4f7fa18201 Merge branch 'MDL-58702-35' of git://github.com/zig-moodle/moodle into MOODLE_35_STABLE 2018-06-19 11:54:39 +02:00
Andrew Nicols 697a28aadf Merge branch 'MDL-60950-35' of git://github.com/damyon/moodle into MOODLE_35_STABLE 2018-06-19 11:40:45 +08:00
Damyon Wiese 26f5750319 MDL-60950 assign: Update incompatible unit test
Maintain compatibility with unit test changes after MDL-55609.
2018-06-19 10:59:12 +08:00
Damyon Wiese d4fea9b20f MDL-60950 assign: Rename class
testable_assign was renamed to mod_assign_testable_assign
2018-06-19 10:59:12 +08:00
Damyon Wiese af280e5929 MDL-60950 assign: Robust filtering of grading table
For assignments with gradetype set to none, we need special handling when generating the query to filter submissions that need grading.
2018-06-19 10:59:12 +08:00
Damyon Wiese fa8d508f36 MDL-60950 assign: Test for the requires grading filter
This test covers when the requires grading filter is applied, but the
assignment has no grade and the feedbackcomments plugin is disabled.
2018-06-19 10:59:12 +08:00
Andrew Nicols e7bf43ba0d Merge branch 'MDL-62519_35' of git://github.com/markn86/moodle into MOODLE_35_STABLE 2018-06-19 10:34:16 +08:00
Andrew Nicols 793da897d1 Merge branch 'MDL-62597-35' of https://github.com/HuongNV13/moodle into MOODLE_35_STABLE 2018-06-19 09:43:54 +08:00
Eloy Lafuente (stronk7) e1aa99e99d Merge branch 'MDL-62638-35' of git://github.com/bmbrands/moodle into MOODLE_35_STABLE 2018-06-19 02:04:00 +02:00
Eloy Lafuente (stronk7) 06b8272791 Merge branch 'MDL-62571_35' of git://github.com/markn86/moodle into MOODLE_35_STABLE 2018-06-19 01:49:52 +02:00
Sara Arjona 15703fb9f1 MDL-62449 scorm: Move related data to data in the privacy export 2018-06-18 10:43:36 +02:00
Sara Arjona 20d8c489d8 MDL-62449 scorm: Add SCORM data to privacy export (name, description) 2018-06-18 10:43:36 +02:00
Jun Pataleta 1d97a7818e Merge branch 'MDL-62614-35' of git://github.com/zig-moodle/moodle into MOODLE_35_STABLE 2018-06-18 16:12:23 +08:00
Jun Pataleta 7c64406d4d Merge branch 'MDL-62709_35' of git://github.com/markn86/moodle into MOODLE_35_STABLE 2018-06-18 15:15:56 +08:00
Jun Pataleta f71b39b960 Merge branch 'MDL-62658_35' of git://github.com/markn86/moodle into MOODLE_35_STABLE 2018-06-18 14:36:47 +08:00
Jun Pataleta 44dd58756b Merge branch 'wip-MDL-62474-35' of git://github.com/abgreeve/moodle into MOODLE_35_STABLE 2018-06-18 14:32:20 +08:00
Mark Nelson 1dbbcaf9ba MDL-62658 message_popup: fixed issue with malformed URL being generated 2018-06-18 14:31:14 +08:00
Adrian Greeve d0e1b2921d MDL-62474 theme_boost: Update to provider and unit test.
Theme boost contains a user preference which was not reported
before. Unit tests have been added to test the update.
2018-06-18 14:06:11 +08:00
Willian Mano 72107631d7 MDL-62474 theme_boost: Fix Boost privacy provider 2018-06-18 14:06:01 +08:00
Huong Nguyen 888cfdc75f MDL-62614 Privacy: Core tag provider delete_item_tags function not work 2018-06-18 14:00:37 +08:00
Andrew Nicols 9886790075 Merge branch 'MDL-62320-35' of git://github.com/vmdef/moodle into MOODLE_35_STABLE 2018-06-18 13:58:50 +08:00
Shamim Rezaie f3323c22fe MDL-62588 enrol_paypal: Add missing instanceid field 2018-06-18 15:18:04 +10:00
Andrew Nicols d8e011f721 Merge branch 'MDL-62234-35' of git://github.com/bmbrands/moodle into MOODLE_35_STABLE 2018-06-18 09:52:33 +08:00
Andrew Nicols 3b63cb559e Merge branch 'MDL-57968-35' of git://github.com/cescobedo/moodle into MOODLE_35_STABLE 2018-06-18 09:09:53 +08:00
AMOS bot f5189e3aa3 Automatically generated installer lang files 2018-06-16 00:08:05 +00:00
Victor Deniz 2c643bb988 MDL-62320 mimetypes: Add JSON to the default mime types list 2018-06-15 13:12:31 +01:00
HungTran c5fd53af5d MDL-62014 course: Add title to activity completion button 2018-06-15 10:19:59 +07:00
Zig Tan e275b0c4a6 MDL-58702 backup: Fix restoration of role mappings labels
Changes:
- Add logic to retrieve all the existing current roles.
- Apply the backup's role shortname by matching on the archetype.
- Added backup lang string 'undefinedrolemapping'.
2018-06-15 09:56:15 +08:00
Matt Porritt 9482a77a04 MDL-62705 Global Search: results order selection does not display
As of 3.5 a search plugin can define the result orders it supports.
When a search plugin defines multiple sort orders and the search is
conducted without a context (no context query param in the URL),
the code to load the available sort orders from the enabled search
backend is never called.

This patch gets the supported search orders for the enabled search
engine regardless of context being provided or not. As prior, the
search order selection will not display if the only result order
option is relevance.
2018-06-15 11:32:28 +10:00
Luca Bösch fab105578b MDL-61894 tag: Modal buttons style. 2018-06-14 17:32:40 +02:00
Luca Bösch b4dcfe9a06 MDL-44791 admin: Open server check doc links in new window. 2018-06-14 17:31:45 +02:00
Ed Basset a31a20a5c6 MDL-36424 wiki: set group so subwiki tabs/pages display correctly 2018-06-14 17:31:28 +02:00
Huong Nguyen e6e7a85087 MDL-62358 Question: Improve number function
Support all 'question numbers' that might be needed
2018-06-14 19:46:36 +07:00
Huong Nguyen 612c718696 MDL-62597 Privacy: Request date column should include time 2018-06-14 19:41:28 +07:00
Eloy Lafuente (stronk7) 180988c999 weekly release 3.5+ 2018-06-14 10:56:06 +02:00
Eloy Lafuente (stronk7) 59380a9a64 Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2018-06-14 10:56:04 +02:00
Mark Nelson d2593fadb6 MDL-62709 core_message: convert NULL format types in task 2018-06-14 13:19:17 +08:00
David M Penner 904aec69a0 MDL-62709 core_message: fix for silently ignored errors in task 2018-06-14 13:19:17 +08:00
Mark Nelson d851bb5c2a MDL-31443 core_backup: change review heading to 'Restore settings' 2018-06-13 18:39:00 +02:00
Luca Bösch 6fa61e69a7 MDL-31443 core_backup: revert changes to util/ui/renderer.php 2018-06-13 18:39:00 +02:00
Bas Brands c0e65fbbf8 MDL-62234 Theme boost: Improve atto dialogue styling 2018-06-13 11:37:09 +02:00
Jun Pataleta cc0b367755 Merge branch 'MDL59980_block_bug_35_STABLE' of git://github.com/kristian-94/moodle into MOODLE_35_STABLE 2018-06-13 09:59:26 +08:00
AMOS bot 1dc3e88cca Automatically generated installer lang files 2018-06-13 00:08:07 +00:00
Kristian Ringer d04e88b78f MDL-59980 core_admin: Fix bug preventing block modification in settings 2018-06-13 09:37:42 +10:00
Simey Lameze d77209e8c5 MDL-62446 mod_glossary: optimize provider code 2018-06-12 18:27:39 +02:00
Simey Lameze 3f27598353 MDL-62446 mod_glossary: improve privacy unit tests 2018-06-12 18:27:38 +02:00
David Monllao 334ced71a1 Merge branch 'MDL-58063-35' of git://github.com/mihailges/moodle into MOODLE_35_STABLE 2018-06-12 11:05:47 +02:00
Andrew Nicols f3a817e959 MDL-55609 mod_assign: Deprecate base_test 2018-06-12 10:22:18 +02:00
Andrew Nicols 9712975595 MDL-55609 assignfeedback_comments: Remove shared setUp 2018-06-12 10:22:11 +02:00
Andrew Nicols 4656db12e1 MDL-55609 mod_assign: Move fixture to folder 2018-06-12 10:22:06 +02:00
Andrew Nicols a1ec9e389e MDL-55609 mod_assign: Remove shared setUp for all tests 2018-06-12 10:22:01 +02:00
Andrew Nicols 3714b3d104 MDL-55609 testing: Add a create_and_enrol helper 2018-06-12 10:21:57 +02:00
Mark Nelson 251358b364 MDL-62519 atto_recordrtc: detach click listener when recording again 2018-06-12 14:37:40 +08:00
Jun Pataleta d2b6ce1555 Merge branch 'wip-MDL-62574-35' of git://github.com/abgreeve/moodle into MOODLE_35_STABLE 2018-06-12 11:56:53 +08:00
Adrian Greeve 3c22418e0a MDL-62574 core_privacy: Unit tests to check the context base.
This now tests the possibility of a context being deleted.
2018-06-12 11:26:23 +08:00
Adrian Greeve 94269e4505 MDL-62574 core_privacy: Check the contextlist for real contexts.
There are situations when contexts will be deleted in the provider.
The context list is not updated and so we have introduced a try
catch.
2018-06-12 11:26:11 +08:00
Andrew Nicols 0f272f06ea Merge branch 'wip_MDL-62576_M35' of git://github.com/gjb2048/moodle into MOODLE_35_STABLE 2018-06-12 10:49:14 +08:00
Andrew Nicols 08ca997c76 Merge branch 'MDL-62628-35' of git://github.com/mihailges/moodle into MOODLE_35_STABLE 2018-06-12 09:49:58 +08:00
Andrew Nicols 7d52baba45 Merge branch 'MDL-60819-35' of https://github.com/snake/moodle into MOODLE_35_STABLE 2018-06-12 09:21:04 +08:00
Eloy Lafuente (stronk7) 72f73d595d Merge branch 'MDL-62649-35' of git://github.com/mihailges/moodle into MOODLE_35_STABLE 2018-06-12 01:45:26 +02:00
Eloy Lafuente (stronk7) bf67536031 Merge branch 'MDL-62460-35' of git://github.com/junpataleta/moodle into MOODLE_35_STABLE 2018-06-12 01:41:49 +02:00
Eloy Lafuente (stronk7) 7a78ae6c10 Merge branch 'MDL-31443-35' of https://github.com/lucaboesch/moodle into MOODLE_35_STABLE 2018-06-12 01:37:57 +02:00
Michael Aherne 2cdbfd1dca MDL-62577 unit tests: Quote class name in re-run command. 2018-06-12 00:08:37 +02:00
Mark Nelson d8924ce166 MDL-62571 mod_forum: fixed redirection URLs 2018-06-11 18:31:03 +08:00
David Monllao 9ccbf4386c Merge branch 'MDL-61484-35' of git://github.com/junpataleta/moodle into MOODLE_35_STABLE 2018-06-11 10:34:30 +02:00
Jun Pataleta fe9d8a7274 MDL-61484 mod_chat: Add CHAT_SESSION_GAP constant 2018-06-11 16:01:03 +08:00
Jun Pataleta 9ec5fa2208 MDL-61484 mod_chat: Fix chat_get_sessions logic 2018-06-11 16:01:03 +08:00
Jun Pataleta bffc50ae9d MDL-61484 mod_chat: More unit tests for chat
* For chat_get_sessions()
* Fix externallib test
2018-06-11 16:01:03 +08:00
Leon Stringer a634c88ced MDL-61786 auth: Changed strings for Test Settings to lang strings
The output strings for the Test Settings function were hard-coded English
strings. They've been changed to standard language strings which can be
translated and maintained via AMOS.
2018-06-11 14:16:31 +08:00
Jun Pataleta 2eb7c6d6cb Merge branch 'MDL-62594-35' of git://github.com/bmbrands/moodle into MOODLE_35_STABLE 2018-06-11 13:55:19 +08:00
Jake Dallimore 8ae9044ef4 MDL-60819 core_files: fix exception thrown from generate_image_thumbnail
This function now requires gdlib.php.
2018-06-11 10:45:27 +08:00
Andrew Nicols eef776a0b4 Merge branch 'MDL-62270-Cron-task-fails-on-data-privacy-related-task' of https://github.com/adamtppaw/moodle into MOODLE_35_STABLE 2018-06-11 09:53:06 +08:00
Luca Bösch ef57748905 MDL-31443 backup: Title and cancel messages stating import and restore 2018-06-08 11:34:06 +02:00
Bas Brands 9903e25871 MDL-62654 Theme Boost: update readme on @supports prefixer issues 2018-06-08 10:15:10 +02:00
Mihail Geshoski 557a9b5674 MDL-62628 resource: Folder name formatting issue when displayed inline 2018-06-08 08:36:04 +08:00
Mihail Geshoski 0ff993ffe3 MDL-58063 blocks: Cannot access manage files in HTML block in clean 2018-06-08 08:29:26 +08:00
Gareth J Barnard a8a9876c1d MDL-62576 theme_boost: Course jump to menu not centred 2018-06-07 20:43:42 +01:00
Eloy Lafuente (stronk7) fd308528cd weekly release 3.5+ 2018-06-07 17:00:05 +02:00
Eloy Lafuente (stronk7) b0f0c2af7e Merge branch 'install_35_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_35_STABLE 2018-06-07 17:00:02 +02:00
Bas Brands ba5dde0347 MDL-62638 Theme boost: remove broken css 2018-06-07 16:44:53 +02:00
Bas Brands 22cab09558 MDL-62594 question type: cloze question theme boost 2018-06-07 14:07:32 +02:00
Jun Pataleta b02f319b78 Merge branch 'MDL-61826-35' of git://github.com/junpataleta/moodle into MOODLE_35_STABLE 2018-06-06 11:22:31 +02:00
Jun Pataleta e03ef397fc Merge branch 'MDL-46513_35' of git://github.com/markn86/moodle into MOODLE_35_STABLE 2018-06-06 11:22:27 +02:00
Jun Pataleta cddd2b461b Merge branch 'MDL-62586-35' of git://github.com/bmbrands/moodle into MOODLE_35_STABLE 2018-06-06 11:22:24 +02:00
Andrew Nicols ec41f1ce1b Merge branch 'm35_MDL-61702_MariaDB_InnoDB10d3d1_Params_Removed' of https://github.com/scara/moodle into MOODLE_35_STABLE 2018-06-06 11:22:21 +02:00
Eloy Lafuente (stronk7) 7ce29a90af Merge branch 'MDL-39486-35' of git://github.com/mihailges/moodle into MOODLE_35_STABLE 2018-06-06 11:22:17 +02:00
David Monllao 4dda9579b3 Merge branch 'MDL-46344-35' of https://github.com/lucaboesch/moodle into MOODLE_35_STABLE 2018-06-06 11:22:14 +02:00
Mark Nelson 9b77f93fa0 MDL-62336 media_videojs: ensure videos have unique IDs 2018-06-06 11:22:11 +02:00
Jun Pataleta 4fc3adb35b Merge branch 'MDL-61778-35' of git://github.com/mickhawkins/moodle into MOODLE_35_STABLE 2018-06-06 11:22:08 +02:00
cescobedo d3e3f0f109 MDL-57968 core_message: Remove multiple unnecessary AJAX requests
Remove AJAX requests core_message_get_unread_conversations_count and
message_popup_get_unread_popup_notification_count and
render the unread messages and notifications into mustache template.
2018-06-06 11:00:54 +02:00
Mark Nelson 8bc2ee1a4a MDL-46513 mod_chat: pass has_capability result to fullname function 2018-06-06 15:34:17 +08:00
Jun Pataleta 93e51e74b4 MDL-61826 auth: Make Facebook endpoints more maintainable 2018-06-06 12:44:43 +08:00
Łukasz Szeremeta 148ef43015 MDL-61826 auth: Facebook OAuth2 - getting a better-quality profile photo
Get 200x200 px instead of 50x50 px user profile picture from Facebook OAuth2.
2018-06-06 12:44:43 +08:00
Mihail Geshoski 8b5dfd0b06 MDL-62649 course: Course and category management items displayed wrongly 2018-06-06 11:38:48 +08:00
Michael Hawkins 1ebd063f4e MDL-61778 message: Renamed online status icon and replaced Boost icon 2018-06-05 16:36:42 +08:00
Luca Bösch 40579dc143 MDL-62605 core_user: course name filter in unenrol modal. 2018-06-05 08:04:41 +02:00
AMOS bot f26629e665 Automatically generated installer lang files 2018-06-05 00:07:04 +00:00
adamtppaw ab96a452c6 MDL-62270 Privacy: Cron task fails when admin directory renamed 2018-06-03 21:59:15 +02:00
Luca Bösch 50e1bbb583 MDL-46344 outputrenderers: Localize 'This page is'. 2018-06-01 11:44:02 +02:00
Jun Pataleta 09b6fbdaca MDL-62460 tool_dataprivacy: Render selected user properly on validation 2018-06-01 16:09:17 +08:00
Matteo Scaramuccia 8f9c8a5826 MDL-61702 dml: MariaDB 10.3 supports only the Barracuda file format.
More details about this change also in https://tracker.moodle.org/browse/MDL-59099

Ref.: https://mariadb.com/kb/en/library/mariadb-1031-release-notes/#other-variables
2018-05-31 22:56:49 +02:00
David Monllao 9c607c0b8c weekly release 3.5+ 2018-05-31 08:39:38 +02:00
Bas Brands 6dc178e448 MDL-62586 Themes: use Boostrap media compontent for author display 2018-05-30 12:41:36 +02:00
David Monllao 187d2a26c0 Merge branch 'MDL-62532-35' of git://github.com/rezaies/moodle into MOODLE_35_STABLE 2018-05-30 10:20:09 +02:00
Andrew Nicols c6755d6f34 Merge branch 'MDL-57486-35-fix' of git://github.com/junpataleta/moodle into MOODLE_35_STABLE 2018-05-30 13:21:25 +08:00
Jun Pataleta bea2c40f53 MDL-57486 tool_recyclebin: Close the files recordset properly 2018-05-30 13:16:11 +08:00
Andrew Nicols 363622d03e Merge branch 'MDL-57600-35' of git://github.com/lameze/moodle into MOODLE_35_STABLE 2018-05-30 09:17:57 +08:00
Andrew Nicols 649c96c259 MDL-57486 tool_recyclebin: Coding style fix 2018-05-30 09:02:00 +08:00
Dan Marsden ac32f752d9 MDL-57486 tool_recyclebin: Delete items when context already deleted. 2018-05-30 09:02:00 +08:00
David Monllao db79038541 MDL-62381 editor_tinymce: Rearrange style modification 2018-05-29 16:36:14 +02:00
David Monllao c390ae4f8d Merge branch 'MDL-62381_moodle35' of https://github.com/Syxton/moodle into MOODLE_35_STABLE 2018-05-29 16:34:46 +02:00
Matt Davidson e0a8cfdb6d MDL-62381 editor: Fix tinymce icons
Icon sizing is being overwritten on images.
2018-05-29 09:03:00 -04:00
Jun Pataleta 52f03c6fc6 Merge branch 'MDL-62544-35' of git://github.com/Dagefoerde/moodle into MOODLE_35_STABLE 2018-05-29 16:24:26 +08:00
Michael Hawkins 9f2c4a9277 MDL-56498 message: Ensure clicked notifications are marked read 2018-05-29 09:37:53 +08:00
David Monllao 575e209b93 Merge branch 'MDL-62316_oauth_guess_image_fix_35_STABLE' of https://github.com/leonstr/moodle into MOODLE_35_STABLE 2018-05-28 16:48:50 +02:00
David Monllao 0b363e6c4b Merge branch 'wip-MDL-62584-35' of git://github.com/marinaglancy/moodle into MOODLE_35_STABLE 2018-05-28 16:32:17 +02:00
Eloy Lafuente (stronk7) 5d8821ae05 MDL-62478 upgrade: add 3.5.0 separation line to all upgrade scripts 2018-05-28 13:16:21 +02:00
David Monllao 512a916207 Merge branch 'MDL-42779-35' of https://github.com/lucaboesch/moodle into MOODLE_35_STABLE 2018-05-28 11:42:53 +02:00
Mark Nelson 86a7cf0909 MDL-57742 core_lib: Updated readme_moodle.txt 2018-05-28 09:17:18 +02:00
David Balch f93f81a9f6 MDL-57742 core_lib: Make tool_consumer_instance_guid optional
In the specification, tool_consumer_instance_guid is only listed
as recommended. However, the library was requiring it.

https://www.imsglobal.org/specs/ltiv2p0/implementation-guide#toc-42
2018-05-28 09:17:17 +02:00
Matteo Scaramuccia bf6f0f03cf MDL-59596 ddl: Support MariaDB 10.2.4+ SQL_MODE new default value. 2018-05-28 08:20:14 +02:00
Matteo Scaramuccia 903182e4a3 MDL-59596 ddl: Added has_breaking_change_sqlmode() 2018-05-28 08:20:12 +02:00
Jun Pataleta e7554e4b67 Merge branch 'wip-MDL-62493-35' of git://github.com/marinaglancy/moodle into MOODLE_35_STABLE 2018-05-28 12:57:37 +08:00
Jun Pataleta 167420bd2e MDL-62515 templates: Remove unnecessary button role for submit button 2018-05-28 12:29:59 +08:00
Marina Glancy f479887c77 MDL-62584 xmlrpc: return correct content-length 2018-05-28 11:20:21 +08:00
Simey Lameze 5441c8fef4 MDL-57600 course: fix typo on courselib test 2018-05-28 09:41:58 +08:00
Alex b104cede75 MDL-57600 format: Fix spelling errors 2018-05-28 09:41:57 +08:00
Jun Pataleta c3c8223ca2 Merge branch 'MDL-61832-35' of https://github.com/lucaboesch/moodle into MOODLE_35_STABLE 2018-05-28 09:21:22 +08:00
Andrew Nicols d4773c32d6 MDL-62543 mod_forum: Add RSS Unit tests 2018-05-28 08:56:44 +08:00
Tobias Reischmann c8511e2808 MDL-62543 forum: Switched the usage of the deleted flag for rss
The deleted flag was introduced through the Privacy API and was used
for post selection for the rss feed. In there it was used in the
wrong way, which lead to the exclusion of all non deleted posts instead
of excluding all deleted ones.
This commit fixes this problem.
2018-05-28 08:56:44 +08:00
Shamim Rezaie b23e6519fe MDL-62532 core_question: Include standard tags in the tags autocomplete 2018-05-28 10:55:05 +10:00
Mark Johnson dc7c48a9d7 MDL-62408 behat: Correct PHPUNIT_TEST checks in behat_config_util 2018-05-28 02:14:08 +02:00
Marina Glancy a210233bfb MDL-62493 tool_policy: correct redirection
do not override session->wantsurl
do not redirect guests to user.php, they get an error there
2018-05-25 14:32:03 +08:00
Marina Glancy 9dcf28af7d MDL-62493 tool_policy: allow guests to sign up 2018-05-25 14:32:03 +08:00
Mihail Geshoski c030a94b68 MDL-39486 lesson: Length of label text too narrow in matching questions 2018-05-25 09:46:01 +08:00
Jan Dageförde bd8b3a91e0 MDL-62544 privacy: error notification type 2018-05-24 16:46:02 +02:00
Jan Dageförde 582eb6e6e2 MDL-62544 privacy: Allow data requests on behalf of a user by admin/DPO 2018-05-24 16:46:02 +02:00
Luca Bösch 9b127bcb70 MDL-61832 lesson: don't hide lesson page editing options 2018-05-24 15:44:00 +02:00
Luca Bösch 171a764049 MDL-42779 course: Pagination in management page preserves view action 2018-05-24 15:43:41 +02:00
David Monllao 0da8ef69a8 weekly release 3.5+ 2018-05-24 10:46:02 +02:00
David Monllao 815be4e003 NOBUG: Fixed SVG browser compatibility 2018-05-24 10:46:01 +02:00
Simey Lameze d897846115 MDL-61189 editor_tinymce: save data before submitting 2018-05-23 10:48:40 +08:00
Jake Dallimore 5427855d50 Merge branch 'MDL-61932-35' of git://github.com/junpataleta/moodle into MOODLE_35_STABLE 2018-05-23 10:01:46 +08:00
Leon Stringer 6d7093e370 MDL-62316 OAuth 2: Only guess image if base URL set.
If you added an OAuth 2 service with no base URL guess_image() would try to
parse this empty value looking for a favicon.ico.  We now check if the base
URL is empty beforehand and skip this if so.
2018-05-22 19:45:04 +01:00
Jun Pataleta d7c805a9f0 MDL-61932 mod_glossary: Fetch the correct front page section 1 ID 2018-05-22 17:16:30 +08:00
Jun Pataleta 2032603ae9 MDL-61932 mod_glossary: Display site-level glossaries on section 1
* Glossary activities created on the front page by importing
entries are being added to section 0, but the front page only shows
activities on section 1.
2018-05-22 17:16:29 +08:00
John Beedell e7e91b8bae MDL-62239 QTYPE: iOS 11.3 broke Moodle drag-drop question types 2018-05-22 10:05:51 +08:00
Andrew Nicols 30cbfbdd61 Merge branch 'MDL-62482_35' of git://github.com/timhunt/moodle into MOODLE_35_STABLE 2018-05-22 09:39:17 +08:00
Andrew Nicols ab7694d82e Merge branch 'MDL-62488_35_STABLE' of https://github.com/marxjohnson/moodle into MOODLE_35_STABLE 2018-05-22 09:31:19 +08:00
Jake Dallimore 61ee9a304e Merge branch 'MDL-61015-35' of https://github.com/lucaboesch/moodle into MOODLE_35_STABLE 2018-05-22 08:15:11 +08:00
Andrew Nicols f59bc1b152 MDL-62500 tag: Update checkbox label when updating tag 2018-05-21 13:46:49 +02:00
Matteo Scaramuccia 3aa57f20af MDL-61893 JavaScript: Bumped npm packages
Performed by removing the current 'npm-shrinkwrap.json' file:
$ rm -Rf node_modules
$ rm -f npm-shrinkwrap.json
$ npm install
$ npm shrinkwrap
2018-05-21 12:05:54 +02:00
Matteo Scaramuccia b875ad18b5 MDL-61893 JavaScript: Accept any node version but Carbon (LTS) 2018-05-21 12:05:46 +02:00
Luca Bösch 2d69b0fbe7 MDL-61015 course: consistently display hidden sections
This commit moves the availability info on top.
2018-05-21 11:34:04 +02:00
Marina Glancy c8663da1a9 MDL-61015 course: consistently display hidden sections
When section is hidden but the course setting says to show hidden sections display the hidden
sections consistently with how we display the sections with access restriction.

The only difference is that we didn't previously display the summary of the hidden section
in this case, the new logic repeats this behavior
2018-05-21 11:34:04 +02:00
Andrew Nicols 1de2735b16 MDL-62516 mod_forum: Only delete files for requested user 2018-05-21 11:17:10 +02:00
Juan Leyva 4a5adabe95 MDL-62229 tool_mobile: Add missing supported module
The Workshop module it’s been supported since Moodle 3.4 so it
should’ve been included here.
2018-05-21 17:05:07 +08:00
Andrew Nicols b566d8002a Merge branch 'wip-MDL-62481-35' of git://github.com/marinaglancy/moodle into MOODLE_35_STABLE 2018-05-21 15:55:12 +08:00
Tim Hunt 2e404995a0 MDL-62440 participants: out-of-memory is many site-wide role assigns 2018-05-21 14:55:42 +08:00
Andrew Nicols 654ad71526 Merge branch 'MDL-62386-master' of git://github.com/bmbrands/moodle into MOODLE_35_STABLE 2018-05-21 09:11:28 +08:00
Marina Glancy 2b50975563 MDL-62481 atto_recordrtc: add svg icons 2018-05-18 10:40:33 +08:00
Mark Johnson 81fb99ed66 MDL-62488 filebrowser: Stop unit tests assuming number of categories 2018-05-17 16:01:36 +01:00
Tim Hunt deb358f16c MDL-62482 file_storage: may have many files with same content hash 2018-05-17 11:25:52 +01:00
Bas Brands 881098f39c MDL-62386 theme: Fix audio playback display for HTML audio
Fixed for boost
Fixed for bootstrapbase
2018-05-11 14:51:04 +02:00
465 changed files with 28176 additions and 6426 deletions
+1
View File
@@ -2,6 +2,7 @@
theme/bootstrapbase/style/
theme/clean/style/custom.css
theme/more/style/custom.css
theme/boost/style/moodle.css
node_modules/
vendor/
admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js
+1 -1
View File
@@ -56,7 +56,7 @@ matrix:
include:
# Run grunt/npm install on highest version ('node' is an alias for the latest node.js version.)
- php: 7.2
env: DB=none TASK=GRUNT NVM_VERSION='8.9'
env: DB=none TASK=GRUNT NVM_VERSION='lts/carbon'
exclude:
# MySQL - it's just too slow.
+14 -2
View File
@@ -137,6 +137,16 @@ module.exports = function(grunt) {
}
}
},
sass: {
dist: {
files: {
"theme/boost/style/moodle.css": "theme/boost/scss/preset/default.scss"
}
},
options: {
includePaths: ["theme/boost/scss/"]
}
},
watch: {
options: {
nospawn: true // We need not to spawn so config can be changed dynamically.
@@ -214,7 +224,8 @@ module.exports = function(grunt) {
'# Generated by "grunt ignorefiles"',
'theme/bootstrapbase/style/',
'theme/clean/style/custom.css',
'theme/more/style/custom.css'
'theme/more/style/custom.css',
'theme/boost/style/moodle.css'
].concat(thirdPartyPaths);
grunt.file.write('.stylelintignore', stylelintIgnores.join('\n'));
};
@@ -362,6 +373,7 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-sass');
grunt.loadNpmTasks('grunt-eslint');
grunt.loadNpmTasks('grunt-stylelint');
@@ -374,7 +386,7 @@ module.exports = function(grunt) {
grunt.registerTask('js', ['amd', 'yui']);
// Register CSS taks.
grunt.registerTask('css', ['stylelint:scss', 'stylelint:less', 'less:bootstrapbase', 'stylelint:css']);
grunt.registerTask('css', ['stylelint:scss', 'sass', 'stylelint:less', 'less:bootstrapbase', 'stylelint:css']);
// Register the startup task.
grunt.registerTask('startup', 'Run the correct tasks for the current directory', tasks.startup);
+1 -1
View File
@@ -1990,7 +1990,7 @@ class core_admin_renderer extends plugin_renderer_base {
if (empty($CFG->docroot) or $environment_result->plugin) {
$report = get_string($stringtouse, 'admin', $rec);
} else {
$report = $this->doc_link(join($linkparts, '/'), get_string($stringtouse, 'admin', $rec));
$report = $this->doc_link(join($linkparts, '/'), get_string($stringtouse, 'admin', $rec), true);
}
// Enclose report text in div so feedback text will be displayed underneath it.
$report = html_writer::div($report);
+111 -84
View File
@@ -28,113 +28,139 @@ admin_externalpage_setup('searchareas');
$areaid = optional_param('areaid', null, PARAM_ALPHAEXT);
$action = optional_param('action', null, PARAM_ALPHA);
$indexingenabled = \core_search\manager::is_indexing_enabled(); // This restricts many of the actions on this page.
// Get a search manager instance, which we'll need for display and to handle some actions.
try {
$searchmanager = \core_search\manager::instance();
} catch (core_search\engine_exception $searchmanagererror) {
// Continue, we return an error later depending on the requested action.
// In action cases, we'll throw this exception below. In non-action cases, we produce a lang string error.
}
// Handle all the actions.
if ($action) {
// If dealing with an areaid, we need to check that the area exists.
if ($areaid) {
// We need to check that the area exists.
$area = \core_search\manager::get_search_area($areaid);
if ($area === false) {
throw new moodle_exception('invalidrequest');
}
}
if ($action !== 'enable' && $action !== 'disable') {
// All actions but enable/disable need the search engine to be ready.
if (!empty($searchmanagererror)) {
throw $searchmanagererror;
}
// All the indexing actions.
if (in_array($action, ['delete', 'indexall', 'reindexall', 'deleteall'])) {
// Show confirm prompt for all these actions as they may be inadvisable, or may cause
// an interruption in search functionality, on production systems.
if (!optional_param('confirm', 0, PARAM_INT)) {
// Display confirmation prompt.
$a = null;
if ($areaid) {
$a = html_writer::tag('strong', $area->get_visible_name());
// All of these actions require that indexing is enabled.
if ($indexingenabled) {
// For all of these actions, we strictly need a manager instance.
if (isset($searchmanagererror)) {
throw $searchmanagererror;
}
$actionparams = ['sesskey' => sesskey(), 'action' => $action, 'confirm' => 1];
if ($areaid) {
$actionparams['areaid'] = $areaid;
}
$actionurl = new moodle_url('/admin/searchareas.php', $actionparams);
$cancelurl = new moodle_url('/admin/searchareas.php');
echo $OUTPUT->header();
echo $OUTPUT->confirm(get_string('confirm_' . $action, 'search', $a),
// Show confirm prompt for all these actions as they may be inadvisable, or may cause
// an interruption in search functionality, on production systems.
if (!optional_param('confirm', 0, PARAM_INT)) {
// Display confirmation prompt.
$a = null;
if ($areaid) {
$a = html_writer::tag('strong', $area->get_visible_name());
}
$actionparams = ['sesskey' => sesskey(), 'action' => $action, 'confirm' => 1];
if ($areaid) {
$actionparams['areaid'] = $areaid;
}
$actionurl = new moodle_url('/admin/searchareas.php', $actionparams);
$cancelurl = new moodle_url('/admin/searchareas.php');
echo $OUTPUT->header();
echo $OUTPUT->confirm(get_string('confirm_' . $action, 'search', $a),
new single_button($actionurl, get_string('continue'), 'post', true),
new single_button($cancelurl, get_string('cancel'), 'get'));
echo $OUTPUT->footer();
exit;
echo $OUTPUT->footer();
exit;
} else {
// Confirmed, so run the required action.
require_sesskey();
switch ($action) {
case 'delete':
$searchmanager->delete_index($areaid);
\core\notification::success(get_string('searchindexdeleted', 'admin'));
break;
case 'indexall':
$searchmanager->index();
\core\notification::success(get_string('searchindexupdated', 'admin'));
break;
case 'reindexall':
$searchmanager->index(true);
\core\notification::success(get_string('searchreindexed', 'admin'));
break;
case 'deleteall':
$searchmanager->delete_index();
\core\notification::success(get_string('searchalldeleted', 'admin'));
break;
default:
break;
}
// Redirect back to the main page after taking action.
redirect(new moodle_url('/admin/searchareas.php'));
}
}
} else if (in_array($action, ['enable', 'disable'])) {
// Toggling search areas requires no confirmation.
require_sesskey();
switch ($action) {
case 'enable':
$area->set_enabled(true);
\core\notification::success(get_string('searchareaenabled', 'admin'));
break;
case 'disable':
$area->set_enabled(false);
core\notification::success(get_string('searchareadisabled', 'admin'));
break;
default:
break;
}
redirect(new moodle_url('/admin/searchareas.php'));
} else {
// Invalid action.
throw new moodle_exception('invalidaction');
}
}
// We are now taking an actual action, so require sesskey.
require_sesskey();
switch ($action) {
case 'enable':
$area->set_enabled(true);
\core\notification::add(get_string('searchareaenabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
break;
case 'disable':
$area->set_enabled(false);
\core\notification::add(get_string('searchareadisabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
break;
case 'delete':
$search = \core_search\manager::instance();
$search->delete_index($areaid);
\core\notification::add(get_string('searchindexdeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
break;
case 'indexall':
$searchmanager->index();
\core\notification::add(get_string('searchindexupdated', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
break;
case 'reindexall':
$searchmanager->index(true);
\core\notification::add(get_string('searchreindexed', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
break;
case 'deleteall':
$searchmanager->delete_index();
\core\notification::add(get_string('searchalldeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
break;
default:
throw new moodle_exception('invalidaction');
break;
}
// Redirect back to the main page after taking action.
redirect(new moodle_url('/admin/searchareas.php'));
// Display.
if (isset($searchmanager) && $indexingenabled) {
\core\notification::info(get_string('indexinginfo', 'admin'));
} else if (isset($searchmanager)) {
$params = (object) [
'url' => (new moodle_url("/admin/settings.php?section=manageglobalsearch#admin-searchindexwhendisabled"))->out(false)
];
\core\notification::error(get_string('indexwhendisabledfullnotice', 'search', $params));
} else {
// In non-action cases, init errors are translated and displayed to the user as error notifications.
$errorstr = get_string($searchmanagererror->errorcode, $searchmanagererror->module, $searchmanagererror->a);
\core\notification::error($errorstr);
}
echo $OUTPUT->header();
$searchareas = \core_search\manager::get_search_areas_list();
if (empty($searchmanagererror)) {
$areasconfig = $searchmanager->get_areas_config($searchareas);
} else {
$areasconfig = false;
}
if (!empty($searchmanagererror)) {
$errorstr = get_string($searchmanagererror->errorcode, $searchmanagererror->module, $searchmanagererror->a);
echo $OUTPUT->notification($errorstr, \core\output\notification::NOTIFY_ERROR);
} else {
echo $OUTPUT->notification(get_string('indexinginfo', 'admin'), \core\output\notification::NOTIFY_INFO);
}
$table = new html_table();
$table->id = 'core-search-areas';
$table->head = [
get_string('searcharea', 'search'),
get_string('enable'),
get_string('newestdocindexed', 'admin'),
get_string('searchlastrun', 'admin'),
get_string('searchindexactions', 'admin')
];
$table->head = array(get_string('searcharea', 'search'), get_string('enable'), get_string('newestdocindexed', 'admin'),
get_string('searchlastrun', 'admin'), get_string('searchindexactions', 'admin'));
$searchareas = \core_search\manager::get_search_areas_list();
$areasconfig = isset($searchmanager) ? $searchmanager->get_areas_config($searchareas) : false;
foreach ($searchareas as $area) {
$areaid = $area->get_area_id();
$columns = array(new html_table_cell($area->get_visible_name()));
@@ -144,7 +170,7 @@ foreach ($searchareas as $area) {
new pix_icon('t/hide', get_string('disable'), 'moodle', array('title' => '', 'class' => 'iconsmall')),
null, array('title' => get_string('disable')));
if ($areasconfig) {
if ($areasconfig && $indexingenabled) {
$columns[] = $areasconfig[$areaid]->lastindexrun;
if ($areasconfig[$areaid]->indexingstart) {
@@ -173,7 +199,11 @@ foreach ($searchareas as $area) {
$columns[] = html_writer::alist($actions, ['class' => 'unstyled list-unstyled']);
} else {
$blankrow = new html_table_cell(get_string('searchnotavailable', 'admin'));
if (!$areasconfig) {
$blankrow = new html_table_cell(get_string('searchnotavailable', 'admin'));
} else {
$blankrow = new html_table_cell(get_string('indexwhendisabledshortnotice', 'search'));
}
$blankrow->colspan = 3;
$columns[] = $blankrow;
}
@@ -192,10 +222,7 @@ foreach ($searchareas as $area) {
}
// Cross-search area tasks.
$options = array();
if (!empty($searchmanagererror)) {
$options['disabled'] = true;
}
$options = (isset($searchmanager) && $indexingenabled) ? [] : ['disabled' => true];
echo $OUTPUT->box_start('search-areas-actions');
echo $OUTPUT->single_button(admin_searcharea_action_url('indexall'), get_string('searchupdateindex', 'admin'), 'get', $options);
echo $OUTPUT->single_button(admin_searcharea_action_url('reindexall'), get_string('searchreindexindex', 'admin'), 'get', $options);
@@ -204,7 +231,7 @@ echo $OUTPUT->box_end();
echo html_writer::table($table);
if (empty($searchmanagererror)) {
if (isset($searchmanager)) {
// Show information about queued index requests for specific contexts.
$searchrenderer = $PAGE->get_renderer('core_search');
echo $searchrenderer->render_index_requests_info($searchmanager->get_index_requests_info());
+2 -1
View File
@@ -38,7 +38,8 @@ if (!($settingspage->check_access())) {
$statusmsg = '';
$errormsg = '';
if ($data = data_submitted() and confirm_sesskey()) {
// Form is submitted with changed settings. Do not want to execute when modifying a block.
if ($data = data_submitted() and confirm_sesskey() and isset($data->action) and $data->action == 'save-settings') {
$count = admin_write_settings($data);
// Regardless of whether any setting change was written (a positive count), check validation errors for those that didn't.
+1
View File
@@ -40,6 +40,7 @@
<div class="settingsform clearfix">
{{#params}}
<input type="hidden" name="{{name}}" value="{{value}}">
<input type="hidden" name="action" value="save-settings">
{{/params}}
<input type="hidden" name="sesskey" value="{{sesskey}}">
<input type="hidden" name="return" value="{{return}}">
+3
View File
@@ -38,5 +38,8 @@ function xmldb_tool_customlang_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+6 -4
View File
@@ -134,11 +134,13 @@ class tool_customlang_renderer extends plugin_renderer_base {
$output .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'translatorsubmitted', 'value'=>1));
$output .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'sesskey', 'value'=>sesskey()));
$output .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'p', 'value'=>$translator->currentpage));
$save1 = html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'savecontinue', 'value'=>get_string('savecontinue', 'tool_customlang')));
$save2 = html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'savecheckin', 'value'=>get_string('savecheckin', 'tool_customlang')));
$output .= html_writer::tag('fieldset', $save1.$save2, array('class'=>'buttonsbar'));
$save1 = html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'savecontinue',
'value' => get_string('savecontinue', 'tool_customlang'), 'class' => 'btn btn-secondary'));
$save2 = html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'savecheckin',
'value' => get_string('savecheckin', 'tool_customlang'), 'class' => 'btn btn-secondary'));
$output .= html_writer::tag('fieldset', $save1 . ' ' . $save2, array('class' => 'buttonsbar'));
$output .= html_writer::table($table);
$output .= html_writer::tag('fieldset', $save1.$save2, array('class'=>'buttonsbar'));
$output .= html_writer::tag('fieldset', $save1 . ' ' . $save2, array('class' => 'buttonsbar'));
$output .= html_writer::end_tag('div');
$output .= html_writer::end_tag('form');
@@ -0,0 +1 @@
define(["jquery","core/form-autocomplete","core/str","core/notification"],function(a,b,c,d){var e={REQUEST_FILTERS:"#request-filters"},f=function(){var f=[{key:"filter",component:"moodle"},{key:"nofiltersapplied",component:"moodle"}];c.get_strings(f).then(function(a){var c=a[0],d=a[1];return b.enhance(e.REQUEST_FILTERS,!1,"",c,!1,!0,d,!0)}).fail(d.exception);var g=a(e.REQUEST_FILTERS).val();a(e.REQUEST_FILTERS).on("change",function(){var b=a(this).val();g.join(",")!==b.join(",")&&(0===b.length&&a("#filters-cleared").val(1),a(this.form).submit())})};return{init:function(){f()}}});
@@ -234,6 +234,7 @@ define(['jquery', 'core/str', 'core/ajax', 'core/notification', 'core/templates'
},
fail: Notification.exception
}]);
return;
}).catch(Notification.exception);
};
@@ -34,7 +34,6 @@ define(['jquery', 'core/url', 'core/str'], function($, url, str) {
*
* @param {object} targetnode The node that we want to expand / collapse
* @param {object} thisnode The node that was clicked.
* @return {null}
*/
expandCollapse: function(targetnode, thisnode) {
if (targetnode.hasClass('hide')) {
@@ -58,7 +57,6 @@ define(['jquery', 'core/url', 'core/str'], function($, url, str) {
* Expand or collapse all nodes on this page.
*
* @param {string} nextstate The next state to change to.
* @return {null}
*/
expandCollapseAll: function(nextstate) {
var currentstate = (nextstate == 'visible') ? 'hide' : 'visible';
@@ -75,6 +73,7 @@ define(['jquery', 'core/url', 'core/str'], function($, url, str) {
str.get_string(currentstate, 'tool_dataprivacy').then(function(langString) {
$('.tool_dataprivacy-expand-all').html(langString);
return;
}).catch(Notification.exception);
$(':header i.fa').each(function() {
@@ -0,0 +1,84 @@
// 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/>.
/**
* JS module for the data requests filter.
*
* @module tool_dataprivacy/request_filter
* @package tool_dataprivacy
* @copyright 2018 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/form-autocomplete', 'core/str', 'core/notification'], function($, Autocomplete, Str, Notification) {
/**
* Selectors.
*
* @access private
* @type {{REQUEST_FILTERS: string}}
*/
var SELECTORS = {
REQUEST_FILTERS: '#request-filters'
};
/**
* Init function.
*
* @method init
* @private
*/
var init = function() {
var stringkeys = [
{
key: 'filter',
component: 'moodle'
},
{
key: 'nofiltersapplied',
component: 'moodle'
}
];
Str.get_strings(stringkeys).then(function(langstrings) {
var placeholder = langstrings[0];
var noSelectionString = langstrings[1];
return Autocomplete.enhance(SELECTORS.REQUEST_FILTERS, false, '', placeholder, false, true, noSelectionString, true);
}).fail(Notification.exception);
var last = $(SELECTORS.REQUEST_FILTERS).val();
$(SELECTORS.REQUEST_FILTERS).on('change', function() {
var current = $(this).val();
// Prevent form from submitting unnecessarily, eg. on blur when no filter is selected.
if (last.join(',') !== current.join(',')) {
// If we're submitting without filters, set the hidden input 'filters-cleared' to 1.
if (current.length === 0) {
$('#filters-cleared').val(1);
}
$(this.form).submit();
}
});
};
return /** @alias module:core/form-autocomplete */ {
/**
* Initialise the unified user filter.
*
* @method init
*/
init: function() {
init();
}
};
});
+2
View File
@@ -24,6 +24,8 @@
require_once(__DIR__ . '/../../../config.php');
require_login(null, false);
$url = new moodle_url("/admin/tool/dataprivacy/categories.php");
$title = get_string('editcategories', 'tool_dataprivacy');
+101 -8
View File
@@ -197,7 +197,7 @@ class api {
} else {
// If not a DPO, only users with the capability to make data requests for the user should be allowed.
// (e.g. users with the Parent role, etc).
if (!api::can_create_data_request_for_user($foruser)) {
if (!self::can_create_data_request_for_user($foruser)) {
$forusercontext = \context_user::instance($foruser);
throw new required_capability_exception($forusercontext,
'tool/dataprivacy:makedatarequestsforchildren', 'nopermissions', '');
@@ -232,16 +232,42 @@ class api {
* (e.g. Users with the Data Protection Officer roles)
*
* @param int $userid The User ID.
* @param int[] $statuses The status filters.
* @param int[] $types The request type filters.
* @param string $sort The order by clause.
* @param int $offset Amount of records to skip.
* @param int $limit Amount of records to fetch.
* @return data_request[]
* @throws coding_exception
* @throws dml_exception
*/
public static function get_data_requests($userid = 0) {
public static function get_data_requests($userid = 0, $statuses = [], $types = [], $sort = '', $offset = 0, $limit = 0) {
global $DB, $USER;
$results = [];
$sort = 'status ASC, timemodified ASC';
$sqlparams = [];
$sqlconditions = [];
// Set default sort.
if (empty($sort)) {
$sort = 'status ASC, timemodified ASC';
}
// Set status filters.
if (!empty($statuses)) {
list($statusinsql, $sqlparams) = $DB->get_in_or_equal($statuses, SQL_PARAMS_NAMED);
$sqlconditions[] = "status $statusinsql";
}
// Set request type filter.
if (!empty($types)) {
list($typeinsql, $typeparams) = $DB->get_in_or_equal($types, SQL_PARAMS_NAMED);
$sqlconditions[] = "type $typeinsql";
$sqlparams = array_merge($sqlparams, $typeparams);
}
if ($userid) {
// Get the data requests for the user or data requests made by the user.
$select = "(userid = :userid OR requestedby = :requestedby)";
$sqlconditions[] = "(userid = :userid OR requestedby = :requestedby)";
$params = [
'userid' => $userid,
'requestedby' => $userid
@@ -256,20 +282,87 @@ class api {
$alloweduserids = array_merge($alloweduserids, array_keys($children));
}
list($insql, $inparams) = $DB->get_in_or_equal($alloweduserids, SQL_PARAMS_NAMED);
$select .= " AND userid $insql";
$params = array_merge($params, $inparams);
$sqlconditions[] .= "userid $insql";
$select = implode(' AND ', $sqlconditions);
$params = array_merge($params, $inparams, $sqlparams);
$results = data_request::get_records_select($select, $params, $sort);
$results = data_request::get_records_select($select, $params, $sort, '*', $offset, $limit);
} else {
// If the current user is one of the site's Data Protection Officers, then fetch all data requests.
if (self::is_site_dpo($USER->id)) {
$results = data_request::get_records(null, $sort, '');
if (!empty($sqlconditions)) {
$select = implode(' AND ', $sqlconditions);
$results = data_request::get_records_select($select, $sqlparams, $sort, '*', $offset, $limit);
} else {
$results = data_request::get_records(null, $sort, '', $offset, $limit);
}
}
}
return $results;
}
/**
* Fetches the count of data request records based on the given parameters.
*
* @param int $userid The User ID.
* @param int[] $statuses The status filters.
* @param int[] $types The request type filters.
* @return int
* @throws coding_exception
* @throws dml_exception
*/
public static function get_data_requests_count($userid = 0, $statuses = [], $types = []) {
global $DB, $USER;
$count = 0;
$sqlparams = [];
$sqlconditions = [];
if (!empty($statuses)) {
list($statusinsql, $sqlparams) = $DB->get_in_or_equal($statuses, SQL_PARAMS_NAMED);
$sqlconditions[] = "status $statusinsql";
}
if (!empty($types)) {
list($typeinsql, $typeparams) = $DB->get_in_or_equal($types, SQL_PARAMS_NAMED);
$sqlconditions[] = "type $typeinsql";
$sqlparams = array_merge($sqlparams, $typeparams);
}
if ($userid) {
// Get the data requests for the user or data requests made by the user.
$sqlconditions[] = "(userid = :userid OR requestedby = :requestedby)";
$params = [
'userid' => $userid,
'requestedby' => $userid
];
// Build a list of user IDs that the user is allowed to make data requests for.
// Of course, the user should be included in this list.
$alloweduserids = [$userid];
// Get any users that the user can make data requests for.
if ($children = helper::get_children_of_user($userid)) {
// Get the list of user IDs of the children and merge to the allowed user IDs.
$alloweduserids = array_merge($alloweduserids, array_keys($children));
}
list($insql, $inparams) = $DB->get_in_or_equal($alloweduserids, SQL_PARAMS_NAMED);
$sqlconditions[] .= "userid $insql";
$select = implode(' AND ', $sqlconditions);
$params = array_merge($params, $inparams, $sqlparams);
$count = data_request::count_records_select($select, $params);
} else {
// If the current user is one of the site's Data Protection Officers, then fetch all data requests.
if (self::is_site_dpo($USER->id)) {
if (!empty($sqlconditions)) {
$select = implode(' AND ', $sqlconditions);
$count = data_request::count_records_select($select, $sqlparams);
} else {
$count = data_request::count_records();
}
}
}
return $count;
}
/**
* Checks whether there is already an existing pending/in-progress data request for a user for a given request type.
*
@@ -289,7 +289,8 @@ class data_registry {
* @param int $forcedcategoryvalue Use this value as if this was this context level category.
* @return int[]
*/
public static function get_effective_default_contextlevel_purpose_and_category($contextlevel, $forcedpurposevalue = false, $forcedcategoryvalue = false) {
public static function get_effective_default_contextlevel_purpose_and_category($contextlevel, $forcedpurposevalue = false,
$forcedcategoryvalue = false) {
list($purposeid, $categoryid) = self::get_defaults($contextlevel);
@@ -23,10 +23,7 @@
*/
namespace tool_dataprivacy;
use tool_dataprivacy\api;
use tool_dataprivacy\purpose;
use tool_dataprivacy\context_instance;
use tool_dataprivacy\data_registry;
use core_privacy\manager;
use tool_dataprivacy\expired_context;
defined('MOODLE_INTERNAL') || die();
@@ -90,7 +87,7 @@ abstract class expired_contexts_manager {
return $numprocessed;
}
$privacymanager = new \core_privacy\manager();
$privacymanager = new manager();
$privacymanager->set_observer(new \tool_dataprivacy\manager_observer());
foreach ($this->get_context_levels() as $level) {
@@ -118,11 +115,11 @@ abstract class expired_contexts_manager {
/**
* Deletes user data from the provided context.
*
* @param \core_privacy\manager $privacymanager
* @param \tool_dataprivacy\expired_context $expiredctx
* @param manager $privacymanager
* @param expired_context $expiredctx
* @return \context|false
*/
protected function delete_expired_context(\core_privacy\manager $privacymanager, \tool_dataprivacy\expired_context $expiredctx) {
protected function delete_expired_context(manager $privacymanager, expired_context $expiredctx) {
$context = \context::instance_by_id($expiredctx->get('contextid'), IGNORE_MISSING);
if (!$context) {
@@ -23,8 +23,7 @@
*/
namespace tool_dataprivacy;
use tool_dataprivacy\purpose;
use tool_dataprivacy\context_instance;
use core_privacy\manager;
defined('MOODLE_INTERNAL') || die();
@@ -110,11 +109,11 @@ class expired_user_contexts extends \tool_dataprivacy\expired_contexts_manager {
*
* Overwritten to delete the user.
*
* @param \core_privacy\manager $privacymanager
* @param \tool_dataprivacy\expired_context $expiredctx
* @param manager $privacymanager
* @param expired_context $expiredctx
* @return \context|false
*/
protected function delete_expired_context(\core_privacy\manager $privacymanager, \tool_dataprivacy\expired_context $expiredctx) {
protected function delete_expired_context(manager $privacymanager, expired_context $expiredctx) {
$context = \context::instance_by_id($expiredctx->get('contextid'), IGNORE_MISSING);
if (!$context) {
api::delete_expired_context($expiredctx->get('contextid'));
@@ -27,6 +27,7 @@ defined('MOODLE_INTERNAL') || die();
use tool_dataprivacy\api;
use tool_dataprivacy\data_registry;
use tool_dataprivacy\purpose;
/**
* Context instance data form.
@@ -186,12 +187,12 @@ class context_instance extends \core\form\persistent {
/**
* Returns the purpose display text.
*
* @param \tool_dataprivacy\purpose $effectivepurpose
* @param purpose $effectivepurpose
* @param int $retentioncontextlevel
* @param \context $context The context, just for displaying (filters) purposes.
* @return string
*/
protected static function get_retention_display_text(\tool_dataprivacy\purpose $effectivepurpose, $retentioncontextlevel, \context $context) {
protected static function get_retention_display_text(purpose $effectivepurpose, $retentioncontextlevel, \context $context) {
global $PAGE;
$renderer = $PAGE->get_renderer('tool_dataprivacy');
+98 -39
View File
@@ -35,6 +35,17 @@ use tool_dataprivacy\api;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class helper {
/** The default number of results to be shown per page. */
const DEFAULT_PAGE_SIZE = 20;
/** Filter constant associated with the request type filter. */
const FILTER_TYPE = 1;
/** Filter constant associated with the request status filter. */
const FILTER_STATUS = 2;
/** The request filters preference key. */
const PREF_REQUEST_FILTERS = 'tool_dataprivacy_request-filters';
/**
* Retrieves the human-readable text value of a data request type.
@@ -45,16 +56,11 @@ class helper {
* @throws moodle_exception
*/
public static function get_request_type_string($requesttype) {
switch ($requesttype) {
case api::DATAREQUEST_TYPE_EXPORT:
return get_string('requesttypeexport', 'tool_dataprivacy');
case api::DATAREQUEST_TYPE_DELETE:
return get_string('requesttypedelete', 'tool_dataprivacy');
case api::DATAREQUEST_TYPE_OTHERS:
return get_string('requesttypeothers', 'tool_dataprivacy');
default:
throw new moodle_exception('errorinvalidrequesttype', 'tool_dataprivacy');
$types = self::get_request_types();
if (!isset($types[$requesttype])) {
throw new moodle_exception('errorinvalidrequesttype', 'tool_dataprivacy');
}
return $types[$requesttype];
}
/**
@@ -66,16 +72,37 @@ class helper {
* @throws moodle_exception
*/
public static function get_shortened_request_type_string($requesttype) {
switch ($requesttype) {
case api::DATAREQUEST_TYPE_EXPORT:
return get_string('requesttypeexportshort', 'tool_dataprivacy');
case api::DATAREQUEST_TYPE_DELETE:
return get_string('requesttypedeleteshort', 'tool_dataprivacy');
case api::DATAREQUEST_TYPE_OTHERS:
return get_string('requesttypeothersshort', 'tool_dataprivacy');
default:
throw new moodle_exception('errorinvalidrequesttype', 'tool_dataprivacy');
$types = self::get_request_types_short();
if (!isset($types[$requesttype])) {
throw new moodle_exception('errorinvalidrequesttype', 'tool_dataprivacy');
}
return $types[$requesttype];
}
/**
* Returns the key value-pairs of request type code and their string value.
*
* @return array
*/
public static function get_request_types() {
return [
api::DATAREQUEST_TYPE_EXPORT => get_string('requesttypeexport', 'tool_dataprivacy'),
api::DATAREQUEST_TYPE_DELETE => get_string('requesttypedelete', 'tool_dataprivacy'),
api::DATAREQUEST_TYPE_OTHERS => get_string('requesttypeothers', 'tool_dataprivacy'),
];
}
/**
* Returns the key value-pairs of request type code and their shortened string value.
*
* @return array
*/
public static function get_request_types_short() {
return [
api::DATAREQUEST_TYPE_EXPORT => get_string('requesttypeexportshort', 'tool_dataprivacy'),
api::DATAREQUEST_TYPE_DELETE => get_string('requesttypedeleteshort', 'tool_dataprivacy'),
api::DATAREQUEST_TYPE_OTHERS => get_string('requesttypeothersshort', 'tool_dataprivacy'),
];
}
/**
@@ -83,30 +110,32 @@ class helper {
*
* @param int $status The request status.
* @return string
* @throws coding_exception
* @throws moodle_exception
*/
public static function get_request_status_string($status) {
switch ($status) {
case api::DATAREQUEST_STATUS_PENDING:
return get_string('statuspending', 'tool_dataprivacy');
case api::DATAREQUEST_STATUS_PREPROCESSING:
return get_string('statuspreprocessing', 'tool_dataprivacy');
case api::DATAREQUEST_STATUS_AWAITING_APPROVAL:
return get_string('statusawaitingapproval', 'tool_dataprivacy');
case api::DATAREQUEST_STATUS_APPROVED:
return get_string('statusapproved', 'tool_dataprivacy');
case api::DATAREQUEST_STATUS_PROCESSING:
return get_string('statusprocessing', 'tool_dataprivacy');
case api::DATAREQUEST_STATUS_COMPLETE:
return get_string('statuscomplete', 'tool_dataprivacy');
case api::DATAREQUEST_STATUS_CANCELLED:
return get_string('statuscancelled', 'tool_dataprivacy');
case api::DATAREQUEST_STATUS_REJECTED:
return get_string('statusrejected', 'tool_dataprivacy');
default:
throw new moodle_exception('errorinvalidrequeststatus', 'tool_dataprivacy');
$statuses = self::get_request_statuses();
if (!isset($statuses[$status])) {
throw new moodle_exception('errorinvalidrequeststatus', 'tool_dataprivacy');
}
return $statuses[$status];
}
/**
* Returns the key value-pairs of request status code and string value.
*
* @return array
*/
public static function get_request_statuses() {
return [
api::DATAREQUEST_STATUS_PENDING => get_string('statuspending', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_PREPROCESSING => get_string('statuspreprocessing', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_AWAITING_APPROVAL => get_string('statusawaitingapproval', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_APPROVED => get_string('statusapproved', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_PROCESSING => get_string('statusprocessing', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_COMPLETE => get_string('statuscomplete', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_CANCELLED => get_string('statuscancelled', 'tool_dataprivacy'),
api::DATAREQUEST_STATUS_REJECTED => get_string('statusrejected', 'tool_dataprivacy'),
];
}
/**
@@ -132,7 +161,7 @@ class helper {
'contextlevel' => CONTEXT_USER
];
// The final list of users that we will return;
// The final list of users that we will return.
$finalresults = [];
// Our prospective list of users.
@@ -146,4 +175,34 @@ class helper {
}
return $finalresults;
}
/**
* Get options for the data requests filter.
*
* @return array
* @throws coding_exception
*/
public static function get_request_filter_options() {
$filters = [
self::FILTER_TYPE => (object)[
'name' => get_string('requesttype', 'tool_dataprivacy'),
'options' => self::get_request_types_short()
],
self::FILTER_STATUS => (object)[
'name' => get_string('requeststatus', 'tool_dataprivacy'),
'options' => self::get_request_statuses()
],
];
$options = [];
foreach ($filters as $category => $filtercategory) {
foreach ($filtercategory->options as $key => $name) {
$option = (object)[
'category' => $filtercategory->name,
'name' => $name
];
$options["{$category}:{$key}"] = get_string('filteroption', 'tool_dataprivacy', $option);
}
}
return $options;
}
}
@@ -25,7 +25,6 @@ namespace tool_dataprivacy\output;
defined('MOODLE_INTERNAL') || die();
use coding_exception;
use dml_exception;
use moodle_exception;
use moodle_url;
use renderable;
@@ -34,7 +33,7 @@ use single_select;
use stdClass;
use templatable;
use tool_dataprivacy\data_request;
use tool_dataprivacy\output\expired_contexts_table;
use tool_dataprivacy\local\helper;
/**
* Class containing data for a user's data requests.
@@ -44,9 +43,6 @@ use tool_dataprivacy\output\expired_contexts_table;
*/
class data_deletion_page implements renderable, templatable {
/** The default number of results to be shown per page. */
const DEFAULT_PAGE_SIZE = 20;
/** @var data_request[] $requests List of data requests. */
protected $filter = null;
@@ -57,7 +53,7 @@ class data_deletion_page implements renderable, templatable {
* Construct this renderable.
*
* @param \tool_dataprivacy\data_request[] $filter
* @param \tool_dataprivacy\expired_contexts_table $expiredcontextstable
* @param expired_contexts_table $expiredcontextstable
*/
public function __construct($filter, expired_contexts_table $expiredcontextstable) {
$this->filter = $filter;
@@ -70,7 +66,6 @@ class data_deletion_page implements renderable, templatable {
* @param renderer_base $output
* @return stdClass
* @throws coding_exception
* @throws dml_exception
* @throws moodle_exception
*/
public function export_for_template(renderer_base $output) {
@@ -87,7 +82,7 @@ class data_deletion_page implements renderable, templatable {
$data->filter = $filterselector->export_for_template($output);
ob_start();
$this->expiredcontextstable->out(self::DEFAULT_PAGE_SIZE, true);
$this->expiredcontextstable->out(helper::DEFAULT_PAGE_SIZE, true);
$expiredcontexts = ob_get_contents();
ob_end_clean();
$data->expiredcontexts = $expiredcontexts;
@@ -24,20 +24,17 @@
namespace tool_dataprivacy\output;
defined('MOODLE_INTERNAL') || die();
use action_menu;
use action_menu_link_secondary;
use coding_exception;
use context_system;
use dml_exception;
use moodle_exception;
use moodle_url;
use renderable;
use renderer_base;
use single_select;
use stdClass;
use templatable;
use tool_dataprivacy\api;
use tool_dataprivacy\data_request;
use tool_dataprivacy\external\data_request_exporter;
use tool_dataprivacy\local\helper;
/**
* Class containing data for a user's data requests.
@@ -47,16 +44,21 @@ use tool_dataprivacy\external\data_request_exporter;
*/
class data_requests_page implements renderable, templatable {
/** @var data_request[] $requests List of data requests. */
protected $requests = [];
/** @var data_requests_table $table The data requests table. */
protected $table;
/** @var int[] $filters The applied filters. */
protected $filters = [];
/**
* Construct this renderable.
*
* @param data_request[] $requests
* @param data_requests_table $table The data requests table.
* @param int[] $filters The applied filters.
*/
public function __construct($requests) {
$this->requests = $requests;
public function __construct($table, $filters) {
$this->table = $table;
$this->filters = $filters;
}
/**
@@ -78,43 +80,17 @@ class data_requests_page implements renderable, templatable {
$data->httpsite = array('message' => $httpwarningmessage, 'announce' => 1);
}
$requests = [];
foreach ($this->requests as $request) {
$requestid = $request->get('id');
$status = $request->get('status');
$requestexporter = new data_request_exporter($request, ['context' => context_system::instance()]);
$item = $requestexporter->export($output);
$url = new moodle_url('/admin/tool/dataprivacy/datarequests.php');
$filteroptions = helper::get_request_filter_options();
$filter = new request_filter($filteroptions, $this->filters, $url);
$data->filter = $filter->export_for_template($output);
// Prepare actions.
$actions = [];
ob_start();
$this->table->out(helper::DEFAULT_PAGE_SIZE, true);
$requests = ob_get_contents();
ob_end_clean();
// View action.
$actionurl = new moodle_url('#');
$actiondata = ['data-action' => 'view', 'data-requestid' => $requestid];
$actiontext = get_string('viewrequest', 'tool_dataprivacy');
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
if ($status == api::DATAREQUEST_STATUS_AWAITING_APPROVAL) {
// Approve.
$actiondata['data-action'] = 'approve';
$actiontext = get_string('approverequest', 'tool_dataprivacy');
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
// Deny.
$actiondata['data-action'] = 'deny';
$actiontext = get_string('denyrequest', 'tool_dataprivacy');
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
}
$actionsmenu = new action_menu($actions);
$actionsmenu->set_menu_trigger(get_string('actions'));
$actionsmenu->set_owner_selector('request-actions-' . $requestid);
$actionsmenu->set_alignment(\action_menu::TL, \action_menu::BL);
$item->actions = $actionsmenu->export_for_template($output);
$requests[] = $item;
}
$data->requests = $requests;
$data->datarequests = $requests;
return $data;
}
}
@@ -0,0 +1,262 @@
<?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/>.
/**
* Contains the class used for the displaying the data requests table.
*
* @package tool_dataprivacy
* @copyright 2018 Jun Pataleta <jun@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_dataprivacy\output;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/tablelib.php');
use action_menu;
use action_menu_link_secondary;
use coding_exception;
use dml_exception;
use html_writer;
use moodle_url;
use stdClass;
use table_sql;
use tool_dataprivacy\api;
use tool_dataprivacy\external\data_request_exporter;
defined('MOODLE_INTERNAL') || die;
/**
* The class for displaying the data requests table.
*
* @copyright 2018 Jun Pataleta <jun@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class data_requests_table extends table_sql {
/** @var int The user ID. */
protected $userid = 0;
/** @var int[] The status filters. */
protected $statuses = [];
/** @var int[] The request type filters. */
protected $types = [];
/** @var bool Whether this table is being rendered for managing data requests. */
protected $manage = false;
/** @var stdClass[] Array of data request persistents. */
protected $datarequests = [];
/**
* data_requests_table constructor.
*
* @param int $userid The user ID
* @param int[] $statuses
* @param int[] $types
* @param bool $manage
* @throws coding_exception
*/
public function __construct($userid = 0, $statuses = [], $types = [], $manage = false) {
parent::__construct('data-requests-table');
$this->userid = $userid;
$this->statuses = $statuses;
$this->types = $types;
$this->manage = $manage;
$columnheaders = [
'type' => get_string('requesttype', 'tool_dataprivacy'),
'userid' => get_string('user', 'tool_dataprivacy'),
'timecreated' => get_string('daterequested', 'tool_dataprivacy'),
'requestedby' => get_string('requestby', 'tool_dataprivacy'),
'status' => get_string('requeststatus', 'tool_dataprivacy'),
'comments' => get_string('message', 'tool_dataprivacy'),
'actions' => '',
];
$this->define_columns(array_keys($columnheaders));
$this->define_headers(array_values($columnheaders));
$this->no_sorting('actions');
}
/**
* The type column.
*
* @param stdClass $data The row data.
* @return string
*/
public function col_type($data) {
if ($this->manage) {
return $data->typenameshort;
}
return $data->typename;
}
/**
* The user column.
*
* @param stdClass $data The row data.
* @return mixed
*/
public function col_userid($data) {
$user = $data->foruser;
return html_writer::link($user->profileurl, $user->fullname, ['title' => get_string('viewprofile')]);
}
/**
* The context information column.
*
* @param stdClass $data The row data.
* @return string
*/
public function col_timecreated($data) {
return userdate($data->timecreated);
}
/**
* The requesting user's column.
*
* @param stdClass $data The row data.
* @return mixed
*/
public function col_requestedby($data) {
$user = $data->requestedbyuser;
return html_writer::link($user->profileurl, $user->fullname, ['title' => get_string('viewprofile')]);
}
/**
* The status column.
*
* @param stdClass $data The row data.
* @return mixed
*/
public function col_status($data) {
return html_writer::span($data->statuslabel, 'label ' . $data->statuslabelclass);
}
/**
* The comments column.
*
* @param stdClass $data The row data.
* @return string
*/
public function col_comments($data) {
return shorten_text($data->comments, 60);
}
/**
* The actions column.
*
* @param stdClass $data The row data.
* @return string
*/
public function col_actions($data) {
global $OUTPUT;
$requestid = $data->id;
$status = $data->status;
// Prepare actions.
$actions = [];
// View action.
$actionurl = new moodle_url('#');
$actiondata = ['data-action' => 'view', 'data-requestid' => $requestid];
$actiontext = get_string('viewrequest', 'tool_dataprivacy');
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
if ($status == api::DATAREQUEST_STATUS_AWAITING_APPROVAL) {
// Approve.
$actiondata['data-action'] = 'approve';
$actiontext = get_string('approverequest', 'tool_dataprivacy');
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
// Deny.
$actiondata['data-action'] = 'deny';
$actiontext = get_string('denyrequest', 'tool_dataprivacy');
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
}
$actionsmenu = new action_menu($actions);
$actionsmenu->set_menu_trigger(get_string('actions'));
$actionsmenu->set_owner_selector('request-actions-' . $requestid);
$actionsmenu->set_alignment(\action_menu::TL, \action_menu::BL);
return $OUTPUT->render($actionsmenu);
}
/**
* Query the database for results to display in the table.
*
* @param int $pagesize size of page for paginated displayed table.
* @param bool $useinitialsbar do you want to use the initials bar.
* @throws dml_exception
* @throws coding_exception
*/
public function query_db($pagesize, $useinitialsbar = true) {
global $PAGE;
// Count data requests from the given conditions.
$total = api::get_data_requests_count($this->userid, $this->statuses, $this->types);
$this->pagesize($pagesize, $total);
$sort = $this->get_sql_sort();
// Get data requests from the given conditions.
$datarequests = api::get_data_requests($this->userid, $this->statuses, $this->types, $sort,
$this->get_page_start(), $this->get_page_size());
$this->rawdata = [];
$context = \context_system::instance();
$renderer = $PAGE->get_renderer('tool_dataprivacy');
foreach ($datarequests as $persistent) {
$exporter = new data_request_exporter($persistent, ['context' => $context]);
$this->rawdata[] = $exporter->export($renderer);
}
// Set initial bars.
if ($useinitialsbar) {
$this->initialbars($total > $pagesize);
}
}
/**
* Override default implementation to display a more meaningful information to the user.
*/
public function print_nothing_to_display() {
global $OUTPUT;
echo $this->render_reset_button();
$this->print_initials_bar();
if (!empty($this->statuses) || !empty($this->types)) {
$message = get_string('nodatarequestsmatchingfilter', 'tool_dataprivacy');
} else {
$message = get_string('nodatarequests', 'tool_dataprivacy');
}
echo $OUTPUT->notification($message, 'warning');
}
/**
* Override the table's show_hide_link method to prevent the show/hide links from rendering.
*
* @param string $column the column name, index into various names.
* @param int $index numerical index of the column.
* @return string HTML fragment.
*/
protected function show_hide_link($column, $index) {
return '';
}
}
@@ -0,0 +1,98 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class containing the filter options data for rendering the autocomplete element for the data requests page.
*
* @package tool_dataprivacy
* @copyright 2018 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_dataprivacy\output;
use moodle_url;
use renderable;
use renderer_base;
use stdClass;
use templatable;
defined('MOODLE_INTERNAL') || die();
/**
* Class containing the filter options data for rendering the autocomplete element for the data requests page.
*
* @copyright 2018 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class request_filter implements renderable, templatable {
/** @var array $filteroptions The filter options. */
protected $filteroptions;
/** @var array $selectedoptions The list of selected filter option values. */
protected $selectedoptions;
/** @var moodle_url|string $baseurl The url with params needed to call up this page. */
protected $baseurl;
/**
* request_filter constructor.
*
* @param array $filteroptions The filter options.
* @param array $selectedoptions The list of selected filter option values.
* @param string|moodle_url $baseurl The url with params needed to call up this page.
*/
public function __construct($filteroptions, $selectedoptions, $baseurl = null) {
$this->filteroptions = $filteroptions;
$this->selectedoptions = $selectedoptions;
if (!empty($baseurl)) {
$this->baseurl = new moodle_url($baseurl);
}
}
/**
* Function to export the renderer data in a format that is suitable for a mustache template.
*
* @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
* @return stdClass|array
*/
public function export_for_template(renderer_base $output) {
global $PAGE;
$data = new stdClass();
if (empty($this->baseurl)) {
$this->baseurl = $PAGE->url;
}
$data->action = $this->baseurl->out(false);
foreach ($this->selectedoptions as $option) {
if (!isset($this->filteroptions[$option])) {
$this->filteroptions[$option] = $option;
}
}
$data->filteroptions = [];
foreach ($this->filteroptions as $value => $label) {
$selected = in_array($value, $this->selectedoptions);
$filteroption = (object)[
'value' => $value,
'label' => $label
];
$filteroption->selected = $selected;
$data->filteroptions[] = $filteroption;
}
return $data;
}
}
@@ -51,7 +51,10 @@ class provider implements
\core_privacy\local\metadata\provider,
// This tool may provide access to and deletion of user data.
\core_privacy\local\request\plugin\provider {
\core_privacy\local\request\plugin\provider,
// This plugin has some sitewide user preferences to export.
\core_privacy\local\request\user_preference_provider {
/**
* Returns meta data about this system.
*
@@ -70,6 +73,10 @@ class provider implements
],
'privacy:metadata:request'
);
$collection->add_user_preference(tool_helper::PREF_REQUEST_FILTERS,
'privacy:metadata:preference:tool_dataprivacy_request-filters');
return $collection;
}
@@ -162,4 +169,36 @@ class provider implements
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
}
/**
* Export all user preferences for the plugin.
*
* @param int $userid The userid of the user whose data is to be exported.
*/
public static function export_user_preferences(int $userid) {
$preffilter = get_user_preferences(tool_helper::PREF_REQUEST_FILTERS, null, $userid);
if ($preffilter !== null) {
$filters = json_decode($preffilter);
$descriptions = [];
foreach ($filters as $filter) {
list($category, $value) = explode(':', $filter);
$option = new stdClass();
switch($category) {
case tool_helper::FILTER_TYPE:
$option->category = get_string('requesttype', 'tool_dataprivacy');
$option->name = tool_helper::get_shortened_request_type_string($value);
break;
case tool_helper::FILTER_STATUS:
$option->category = get_string('requeststatus', 'tool_dataprivacy');
$option->name = tool_helper::get_request_status_string($value);
break;
}
$descriptions[] = get_string('filteroption', 'tool_dataprivacy', $option);
}
// Export the filter preference as comma-separated values and text descriptions.
$values = implode(', ', $filters);
$descriptionstext = implode(', ', $descriptions);
writer::export_user_preference('tool_dataprivacy', tool_helper::PREF_REQUEST_FILTERS, $values, $descriptionstext);
}
}
}
+1 -1
View File
@@ -64,7 +64,7 @@ class purpose extends \core\persistent {
// Replicate self::read.
$this->from_record($data);
// Using validate() as self::$validated is private.
// Validate the purpose record.
$this->validate();
// Now replicate the parent constructor.
@@ -54,7 +54,7 @@ class initiate_data_request_task extends adhoc_task {
public function execute() {
global $CFG;
require_once($CFG->dirroot . '/admin/tool/dataprivacy/lib.php');
require_once($CFG->dirroot . "/{$CFG->admin}/tool/dataprivacy/lib.php");
if (!isset($this->get_custom_data()->requestid)) {
throw new coding_exception('The custom data \'requestid\' is required.');
@@ -57,7 +57,7 @@ class process_data_request_task extends adhoc_task {
public function execute() {
global $CFG, $PAGE, $SITE;
require_once($CFG->dirroot . '/admin/tool/dataprivacy/lib.php');
require_once($CFG->dirroot . "/{$CFG->admin}/tool/dataprivacy/lib.php");
if (!isset($this->get_custom_data()->requestid)) {
throw new coding_exception('The custom data \'requestid\' is required.');
+3 -2
View File
@@ -53,8 +53,9 @@ if ($manage) {
$PAGE->set_context($context);
// If contactdataprotectionofficer is disabled, send the user back to the profile page, or the privacy policy page.
if (!\tool_dataprivacy\api::can_contact_dpo()) {
redirect($returnurl, get_string('contactdpoviaprivacypolicy', 'tool_dataprivacy'), \core\output\notification::NOTIFY_ERROR);
// That is, unless you have sufficient capabilities to perform this on behalf of a user.
if (!$manage && !\tool_dataprivacy\api::can_contact_dpo()) {
redirect($returnurl, get_string('contactdpoviaprivacypolicy', 'tool_dataprivacy'), 0, \core\output\notification::NOTIFY_ERROR);
}
$mform = new tool_dataprivacy_data_request_form($url->out(false), ['manage' => !empty($manage)]);
@@ -44,19 +44,28 @@ class tool_dataprivacy_data_request_form extends moodleform {
/**
* Form definition.
*
* @throws HTML_QuickForm_Error
* @throws coding_exception
* @throws dml_exception
*/
public function definition() {
global $DB, $USER;
global $USER;
$mform =& $this->_form;
$this->manage = $this->_customdata['manage'];
if ($this->manage) {
$options = [
'ajax' => 'tool_dataprivacy/form-user-selector',
'multiple' => false
'valuehtmlcallback' => function($value) {
global $OUTPUT;
$allusernames = get_all_user_name_fields(true);
$fields = 'id, email, ' . $allusernames;
$user = \core_user::get_user($value, $fields);
$useroptiondata = [
'fullname' => fullname($user),
'email' => $user->email
];
return $OUTPUT->render_from_template('tool_dataprivacy/form-user-selector-suggestion', $useroptiondata);
}
];
$mform->addElement('autocomplete', 'userid', get_string('requestfor', 'tool_dataprivacy'), [], $options);
$mform->addRule('userid', null, 'required', null, 'client');
+2
View File
@@ -25,6 +25,8 @@
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
require_login(null, false);
$filter = optional_param('filter', CONTEXT_COURSE, PARAM_INT);
$url = new moodle_url('/admin/tool/dataprivacy/datadeletion.php');
+2
View File
@@ -25,6 +25,8 @@
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
require_login(null, false);
$contextlevel = optional_param('contextlevel', CONTEXT_SYSTEM, PARAM_INT);
$contextid = optional_param('contextid', 0, PARAM_INT);
+34 -2
View File
@@ -25,6 +25,8 @@
require_once("../../../config.php");
require_once('lib.php');
require_login(null, false);
$url = new moodle_url('/admin/tool/dataprivacy/datarequests.php');
$title = get_string('datarequests', 'tool_dataprivacy');
@@ -34,8 +36,38 @@ $title = get_string('datarequests', 'tool_dataprivacy');
echo $OUTPUT->header();
echo $OUTPUT->heading($title);
$requests = tool_dataprivacy\api::get_data_requests();
$requestlist = new tool_dataprivacy\output\data_requests_page($requests);
$filtersapplied = optional_param_array('request-filters', [-1], PARAM_NOTAGS);
$filterscleared = optional_param('filters-cleared', 0, PARAM_INT);
if ($filtersapplied === [-1]) {
// If there are no filters submitted, check if there is a saved filters from the user preferences.
$filterprefs = get_user_preferences(\tool_dataprivacy\local\helper::PREF_REQUEST_FILTERS, null);
if ($filterprefs && empty($filterscleared)) {
$filtersapplied = json_decode($filterprefs);
} else {
$filtersapplied = [];
}
}
// Save the current applied filters to the user preferences.
set_user_preference(\tool_dataprivacy\local\helper::PREF_REQUEST_FILTERS, json_encode($filtersapplied));
$types = [];
$statuses = [];
foreach ($filtersapplied as $filter) {
list($category, $value) = explode(':', $filter);
switch($category) {
case \tool_dataprivacy\local\helper::FILTER_TYPE:
$types[] = $value;
break;
case \tool_dataprivacy\local\helper::FILTER_STATUS:
$statuses[] = $value;
break;
}
}
$table = new \tool_dataprivacy\output\data_requests_table(0, $statuses, $types, true);
$table->baseurl = $url;
$requestlist = new tool_dataprivacy\output\data_requests_page($table, $filtersapplied);
$requestlistoutput = $PAGE->get_renderer('tool_dataprivacy');
echo $requestlistoutput->render($requestlist);
+2
View File
@@ -25,6 +25,8 @@
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
require_login(null, false);
$url = new \moodle_url('/admin/tool/dataprivacy/defaults.php');
$title = get_string('setdefaults', 'tool_dataprivacy');
+2
View File
@@ -24,6 +24,8 @@
require_once(__DIR__ . '/../../../config.php');
require_login(null, false);
$id = optional_param('id', 0, PARAM_INT);
$url = new \moodle_url('/admin/tool/dataprivacy/editcategory.php', array('id' => $id));
+2
View File
@@ -24,6 +24,8 @@
require_once(__DIR__ . '/../../../config.php');
require_login(null, false);
$id = optional_param('id', 0, PARAM_INT);
$url = new \moodle_url('/admin/tool/dataprivacy/editpurpose.php', array('id' => $id));
@@ -48,8 +48,8 @@ $string['compliant'] = 'Compliant';
$string['confirmapproval'] = 'Do you really want to approve this data request?';
$string['confirmcontextdeletion'] = 'Do you really want to confirm the deletion of the selected contexts? This will also delete all of the user data for their respective sub-contexts.';
$string['confirmdenial'] = 'Do you really want deny this data request?';
$string['contactdataprotectionofficer'] = 'Contact Data Protection Officer';
$string['contactdataprotectionofficer_desc'] = 'If enabled, users will be able to contact the Data Protection Officer and make a data request via a link on their profile page.';
$string['contactdataprotectionofficer'] = 'Contact the privacy officer';
$string['contactdataprotectionofficer_desc'] = 'If enabled, users will be able to contact the privacy officer and make a data request via a link on their profile page.';
$string['contextlevelname10'] = 'Site';
$string['contextlevelname30'] = 'Users';
$string['contextlevelname40'] = 'Course categories';
@@ -57,7 +57,7 @@ $string['contextlevelname50'] = 'Courses';
$string['contextlevelname70'] = 'Activity modules';
$string['contextlevelname80'] = 'Blocks';
$string['contextpurposecategorysaved'] = 'Purpose and category saved.';
$string['contactdpoviaprivacypolicy'] = 'Please contact the Data Protection Officer as described in the privacy policy.';
$string['contactdpoviaprivacypolicy'] = 'Please contact the privacy officer as described in the privacy policy.';
$string['createcategory'] = 'Create data category';
$string['createpurpose'] = 'Create data purpose';
$string['datadeletion'] = 'Data deletion';
@@ -82,8 +82,8 @@ $string['defaultssaved'] = 'Defaults saved';
$string['deny'] = 'Deny';
$string['denyrequest'] = 'Deny request';
$string['download'] = 'Download';
$string['dporolemapping'] = 'Data Protection Officer role mapping';
$string['dporolemapping_desc'] = 'The Data Protection Officer can manage data requests. The capability tool/dataprivacy:managedatarequests must be allowed for a role to be listed as a Data Protection Officer role mapping option.';
$string['dporolemapping'] = 'Privacy officer role mapping';
$string['dporolemapping_desc'] = 'The privacy officer can manage data requests. The capability tool/dataprivacy:managedatarequests must be allowed for a role to be listed as a privacy officer role mapping option.';
$string['editcategories'] = 'Edit categories';
$string['editcategory'] = 'Edit category';
$string['editcategories'] = 'Edit categories';
@@ -101,8 +101,10 @@ $string['errorrequestalreadyexists'] = 'You already have an ongoing request.';
$string['errorrequestnotfound'] = 'Request not found';
$string['errorrequestnotwaitingforapproval'] = 'The request is not awaiting approval. Either it is not yet ready or it has already been processed.';
$string['errorsendingmessagetodpo'] = 'An error was encountered while trying to send a message to {$a}.';
$string['exceptionnotificationsubject'] = "Exception occured while processing privacy data";
$string['exceptionnotificationbody'] = "<p>Exception occured while calling <b>{\$a->fullmethodname}</b>.<br>This means that plugin <b>{\$a->component}</b> did not complete processing data. Below you can find exception information that can be passed to the plugin developer.</p><pre>{\$a->message}<br>\n\n{\$a->backtrace}</pre>";
$string['exceptionnotificationsubject'] = 'Exception occurred while processing privacy data';
$string['exceptionnotificationbody'] = '<p>Exception occurred while calling <b>{$a->fullmethodname}</b>.<br>This means that plugin <b>{$a->component}</b> did not complete the processing of data. The following exception information may be passed on to the plugin developer:</p><pre>{$a->message}<br>
{$a->backtrace}</pre>';
$string['expiredretentionperiodtask'] = 'Expired retention period';
$string['expiry'] = 'Expiry';
$string['expandplugin'] = 'Expand and collapse plugin.';
@@ -110,6 +112,7 @@ $string['expandplugintype'] = 'Expand and collapse plugin type.';
$string['explanationtitle'] = 'Icons used on this page and what they mean.';
$string['external'] = 'Additional';
$string['externalexplanation'] = 'An additional plugin installed on this site.';
$string['filteroption'] = '{$a->category}: {$a->name}';
$string['frontpagecourse'] = 'Front page course';
$string['gdpr_art_6_1_a_description'] = 'The data subject has given consent to the processing of his or her personal data for one or more specific purposes';
$string['gdpr_art_6_1_a_name'] = 'Consent (GDPR Art. 6.1(a))';
@@ -160,6 +163,7 @@ $string['nameemail'] = '{$a->name} ({$a->email})';
$string['nchildren'] = '{$a} children';
$string['newrequest'] = 'New request';
$string['nodatarequests'] = 'There are no data requests';
$string['nodatarequestsmatchingfilter'] = 'There are no data requests matching the given filter';
$string['noactivitiestoload'] = 'No activities';
$string['noassignedroles'] = 'No assigned roles in this context';
$string['noblockstoload'] = 'No blocks';
@@ -174,11 +178,12 @@ $string['notset'] = 'Not set (use the default value)';
$string['pluginregistry'] = 'Plugin privacy registry';
$string['pluginregistrytitle'] = 'Plugin privacy compliance registry';
$string['privacy'] = 'Privacy';
$string['privacy:metadata:preference:tool_dataprivacy_request-filters'] = 'The filters currently applied to the data requests page.';
$string['privacy:metadata:request'] = 'Information from personal data requests (subject access and deletion requests) made for this site.';
$string['privacy:metadata:request:comments'] = 'Any user comments accompanying the request.';
$string['privacy:metadata:request:userid'] = 'The ID of the user to whom the request belongs';
$string['privacy:metadata:request:requestedby'] = 'The ID of the user making the request, if made on behalf of another user.';
$string['privacy:metadata:request:dpocomment'] = 'Any comments made by the site\'s Data Protection Officer regarding the request.';
$string['privacy:metadata:request:dpocomment'] = 'Any comments made by the site\'s privacy officer regarding the request.';
$string['privacy:metadata:request:timecreated'] = 'The timestamp indicating when the request was made by the user.';
$string['protected'] = 'Protected';
$string['protectedlabel'] = 'The retention of this data has a higher legal precedent over a user\'s request to be forgotten. This data will only be deleted after the retention period has expired.';
@@ -193,21 +198,22 @@ $string['purposeupdated'] = 'Purpose updated';
$string['replyto'] = 'Reply to';
$string['requestactions'] = 'Actions';
$string['requestby'] = 'Requested by';
$string['requestbydetail'] = 'Requested by:';
$string['requestcomments'] = 'Comments';
$string['requestcomments_help'] = 'This box enables you to enter any further details about your data request.';
$string['requestemailintro'] = 'You have received a data request:';
$string['requestfor'] = 'Requesting for';
$string['requeststatus'] = 'Status';
$string['requestsubmitted'] = 'Your request has been submitted to the site\'s Data Protection Officer';
$string['requestsubmitted'] = 'Your request has been submitted to the privacy officer';
$string['requesttype'] = 'Type';
$string['requesttypeuser'] = '{$a->typename} ({$a->user})';
$string['requesttype_help'] = 'Select the reason why you would like to contact the site\'s Data Protection Officer';
$string['requesttype_help'] = 'Select the reason why you would like to contact the privacy officer';
$string['requesttypedelete'] = 'Delete all of my personal data';
$string['requesttypedeleteshort'] = 'Delete';
$string['requesttypeexport'] = 'Export all of my personal data';
$string['requesttypeexportshort'] = 'Export';
$string['requesttypeothers'] = 'General inquiry';
$string['requesttypeothersshort'] = 'Others';
$string['requesttypeothersshort'] = 'Message';
$string['requiresattention'] = 'Requires attention.';
$string['requiresattentionexplanation'] = 'This plugin does not implement the Moodle privacy API. If this plugin stores any personal data it will not be able to be exported or deleted through Moodle\'s privacy system.';
$string['resultdeleted'] = 'You recently requested to have your account and personal data in {$a} to be deleted. This process has been completed and you will no longer be able to log in.';
@@ -25,6 +25,8 @@
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
require_login(null, false);
$contextlevel = optional_param('contextlevel', CONTEXT_SYSTEM, PARAM_INT);
$contextid = optional_param('contextid', 0, PARAM_INT);
+2
View File
@@ -24,6 +24,8 @@
require_once(__DIR__ . '/../../../config.php');
require_login(null, false);
$url = new moodle_url("/admin/tool/dataprivacy/purposes.php");
$title = get_string('editpurposes', 'tool_dataprivacy');
@@ -46,7 +46,7 @@
}
}}
<div classs="container-fluid">
<div class="container-fluid">
<hr />
<div class="row">
{{#compliant}}
@@ -64,17 +64,17 @@
</div>
{{#compliant}}
<div class="hide" data-section="{{raw_component}}" aria-expanded="false">
<div class="hide" data-section="{{raw_component}}" aria-expanded="false" role="contentinfo">
{{#metadata}}
<hr />
<div class="p-l-3">
<dl class="row">
<dt class="span3 col-xs-3">
{{#link}}
<a href="#{{name}}"><h5 style="word-wrap:break-word">{{name}}</h5></a>
<a href="#{{name}}"><strong style="word-wrap:break-word">{{name}}</strong></a>
{{/link}}
{{^link}}
<h5 style="word-wrap:break-word">{{name}}</h5>
<strong style="word-wrap:break-word">{{name}}</strong>
{{/link}}
<div class="small text-muted" style="word-wrap:break-word">{{type}}</div>
</dt>
File diff suppressed because one or more lines are too long
@@ -55,7 +55,7 @@
<h3 id="{{plugin_type_raw}}">{{#pix}}t/collapsed, moodle, {{#str}}expandplugintype, tool_dataprivacy{{/str}}{{/pix}}{{plugin_type}}</h3>
</a>
</div>
<div class="hide p-b-1" data-plugintarget="{{plugin_type_raw}}" aria-expanded="false">
<div class="hide p-b-1" data-plugintarget="{{plugin_type_raw}}" aria-expanded="false" role="contentinfo">
{{#plugins}}
{{> tool_dataprivacy/component_status}}
{{/plugins}}
@@ -47,59 +47,68 @@
"datarequestsurl": "#"
}
}}
<style>
table, th, td {
border: 1px solid black;
padding: 0.5em;
}
</style>
<div>
<p>{{#str}}emailsalutation, tool_dataprivacy, {{dponame}}{{/str}}</p>
<p>{{#str}}requestemailintro, tool_dataprivacy{{/str}}</p>
<table>
<tr>
<th scope="row">
{{#str}}requesttype, tool_dataprivacy{{/str}}
</th>
<td>
{{requesttype}}
</td>
</tr>
<tr>
<th scope="row">
{{#str}}requestfor, tool_dataprivacy{{/str}}
</th>
<td>
{{requestfor}}
</td>
</tr>
{{^forself}}
<tr>
<th scope="row">
{{#str}}requestby, tool_dataprivacy{{/str}}
</th>
<td>
{{requestedby}}
</td>
</tr>
{{/forself}}
<tr>
<th scope="row">
{{#str}}requestcomments, tool_dataprivacy{{/str}}
</th>
<td>
{{{requestcomments}}}
</td>
</tr>
<tr>
<th scope="row">
{{#str}}daterequested, tool_dataprivacy{{/str}}
</th>
<td>
{{requestdate}}
</td>
</tr>
</table>
<hr>
<a href="{{datarequestsurl}}">{{#str}}viewrequest, tool_dataprivacy{{/str}}</a>
</div>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
table, th, td {
border: 1px solid black;
padding: 0.5em;
}
</style>
<title>{{#str}}datarequestemailsubject, tool_dataprivacy, {{requesttype}}{{/str}}</title>
</head>
<body>
<div>
<p>{{#str}}emailsalutation, tool_dataprivacy, {{dponame}}{{/str}}</p>
<p>{{#str}}requestemailintro, tool_dataprivacy{{/str}}</p>
<table>
<tr>
<th scope="row">
{{#str}}requesttype, tool_dataprivacy{{/str}}
</th>
<td>
{{requesttype}}
</td>
</tr>
<tr>
<th scope="row">
{{#str}}requestfor, tool_dataprivacy{{/str}}
</th>
<td>
{{requestfor}}
</td>
</tr>
{{^forself}}
<tr>
<th scope="row">
{{#str}}requestby, tool_dataprivacy{{/str}}
</th>
<td>
{{requestedby}}
</td>
</tr>
{{/forself}}
<tr>
<th scope="row">
{{#str}}requestcomments, tool_dataprivacy{{/str}}
</th>
<td>
{{{requestcomments}}}
</td>
</tr>
<tr>
<th scope="row">
{{#str}}daterequested, tool_dataprivacy{{/str}}
</th>
<td>
{{requestdate}}
</td>
</tr>
</table>
<hr>
<a href="{{datarequestsurl}}">{{#str}}viewrequest, tool_dataprivacy{{/str}}</a>
</div>
</body>
</html>
@@ -30,6 +30,7 @@
Example context (json):
{
"title": "Data request modal title"
}
}}
{{< core/modal }}
@@ -26,72 +26,32 @@
* none
Context variables required for this template:
* requests - Array of data requests.
* newdatarequesturl string The URL pointing to the data request creation page.
* datarequests string The HTML of the data requests table.
Example context (json):
{
"requests": [
{
"id": 1,
"foruser" : {
"fullname": "Oscar Olsen",
"profileurl": "#"
"newdatarequesturl": "#",
"datarequests": "<table><tr><td>This is the table where the list of data requests will be rendered</td></tr></table>",
"filter": {
"action": "#",
"filteroptions": [
{
"value": "1",
"label": "Option 1"
},
"typenameshort" : "Export",
"comments": "I would like to download all of my daughter's personal data",
"statuslabelclass": "label-default",
"statuslabel": "Pending",
"timecreated" : 1517902435
},
{
"id": 2,
"foruser" : {
"fullname": "Alexandre Denys",
"profileurl": "#"
{
"value": "2",
"label": "Option 2",
"selected": true
},
"typenameshort" : "Export",
"comments": "Please give me all of the information you have about me...",
"statuslabelclass": "label-warning",
"statuslabel": "Awaiting completion",
"timecreated" : 1517902435
},
{
"id": 3,
"foruser" : {
"fullname": "Hirondino Moura",
"profileurl": "#"
},
"typenameshort" : "Delete",
"comments": "Please delete all of my son's personal data.",
"statuslabelclass": "label-success",
"statuslabel": "Complete",
"timecreated" : 1517902435
},
{
"id": 4,
"foruser" : {
"fullname": "Florian Krause",
"profileurl": "#"
},
"typenameshort" : "Delete",
"comments": "I would like to request for my personal data to be deleted from your site. Thanks!",
"statuslabelclass": "label-danger",
"statuslabel": "Rejected",
"timecreated" : 1517902435
},
{
"id": 5,
"foruser" : {
"fullname": "Nicklas Sørensen",
"profileurl": "#"
},
"typenameshort" : "Export",
"comments": "Please let me download my data",
"statuslabelclass": "label-info",
"statuslabel": "Processing",
"timecreated" : 1517902435
}
]
{
"value": "3",
"label": "Option 3",
"selected": true
}
]
}
}
}}
@@ -101,51 +61,19 @@
<div data-region="datarequests">
<div class="m-t-1 m-b-1">
<a href="{{newdatarequesturl}}" class="btn btn-primary" data-action="new-request">
{{#str}}newrequest, tool_dataprivacy{{/str}}
</a>
<div class="pull-right">
<a href="{{newdatarequesturl}}" class="btn btn-primary" data-action="new-request">
{{#str}}newrequest, tool_dataprivacy{{/str}}
</a>
</div>
{{#filter}}
{{>tool_dataprivacy/request_filter}}
{{/filter}}
</div>
<div class="m-t-1 m-b-1" data-region="data-requests-table">
{{{datarequests}}}
</div>
<table class="generaltable fullwidth">
<thead>
<tr>
<th scope="col">{{#str}}requesttype, tool_dataprivacy{{/str}}</th>
<th scope="col">{{#str}}user, tool_dataprivacy{{/str}}</th>
<th scope="col">{{#str}}daterequested, tool_dataprivacy{{/str}}</th>
<th scope="col">{{#str}}requeststatus, tool_dataprivacy{{/str}}</th>
<th scope="col" colspan="2">{{#str}}message, tool_dataprivacy{{/str}}</th>
</tr>
</thead>
<tbody>
{{#requests}}
<tr {{!
}} data-region="request-node"{{!
}} data-id="{{id}}"{{!
}} data-type="{{type}}"{{!
}} data-status="{{status}}"{{!
}}>
<td>{{typenameshort}}</td>
<td><a href="{{foruser.profileurl}}" title="{{#str}}viewprofile{{/str}}">{{foruser.fullname}}</a></td>
<td>{{#userdate}} {{timecreated}}, {{#str}} strftimedate {{/str}} {{/userdate}}</td>
<td>
<span class="label {{statuslabelclass}}">{{statuslabel}}</span>
</td>
<td>{{#shortentext}}60, {{comments}}{{/shortentext}}</td>
<td>
{{#actions}}
{{> core/action_menu}}
{{/actions}}
</td>
</tr>
{{/requests}}
{{^requests}}
<tr>
<td class="text-muted" colspan="6">
{{#str}}nodatarequests, tool_dataprivacy{{/str}}
</td>
</tr>
{{/requests}}
</tbody>
</table>
</div>
{{#js}}
@@ -37,7 +37,11 @@
"comments": "I would like to download all of my daughter's personal data",
"statuslabelclass": "label-default",
"statuslabel": "Pending",
"timecreated" : 1517902087
"timecreated" : 1517902087,
"requestedbyuser" : {
"fullname": "Martha Smith",
"profileurl": "#"
}
},
{
"id": 2,
@@ -45,7 +49,11 @@
"comments": "Give me all of the information you have about me, or else...",
"statuslabelclass": "label-warning",
"statuslabel": "Awaiting completion",
"timecreated" : 1517902087
"timecreated" : 1517902087,
"requestedbyuser" : {
"fullname": "Martha Smith",
"profileurl": "#"
}
},
{
"id": 3,
@@ -53,7 +61,11 @@
"comments": "Please delete all of my son's personal data.",
"statuslabelclass": "label-success",
"statuslabel": "Complete",
"timecreated" : 1517902087
"timecreated" : 1517902087,
"requestedbyuser" : {
"fullname": "Martha Smith",
"profileurl": "#"
}
},
{
"id": 4,
@@ -61,7 +73,11 @@
"comments": "Delete my data or I'm coming for you...",
"statuslabelclass": "label-danger",
"statuslabel": "Rejected",
"timecreated" : 1517902087
"timecreated" : 1517902087,
"requestedbyuser" : {
"fullname": "Martha Smith",
"profileurl": "#"
}
},
{
"id": 5,
@@ -69,7 +85,11 @@
"comments": "Please let me download my data",
"statuslabelclass": "label-info",
"statuslabel": "Processing",
"timecreated" : 1517902087
"timecreated" : 1517902087,
"requestedbyuser" : {
"fullname": "Martha Smith",
"profileurl": "#"
}
}
]
}
@@ -90,6 +110,7 @@
<tr>
<th scope="col">{{#str}}requesttype, tool_dataprivacy{{/str}}</th>
<th scope="col">{{#str}}daterequested, tool_dataprivacy{{/str}}</th>
<th scope="col">{{#str}}requestby, tool_dataprivacy{{/str}}</th>
<th scope="col">{{#str}}requeststatus, tool_dataprivacy{{/str}}</th>
<th scope="col" colspan="2">{{#str}}message, tool_dataprivacy{{/str}}</th>
</tr>
@@ -103,7 +124,8 @@
}} data-status="{{status}}"{{!
}}>
<td>{{typename}}</td>
<td>{{#userdate}} {{timecreated}}, {{#str}} strftimedate {{/str}} {{/userdate}}</td>
<td>{{#userdate}} {{timecreated}}, {{#str}} strftimedatetime {{/str}} {{/userdate}}</td>
<td><a href="{{requestedbyuser.profileurl}}" title="{{#str}}viewprofile{{/str}}">{{requestedbyuser.fullname}}</a></td>
<td>
<span class="label {{statuslabelclass}}">{{statuslabel}}</span>
</td>
@@ -44,6 +44,10 @@
"canreview": true,
"reviewurl": "#",
"timecreated": 1517561224,
"requestedbyuser" : {
"fullname": "Martha Smith",
"profileurl": "#"
},
"statuslabel": "Pending",
"statuslabelclass": "label-default",
"messagehtml": "<p>Hello,</p><p>I would like to download all of my personal data.</p><p>Thanks!</p>"
@@ -63,12 +67,16 @@
<a href="mailto:{{foruser.email}}">{{foruser.email}}</a>
<div class="clearfix m-t-1 m-b-1">
<span class="pull-left m-r-1">
<strong>{{#str}}daterequesteddetail, tool_dataprivacy{{/str}}</strong> {{#userdate}} {{timecreated}}, {{#str}} strftimedate {{/str}} {{/userdate}}
<strong>{{#str}}daterequesteddetail, tool_dataprivacy{{/str}}</strong> {{#userdate}} {{timecreated}}, {{#str}} strftimedatetime {{/str}} {{/userdate}}
</span>
<span class="pull-left m-r-1">
<strong>{{#str}}statusdetail, tool_dataprivacy{{/str}}</strong>
<span class="label {{statuslabelclass}}">{{statuslabel}}</span>
</span>
<span class="pull-left m-r-1">
<strong>{{#str}}requestbydetail, tool_dataprivacy{{/str}}</strong>
<span><a href="{{requestedbyuser.profileurl}}" title="{{#str}}viewprofile{{/str}}">{{requestedbyuser.fullname}}</a></span>
</span>
</div>
{{#canreview}}
<!--a href="{{reviewurl}}" class="btn btn-default">{{#str}}reviewdata, tool_dataprivacy{{/str}}</a-->
@@ -0,0 +1,67 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template tool_dataprivacy/request_filter
Template for the request filter element.
Context variables required for this template:
* action string - The action URL for the form.
* filteroptions - Array of filter options.
* value string - The option value.
* label string - The option label.
* selected boolean - Whether the option is selected
Example context (json):
{
"action": "#",
"filteroptions": [
{
"value": "1",
"label": "Option 1"
},
{
"value": "2",
"label": "Option 2",
"selected": true
},
{
"value": "3",
"label": "Option 3",
"selected": true
},
{
"value": "4",
"label": "Option 4"
}
]
}
}}
<form method="post" action="{{action}}" class="m-b-1" role="search" id="request_filter_form">
<label for="request-filters" class="sr-only">{{#str}}filters{{/str}}</label>
<select name="request-filters[]" id="request-filters" multiple="multiple" class="form-autocomplete-original-select">
{{#filteroptions}}
<option value="{{value}}" {{#selected}}selected="selected"{{/selected}}>{{{label}}}</option>
{{/filteroptions}}
</select>
<input type="hidden" id="filters-cleared" name="filters-cleared" value="0" />
</form>
{{#js}}
require(['tool_dataprivacy/request_filter'], function(Filter) {
Filter.init();
});
{{/js}}
+114 -27
View File
@@ -29,6 +29,7 @@ use tool_dataprivacy\api;
use tool_dataprivacy\data_registry;
use tool_dataprivacy\expired_context;
use tool_dataprivacy\data_request;
use tool_dataprivacy\local\helper;
use tool_dataprivacy\task\initiate_data_request_task;
use tool_dataprivacy\task\process_data_request_task;
@@ -411,42 +412,128 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
}
/**
* Test for api::get_data_requests()
* Data provider for \tool_dataprivacy_api_testcase::test_get_data_requests().
*
* @return array
*/
public function test_get_data_requests() {
public function get_data_requests_provider() {
$generator = new testing_data_generator();
$user1 = $generator->create_user();
$user2 = $generator->create_user();
$comment = 'sample comment';
$user3 = $generator->create_user();
$user4 = $generator->create_user();
$user5 = $generator->create_user();
$users = [$user1, $user2, $user3, $user4, $user5];
$completeonly = [api::DATAREQUEST_STATUS_COMPLETE];
$completeandcancelled = [api::DATAREQUEST_STATUS_COMPLETE, api::DATAREQUEST_STATUS_CANCELLED];
// Make a data request as user 1.
$this->setUser($user1);
$d1 = api::create_data_request($user1->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
// Make a data request as user 2.
$this->setUser($user2);
$d2 = api::create_data_request($user2->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
return [
// Own data requests.
[$users, $user1, false, $completeonly],
// Non-DPO fetching all requets.
[$users, $user2, true, $completeonly],
// Admin fetching all completed and cancelled requests.
[$users, get_admin(), true, $completeandcancelled],
// Admin fetching all completed requests.
[$users, get_admin(), true, $completeonly],
// Guest fetching all requests.
[$users, guest_user(), true, $completeonly],
];
}
// Fetching data requests of specific users.
$requests = api::get_data_requests($user1->id);
$this->assertCount(1, $requests);
$datarequest = reset($requests);
$this->assertEquals($d1->to_record(), $datarequest->to_record());
/**
* Test for api::get_data_requests()
*
* @dataProvider get_data_requests_provider
* @param stdClass[] $users Array of users to create data requests for.
* @param stdClass $loggeduser The user logging in.
* @param boolean $fetchall Whether to fetch all records.
* @param int[] $statuses Status filters.
*/
public function test_get_data_requests($users, $loggeduser, $fetchall, $statuses) {
$comment = 'Data %s request comment by user %d';
$exportstring = helper::get_shortened_request_type_string(api::DATAREQUEST_TYPE_EXPORT);
$deletionstring = helper::get_shortened_request_type_string(api::DATAREQUEST_TYPE_DELETE);
// Make a data requests for the users.
foreach ($users as $user) {
$this->setUser($user);
api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, sprintf($comment, $exportstring, $user->id));
api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, sprintf($comment, $deletionstring, $user->id));
}
$requests = api::get_data_requests($user2->id);
$this->assertCount(1, $requests);
$datarequest = reset($requests);
$this->assertEquals($d2->to_record(), $datarequest->to_record());
// Log in as the target user.
$this->setUser($loggeduser);
// Get records count based on the filters.
$userid = $loggeduser->id;
if ($fetchall) {
$userid = 0;
}
$count = api::get_data_requests_count($userid);
if (api::is_site_dpo($loggeduser->id)) {
// DPOs should see all the requests.
$this->assertEquals(count($users) * 2, $count);
} else {
if (empty($userid)) {
// There should be no data requests for this user available.
$this->assertEquals(0, $count);
} else {
// There should be only one (request with pending status).
$this->assertEquals(2, $count);
}
}
// Get data requests.
$requests = api::get_data_requests($userid);
// The number of requests should match the count.
$this->assertCount($count, $requests);
// Fetching data requests of all users.
// As guest.
$this->setGuestUser();
$requests = api::get_data_requests();
$this->assertEmpty($requests);
// Test filtering by status.
if ($count && !empty($statuses)) {
$filteredcount = api::get_data_requests_count($userid, $statuses);
// There should be none as they are all pending.
$this->assertEquals(0, $filteredcount);
$filteredrequests = api::get_data_requests($userid, $statuses);
$this->assertCount($filteredcount, $filteredrequests);
// As DPO (admin in this case, which is default if no site DPOs are set).
$this->setAdminUser();
$requests = api::get_data_requests();
$this->assertCount(2, $requests);
$statuscounts = [];
foreach ($statuses as $stat) {
$statuscounts[$stat] = 0;
}
$numstatus = count($statuses);
// Get all requests with status filter and update statuses, randomly.
foreach ($requests as $request) {
if (rand(0, 1)) {
continue;
}
if ($numstatus > 1) {
$index = rand(0, $numstatus - 1);
$status = $statuses[$index];
} else {
$status = reset($statuses);
}
$statuscounts[$status]++;
api::update_request_status($request->get('id'), $status);
}
$total = array_sum($statuscounts);
$filteredcount = api::get_data_requests_count($userid, $statuses);
$this->assertEquals($total, $filteredcount);
$filteredrequests = api::get_data_requests($userid, $statuses);
$this->assertCount($filteredcount, $filteredrequests);
// Confirm the filtered requests match the status filter(s).
foreach ($filteredrequests as $request) {
$this->assertContains($request->get('status'), $statuses);
}
if ($numstatus > 1) {
// Fetch by individual status to check the numbers match.
foreach ($statuses as $status) {
$filteredcount = api::get_data_requests_count($userid, [$status]);
$this->assertEquals($statuscounts[$status], $filteredcount);
$filteredrequests = api::get_data_requests($userid, [$status]);
$this->assertCount($filteredcount, $filteredrequests);
}
}
}
}
/**
@@ -142,7 +142,7 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
global $DB;
$purpose1 = api::create_purpose((object)['name' => 'p1', 'retentionperiod' => 'PT1H', 'lawfulbases' => 'gdpr_art_6_1_a']);
$purpose2 = api::create_purpose((object)['name' => 'p1', 'retentionperiod' => 'P1000Y', 'lawfulbases' => 'gdpr_art_6_1_b']);
$purpose2 = api::create_purpose((object)['name' => 'p1', 'retentionperiod' => 'P1Y', 'lawfulbases' => 'gdpr_art_6_1_b']);
$cat = api::create_category((object)['name' => 'a']);
$record = (object)[
@@ -165,8 +165,12 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
$course1 = $this->getDataGenerator()->create_course();
// Old course.
$course2 = $this->getDataGenerator()->create_course(['startdate' => '1', 'enddate' => '2']);
// Course finished last week (so purpose1 retention period does delete stuff but purpose2 retention period does not).
$dt = new \DateTime();
$di = new \DateInterval('P7D');
$dt->sub($di);
$course2 = $this->getDataGenerator()->create_course(['startdate' => '1', 'enddate' => $dt->getTimestamp()]);
$forum1 = $this->getDataGenerator()->create_module('forum', array('course' => $course2->id));
$forum2 = $this->getDataGenerator()->create_module('forum', array('course' => $course2->id));
@@ -99,8 +99,8 @@ class helper {
'name' => $name,
'description' => $description,
'timecreated' => transform::datetime($record->timecreated),
'ip' => $record->ip,
'origin' => static::transform_origin($record->origin),
'ip' => $isauthor ? $record->ip : '',
'other' => $other ? $other : []
];
+3 -1
View File
@@ -74,7 +74,9 @@ class provider implements
* @param approved_contextlist $contextlist The approved contexts to export information for.
*/
public static function export_user_data(approved_contextlist $contextlist) {
static::call_subplugins_method_with_args('export_user_data', [$contextlist]);
if (get_config('tool_log', 'exportlog')) {
static::call_subplugins_method_with_args('export_user_data', [$contextlist]);
}
}
/**
+3
View File
@@ -42,5 +42,8 @@ function xmldb_tool_log_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+2
View File
@@ -24,6 +24,8 @@
$string['actlogshdr'] = 'Available log stores';
$string['configlogplugins'] = 'Please enable all required plugins and arrange them in appropriate order.';
$string['exportlog'] = 'Include logs when exporting.';
$string['exportlogdetail'] = 'Include logs that relate to the user when exporting.';
$string['logging'] = 'Logging';
$string['managelogging'] = 'Manage log stores';
$string['pluginname'] = 'Log store manager';
+10
View File
@@ -25,6 +25,16 @@
defined('MOODLE_INTERNAL') || die();
if ($hassiteconfig) {
$privacysettings = $ADMIN->locate('privacysettings');
if ($ADMIN->fulltree) {
$privacysettings->add(new admin_setting_configcheckbox('tool_log/exportlog',
new lang_string('exportlog', 'tool_log'),
new lang_string('exportlogdetail', 'tool_log'), 1)
);
}
$ADMIN->add('modules', new admin_category('logging', new lang_string('logging', 'tool_log')));
$temp = new admin_settingpage('managelogging', new lang_string('managelogging', 'tool_log'));
@@ -36,5 +36,8 @@ function xmldb_logstore_database_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -36,5 +36,8 @@ function xmldb_logstore_standard_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+1 -1
View File
@@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
$plugin->version = 2018051401; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2018050800; // Requires this Moodle version.
$plugin->component = 'tool_log'; // Full name of the plugin (used for diagnostics).
+2 -1
View File
@@ -343,7 +343,8 @@ class api {
$availablemods = core_plugin_manager::instance()->get_plugins_of_type('mod');
$coursemodules = array();
$appsupportedmodules = array('assign', 'book', 'chat', 'choice', 'data', 'feedback', 'folder', 'forum', 'glossary', 'imscp',
'label', 'lesson', 'lti', 'page', 'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki');
'label', 'lesson', 'lti', 'page', 'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop');
foreach ($availablemods as $mod) {
if (in_array($mod->name, $appsupportedmodules)) {
$coursemodules['$mmCourseDelegate_mmaMod' . ucfirst($mod->name)] = $mod->displayname;
+3
View File
@@ -76,5 +76,8 @@ function xmldb_tool_monitor_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -69,6 +69,9 @@ class page_agreedocs implements renderable, templatable {
/** @var array Info or error messages to show. */
protected $messages = [];
/** @var bool This is an existing user (rather than non-loggedin/guest). */
protected $isexistinguser;
/**
* Prepare the page for rendering.
*
@@ -87,6 +90,7 @@ class page_agreedocs implements renderable, templatable {
$this->action = $action;
$this->isexistinguser = isloggedin() && !isguestuser();
$behalfid = $behalfid ?: $USER->id;
if ($realuser->id != $behalfid) {
$this->behalfuser = core_user::get_user($behalfid, '*', MUST_EXIST);
@@ -112,7 +116,7 @@ class page_agreedocs implements renderable, templatable {
protected function accept_and_revoke_policies() {
global $USER;
if (!empty($USER->id)) {
if ($this->isexistinguser) {
// Existing user.
if (!empty($this->action) && confirm_sesskey()) {
// The form has been sent. Update policies acceptances according to $this->agreedocs.
@@ -182,15 +186,13 @@ class page_agreedocs implements renderable, templatable {
* Before display the consent page, the user has to view all the still-non-accepted policy docs.
* This function checks if the non-accepted policy docs have been shown and redirect to them.
*
* @param array $userid User identifier who wants to access to the consent page.
* @param url $returnurl URL to return after shown the policy docs.
* @param int $userid User identifier who wants to access to the consent page.
* @param moodle_url $returnurl URL to return after shown the policy docs.
*/
protected function redirect_to_policies($userid, $returnurl = null) {
global $USER;
$acceptances = api::get_user_acceptances($userid);
$allpolicies = $this->policies;
if (!empty($userid)) {
if ($this->isexistinguser) {
$acceptances = api::get_user_acceptances($userid);
foreach ($allpolicies as $policy) {
if (api::is_user_version_accepted($userid, $policy->id, $acceptances)) {
// If this version is accepted by the user, remove from the pending policies list.
@@ -234,16 +236,30 @@ class page_agreedocs implements renderable, templatable {
}
/**
* Redirect to $SESSION->wantsurl if defined or to $CFG->wwwroot if not.
* Redirect to signup page if defined or to $CFG->wwwroot if not.
*/
protected function redirect_to_previous_url() {
global $SESSION;
if (!empty($SESSION->wantsurl)) {
$returnurl = $SESSION->wantsurl;
unset($SESSION->wantsurl);
if ($this->isexistinguser) {
// Existing user.
if (!empty($SESSION->wantsurl)) {
$returnurl = $SESSION->wantsurl;
unset($SESSION->wantsurl);
} else {
$returnurl = new moodle_url('/admin/tool/policy/user.php');
}
} else {
$returnurl = (new moodle_url('/admin/tool/policy/user.php'))->out();
// Non-authenticated user.
$issignup = \cache::make('core', 'presignup')->get('tool_policy_issignup');
if ($issignup) {
// User came here from signup page - redirect back there.
$returnurl = new moodle_url('/login/signup.php');
\cache::make('core', 'presignup')->set('tool_policy_issignup', false);
} else {
// Guests should not be on this page unless it's part of signup - redirect home.
$returnurl = new moodle_url('/');
}
}
redirect($returnurl);
@@ -255,35 +271,35 @@ class page_agreedocs implements renderable, templatable {
* @param int $userid
*/
protected function prepare_global_page_access($userid) {
global $PAGE, $SESSION, $SITE, $USER;
global $PAGE, $SITE, $USER;
// Guest users or not logged users (but the users during the signup process) are not allowed to access to this page.
$newsignupuser = !empty($SESSION->wantsurl) && strpos($SESSION->wantsurl, 'login/signup.php') !== false;
if (isguestuser() || (empty($USER->id) && !$newsignupuser)) {
$newsignupuser = \cache::make('core', 'presignup')->get('tool_policy_issignup');
if (!$this->isexistinguser && !$newsignupuser) {
$this->redirect_to_previous_url();
}
// Check for correct user capabilities.
if (!empty($USER->id)) {
if ($this->isexistinguser) {
// For existing users, it's needed to check if they have the capability for accepting policies.
api::can_accept_policies($this->behalfid, true);
} else {
// For new users, the behalfid parameter is ignored.
if ($this->behalfid != $USER->id) {
if ($this->behalfid) {
redirect(new moodle_url('/admin/tool/policy/index.php'));
}
}
// If the current user has the $USER->policyagreed = 1 or $userpolicyagreed = 1
// and $SESSION->wantsurl is defined, redirect to the return page.
$hasagreedsignupuser = empty($USER->id) && $this->signupuserpolicyagreed;
// redirect to the return page.
$hasagreedsignupuser = !$this->isexistinguser && $this->signupuserpolicyagreed;
$hasagreedloggeduser = $USER->id == $userid && !empty($USER->policyagreed);
if (!is_siteadmin() && ($hasagreedsignupuser || $hasagreedloggeduser)) {
$this->redirect_to_previous_url();
}
$myparams = [];
if (!empty($USER->id) && !empty($this->behalfid) && $this->behalfid != $USER->id) {
if ($this->isexistinguser && !empty($this->behalfid) && $this->behalfid != $USER->id) {
$myparams['userid'] = $this->behalfid;
}
$myurl = new moodle_url('/admin/tool/policy/index.php', $myparams);
@@ -308,7 +324,6 @@ class page_agreedocs implements renderable, templatable {
global $USER;
// Get all the policy version acceptances for this user.
$acceptances = api::get_user_acceptances($userid);
$lang = current_language();
foreach ($this->policies as $policy) {
// Get a link to display the full policy document.
@@ -320,9 +335,10 @@ class page_agreedocs implements renderable, templatable {
$policymodal = html_writer::link($policy->url, $policy->name, $policyattributes);
// Check if this policy version has been agreed or not.
if (!empty($userid)) {
if ($this->isexistinguser) {
// Existing user.
$versionagreed = false;
$acceptances = api::get_user_acceptances($userid);
$policy->versionacceptance = api::get_user_version_acceptance($userid, $policy->id, $acceptances);
if (!empty($policy->versionacceptance)) {
// The policy version has ever been agreed. Check if status = 1 to know if still is accepted.
@@ -352,13 +368,13 @@ class page_agreedocs implements renderable, templatable {
* Export the page data for the mustache template.
*
* @param renderer_base $output renderer to be used to render the page elements.
* @return stdClass
* @return \stdClass
*/
public function export_for_template(renderer_base $output) {
global $USER;
$myparams = [];
if (!empty($USER->id) && !empty($this->behalfid) && $this->behalfid != $USER->id) {
if ($this->isexistinguser && !empty($this->behalfid) && $this->behalfid != $USER->id) {
$myparams['userid'] = $this->behalfid;
}
$data = (object) [
@@ -63,14 +63,7 @@ class page_viewalldoc implements renderable, templatable {
*
*/
protected function prepare_policies() {
global $USER;
if (isguestuser() || empty($USER->id)) {
$audience = policy_version::AUDIENCE_GUESTS;
} else {
$audience = policy_version::AUDIENCE_LOGGEDIN;
}
$this->policies = api::list_current_versions($audience);
$this->policies = api::list_current_versions();
}
/**
+1 -1
View File
@@ -45,7 +45,7 @@ $PAGE->set_pagelayout('standard');
$PAGE->set_url('/admin/tool/policy/index.php');
$PAGE->set_popup_notification_allowed(false);
if (!empty($USER->id)) {
if (isloggedin() && !isguestuser()) {
// Existing user.
$haspermissionagreedocs = api::can_accept_policies($behalfid);
} else {
+2 -1
View File
@@ -54,7 +54,7 @@ $string['backtotop'] = 'Back to top';
$string['consentbulk'] = 'Consent';
$string['consentdetails'] = 'Give consent on behalf of user';
$string['consentpagetitle'] = 'Consent';
$string['contactdpo'] = 'For questions regarding the policies please contact the Data Protection Officer.';
$string['contactdpo'] = 'For any questions about the policies please contact the privacy officer.';
$string['dataproc'] = 'Personal data processing';
$string['deleting'] = 'Deleting a version';
$string['deleteconfirm'] = '<p>Are you sure you want to delete policy <em>\'{$a->name}\'</em>?</p><p>This operation can not be undone.</p>';
@@ -92,6 +92,7 @@ $string['moveup'] = 'Move up';
$string['mustagreetocontinue'] = 'Before continuing you must agree to all these policies.';
$string['newpolicy'] = 'New policy';
$string['newversion'] = 'New version';
$string['noactivepolicies'] = 'There are no policies with an active version.';
$string['nofiltersapplied'] = 'No filters applied';
$string['nopermissiontoagreedocs'] = 'No permission to agree to the policies';
$string['nopermissiontoagreedocs_desc'] = 'Sorry, you do not have the required permissions to agree to the policies.<br />You will not be able to use this site until the following policies are agreed:';
+9 -6
View File
@@ -83,7 +83,7 @@ function tool_policy_before_standard_html_head() {
if (!empty($CFG->sitepolicyhandler)
&& $CFG->sitepolicyhandler == 'tool_policy'
&& empty($USER->policyagreed)
&& isguestuser()) {
&& (isguestuser() || !isloggedin())) {
$output = $PAGE->get_renderer('tool_policy');
$page = new \tool_policy\output\guestconsent();
@@ -104,9 +104,12 @@ function tool_policy_standard_footer_html() {
$output = '';
if (!empty($CFG->sitepolicyhandler)
&& $CFG->sitepolicyhandler == 'tool_policy') {
$url = (new moodle_url('/admin/tool/policy/viewall.php'))->out();
$output .= html_writer::link($url, get_string('userpolicysettings', 'tool_policy'));
$output = html_writer::div($output, 'policiesfooter');
$policies = api::get_current_versions_ids();
if (!empty($policies)) {
$url = (new moodle_url('/admin/tool/policy/viewall.php'))->out();
$output .= html_writer::link($url, get_string('userpolicysettings', 'tool_policy'));
$output = html_writer::div($output, 'policiesfooter');
}
}
return $output;
@@ -116,7 +119,7 @@ function tool_policy_standard_footer_html() {
* Hooks redirection to policy acceptance pages before sign up.
*/
function tool_policy_pre_signup_requests() {
global $CFG, $SESSION;
global $CFG;
// Do nothing if we are not set as the site policies handler.
if (empty($CFG->sitepolicyhandler) || $CFG->sitepolicyhandler !== 'tool_policy') {
@@ -127,7 +130,7 @@ function tool_policy_pre_signup_requests() {
$userpolicyagreed = cache::make('core', 'presignup')->get('tool_policy_userpolicyagreed');
if (!empty($policies) && !$userpolicyagreed) {
// Redirect to "Policy" pages for consenting before creating the user.
$SESSION->wantsurl = (new \moodle_url('/login/signup.php'))->out();
cache::make('core', 'presignup')->set('tool_policy_issignup', 1);
redirect(new \moodle_url('/admin/tool/policy/index.php'));
}
}
@@ -58,6 +58,10 @@
</ul>
</div>
{{^policies }}
{{# str }} noactivepolicies, tool_policy {{/ str }}
{{/policies }}
{{#policies }}
<hr>
<div class="policy_version m-b-3">
+69 -17
View File
@@ -81,12 +81,12 @@ Feature: User must accept policy managed by this plugin when logging in and sign
And I am on site homepage
And I follow "Log in"
When I press "Create new account"
Then I should see "This site policy"
Then I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should see "full text2"
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "This site policy"
And I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should not see "full text2"
And I set the field "I agree to the This site policy" to "1"
@@ -129,22 +129,22 @@ Feature: User must accept policy managed by this plugin when logging in and sign
And I am on site homepage
And I follow "Log in"
When I press "Create new account"
Then I should see "This site policy"
Then I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should see "full text2"
And I press "Next"
And I should see "This privacy policy"
And I should see "This privacy policy" in the "region-main" "region"
And I should see "short text3"
And I should see "full text3"
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "This site policy"
And I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should not see "full text2"
And I should see "This privacy policy"
And I should see "This privacy policy" in the "region-main" "region"
And I should see "short text3"
And I should not see "full text3"
And I should not see "This guests policy"
And I should not see "This guests policy" in the "region-main" "region"
And I should not see "short text4"
And I should not see "full text4"
And I set the field "I agree to the This site policy" to "1"
@@ -495,20 +495,20 @@ Feature: User must accept policy managed by this plugin when logging in and sign
And I am on site homepage
And I follow "Log in"
When I press "Create new account"
Then I should see "This site policy"
Then I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should see "full text2"
When I press "Next"
Then I should see "This privacy policy"
Then I should see "This privacy policy" in the "region-main" "region"
And I should see "short text3"
And I should see "full text3"
When I press "Next"
Then I should see "Please agree to the following policies"
And I should see "This site policy"
And I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should see "This privacy policy"
And I should see "This privacy policy" in the "region-main" "region"
And I should see "short text3"
And I should not see "This guests policy"
And I should not see "This guests policy" in the "region-main" "region"
And I should not see "short text4"
And I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "1"
@@ -528,22 +528,22 @@ Feature: User must accept policy managed by this plugin when logging in and sign
And I follow "Log in"
When I press "Create new account"
# Confirm that the user can view and accept policies when attempting to create another account.
Then I should see "This site policy"
Then I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should see "full text2"
When I press "Next"
Then I should see "This privacy policy"
Then I should see "This privacy policy" in the "region-main" "region"
And I should see "short text3"
And I should see "full text3"
When I press "Next"
Then I should see "Please agree to the following policies"
And I should see "This site policy"
And I should see "This site policy" in the "region-main" "region"
And I should see "short text2"
And I should not see "full text2"
And I should see "This privacy policy"
And I should see "This privacy policy" in the "region-main" "region"
And I should see "short text3"
And I should not see "full text3"
And I should not see "This guests policy"
And I should not see "This guests policy" in the "region-main" "region"
And I should not see "short text4"
And I should not see "full text4"
And I set the field "I agree to the This site policy" to "1"
@@ -611,3 +611,55 @@ Feature: User must accept policy managed by this plugin when logging in and sign
And I should see "Policies and agreements"
And I should see "No permission to agree to the policies on behalf of this user"
And I should see "Sorry, you do not have the required permission to agree to the following policies on behalf of User 1"
Scenario: Accept policy on sign up as a guest, one policy
Given the following config values are set as admin:
| registerauth | email |
| passwordpolicy | 0 |
| sitepolicyhandler | tool_policy |
Given the following policies exist:
| Policy | Name | Revision | Content | Summary | Status |
| P1 | This site policy | | full text1 | short text1 | archived |
| P1 | This site policy | | full text2 | short text2 | active |
| P1 | This site policy | | full text3 | short text3 | draft |
And I am on site homepage
And I follow "Log in"
# First log in as a guest
And I press "Log in as a guest"
# Now sign up
And I follow "Log in"
When I press "Create new account"
Then I should see "This site policy"
And I should see "short text2"
And I should see "full text2"
And I press "Next"
And I should see "Please agree to the following policies"
And I should see "This site policy"
And I should see "short text2"
And I should not see "full text2"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
And I should not see "I understand and agree"
And I set the following fields to these values:
| Username | user1 |
| Password | user1 |
| Email address | user1@address.invalid |
| Email (again) | user1@address.invalid |
| First name | User1 |
| Surname | L1 |
And I press "Create my new account"
And I should see "Confirm your account"
And I should see "An email should have been sent to your address at user1@address.invalid"
And I confirm email for "user1"
And I should see "Thanks, User1 L1"
And I should see "Your registration has been confirmed"
And I open my profile in edit mode
And the field "First name" matches value "User1"
And I log out
# Confirm that user can login and browse the site.
And I log in as "user1"
And I follow "Profile" in the user menu
# User can see his own agreements in the profile.
And I follow "Policies and agreements"
And "Agreed" "icon" should exist in the "This site policy" "table_row"
And I log out
+23 -7
View File
@@ -288,13 +288,24 @@ class category_bin extends base_bin {
global $DB;
// Grab the course category context.
$context = \context_coursecat::instance($this->_categoryid);
// Delete the files.
$fs = get_file_storage();
$files = $fs->get_area_files($context->id, 'tool_recyclebin', TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA, $item->id);
foreach ($files as $file) {
$file->delete();
$context = \context_coursecat::instance($this->_categoryid, IGNORE_MISSING);
if (!empty($context)) {
// Delete the files.
$fs = get_file_storage();
$fs->delete_area_files($context->id, 'tool_recyclebin', TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA, $item->id);
} else {
// Course category has been deleted. Find records using $item->id as this is unique for coursecat recylebin.
$files = $DB->get_recordset('files', [
'component' => 'tool_recyclebin',
'filearea' => TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA,
'itemid' => $item->id,
]);
$fs = get_file_storage();
foreach ($files as $filer) {
$file = $fs->get_file_instance($filer);
$file->delete();
}
$files->close();
}
// Delete the record.
@@ -302,6 +313,11 @@ class category_bin extends base_bin {
'id' => $item->id
));
// The coursecat might have been deleted, check we have a context before triggering event.
if (!$context) {
return;
}
// Fire event.
$event = \tool_recyclebin\event\category_bin_item_deleted::create(array(
'objectid' => $item->id,
+18 -6
View File
@@ -274,13 +274,25 @@ class course_bin extends base_bin {
global $DB;
// Grab the course context.
$context = \context_course::instance($this->_courseid);
$context = \context_course::instance($this->_courseid, IGNORE_MISSING);
// Delete the files.
$fs = get_file_storage();
$files = $fs->get_area_files($context->id, 'tool_recyclebin', TOOL_RECYCLEBIN_COURSE_BIN_FILEAREA, $item->id);
foreach ($files as $file) {
$file->delete();
if (!empty($context)) {
// Delete the files.
$fs = get_file_storage();
$fs->delete_area_files($context->id, 'tool_recyclebin', TOOL_RECYCLEBIN_COURSE_BIN_FILEAREA, $item->id);
} else {
// Course context has been deleted. Find records using $item->id as this is unique for course bin recyclebin.
$files = $DB->get_recordset('files', [
'component' => 'tool_recyclebin',
'filearea' => TOOL_RECYCLEBIN_COURSE_BIN_FILEAREA,
'itemid' => $item->id,
]);
$fs = get_file_storage();
foreach ($files as $filer) {
$file = $fs->get_file_instance($filer);
$file->delete();
}
$files->close();
}
// Delete the record.
+3
View File
@@ -51,5 +51,8 @@ function xmldb_tool_usertours_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -124,7 +124,7 @@ class edit_table extends XMLDBAction {
$o.= ' <input type="hidden" name ="action" value="edit_table_save" />';
$o.= ' <input type="hidden" name ="sesskey" value="' . sesskey() .'" />';
$o.= ' <input type="hidden" name ="postaction" value="edit_table" />';
$o.= ' <table id="formelements" class="boxaligncenter">';
$o .= ' <table id="formelements">';
// If the table is being used, we cannot rename it
if ($structure->getTableUses($table->getName())) {
$o.= ' <tr valign="top"><td>Name:</td><td><input type="hidden" name ="name" value="' . s($table->getName()) . '" />' . s($table->getName()) .'</td></tr>';
@@ -243,7 +243,7 @@ class edit_table extends XMLDBAction {
// The readable info
$r = '</td><td class="readableinfo cell">' . $field->readableInfo() . '</td>';
// Print table row
$o .= '<tr class="r' . $row . '"><td class="table cell">' . $f . $b . $r . '</tr>';
$o .= '<tr class="r' . $row . '"><td class="cell firstcol">' . $f . $b . $r . '</tr>';
$row = ($row + 1) % 2;
}
$o .= '</table>';
@@ -296,7 +296,7 @@ class edit_table extends XMLDBAction {
// The readable info
$r = '</td><td class="readableinfo cell">' . $key->readableInfo() . '</td>';
// Print table row
$o .= '<tr class="r' . $row . '"><td class="table cell">' . $k . $b . $r .'</tr>';
$o .= '<tr class="r' . $row . '"><td class="cell firstcol">' . $k . $b . $r .'</tr>';
$row = ($row + 1) % 2;
}
$o .= '</table>';
@@ -337,7 +337,7 @@ class edit_table extends XMLDBAction {
// The readable info
$r = '</td><td class="readableinfo cell">' . $index->readableInfo() . '</td>';
// Print table row
$o .= '<tr class="r' . $row . '"><td class="table cell">' . $i . $b . $r .'</tr>';
$o .= '<tr class="r' . $row . '"><td class="cell firstcol">' . $i . $b . $r .'</tr>';
$row = ($row + 1) % 2;
}
$o .= '</table>';
@@ -115,7 +115,7 @@ class edit_xml_file extends XMLDBAction {
$o.= ' <input type="hidden" name ="path" value="' . s($structure->getPath()) .'" />';
$o.= ' <input type="hidden" name ="version" value="' . s($structure->getVersion()) .'" />';
$o.= ' <input type="hidden" name ="sesskey" value="' . sesskey() .'" />';
$o.= ' <table id="formelements" class="boxaligncenter">';
$o .= ' <table id="formelements">';
$o.= ' <tr valign="top"><td>Path:</td><td>' . s($structure->getPath()) . '</td></tr>';
$o.= ' <tr valign="top"><td>Version:</td><td>' . s($structure->getVersion()) . '</td></tr>';
$o.= ' <tr valign="top"><td><label for="comment" accesskey="c">Comment:</label></td><td><textarea name="comment" rows="3" cols="80" id="comment">' . $structure->getComment() . '</textarea></td></tr>';
@@ -216,7 +216,7 @@ class edit_xml_file extends XMLDBAction {
}
$b .= '</td>';
// Print table row
$o .= '<tr class="r' . $row . '"><td class="table cell">' . $t . $b . '</tr>';
$o .= '<tr class="r' . $row . '"><td class="cell firstcol">' . $t . $b . '</tr>';
$row = ($row + 1) % 2;
}
$o .= '</table>';
@@ -162,7 +162,7 @@ class view_table_php extends XMLDBAction {
$o.= ' <input type="hidden" name ="dir" value="' . str_replace($CFG->dirroot, '', $dirpath) . '" />';
$o.= ' <input type="hidden" name ="table" value="' . s($tableparam) . '" />';
$o.= ' <input type="hidden" name ="action" value="view_table_php" />';
$o.= ' <table id="formelements" class="boxaligncenter" cellpadding="5">';
$o .= ' <table id="formelements" cellpadding="5">';
$o.= ' <tr><td><label for="menucommand" accesskey="c">' . $this->str['selectaction'] .' </label>' . html_writer::select($popcommands, 'command', $commandparam, false) . '&nbsp;<label for="menufieldkeyindex" accesskey="f">' . $this->str['selectfieldkeyindex'] . ' </label>' .html_writer::select($popfields, 'fieldkeyindex', $origfieldkeyindexparam, false) . '</td></tr>';
$o.= ' <tr><td colspan="2" align="center"><input type="submit" value="' .$this->str['view'] . '" /></td></tr>';
$o.= ' </table>';
+3
View File
@@ -48,5 +48,8 @@ function xmldb_auth_cas_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+6 -6
View File
@@ -709,12 +709,12 @@ class auth_plugin_db extends auth_plugin_base {
raise_memory_limit(MEMORY_HUGE);
if (empty($this->config->table)) {
echo $OUTPUT->notification('External table not specified.', 'notifyproblem');
echo $OUTPUT->notification(get_string('auth_dbnoexttable', 'auth_db'), 'notifyproblem');
return;
}
if (empty($this->config->fielduser)) {
echo $OUTPUT->notification('External user field not specified.', 'notifyproblem');
echo $OUTPUT->notification(get_string('auth_dbnouserfield', 'auth_db'), 'notifyproblem');
return;
}
@@ -735,7 +735,7 @@ class auth_plugin_db extends auth_plugin_base {
error_reporting($CFG->debug);
ob_end_flush();
echo $OUTPUT->notification('Cannot connect the database.', 'notifyproblem');
echo $OUTPUT->notification(get_string('auth_dbcannotconnect', 'auth_db'), 'notifyproblem');
return;
}
@@ -744,17 +744,17 @@ class auth_plugin_db extends auth_plugin_base {
WHERE {$this->config->fielduser} <> 'random_unlikely_username'"); // Any unlikely name is ok here.
if (!$rs) {
echo $OUTPUT->notification('Can not read external table.', 'notifyproblem');
echo $OUTPUT->notification(get_string('auth_dbcannotreadtable', 'auth_db'), 'notifyproblem');
} else if ($rs->EOF) {
echo $OUTPUT->notification('External table is empty.', 'notifyproblem');
echo $OUTPUT->notification(get_string('auth_dbtableempty', 'auth_db'), 'notifyproblem');
$rs->close();
} else {
$fields_obj = $rs->FetchObj();
$columns = array_keys((array)$fields_obj);
echo $OUTPUT->notification('External table contains following columns:<br />'.implode(', ', $columns), 'notifysuccess');
echo $OUTPUT->notification(get_string('auth_dbcolumnlist', 'auth_db', implode(', ', $columns)), 'notifysuccess');
$rs->close();
}
+3
View File
@@ -48,5 +48,8 @@ function xmldb_auth_db_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+6
View File
@@ -68,5 +68,11 @@ $string['auth_dbuser'] = 'Username with read access to the database';
$string['auth_dbuser_key'] = 'DB user';
$string['auth_dbuserstoadd'] = 'User entries to add: {$a}';
$string['auth_dbuserstoremove'] = 'User entries to remove: {$a}';
$string['auth_dbnoexttable'] = 'External table not specified.';
$string['auth_dbnouserfield'] = 'External user field not specified.';
$string['auth_dbcannotconnect'] = 'Cannot connect to external database.';
$string['auth_dbcannotreadtable'] = 'Cannot read external table.';
$string['auth_dbtableempty'] = 'External table is empty.';
$string['auth_dbcolumnlist'] = 'External table contains the following columns:<br />{$a}';
$string['pluginname'] = 'External database';
$string['privacy:metadata'] = 'The External database authentication plugin does not store any personal data.';
+3
View File
@@ -48,5 +48,8 @@ function xmldb_auth_email_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+11 -2
View File
@@ -1187,9 +1187,18 @@ class auth_plugin_ldap extends auth_plugin_base {
$nuvalue = core_text::convert($newvalue, 'utf-8', $this->config->ldapencoding);
empty($nuvalue) ? $nuvalue = array() : $nuvalue;
$ouvalue = core_text::convert($oldvalue, 'utf-8', $this->config->ldapencoding);
foreach ($ldapkeys as $ldapkey) {
$ldapkey = $ldapkey;
// Skip update if $ldapkey does not exist in LDAP.
if (!isset($user_entry[$ldapkey][0])) {
$success = false;
error_log($this->errorlogtag.get_string('updateremfailfield', 'auth_ldap',
array('ldapkey' => $ldapkey,
'key' => $key,
'ouvalue' => $ouvalue,
'nuvalue' => $nuvalue)));
continue;
}
$ldapvalue = $user_entry[$ldapkey][0];
if (!$ambiguous) {
// Skip update if the values already match
+3
View File
@@ -66,5 +66,8 @@ function xmldb_auth_ldap_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+1
View File
@@ -149,6 +149,7 @@ $string['start_tls'] = 'Use regular LDAP service (port 389) with TLS encryption'
$string['start_tls_key'] = 'Use TLS';
$string['updateremfail'] = 'Error updating LDAP record. Error code: {$a->errno}; Error string: {$a->errstring}<br/>Key ({$a->key}) - old moodle value: \'{$a->ouvalue}\' new value: \'{$a->nuvalue}\'';
$string['updateremfailamb'] = 'Failed to update LDAP with ambiguous field {$a->key}; old moodle value: \'{$a->ouvalue}\', new value: \'{$a->nuvalue}\'';
$string['updateremfailfield'] = 'Failed to update LDAP with non-existent field (\'{$a->ldapkey}\'). Key ({$a->key}) - old moodle value: \'{$a->ouvalue}\' new value: \'{$a->nuvalue}\'';
$string['updatepasserror'] = 'Error in user_update_password(). Error code: {$a->errno}; Error string: {$a->errstring}';
$string['updatepasserrorexpire'] = 'Error in user_update_password() when reading password expiry time. Error code: {$a->errno}; Error string: {$a->errstring}';
$string['updatepasserrorexpiregrace'] = 'Error in user_update_password() when modifying expirationtime and/or gracelogins. Error code: {$a->errno}; Error string: {$a->errstring}';
+3
View File
@@ -48,5 +48,8 @@ function xmldb_auth_manual_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -47,5 +47,8 @@ function xmldb_auth_mnet_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+9 -9
View File
@@ -38,7 +38,7 @@ $string['pluginname'] = 'MNet authentication';
$string['privacy:metadata:external:mahara'] = 'This plugin can send data externally to a linked Mahara application.';
$string['privacy:metadata:external:moodle'] = 'This plugin can send data externally to a linked Moodle application.';
$string['privacy:metadata:mnet_external:address'] = 'The address of the user.';
$string['privacy:metadata:mnet_external:aim'] = 'The AIM identifier of the user.';
$string['privacy:metadata:mnet_external:aim'] = 'The AIM identifier of the user';
$string['privacy:metadata:mnet_external:alternatename'] = 'An alternative name for the user.';
$string['privacy:metadata:mnet_external:autosubscribe'] = 'A preference as to if the user should be auto-subscribed to forums the user posts in.';
$string['privacy:metadata:mnet_external:calendartype'] = 'A user preference for the type of calendar to use.';
@@ -53,8 +53,8 @@ $string['privacy:metadata:mnet_external:firstaccess'] = 'The time that this user
$string['privacy:metadata:mnet_external:firstname'] = 'The first name of the user.';
$string['privacy:metadata:mnet_external:firstnamephonetic'] = 'The phonetic details about the user\'s first name.';
$string['privacy:metadata:mnet_external:icq'] = 'The ICQ number of the user.';
$string['privacy:metadata:mnet_external:id'] = 'The identifier for the user.';
$string['privacy:metadata:mnet_external:idnumber'] = 'An identification number given by the institution.';
$string['privacy:metadata:mnet_external:id'] = 'The user ID';
$string['privacy:metadata:mnet_external:idnumber'] = 'An identification number given by the institution';
$string['privacy:metadata:mnet_external:imagealt'] = 'Alternative text for the user\'s image.';
$string['privacy:metadata:mnet_external:institution'] = 'The institution that this user is a member of.';
$string['privacy:metadata:mnet_external:lang'] = 'A user preference for the language shown.';
@@ -64,20 +64,20 @@ $string['privacy:metadata:mnet_external:lastname'] = 'The surname of the user.';
$string['privacy:metadata:mnet_external:lastnamephonetic'] = 'The phonetic details about the user\'s surname.';
$string['privacy:metadata:mnet_external:maildigest'] = 'A setting for the mail digest for this user.';
$string['privacy:metadata:mnet_external:maildisplay'] = 'A preference for the user about displaying their email address to other users.';
$string['privacy:metadata:mnet_external:middlename'] = 'The middle name of the user.';
$string['privacy:metadata:mnet_external:msn'] = 'The MSN identifier of the user.';
$string['privacy:metadata:mnet_external:middlename'] = 'The middle name of the user';
$string['privacy:metadata:mnet_external:msn'] = 'The MSN identifier of the user';
$string['privacy:metadata:mnet_external:phone1'] = 'A phone number for the user.';
$string['privacy:metadata:mnet_external:phone2'] = 'An additional phone number for the user.';
$string['privacy:metadata:mnet_external:picture'] = 'The picture details associated with this user.';
$string['privacy:metadata:mnet_external:policyagreed'] = 'A flag to determine if the user has agreed to the site policy.';
$string['privacy:metadata:mnet_external:skype'] = 'The skype identifier of the user.';
$string['privacy:metadata:mnet_external:skype'] = 'The Skype identifier of the user';
$string['privacy:metadata:mnet_external:suspended'] = 'A flag to show if the user has been suspended on this system.';
$string['privacy:metadata:mnet_external:timezone'] = 'The timezone that the user resides in.';
$string['privacy:metadata:mnet_external:timezone'] = 'The timezone of the user';
$string['privacy:metadata:mnet_external:trackforums'] = 'A preference for forums and tracking them.';
$string['privacy:metadata:mnet_external:trustbitmask'] = 'The trust bit mask';
$string['privacy:metadata:mnet_external:url'] = 'A URL related to this user.';
$string['privacy:metadata:mnet_external:username'] = 'The username for this user.';
$string['privacy:metadata:mnet_external:yahoo'] = 'The yahoo identifier of the user.';
$string['privacy:metadata:mnet_external:yahoo'] = 'The Yahoo identifier of the user';
$string['privacy:metadata:mnet_log'] = 'Details of remote actions carried out by a local user logged in a remote system.';
$string['privacy:metadata:mnet_log:action'] = 'Action carried out by the user.';
$string['privacy:metadata:mnet_log:cmid'] = 'ID of the course module.';
@@ -94,7 +94,7 @@ $string['privacy:metadata:mnet_log:userid'] = 'Local ID of the user who carried
$string['privacy:metadata:mnet_session'] = 'The details of each MNet user session in a remote system is stored temporarily.';
$string['privacy:metadata:mnet_session:expires'] = 'Time when the session expires.';
$string['privacy:metadata:mnet_session:mnethostid'] = 'Remote system MNet ID.';
$string['privacy:metadata:mnet_session:token'] = 'Unique session identifier.';
$string['privacy:metadata:mnet_session:token'] = 'Unique session identifier';
$string['privacy:metadata:mnet_session:useragent'] = 'String denoting the user agent being which is accessing the page.';
$string['privacy:metadata:mnet_session:userid'] = 'ID of the user jumping to remote system.';
$string['privacy:metadata:mnet_session:username'] = 'Username of the user jumping to remote system.';
+3
View File
@@ -48,5 +48,8 @@ function xmldb_auth_none_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -44,5 +44,8 @@ function xmldb_auth_oauth2_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+1 -1
View File
@@ -93,7 +93,7 @@ $string['privacy:metadata:auth_oauth2:authsubsystem'] = 'This plugin is connecte
$string['privacy:metadata:auth_oauth2:confirmtoken'] = 'The confirmation token.';
$string['privacy:metadata:auth_oauth2:confirmtokenexpires'] = 'The timestamp when the confirmation token expires.';
$string['privacy:metadata:auth_oauth2:email'] = 'The external email that maps to this account.';
$string['privacy:metadata:auth_oauth2:issuerid'] = 'The identifier of the OAuth 2 issuer for this OAuth 2 login.';
$string['privacy:metadata:auth_oauth2:issuerid'] = 'The ID of the OAuth 2 issuer for this OAuth 2 login';
$string['privacy:metadata:auth_oauth2:tableexplanation'] = 'OAuth 2 accounts linked to a user\'s Moodle account.';
$string['privacy:metadata:auth_oauth2:timecreated'] = 'The timestamp when the user account was linked to the OAuth 2 login.';
$string['privacy:metadata:auth_oauth2:timemodified'] = 'The timestamp when this record was modified.';
+3
View File
@@ -48,5 +48,8 @@ function xmldb_auth_shibboleth_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -244,7 +244,7 @@ class restore_stepslib_date_testcase extends restore_date_testcase {
// Testing the restore of an overridden grade.
list($course, $assign) = $this->create_course_and_module('assign', []);
$cm = $DB->get_record('course_modules', ['course' => $course->id, 'instance' => $assign->id]);
$assignobj = new testable_assign(context_module::instance($cm->id), $cm, $course);
$assignobj = new mod_assign_testable_assign(context_module::instance($cm->id), $cm, $course);
$submission = $assignobj->get_user_submission($USER->id, true);
$grade = $assignobj->get_user_grade($USER->id, true);
$grade->grade = 75;
@@ -300,7 +300,7 @@ class restore_stepslib_date_testcase extends restore_date_testcase {
'completionusegrade' => 1 // Student must receive a grade to complete this activity.
]);
$cm = $DB->get_record('course_modules', ['course' => $course->id, 'instance' => $assign->id]);
$assignobj = new testable_assign(context_module::instance($cm->id), $cm, $course);
$assignobj = new mod_assign_testable_assign(context_module::instance($cm->id), $cm, $course);
$submission = $assignobj->get_user_submission($USER->id, true);
$grade = $assignobj->get_user_grade($USER->id, true);
$grade->grade = 75;
@@ -410,4 +410,4 @@ class restore_stepslib_date_testcase extends restore_date_testcase {
$this->assertEquals($dates['originaldate'], $dates['restoredate']);
}
}
}
}
+10 -2
View File
@@ -183,7 +183,11 @@ class backup_ui_stage_initial extends backup_ui_stage {
foreach ($tasks as &$task) {
// For the initial stage we are only interested in the root settings.
if ($task instanceof backup_root_task) {
$form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
if ($this->ui instanceof import_ui) {
$form->add_heading('rootsettings', get_string('importrootsettings', 'backup'));
} else {
$form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
}
$settings = $task->get_settings();
// First add all settings except the filename setting.
foreach ($settings as &$setting) {
@@ -469,7 +473,11 @@ class backup_ui_stage_confirmation extends backup_ui_stage {
foreach ($tasks as $task) {
if ($task instanceof backup_root_task) {
// If its a backup root add a root settings heading to group nicely.
$form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
if ($this->ui instanceof import_ui) {
$form->add_heading('rootsettings', get_string('importrootsettings', 'backup'));
} else {
$form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
}
} else if (!$courseheading) {
// We haven't already add a course heading.
$form->add_heading('coursesettings', get_string('includeditems', 'backup'));
+7 -1
View File
@@ -383,7 +383,13 @@ abstract class base_moodleform extends moodleform {
$this->require_definition_after_data();
$config = new stdClass;
$config->title = get_string('confirmcancel', 'backup');
if ($this->uistage->get_ui() instanceof import_ui) {
$config->title = get_string('confirmcancelimport', 'backup');
} else if ($this->uistage->get_ui() instanceof restore_ui) {
$config->title = get_string('confirmcancelrestore', 'backup');
} else {
$config->title = get_string('confirmcancel', 'backup');
}
$config->question = get_string('confirmcancelquestion', 'backup');
$config->yesLabel = get_string('confirmcancelyes', 'backup');
$config->noLabel = get_string('confirmcancelno', 'backup');
+21 -2
View File
@@ -901,7 +901,7 @@ class restore_ui_stage_review extends restore_ui_stage {
foreach ($tasks as $task) {
if ($task instanceof restore_root_task) {
// If its a backup root add a root settings heading to group nicely.
$form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
$form->add_heading('rootsettings', get_string('restorerootsettings', 'backup'));
} else if (!$courseheading) {
// We haven't already add a course heading.
$form->add_heading('coursesettings', get_string('coursesettings', 'backup'));
@@ -1061,7 +1061,26 @@ class restore_ui_stage_process extends restore_ui_stage {
if (!empty($info->role_mappings->mappings)) {
$context = context_course::instance($this->ui->get_controller()->get_courseid());
$assignableroles = get_assignable_roles($context, ROLENAME_ALIAS, false);
$html .= $renderer->role_mappings($info->role_mappings->mappings, $assignableroles);
// Get current role mappings.
$currentroles = role_fix_names(get_all_roles(), $context);
// Get backup role mappings.
$rolemappings = $info->role_mappings->mappings;
array_map(function($rolemapping) use ($currentroles) {
foreach ($currentroles as $role) {
// Find matching archetype to determine the backup's shortname for label display.
if ($rolemapping->archetype == $role->archetype) {
$rolemapping->name = $rolemapping->shortname;
break;
}
}
if ($rolemapping->name == null) {
$rolemapping->name = get_string('undefinedrolemapping', 'backup', $rolemapping->archetype);
}
}, $rolemappings);
$html .= $renderer->role_mappings($rolemappings, $assignableroles);
}
break;
default:
+3
View File
@@ -54,5 +54,8 @@ function xmldb_block_badges_upgrade($oldversion, $block) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -54,5 +54,8 @@ function xmldb_block_calendar_month_upgrade($oldversion, $block) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -54,5 +54,8 @@ function xmldb_block_calendar_upcoming_upgrade($oldversion, $block) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -55,5 +55,8 @@ function xmldb_block_community_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -57,5 +57,8 @@ function xmldb_block_completionstatus_upgrade($oldversion, $block) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -57,5 +57,8 @@ function xmldb_block_course_summary_upgrade($oldversion, $block) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -42,5 +42,8 @@ function xmldb_block_html_upgrade($oldversion) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+3
View File
@@ -64,5 +64,8 @@ function xmldb_block_navigation_upgrade($oldversion, $block) {
// Automatically generated Moodle v3.4.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
return true;
}

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