Merge branch 'MDL-61651_master' of git://github.com/markn86/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Tue, 24 Apr 2018 00:45:30 +0000 (08:45 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Tue, 24 Apr 2018 00:45:30 +0000 (08:45 +0800)
768 files changed:
.eslintignore
.stylelintignore
admin/registration/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/analytics/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/analytics/lang/en/tool_analytics.php
admin/tool/assignmentupgrade/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/assignmentupgrade/lang/en/tool_assignmentupgrade.php
admin/tool/assignmentupgrade/tests/privacy_test.php [new file with mode: 0644]
admin/tool/availabilityconditions/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/availabilityconditions/lang/en/tool_availabilityconditions.php
admin/tool/behat/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/behat/lang/en/tool_behat.php
admin/tool/capability/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/capability/lang/en/tool_capability.php
admin/tool/cohortroles/classes/output/cohort_role_assignments_table.php
admin/tool/cohortroles/templates/cohort-in-list.mustache
admin/tool/customlang/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/customlang/lang/en/tool_customlang.php
admin/tool/dataprivacy/amd/build/add_category.min.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/build/add_purpose.min.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/build/categoriesactions.min.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/build/data_deletion.min.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/build/data_registry.min.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/build/data_request_modal.min.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/build/effective_retention_period.min.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/build/events.min.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/build/expand_contract.min.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/build/form-user-selector.min.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/build/myrequestactions.min.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/build/purposesactions.min.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/build/requestactions.min.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/src/add_category.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/src/add_purpose.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/src/categoriesactions.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/src/data_deletion.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/src/data_registry.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/src/data_request_modal.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/src/effective_retention_period.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/src/events.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/src/expand_contract.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/src/form-user-selector.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/src/myrequestactions.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/src/purposesactions.js [new file with mode: 0644]
admin/tool/dataprivacy/amd/src/requestactions.js [new file with mode: 0644]
admin/tool/dataprivacy/categories.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/api.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/category.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/context_instance.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/contextlevel.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/contextlist.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/contextlist_context.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/data_registry.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/data_request.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/expired_context.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/expired_contexts_manager.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/expired_course_related_contexts.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/expired_user_contexts.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/external.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/external/category_exporter.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/external/context_instance_exporter.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/external/data_request_exporter.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/external/name_description_exporter.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/external/purpose_exporter.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/form/category.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/form/context_instance.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/form/contextlevel.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/form/defaults.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/form/purpose.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/local/helper.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/metadata_registry.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/output/categories.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/output/crud_element.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/output/data_deletion_page.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/output/data_registry_compliance_page.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/output/data_registry_page.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/output/data_requests_page.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/output/expired_contexts_table.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/output/my_data_requests_page.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/output/purposes.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/output/renderer.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/page_helper.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/purpose.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/request_contextlist.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/task/delete_expired_contexts.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/task/expired_retention_period.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/task/initiate_data_request_task.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/task/process_data_request_task.php [new file with mode: 0644]
admin/tool/dataprivacy/createdatarequest.php [new file with mode: 0644]
admin/tool/dataprivacy/createdatarequest_form.php [new file with mode: 0644]
admin/tool/dataprivacy/datadeletion.php [new file with mode: 0644]
admin/tool/dataprivacy/dataregistry.php [new file with mode: 0644]
admin/tool/dataprivacy/datarequests.php [new file with mode: 0644]
admin/tool/dataprivacy/db/access.php [new file with mode: 0644]
admin/tool/dataprivacy/db/caches.php [new file with mode: 0644]
admin/tool/dataprivacy/db/install.xml [new file with mode: 0644]
admin/tool/dataprivacy/db/messages.php [new file with mode: 0644]
admin/tool/dataprivacy/db/services.php [new file with mode: 0644]
admin/tool/dataprivacy/db/tasks.php [new file with mode: 0644]
admin/tool/dataprivacy/defaults.php [new file with mode: 0644]
admin/tool/dataprivacy/editcategory.php [new file with mode: 0644]
admin/tool/dataprivacy/editpurpose.php [new file with mode: 0644]
admin/tool/dataprivacy/lang/en/tool_dataprivacy.php [new file with mode: 0644]
admin/tool/dataprivacy/lib.php [new file with mode: 0644]
admin/tool/dataprivacy/mydatarequests.php [new file with mode: 0644]
admin/tool/dataprivacy/pluginregistry.php [new file with mode: 0644]
admin/tool/dataprivacy/purposes.php [new file with mode: 0644]
admin/tool/dataprivacy/settings.php [new file with mode: 0644]
admin/tool/dataprivacy/styles.css [new file with mode: 0644]
admin/tool/dataprivacy/templates/categories.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/component_status.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/contact_dpo.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/context_tree_branches.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/context_tree_node.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/data_deletion.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/data_registry.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/data_registry_compliance.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/data_request_email.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/data_request_modal.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/data_request_results_email.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/data_requests.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/form-user-selector-suggestion.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/my_data_requests.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/purposes.mustache [new file with mode: 0644]
admin/tool/dataprivacy/templates/request_details.mustache [new file with mode: 0644]
admin/tool/dataprivacy/tests/api_test.php [new file with mode: 0644]
admin/tool/dataprivacy/tests/expired_contexts_test.php [new file with mode: 0644]
admin/tool/dataprivacy/tests/external_test.php [new file with mode: 0644]
admin/tool/dataprivacy/tests/metadata_registry_test.php [new file with mode: 0644]
admin/tool/dataprivacy/version.php [new file with mode: 0644]
admin/tool/dbtransfer/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/dbtransfer/lang/en/tool_dbtransfer.php
admin/tool/filetypes/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/filetypes/lang/en/tool_filetypes.php
admin/tool/generator/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/generator/lang/en/tool_generator.php
admin/tool/health/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/health/lang/en/tool_health.php
admin/tool/httpsreplace/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/httpsreplace/lang/en/tool_httpsreplace.php
admin/tool/innodb/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/innodb/lang/en/tool_innodb.php
admin/tool/installaddon/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/installaddon/lang/en/tool_installaddon.php
admin/tool/langimport/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/langimport/lang/en/tool_langimport.php
admin/tool/lp/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/lp/lang/en/tool_lp.php
admin/tool/lp/templates/manage_competencies_page.mustache
admin/tool/lpimportcsv/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/lpimportcsv/lang/en/tool_lpimportcsv.php
admin/tool/lpmigrate/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/lpmigrate/lang/en/tool_lpmigrate.php
admin/tool/multilangupgrade/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/multilangupgrade/lang/en/tool_multilangupgrade.php
admin/tool/oauth2/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/oauth2/lang/en/tool_oauth2.php
admin/tool/phpunit/lang/en/tool_phpunit.php
admin/tool/policy/accept.php [new file with mode: 0644]
admin/tool/policy/acceptances.php [new file with mode: 0644]
admin/tool/policy/amd/build/acceptances_filter.min.js [new file with mode: 0644]
admin/tool/policy/amd/build/acceptances_filter_datasource.min.js [new file with mode: 0644]
admin/tool/policy/amd/build/acceptmodal.min.js [new file with mode: 0644]
admin/tool/policy/amd/build/jquery-eu-cookie-law-popup.min.js [new file with mode: 0644]
admin/tool/policy/amd/build/managedocsactions.min.js [new file with mode: 0644]
admin/tool/policy/amd/build/policyactions.min.js [new file with mode: 0644]
admin/tool/policy/amd/src/acceptances_filter.js [new file with mode: 0644]
admin/tool/policy/amd/src/acceptances_filter_datasource.js [new file with mode: 0644]
admin/tool/policy/amd/src/acceptmodal.js [new file with mode: 0644]
admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js [new file with mode: 0644]
admin/tool/policy/amd/src/managedocsactions.js [new file with mode: 0644]
admin/tool/policy/amd/src/policyactions.js [new file with mode: 0644]
admin/tool/policy/classes/acceptances_table.php [new file with mode: 0644]
admin/tool/policy/classes/api.php [new file with mode: 0644]
admin/tool/policy/classes/event/acceptance_base.php [new file with mode: 0644]
admin/tool/policy/classes/event/acceptance_created.php [new file with mode: 0644]
admin/tool/policy/classes/event/acceptance_updated.php [new file with mode: 0644]
admin/tool/policy/classes/external.php [new file with mode: 0644]
admin/tool/policy/classes/form/accept_policy.php [new file with mode: 0644]
admin/tool/policy/classes/form/policydoc.php [new file with mode: 0644]
admin/tool/policy/classes/output/acceptances.php [new file with mode: 0644]
admin/tool/policy/classes/output/acceptances_filter.php [new file with mode: 0644]
admin/tool/policy/classes/output/guestconsent.php [new file with mode: 0644]
admin/tool/policy/classes/output/page_agreedocs.php [new file with mode: 0644]
admin/tool/policy/classes/output/page_managedocs_list.php [new file with mode: 0644]
admin/tool/policy/classes/output/page_nopermission.php [new file with mode: 0644]
admin/tool/policy/classes/output/page_viewalldoc.php [new file with mode: 0644]
admin/tool/policy/classes/output/page_viewdoc.php [new file with mode: 0644]
admin/tool/policy/classes/output/renderer.php [new file with mode: 0644]
admin/tool/policy/classes/output/user_agreement.php [new file with mode: 0644]
admin/tool/policy/classes/policy_exporter.php [new file with mode: 0644]
admin/tool/policy/classes/policy_version.php [new file with mode: 0644]
admin/tool/policy/classes/policy_version_exporter.php [new file with mode: 0644]
admin/tool/policy/classes/privacy/local/sitepolicy/handler.php [new file with mode: 0644]
admin/tool/policy/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/policy/db/access.php [new file with mode: 0644]
admin/tool/policy/db/events.php [new file with mode: 0644]
admin/tool/policy/db/install.xml [new file with mode: 0644]
admin/tool/policy/db/services.php [new file with mode: 0644]
admin/tool/policy/editpolicydoc.php [new file with mode: 0644]
admin/tool/policy/index.php [new file with mode: 0644]
admin/tool/policy/lang/en/tool_policy.php [new file with mode: 0644]
admin/tool/policy/lib.php [new file with mode: 0644]
admin/tool/policy/managedocs.php [new file with mode: 0644]
admin/tool/policy/pix/agreedno.png [new file with mode: 0644]
admin/tool/policy/pix/agreedno.svg [new file with mode: 0644]
admin/tool/policy/pix/agreedyes.png [new file with mode: 0644]
admin/tool/policy/pix/agreedyes.svg [new file with mode: 0644]
admin/tool/policy/pix/agreedyesonbehalf.png [new file with mode: 0644]
admin/tool/policy/pix/agreedyesonbehalf.svg [new file with mode: 0644]
admin/tool/policy/pix/level.png [new file with mode: 0644]
admin/tool/policy/pix/level.svg [new file with mode: 0644]
admin/tool/policy/readme_moodle.txt [new file with mode: 0644]
admin/tool/policy/settings.php [new file with mode: 0644]
admin/tool/policy/styles.css [new file with mode: 0644]
admin/tool/policy/templates/acceptances.mustache [new file with mode: 0644]
admin/tool/policy/templates/acceptances_filter.mustache [new file with mode: 0644]
admin/tool/policy/templates/guestconsent.mustache [new file with mode: 0644]
admin/tool/policy/templates/page_agreedocs.mustache [new file with mode: 0644]
admin/tool/policy/templates/page_managedocs_list.mustache [new file with mode: 0644]
admin/tool/policy/templates/page_nopermission.mustache [new file with mode: 0644]
admin/tool/policy/templates/page_viewalldoc.mustache [new file with mode: 0644]
admin/tool/policy/templates/page_viewdoc.mustache [new file with mode: 0644]
admin/tool/policy/templates/user_agreement.mustache [new file with mode: 0644]
admin/tool/policy/tests/api_test.php [new file with mode: 0644]
admin/tool/policy/tests/behat/acceptances.feature [new file with mode: 0644]
admin/tool/policy/tests/behat/behat_tool_policy.php [new file with mode: 0644]
admin/tool/policy/tests/behat/consent.feature [new file with mode: 0644]
admin/tool/policy/tests/behat/managepolicies.feature [new file with mode: 0644]
admin/tool/policy/tests/externallib_test.php [new file with mode: 0644]
admin/tool/policy/tests/privacy_provider_test.php [new file with mode: 0644]
admin/tool/policy/thirdpartylibs.xml [new file with mode: 0644]
admin/tool/policy/user.php [new file with mode: 0644]
admin/tool/policy/version.php [new file with mode: 0644]
admin/tool/policy/view.php [new file with mode: 0644]
admin/tool/policy/viewall.php [new file with mode: 0644]
admin/tool/recyclebin/classes/privacy/provider.php [moved from theme/boost/classes/output/core/admin_renderer.php with 58% similarity]
admin/tool/recyclebin/lang/en/tool_recyclebin.php
admin/tool/replace/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/replace/lang/en/tool_replace.php
admin/tool/spamcleaner/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/spamcleaner/lang/en/tool_spamcleaner.php
admin/tool/task/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/task/lang/en/tool_task.php
admin/tool/templatelibrary/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/templatelibrary/lang/en/tool_templatelibrary.php
admin/tool/unsuproles/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/unsuproles/lang/en/tool_unsuproles.php
admin/tool/uploadcourse/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/uploadcourse/lang/en/tool_uploadcourse.php
admin/tool/uploaduser/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/uploaduser/lang/en/tool_uploaduser.php
admin/tool/xmldb/classes/privacy/provider.php [new file with mode: 0644]
admin/tool/xmldb/lang/en/tool_xmldb.php
auth/manual/classes/privacy/provider.php [new file with mode: 0644]
auth/manual/lang/en/auth_manual.php
auth/manual/tests/privacy_provider_test.php [new file with mode: 0644]
auth/tests/behat/displayloginfailures.feature
backup/util/ui/base_moodleform.class.php
backup/util/ui/renderer.php
blocks/myoverview/classes/output/courses_view.php
blocks/myoverview/classes/privacy/provider.php
blocks/myoverview/lang/en/block_myoverview.php
blocks/myoverview/templates/course-event-list-item.mustache
blocks/myoverview/templates/course-paging-content-item.mustache
blocks/myoverview/templates/course-summary.mustache
blocks/myoverview/templates/courses-view-course-item.mustache
blocks/myoverview/templates/courses-view.mustache
blocks/myoverview/templates/event-list-item.mustache
blocks/myoverview/templates/timeline-view.mustache
blocks/myoverview/tests/privacy_test.php [new file with mode: 0644]
blocks/tests/behat/behat_blocks.php
blog/classes/privacy/provider.php [new file with mode: 0644]
blog/tests/privacy_test.php [new file with mode: 0644]
calendar/templates/month_detailed.mustache
comment/classes/privacy/provider.php
comment/lib.php
comment/tests/privacy_test.php
course/classes/management_renderer.php
course/tests/behat/category_change_visibility.feature
course/tests/behat/category_management.feature
course/tests/behat/course_category_management_listing.feature
course/tests/behat/course_change_visibility.feature
course/tests/behat/course_search.feature
course/tests/behat/navigate_course_list.feature
enrol/category/classes/privacy/provider.php [new file with mode: 0644]
enrol/category/lang/en/enrol_category.php
enrol/cohort/classes/privacy/provider.php [new file with mode: 0644]
enrol/cohort/lang/en/enrol_cohort.php
enrol/database/classes/privacy/provider.php [new file with mode: 0644]
enrol/database/lang/en/enrol_database.php
enrol/flatfile/classes/privacy/provider.php [new file with mode: 0644]
enrol/flatfile/lang/en/enrol_flatfile.php
enrol/guest/classes/privacy/provider.php [new file with mode: 0644]
enrol/guest/lang/en/enrol_guest.php
enrol/imsenterprise/classes/privacy/provider.php [new file with mode: 0644]
enrol/imsenterprise/lang/en/enrol_imsenterprise.php
enrol/ldap/classes/privacy/provider.php [new file with mode: 0644]
enrol/ldap/lang/en/enrol_ldap.php
enrol/manual/classes/privacy/provider.php [new file with mode: 0644]
enrol/manual/lang/en/enrol_manual.php
enrol/meta/classes/privacy/provider.php [new file with mode: 0644]
enrol/meta/lang/en/enrol_meta.php
enrol/mnet/classes/privacy/provider.php [new file with mode: 0644]
enrol/mnet/lang/en/enrol_mnet.php
enrol/self/classes/privacy/provider.php [new file with mode: 0644]
enrol/self/lang/en/enrol_self.php
files/converter/googledrive/classes/privacy/provider.php [new file with mode: 0644]
files/converter/googledrive/lang/en/fileconverter_googledrive.php
files/converter/googledrive/tests/privacy_test.php [new file with mode: 0644]
grade/report/grader/classes/privacy/provider.php [new file with mode: 0644]
grade/report/grader/lang/en/gradereport_grader.php
grade/report/grader/tests/privacy_test.php [new file with mode: 0644]
grade/report/history/classes/privacy/provider.php [new file with mode: 0644]
grade/report/history/lang/en/gradereport_history.php
grade/report/outcomes/classes/privacy/provider.php [new file with mode: 0644]
grade/report/outcomes/lang/en/gradereport_outcomes.php
grade/report/overview/classes/privacy/provider.php [new file with mode: 0644]
grade/report/overview/lang/en/gradereport_overview.php
grade/report/singleview/classes/privacy/provider.php [new file with mode: 0644]
grade/report/singleview/lang/en/gradereport_singleview.php
grade/report/user/classes/privacy/provider.php [new file with mode: 0644]
grade/report/user/lang/en/gradereport_user.php
grade/report/user/tests/privacy_test.php [new file with mode: 0644]
lang/en/blog.php
lang/en/install.php
lang/en/media.php
lang/en/moodle.php
lang/en/register.php [new file with mode: 0644]
lang/en/repository.php
lang/en/rss.php [new file with mode: 0644]
lang/en/search.php
lang/en/userkey.php
lib/amd/build/custom_interaction_events.min.js
lib/amd/build/form-autocomplete.min.js
lib/amd/build/modal_factory.min.js
lib/amd/build/paged_content_events.min.js [new file with mode: 0644]
lib/amd/build/paged_content_factory.min.js [new file with mode: 0644]
lib/amd/build/paged_content_pages.min.js [new file with mode: 0644]
lib/amd/build/paged_content_paging_bar.min.js [new file with mode: 0644]
lib/amd/build/paged_content_paging_dropdown.min.js [new file with mode: 0644]
lib/amd/src/custom_interaction_events.js
lib/amd/src/form-autocomplete.js
lib/amd/src/modal_factory.js
lib/amd/src/paged_content_events.js [new file with mode: 0644]
lib/amd/src/paged_content_factory.js [new file with mode: 0644]
lib/amd/src/paged_content_pages.js [new file with mode: 0644]
lib/amd/src/paged_content_paging_bar.js [new file with mode: 0644]
lib/amd/src/paged_content_paging_dropdown.js [new file with mode: 0644]
lib/behat/classes/partial_named_selector.php
lib/blocklib.php
lib/classes/component.php
lib/classes/geopattern.php [new file with mode: 0644]
lib/classes/oauth2/api.php
lib/classes/oauth2/user_field_mapping.php
lib/classes/plugin_manager.php
lib/classes/text.php
lib/classes/user.php
lib/db/services.php
lib/form/autocomplete.php
lib/formslib.php
lib/geopattern-php/GeoPattern/GeoPattern.php [new file with mode: 0644]
lib/geopattern-php/GeoPattern/SVG.php [new file with mode: 0644]
lib/geopattern-php/GeoPattern/SVGElements/Base.php [new file with mode: 0644]
lib/geopattern-php/GeoPattern/SVGElements/Circle.php [new file with mode: 0644]
lib/geopattern-php/GeoPattern/SVGElements/Group.php [new file with mode: 0644]
lib/geopattern-php/GeoPattern/SVGElements/Path.php [new file with mode: 0644]
lib/geopattern-php/GeoPattern/SVGElements/Polyline.php [new file with mode: 0644]
lib/geopattern-php/GeoPattern/SVGElements/Rectangle.php [new file with mode: 0644]
lib/geopattern-php/LICENSE.md [new file with mode: 0644]
lib/geopattern-php/moodle_readme.txt [new file with mode: 0644]
lib/moodlelib.php
lib/outputrenderers.php
lib/templates/form_autocomplete_selection.mustache
lib/templates/initials_bar.mustache
lib/templates/paged_content.mustache [new file with mode: 0644]
lib/templates/paged_content_page.mustache [new file with mode: 0644]
lib/templates/paged_content_pages.mustache [new file with mode: 0644]
lib/templates/paged_content_paging_bar.mustache [new file with mode: 0644]
lib/templates/paged_content_paging_bar_item.mustache [new file with mode: 0644]
lib/templates/paged_content_paging_dropdown.mustache [new file with mode: 0644]
lib/templates/paged_content_paging_dropdown_item.mustache [new file with mode: 0644]
lib/tests/behat/alpha_chooser.feature
lib/tests/behat/behat_forms.php
lib/tests/behat/readonlyform.feature [new file with mode: 0644]
lib/tests/blocklib_test.php
lib/tests/component_test.php
lib/tests/fixtures/readonlyform.php [new file with mode: 0644]
lib/tests/text_test.php
lib/tests/user_test.php
lib/thirdpartylibs.xml
lib/userkey/classes/privacy/provider.php [new file with mode: 0644]
lib/userkey/tests/privacy_provider.php [new file with mode: 0644]
media/classes/privacy/provider.php [new file with mode: 0644]
mod/choice/classes/privacy/provider.php
mod/data/preset/imagegallery/classes/privacy/provider.php [new file with mode: 0644]
mod/data/preset/imagegallery/lang/en/datapreset_imagegallery.php
mod/imscp/classes/privacy/provider.php [new file with mode: 0644]
mod/imscp/lang/en/imscp.php
mod/quiz/addrandomform.php
mod/quiz/amd/build/add_question_modal_launcher.min.js [new file with mode: 0644]
mod/quiz/amd/build/add_random_form.min.js [new file with mode: 0644]
mod/quiz/amd/build/add_random_question.min.js [new file with mode: 0644]
mod/quiz/amd/build/modal_add_random_question.min.js [new file with mode: 0644]
mod/quiz/amd/build/quizquestionbank.min.js
mod/quiz/amd/build/random_question_form_preview.min.js [new file with mode: 0644]
mod/quiz/amd/src/add_question_modal_launcher.js [new file with mode: 0644]
mod/quiz/amd/src/add_random_form.js [new file with mode: 0644]
mod/quiz/amd/src/add_random_question.js [new file with mode: 0644]
mod/quiz/amd/src/modal_add_random_question.js [new file with mode: 0644]
mod/quiz/amd/src/quizquestionbank.js
mod/quiz/amd/src/random_question_form_preview.js [new file with mode: 0644]
mod/quiz/attemptlib.php
mod/quiz/backup/moodle2/backup_quiz_stepslib.php
mod/quiz/backup/moodle2/restore_quiz_stepslib.php
mod/quiz/classes/local/structure/slot_random.php
mod/quiz/classes/output/edit_renderer.php
mod/quiz/classes/structure.php
mod/quiz/db/install.xml
mod/quiz/db/upgrade.php
mod/quiz/editrandom.php
mod/quiz/lang/en/quiz.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/quiz/report/overview/overview_table.php
mod/quiz/report/overview/tests/report_test.php
mod/quiz/report/responses/last_responses_table.php
mod/quiz/report/statistics/report.php
mod/quiz/templates/modal_add_random_question.mustache [new file with mode: 0644]
mod/quiz/templates/random_question_form_preview.mustache [new file with mode: 0644]
mod/quiz/templates/random_question_form_preview_question_list.mustache [new file with mode: 0644]
mod/quiz/tests/fixtures/random_by_tag_quiz.mbz
mod/quiz/tests/local_structure_slot_random_test.php [new file with mode: 0644]
mod/quiz/tests/locallib_test.php
mod/quiz/tests/tags_test.php
mod/quiz/version.php
mod/quiz/yui/build/moodle-mod_quiz-randomquestion/moodle-mod_quiz-randomquestion-debug.js [deleted file]
mod/quiz/yui/build/moodle-mod_quiz-randomquestion/moodle-mod_quiz-randomquestion-min.js [deleted file]
mod/quiz/yui/build/moodle-mod_quiz-randomquestion/moodle-mod_quiz-randomquestion.js [deleted file]
mod/quiz/yui/src/randomquestion/build.json [deleted file]
mod/quiz/yui/src/randomquestion/js/randomquestion.js [deleted file]
mod/quiz/yui/src/randomquestion/meta/randomquestion.json [deleted file]
mod/scorm/db/install.xml
mod/scorm/db/upgrade.php
mod/scorm/version.php
mod/survey/classes/privacy/provider.php [new file with mode: 0644]
mod/survey/lang/en/survey.php
mod/survey/tests/privacy_test.php [new file with mode: 0644]
phpunit.xml.dist
plagiarism/classes/privacy/provider.php
portfolio/classes/privacy/provider.php
privacy/classes/local/request/moodle_content_writer.php
privacy/classes/local/request/transform.php
privacy/classes/local/sitepolicy/default_handler.php
privacy/classes/local/sitepolicy/manager.php
privacy/classes/manager.php
privacy/classes/tests/request/content_writer.php
privacy/tests/moodle_content_writer_test.php
privacy/tests/provider_test.php
privacy/tests/request_transform_test.php
privacy/tests/sitepolicy_test.php
question/amd/build/edit_tags.min.js
question/amd/src/edit_tags.js
question/classes/bank/random_question_loader.php
question/classes/external.php
question/classes/external/question_icon_exporter.php [new file with mode: 0644]
question/classes/external/question_summary_exporter.php [new file with mode: 0644]
question/classes/statistics/questions/calculator.php
question/question.php
question/tests/externallib_test.php
question/tests/random_question_loader_test.php
repository/areafiles/classes/privacy/provider.php [new file with mode: 0644]
repository/areafiles/lang/en/repository_areafiles.php
repository/boxnet/classes/privacy/provider.php [new file with mode: 0644]
repository/boxnet/lang/en/repository_boxnet.php
repository/classes/privacy/provider.php [new file with mode: 0644]
repository/coursefiles/classes/privacy/provider.php [new file with mode: 0644]
repository/coursefiles/lang/en/repository_coursefiles.php
repository/dropbox/classes/privacy/provider.php [new file with mode: 0644]
repository/dropbox/lang/en/repository_dropbox.php
repository/equella/classes/privacy/provider.php [new file with mode: 0644]
repository/equella/lang/en/repository_equella.php
repository/filesystem/classes/privacy/provider.php [new file with mode: 0644]
repository/filesystem/lang/en/repository_filesystem.php
repository/flickr/classes/privacy/provider.php [new file with mode: 0644]
repository/flickr/lang/en/repository_flickr.php
repository/flickr/tests/privacy_test.php [new file with mode: 0644]
repository/flickr_public/classes/privacy/provider.php [new file with mode: 0644]
repository/flickr_public/lang/en/repository_flickr_public.php
repository/googledocs/classes/privacy/provider.php [new file with mode: 0644]
repository/googledocs/lang/en/repository_googledocs.php
repository/local/classes/privacy/provider.php [new file with mode: 0644]
repository/local/lang/en/repository_local.php
repository/merlot/classes/privacy/provider.php [new file with mode: 0644]
repository/merlot/lang/en/repository_merlot.php
repository/onedrive/classes/privacy/provider.php [new file with mode: 0644]
repository/onedrive/lang/en/repository_onedrive.php
repository/onedrive/tests/privacy_test.php [new file with mode: 0644]
repository/picasa/classes/privacy/provider.php [new file with mode: 0644]
repository/picasa/lang/en/repository_picasa.php
repository/recent/classes/privacy/provider.php [new file with mode: 0644]
repository/recent/lang/en/repository_recent.php
repository/s3/classes/privacy/provider.php [new file with mode: 0644]
repository/s3/lang/en/repository_s3.php
repository/skydrive/classes/privacy/provider.php [new file with mode: 0644]
repository/skydrive/lang/en/repository_skydrive.php
repository/tests/privacy_test.php [new file with mode: 0644]
repository/upload/classes/privacy/provider.php [new file with mode: 0644]
repository/upload/lang/en/repository_upload.php
repository/url/classes/privacy/provider.php [new file with mode: 0644]
repository/url/lang/en/repository_url.php
repository/user/classes/privacy/provider.php [new file with mode: 0644]
repository/user/lang/en/repository_user.php
repository/webdav/classes/privacy/provider.php [new file with mode: 0644]
repository/webdav/lang/en/repository_webdav.php
repository/wikimedia/classes/privacy/provider.php [new file with mode: 0644]
repository/wikimedia/lang/en/repository_wikimedia.php
repository/youtube/classes/privacy/provider.php [new file with mode: 0644]
repository/youtube/lang/en/repository_youtube.php
rss/classes/privacy/provider.php [new file with mode: 0644]
rss/renderer.php
rss/tests/privacy_test.php [new file with mode: 0644]
search/amd/build/form-search-user-selector.min.js [new file with mode: 0644]
search/amd/src/form-search-user-selector.js [new file with mode: 0644]
search/classes/document.php
search/classes/engine.php
search/classes/external.php [new file with mode: 0644]
search/classes/manager.php
search/classes/output/form/search.php
search/engine/solr/classes/engine.php
search/engine/solr/tests/engine_test.php
search/index.php
search/templates/form-user-selector-suggestion.mustache [new file with mode: 0644]
search/tests/behat/search_by_user.feature [new file with mode: 0644]
search/tests/external_test.php [new file with mode: 0644]
search/upgrade.txt
tag/classes/tag.php
tag/tests/behat/edit_tag.feature
theme/boost/amd/build/alert.min.js
theme/boost/amd/build/button.min.js
theme/boost/amd/build/carousel.min.js
theme/boost/amd/build/collapse.min.js
theme/boost/amd/build/dropdown.min.js
theme/boost/amd/build/form-display-errors.min.js
theme/boost/amd/build/modal.min.js
theme/boost/amd/build/popover.min.js
theme/boost/amd/build/scrollspy.min.js
theme/boost/amd/build/tab.min.js
theme/boost/amd/build/tooltip.min.js
theme/boost/amd/build/util.min.js
theme/boost/amd/src/alert.js
theme/boost/amd/src/button.js
theme/boost/amd/src/carousel.js
theme/boost/amd/src/collapse.js
theme/boost/amd/src/dropdown.js
theme/boost/amd/src/form-display-errors.js
theme/boost/amd/src/modal.js
theme/boost/amd/src/popover.js
theme/boost/amd/src/scrollspy.js
theme/boost/amd/src/tab.js
theme/boost/amd/src/tooltip.js
theme/boost/amd/src/util.js
theme/boost/classes/output/core_course/management/renderer.php [new file with mode: 0644]
theme/boost/classes/output/core_renderer.php
theme/boost/cli/import-bootswatch.php
theme/boost/lang/en/theme_boost.php
theme/boost/lib.php
theme/boost/readme_moodle.txt
theme/boost/scss/bootstrap.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/LICENSE
theme/boost/scss/bootstrap/_alert.scss
theme/boost/scss/bootstrap/_animation.scss [deleted file]
theme/boost/scss/bootstrap/_badge.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/_breadcrumb.scss
theme/boost/scss/bootstrap/_button-group.scss
theme/boost/scss/bootstrap/_buttons.scss
theme/boost/scss/bootstrap/_card.scss
theme/boost/scss/bootstrap/_carousel.scss
theme/boost/scss/bootstrap/_close.scss
theme/boost/scss/bootstrap/_code.scss
theme/boost/scss/bootstrap/_custom-forms.scss
theme/boost/scss/bootstrap/_custom.scss [deleted file]
theme/boost/scss/bootstrap/_dropdown.scss
theme/boost/scss/bootstrap/_forms.scss
theme/boost/scss/bootstrap/_functions.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/_grid.scss
theme/boost/scss/bootstrap/_images.scss
theme/boost/scss/bootstrap/_input-group.scss
theme/boost/scss/bootstrap/_jumbotron.scss
theme/boost/scss/bootstrap/_list-group.scss
theme/boost/scss/bootstrap/_media.scss
theme/boost/scss/bootstrap/_mixins.scss
theme/boost/scss/bootstrap/_modal.scss
theme/boost/scss/bootstrap/_nav.scss
theme/boost/scss/bootstrap/_navbar.scss
theme/boost/scss/bootstrap/_normalize.scss [deleted file]
theme/boost/scss/bootstrap/_pagination.scss
theme/boost/scss/bootstrap/_popover.scss
theme/boost/scss/bootstrap/_print.scss
theme/boost/scss/bootstrap/_progress.scss
theme/boost/scss/bootstrap/_reboot.scss
theme/boost/scss/bootstrap/_root.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/_tables.scss
theme/boost/scss/bootstrap/_tags.scss [deleted file]
theme/boost/scss/bootstrap/_tooltip.scss
theme/boost/scss/bootstrap/_transitions.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/_type.scss
theme/boost/scss/bootstrap/_utilities.scss
theme/boost/scss/bootstrap/_variables.scss
theme/boost/scss/bootstrap/bootstrap-flex.scss [deleted file]
theme/boost/scss/bootstrap/bootstrap-grid.scss
theme/boost/scss/bootstrap/bootstrap-reboot.scss
theme/boost/scss/bootstrap/bootstrap.scss
theme/boost/scss/bootstrap/mixins/_alert.scss
theme/boost/scss/bootstrap/mixins/_background-variant.scss
theme/boost/scss/bootstrap/mixins/_badge.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/mixins/_border-radius.scss
theme/boost/scss/bootstrap/mixins/_box-shadow.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/mixins/_breakpoints.scss
theme/boost/scss/bootstrap/mixins/_buttons.scss
theme/boost/scss/bootstrap/mixins/_cards.scss [deleted file]
theme/boost/scss/bootstrap/mixins/_caret.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/mixins/_clearfix.scss
theme/boost/scss/bootstrap/mixins/_float.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/mixins/_forms.scss
theme/boost/scss/bootstrap/mixins/_gradients.scss
theme/boost/scss/bootstrap/mixins/_grid-framework.scss
theme/boost/scss/bootstrap/mixins/_grid.scss
theme/boost/scss/bootstrap/mixins/_hover.scss
theme/boost/scss/bootstrap/mixins/_image.scss
theme/boost/scss/bootstrap/mixins/_list-group.scss
theme/boost/scss/bootstrap/mixins/_nav-divider.scss
theme/boost/scss/bootstrap/mixins/_navbar-align.scss
theme/boost/scss/bootstrap/mixins/_pagination.scss
theme/boost/scss/bootstrap/mixins/_progress.scss [deleted file]
theme/boost/scss/bootstrap/mixins/_pulls.scss [deleted file]
theme/boost/scss/bootstrap/mixins/_reset-filter.scss [deleted file]
theme/boost/scss/bootstrap/mixins/_reset-text.scss
theme/boost/scss/bootstrap/mixins/_resize.scss
theme/boost/scss/bootstrap/mixins/_screen-reader.scss
theme/boost/scss/bootstrap/mixins/_tab-focus.scss [deleted file]
theme/boost/scss/bootstrap/mixins/_tag.scss [deleted file]
theme/boost/scss/bootstrap/mixins/_text-emphasis.scss
theme/boost/scss/bootstrap/mixins/_text-hide.scss
theme/boost/scss/bootstrap/mixins/_text-truncate.scss
theme/boost/scss/bootstrap/mixins/_transition.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/mixins/_visibility.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/utilities/_align.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/utilities/_background.scss
theme/boost/scss/bootstrap/utilities/_borders.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/utilities/_display.scss
theme/boost/scss/bootstrap/utilities/_embed.scss [moved from theme/boost/scss/bootstrap/_responsive-embed.scss with 60% similarity]
theme/boost/scss/bootstrap/utilities/_flex.scss
theme/boost/scss/bootstrap/utilities/_float.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/utilities/_position.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/utilities/_pulls.scss [deleted file]
theme/boost/scss/bootstrap/utilities/_sizing.scss [new file with mode: 0644]
theme/boost/scss/bootstrap/utilities/_spacing.scss
theme/boost/scss/bootstrap/utilities/_text.scss
theme/boost/scss/bootstrap/utilities/_visibility.scss
theme/boost/scss/fontawesome.scss [new file with mode: 0644]
theme/boost/scss/fontawesome/moodle-path.scss [deleted file]
theme/boost/scss/fontawesome/readme_moodle.txt
theme/boost/scss/moodle.scss
theme/boost/scss/moodle/admin.scss
theme/boost/scss/moodle/backup-restore.scss
theme/boost/scss/moodle/blocks.scss
theme/boost/scss/moodle/bootswatch.scss
theme/boost/scss/moodle/bs2-compat.scss
theme/boost/scss/moodle/bs4alphacompat.scss [new file with mode: 0644]
theme/boost/scss/moodle/calendar.scss
theme/boost/scss/moodle/core.scss
theme/boost/scss/moodle/course.scss
theme/boost/scss/moodle/debug.scss
theme/boost/scss/moodle/drawer.scss
theme/boost/scss/moodle/forms.scss
theme/boost/scss/moodle/icons.scss
theme/boost/scss/moodle/message.scss
theme/boost/scss/moodle/modal.scss
theme/boost/scss/moodle/modules.scss
theme/boost/scss/moodle/question.scss
theme/boost/scss/moodle/responsive-tabs.scss [deleted file]
theme/boost/scss/moodle/search.scss
theme/boost/scss/moodle/undo.scss
theme/boost/scss/moodle/user.scss
theme/boost/scss/preset/default.scss
theme/boost/scss/preset/plain.scss
theme/boost/templates/admin_setting_tabs.mustache
theme/boost/templates/block_search_forums/search_form.mustache
theme/boost/templates/columns1.mustache
theme/boost/templates/columns2.mustache
theme/boost/templates/core/action_menu.mustache
theme/boost/templates/core/action_menu_trigger.mustache
theme/boost/templates/core/block.mustache
theme/boost/templates/core/custom_menu_item.mustache
theme/boost/templates/core/dataformat_selector.mustache
theme/boost/templates/core/filemanager_fileselect.mustache
theme/boost/templates/core/filemanager_modal_generallayout.mustache
theme/boost/templates/core/filemanager_page_generallayout.mustache
theme/boost/templates/core/form_autocomplete_selection.mustache
theme/boost/templates/core/help_icon.mustache
theme/boost/templates/core/initials_bar.mustache [new file with mode: 0644]
theme/boost/templates/core/loginform.mustache
theme/boost/templates/core/modal.mustache
theme/boost/templates/core/preferences_groups.mustache
theme/boost/templates/core/settings_link_page.mustache
theme/boost/templates/core/settings_link_page_single.mustache
theme/boost/templates/core/signup_form_layout.mustache
theme/boost/templates/core/tabtree.mustache
theme/boost/templates/core_admin/setting.mustache
theme/boost/templates/core_form/element-advcheckbox-inline.mustache
theme/boost/templates/core_form/element-advcheckbox.mustache
theme/boost/templates/core_form/element-autocomplete-inline.mustache
theme/boost/templates/core_form/element-autocomplete.mustache
theme/boost/templates/core_form/element-button.mustache
theme/boost/templates/core_form/element-checkbox-inline.mustache
theme/boost/templates/core_form/element-checkbox.mustache
theme/boost/templates/core_form/element-date_time_selector-inline.mustache
theme/boost/templates/core_form/element-date_time_selector.mustache
theme/boost/templates/core_form/element-password.mustache
theme/boost/templates/core_form/element-passwordunmask.mustache
theme/boost/templates/core_form/element-radio-inline.mustache
theme/boost/templates/core_form/element-radio.mustache
theme/boost/templates/core_form/element-select-inline.mustache
theme/boost/templates/core_form/element-select.mustache
theme/boost/templates/core_form/element-selectgroups-inline.mustache
theme/boost/templates/core_form/element-selectgroups.mustache
theme/boost/templates/core_form/element-selectwithlink.mustache
theme/boost/templates/core_form/element-tags-inline.mustache
theme/boost/templates/core_form/element-tags.mustache
theme/boost/templates/core_form/element-template-inline.mustache
theme/boost/templates/core_form/element-template.mustache
theme/boost/templates/core_form/element-text-inline.mustache
theme/boost/templates/core_form/element-text.mustache
theme/boost/templates/core_form/element-textarea.mustache
theme/boost/templates/core_form/element-url.mustache
theme/boost/templates/core_grades/edit_tree.mustache
theme/boost/templates/custom_menu_footer.mustache
theme/boost/templates/flat_navigation.mustache
theme/boost/templates/footer.mustache [new file with mode: 0644]
theme/boost/templates/head.mustache [new file with mode: 0644]
theme/boost/templates/header.mustache
theme/boost/templates/login.mustache
theme/boost/templates/maintenance.mustache
theme/boost/templates/mod_forum/quick_search_form.mustache
theme/boost/templates/nav-drawer.mustache
theme/boost/templates/navbar-secure.mustache [moved from theme/boost/templates/header-secure.mustache with 66% similarity]
theme/boost/templates/navbar.mustache [new file with mode: 0644]
theme/boost/templates/secure.mustache
theme/boost/templates/tool_usertours/tourstep.mustache
theme/boost/tests/behat/behat_theme_boost_behat_action_menu.php
theme/boost/tests/behat/behat_theme_boost_behat_navigation.php
theme/boost/tests/behat/contextmenu.feature
theme/boost/tests/behat/regionmainsettingsmenu.feature
theme/boost/thirdpartylibs.xml
theme/boost/upgrade.txt
theme/bootstrapbase/less/moodle/blocks.less
theme/bootstrapbase/style/moodle.css
theme/bootstrapbase/templates/block_myoverview/course-paging-content-item.mustache
theme/bootstrapbase/templates/block_myoverview/courses-view-course-item.mustache
theme/bootstrapbase/templates/block_myoverview/paging-content-item.mustache [new file with mode: 0644]
theme/bootstrapbase/templates/core/paged_content_paging_bar.mustache [new file with mode: 0644]
theme/bootstrapbase/templates/core/paged_content_paging_dropdown.mustache [new file with mode: 0644]
theme/bootstrapbase/templates/core/paged_content_paging_dropdown_item.mustache [new file with mode: 0644]
theme/bootstrapbase/templates/mod_quiz/modal_add_random_question.mustache [new file with mode: 0644]
theme/bootstrapbase/templates/mod_quiz/random_question_form_preview.mustache [new file with mode: 0644]
user/tests/behat/set_default_homepage.feature
version.php

index 84e8dc0..6345306 100644 (file)
@@ -3,6 +3,7 @@
 */**/build/
 node_modules/
 vendor/
+admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js
 admin/tool/usertours/amd/src/tour.js
 auth/cas/CAS/
 enrol/lti/ims-blti/
@@ -59,6 +60,7 @@ lib/fonts/
 lib/amd/src/adapter.js
 lib/validateurlsyntax.php
 lib/amd/src/popper.js
+lib/geopattern-php/
 media/player/videojs/amd/src/video-lazy.js
 media/player/videojs/amd/src/Youtube-lazy.js
 media/player/videojs/videojs/
index 83f1e76..6368112 100644 (file)
@@ -4,6 +4,7 @@ theme/clean/style/custom.css
 theme/more/style/custom.css
 node_modules/
 vendor/
+admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js
 admin/tool/usertours/amd/src/tour.js
 auth/cas/CAS/
 enrol/lti/ims-blti/
@@ -60,6 +61,7 @@ lib/fonts/
 lib/amd/src/adapter.js
 lib/validateurlsyntax.php
 lib/amd/src/popper.js
+lib/geopattern-php/
 media/player/videojs/amd/src/video-lazy.js
 media/player/videojs/amd/src/Youtube-lazy.js
 media/player/videojs/videojs/
diff --git a/admin/registration/classes/privacy/provider.php b/admin/registration/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..c1c4ea4
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+/**
+ * Privacy Subsystem implementation for core_register.
+ *
+ * @package    core_register
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace core_register\privacy;
+defined('MOODLE_INTERNAL') || die();
+/**
+ * Privacy Subsystem for core_register implementing null_provider.
+ *
+ * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
\ No newline at end of file
diff --git a/admin/tool/analytics/classes/privacy/provider.php b/admin/tool/analytics/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..904b627
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Privacy Subsystem implementation for tool_analytics.
+ *
+ * @package    tool_analytics
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_analytics\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy Subsystem for tool_analytics implementing null_provider.
+ *
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
\ No newline at end of file
index f08f71c..8df2cc3 100644 (file)
@@ -94,3 +94,4 @@ $string['trainmodels'] = 'Train models';
 $string['viewlog'] = 'Log';
 $string['weeksenddateautomaticallyset'] = 'End date automatically set based on start date and the number of sections';
 $string['weeksenddatedefault'] = 'End date automatically calculated from the course start date.';
+$string['privacy:metadata'] = 'The Analytic models plugin does not store any personal data.';
diff --git a/admin/tool/assignmentupgrade/classes/privacy/provider.php b/admin/tool/assignmentupgrade/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..452cc15
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Privacy Subsystem implementation for tool_assignmentupgrade.
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_assignmentupgrade\privacy;
+
+use core_privacy\local\metadata\collection;
+use core_privacy\local\request\writer;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy Subsystem for tool_assignmentupgrade implementing metadata, plugin, and user_preference providers.
+ *
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements
+    \core_privacy\local\metadata\provider,
+    \core_privacy\local\request\user_preference_provider {
+
+    /**
+     * Returns meta data about this system.
+     *
+     * @param   collection $collection The initialised collection to add items to.
+     * @return  collection     A listing of user data stored through this system.
+     */
+    public static function get_metadata(collection $collection) : collection {
+        $collection->add_user_preference(
+            'tool_assignmentupgrade_perpage',
+            'privacy:metadata:preference:perpage'
+        );
+        return $collection;
+    }
+
+    /**
+     * 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) {
+        $perpage = get_user_preferences('tool_assignmentupgrade_perpage', null, $userid);
+        if ($perpage !== null) {
+            writer::export_user_preference(
+                'tool_assignmentupgrade',
+                'perpage',
+                $perpage,
+                get_string('privacy:metadata:preference:perpage', 'tool_assignmentupgrade')
+            );
+        }
+    }
+
+}
index 9dc8c6e..c7e7d26 100644 (file)
@@ -58,3 +58,4 @@ $string['upgradeallconfirm'] = 'Upgrade all assignments?';
 $string['upgradeprogress'] = 'Upgrade assignment {$a->current} of {$a->total}';
 $string['upgradesingle'] = 'Upgrade single assignment';
 $string['viewcourse'] = 'View the course with the converted assignment';
+$string['privacy:metadata:preference:perpage'] = 'The assignment upgrade records per page preference set for the user.';
diff --git a/admin/tool/assignmentupgrade/tests/privacy_test.php b/admin/tool/assignmentupgrade/tests/privacy_test.php
new file mode 100644 (file)
index 0000000..d22770c
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Privacy tests for tool_assignmentupgrade.
+ *
+ * @package    tool_assignmentupgrade
+ * @category   test
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+use \core_privacy\tests\provider_testcase;
+use \core_privacy\local\request\writer;
+use \tool_assignmentupgrade\privacy\provider;
+
+/**
+ * Unit tests for tool_assignmentupgrade/classes/privacy/policy
+ *
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_assignmentupgrade_privacy_testcase extends provider_testcase {
+
+    /**
+     * Overriding setUp() function to always reset after tests.
+     */
+    public function setUp() {
+        $this->resetAfterTest(true);
+    }
+
+    /**
+     * Test for provider::test_export_user_preferences().
+     */
+    public function test_export_user_preferences() {
+        // Test setup.
+        $user = $this->getDataGenerator()->create_user();
+        $this->setUser($user);
+
+        // Add a user home page preference for the User.
+        set_user_preference('tool_assignmentupgrade_perpage', '100', $user);
+
+        // Test the user preference exists.
+        $params = [
+            'userid' => $user->id,
+            'name' => 'tool_assignmentupgrade_perpage'
+        ];
+
+        // Test the user preferences export contains 1 user preference record for the User.
+        provider::export_user_preferences($user->id);
+        $contextuser = context_user::instance($user->id);
+        $writer = writer::with_context($contextuser);
+        $this->assertTrue($writer->has_any_data());
+
+        $exportedpreferences = $writer->get_user_preferences('tool_assignmentupgrade');
+        $this->assertCount(1, (array) $exportedpreferences);
+        $this->assertEquals('100', $exportedpreferences->perpage->value);
+    }
+
+}
diff --git a/admin/tool/availabilityconditions/classes/privacy/provider.php b/admin/tool/availabilityconditions/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..e0094cd
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Privacy Subsystem implementation for tool_availabilityconditions.
+ *
+ * @package    tool_availabilityconditions
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_availabilityconditions\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy Subsystem for tool_availabilityconditions implementing null_provider.
+ *
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
\ No newline at end of file
index 98efe55..651e576 100644 (file)
@@ -24,3 +24,4 @@
 
 $string['manageplugins'] = 'Manage restrictions';
 $string['pluginname'] = 'Availability condition management';
+$string['privacy:metadata'] = 'The Availability condition management plugin does not store any personal data.';
diff --git a/admin/tool/behat/classes/privacy/provider.php b/admin/tool/behat/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..608773a
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Privacy Subsystem implementation for tool_behat.
+ *
+ * @package    tool_behat
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_behat\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy Subsystem for tool_behat implementing null_provider.
+ *
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
\ No newline at end of file
index 88a1ef7..7aea934 100644 (file)
@@ -50,3 +50,4 @@ $string['wrongbehatsetup'] = 'Something is wrong with the behat setup and so ste
 <li>$CFG->behat_dataroot, $CFG->behat_prefix and $CFG->behat_wwwroot are set in config.php with different values from $CFG->dataroot, $CFG->prefix and $CFG->wwwroot.</li>
 <li>You ran "{$a->behatinit}" from your Moodle root directory.</li>
 <li>Dependencies are installed in vendor/ and {$a->behatcommand} file has execution permissions.</li></ul>';
+$string['privacy:metadata'] = 'The Acceptance testing plugin does not store any personal data.';
diff --git a/admin/tool/capability/classes/privacy/provider.php b/admin/tool/capability/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..a5557fa
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Privacy Subsystem implementation for tool_capability.
+ *
+ * @package    tool_capability
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_capability\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy Subsystem for tool_capability implementing null_provider.
+ *
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
\ No newline at end of file
index bb477ee..b44db22 100644 (file)
@@ -34,3 +34,4 @@ $string['pluginname'] = 'Capability overview';
 $string['reportforcapability'] = 'Report for capability \'{$a}\'';
 $string['reportsettings'] = 'Report settings';
 $string['roleslabel'] = 'Roles:';
+$string['privacy:metadata'] = 'The Capability overview plugin does not store any personal data.';
index 29405c2..496a4ac 100644 (file)
@@ -94,7 +94,8 @@ class cohort_role_assignments_table extends table_sql {
             'idnumber' => $data->cohortidnumber,
             'description' => $data->cohortdescription,
             'visible' => $data->cohortvisible,
-            'name' => $data->cohortname
+            'name' => $data->cohortname,
+            'theme' => $data->cohorttheme
         );
         $context = context_helper::instance_by_id($data->cohortcontextid);
 
@@ -169,7 +170,7 @@ class cohort_role_assignments_table extends table_sql {
     protected function get_sql_and_params($count = false) {
         $fields = 'uca.id, uca.cohortid, uca.userid, uca.roleid, ';
         $fields .= 'c.name as cohortname, c.idnumber as cohortidnumber, c.contextid as cohortcontextid, ';
-        $fields .= 'c.visible as cohortvisible, c.description as cohortdescription, ';
+        $fields .= 'c.visible as cohortvisible, c.description as cohortdescription, c.theme as cohorttheme, ';
 
         // Add extra user fields that we need for the graded user.
         $extrafields = get_extra_user_fields($this->context);
index 971065a..ff2199c 100644 (file)
     * idnumber cohort idnumber field
     * description cohort description field
     * visible cohort visible field
+    * theme cohort theme field
 
     Example context (json):
     { "id": "1",
       "name": "Cohort 1",
       "visible": true,
       "idnumber": "014",
-      "description": "Some users"
+      "description": "Some users",
+      "theme": "clean"
     }
 }}
 {{> tool_lp/form-cohort-selector-suggestion }}
diff --git a/admin/tool/customlang/classes/privacy/provider.php b/admin/tool/customlang/classes/privacy/provider.php
new file mode 100644 (file)
index 0000000..c847481
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Privacy Subsystem implementation for tool_customlang.
+ *
+ * @package    tool_customlang
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_customlang\privacy;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Privacy Subsystem for tool_customlang implementing null_provider.
+ *
+ * @copyright  2018 Zig Tan <zig@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+
+    /**
+     * Get the language string identifier with the component's language
+     * file to explain why this plugin stores no data.
+     *
+     * @return  string
+     */
+    public static function get_reason() : string {
+        return 'privacy:metadata';
+    }
+}
\ No newline at end of file
index c9ba4bc..7192622 100644 (file)
@@ -59,3 +59,4 @@ $string['placeholderwarning'] = 'string contains a placeholder';
 $string['pluginname'] = 'Language customisation';
 $string['savecheckin'] = 'Save changes to the language pack';
 $string['savecontinue'] = 'Apply changes and continue editing';
+$string['privacy:metadata'] = 'The Language customisation plugin does not store any personal data.';
diff --git a/admin/tool/dataprivacy/amd/build/add_category.min.js b/admin/tool/dataprivacy/amd/build/add_category.min.js
new file mode 100644 (file)
index 0000000..92cd4ee
Binary files /dev/null and b/admin/tool/dataprivacy/amd/build/add_category.min.js differ
diff --git a/admin/tool/dataprivacy/amd/build/add_purpose.min.js b/admin/tool/dataprivacy/amd/build/add_purpose.min.js
new file mode 100644 (file)
index 0000000..dec27cc
Binary files /dev/null and b/admin/tool/dataprivacy/amd/build/add_purpose.min.js differ
diff --git a/admin/tool/dataprivacy/amd/build/categoriesactions.min.js b/admin/tool/dataprivacy/amd/build/categoriesactions.min.js
new file mode 100644 (file)
index 0000000..66e3b0a
Binary files /dev/null and b/admin/tool/dataprivacy/amd/build/categoriesactions.min.js differ
diff --git a/admin/tool/dataprivacy/amd/build/data_deletion.min.js b/admin/tool/dataprivacy/amd/build/data_deletion.min.js
new file mode 100644 (file)
index 0000000..5aa1e48
Binary files /dev/null and b/admin/tool/dataprivacy/amd/build/data_deletion.min.js differ
diff --git a/admin/tool/dataprivacy/amd/build/data_registry.min.js b/admin/tool/dataprivacy/amd/build/data_registry.min.js
new file mode 100644 (file)
index 0000000..f2964fe
Binary files /dev/null and b/admin/tool/dataprivacy/amd/build/data_registry.min.js differ
diff --git a/admin/tool/dataprivacy/amd/build/data_request_modal.min.js b/admin/tool/dataprivacy/amd/build/data_request_modal.min.js
new file mode 100644 (file)
index 0000000..7e9bda8
Binary files /dev/null and b/admin/tool/dataprivacy/amd/build/data_request_modal.min.js differ
diff --git a/admin/tool/dataprivacy/amd/build/effective_retention_period.min.js b/admin/tool/dataprivacy/amd/build/effective_retention_period.min.js
new file mode 100644 (file)
index 0000000..48f7d3c
Binary files /dev/null and b/admin/tool/dataprivacy/amd/build/effective_retention_period.min.js differ
diff --git a/admin/tool/dataprivacy/amd/build/events.min.js b/admin/tool/dataprivacy/amd/build/events.min.js
new file mode 100644 (file)
index 0000000..1d4e973
Binary files /dev/null and b/admin/tool/dataprivacy/amd/build/events.min.js differ
diff --git a/admin/tool/dataprivacy/amd/build/expand_contract.min.js b/admin/tool/dataprivacy/amd/build/expand_contract.min.js
new file mode 100644 (file)
index 0000000..3419d58
Binary files /dev/null and b/admin/tool/dataprivacy/amd/build/expand_contract.min.js differ
diff --git a/admin/tool/dataprivacy/amd/build/form-user-selector.min.js b/admin/tool/dataprivacy/amd/build/form-user-selector.min.js
new file mode 100644 (file)
index 0000000..faa073f
Binary files /dev/null and b/admin/tool/dataprivacy/amd/build/form-user-selector.min.js differ
diff --git a/admin/tool/dataprivacy/amd/build/myrequestactions.min.js b/admin/tool/dataprivacy/amd/build/myrequestactions.min.js
new file mode 100644 (file)
index 0000000..937367c
Binary files /dev/null and b/admin/tool/dataprivacy/amd/build/myrequestactions.min.js differ
diff --git a/admin/tool/dataprivacy/amd/build/purposesactions.min.js b/admin/tool/dataprivacy/amd/build/purposesactions.min.js
new file mode 100644 (file)
index 0000000..0b15981
Binary files /dev/null and b/admin/tool/dataprivacy/amd/build/purposesactions.min.js differ
diff --git a/admin/tool/dataprivacy/amd/build/requestactions.min.js b/admin/tool/dataprivacy/amd/build/requestactions.min.js
new file mode 100644 (file)
index 0000000..586a2da
Binary files /dev/null and b/admin/tool/dataprivacy/amd/build/requestactions.min.js differ
diff --git a/admin/tool/dataprivacy/amd/src/add_category.js b/admin/tool/dataprivacy/amd/src/add_category.js
new file mode 100644 (file)
index 0000000..7ea22c8
--- /dev/null
@@ -0,0 +1,172 @@
+// 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/>.
+
+/**
+ * Module to add categories.
+ *
+ * @module     tool_dataprivacy/add_category
+ * @package    tool_dataprivacy
+ * @copyright  2018 David Monllao
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery', 'core/str', 'core/ajax', 'core/notification', 'core/modal_factory', 'core/modal_events', 'core/fragment'],
+    function($, Str, Ajax, Notification, ModalFactory, ModalEvents, Fragment) {
+
+        var SELECTORS = {
+            CATEGORY_LINK: '[data-add-element="category"]',
+        };
+
+        var AddCategory = function(contextId) {
+            this.contextId = contextId;
+
+            var stringKeys = [
+                {
+                    key: 'addcategory',
+                    component: 'tool_dataprivacy'
+                },
+                {
+                    key: 'save',
+                    component: 'admin'
+                }
+            ];
+            this.strings = Str.get_strings(stringKeys);
+
+            this.registerEventListeners();
+        };
+
+        /**
+         * @var {int} contextId
+         * @private
+         */
+        AddCategory.prototype.contextId = 0;
+
+        /**
+         * @var {Promise}
+         * @private
+         */
+        AddCategory.prototype.strings = 0;
+
+        AddCategory.prototype.registerEventListeners = function() {
+
+            var trigger = $(SELECTORS.CATEGORY_LINK);
+            trigger.on('click', function() {
+                return this.strings.then(function(strings) {
+                    ModalFactory.create({
+                        type: ModalFactory.types.SAVE_CANCEL,
+                        title: strings[0],
+                        body: '',
+                    }, trigger).done(function(modal) {
+                        this.setupFormModal(modal, strings[1]);
+                    }.bind(this));
+                }.bind(this))
+                .fail(Notification.exception);
+            }.bind(this));
+
+        };
+
+        /**
+         * @method getBody
+         * @param {Object} formdata
+         * @private
+         * @return {Promise}
+         */
+        AddCategory.prototype.getBody = function(formdata) {
+
+            var params = null;
+            if (typeof formdata !== "undefined") {
+                params = {jsonformdata: JSON.stringify(formdata)};
+            }
+            // Get the content of the modal.
+            return Fragment.loadFragment('tool_dataprivacy', 'addcategory_form', this.contextId, params);
+        };
+
+        AddCategory.prototype.setupFormModal = function(modal, saveText) {
+            modal.setLarge();
+
+            modal.setSaveButtonText(saveText);
+
+            // We want to reset the form every time it is opened.
+            modal.getRoot().on(ModalEvents.hidden, this.destroy.bind(this));
+
+            modal.setBody(this.getBody());
+
+            // We catch the modal save event, and use it to submit the form inside the modal.
+            // Triggering a form submission will give JS validation scripts a chance to check for errors.
+            modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));
+            // We also catch the form submit event and use it to submit the form with ajax.
+            modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));
+
+            this.modal = modal;
+
+            modal.show();
+        };
+
+        /**
+         * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.
+         *
+         * @method submitForm
+         * @param {Event} e Form submission event.
+         * @private
+         */
+        AddCategory.prototype.submitForm = function(e) {
+            e.preventDefault();
+            this.modal.getRoot().find('form').submit();
+        };
+
+        AddCategory.prototype.submitFormAjax = function(e) {
+            // We don't want to do a real form submission.
+            e.preventDefault();
+
+            // Convert all the form elements values to a serialised string.
+            var formData = this.modal.getRoot().find('form').serialize();
+
+            Ajax.call([{
+                methodname: 'tool_dataprivacy_create_category_form',
+                args: {jsonformdata: JSON.stringify(formData)},
+                done: function(data) {
+                    if (data.validationerrors) {
+                        this.modal.setBody(this.getBody(formData));
+                    } else {
+                        this.close();
+                    }
+                }.bind(this),
+                fail: Notification.exception
+            }]);
+        };
+
+        AddCategory.prototype.close = function() {
+            this.destroy();
+            document.location.reload();
+        };
+
+        AddCategory.prototype.destroy = function() {
+            Y.use('moodle-core-formchangechecker', function() {
+                M.core_formchangechecker.reset_form_dirty_state();
+            });
+            this.modal.destroy();
+        };
+
+        AddCategory.prototype.removeListeners = function() {
+            $(SELECTORS.CATEGORY_LINK).off('click');
+        };
+
+        return /** @alias module:tool_dataprivacy/add_category */ {
+            getInstance: function(contextId) {
+                return new AddCategory(contextId);
+            }
+        };
+    }
+);
+
diff --git a/admin/tool/dataprivacy/amd/src/add_purpose.js b/admin/tool/dataprivacy/amd/src/add_purpose.js
new file mode 100644 (file)
index 0000000..f3c573c
--- /dev/null
@@ -0,0 +1,173 @@
+// 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/>.
+
+/**
+ * Module to add purposes.
+ *
+ * @module     tool_dataprivacy/add_purpose
+ * @package    tool_dataprivacy
+ * @copyright  2018 David Monllao
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery', 'core/str', 'core/ajax', 'core/notification', 'core/modal_factory', 'core/modal_events', 'core/fragment'],
+    function($, Str, Ajax, Notification, ModalFactory, ModalEvents, Fragment) {
+
+        var SELECTORS = {
+            PURPOSE_LINK: '[data-add-element="purpose"]',
+        };
+
+        var AddPurpose = function(contextId) {
+            this.contextId = contextId;
+
+            var stringKeys = [
+                {
+                    key: 'addpurpose',
+                    component: 'tool_dataprivacy'
+                },
+                {
+                    key: 'save',
+                    component: 'admin'
+                }
+            ];
+            this.strings = Str.get_strings(stringKeys);
+
+            this.registerEventListeners();
+        };
+
+        /**
+         * @var {int} contextId
+         * @private
+         */
+        AddPurpose.prototype.contextId = 0;
+
+        /**
+         * @var {Promise}
+         * @private
+         */
+        AddPurpose.prototype.strings = 0;
+
+        AddPurpose.prototype.registerEventListeners = function() {
+
+            var trigger = $(SELECTORS.PURPOSE_LINK);
+            trigger.on('click', function() {
+                return this.strings.then(function(strings) {
+                    ModalFactory.create({
+                        type: ModalFactory.types.SAVE_CANCEL,
+                        title: strings[0],
+                        body: '',
+                    }, trigger).done(function(modal) {
+                        this.setupFormModal(modal, strings[1]);
+                    }.bind(this));
+                }.bind(this))
+                .fail(Notification.exception);
+            }.bind(this));
+
+        };
+
+        /**
+         * @method getBody
+         * @param {Object} formdata
+         * @private
+         * @return {Promise}
+         */
+        AddPurpose.prototype.getBody = function(formdata) {
+
+            var params = null;
+            if (typeof formdata !== "undefined") {
+                params = {jsonformdata: JSON.stringify(formdata)};
+            }
+            // Get the content of the modal.
+            return Fragment.loadFragment('tool_dataprivacy', 'addpurpose_form', this.contextId, params);
+        };
+
+        AddPurpose.prototype.setupFormModal = function(modal, saveText) {
+            modal.setLarge();
+
+            modal.setSaveButtonText(saveText);
+
+            // We want to reset the form every time it is opened.
+            modal.getRoot().on(ModalEvents.hidden, this.destroy.bind(this));
+
+            modal.setBody(this.getBody());
+
+            // We catch the modal save event, and use it to submit the form inside the modal.
+            // Triggering a form submission will give JS validation scripts a chance to check for errors.
+            modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));
+            // We also catch the form submit event and use it to submit the form with ajax.
+            modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));
+
+            this.modal = modal;
+
+            modal.show();
+        };
+
+        /**
+         * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.
+         *
+         * @method submitForm
+         * @param {Event} e Form submission event.
+         * @private
+         */
+        AddPurpose.prototype.submitForm = function(e) {
+            e.preventDefault();
+            this.modal.getRoot().find('form').submit();
+        };
+
+        AddPurpose.prototype.submitFormAjax = function(e) {
+            // We don't want to do a real form submission.
+            e.preventDefault();
+
+            // Convert all the form elements values to a serialised string.
+            var formData = this.modal.getRoot().find('form').serialize();
+
+            Ajax.call([{
+                methodname: 'tool_dataprivacy_create_purpose_form',
+                args: {jsonformdata: JSON.stringify(formData)},
+                done: function(data) {
+                    if (data.validationerrors) {
+                        this.modal.setBody(this.getBody(formData));
+                    } else {
+                        this.close();
+                    }
+                }.bind(this),
+
+                fail: Notification.exception
+            }]);
+        };
+
+        AddPurpose.prototype.close = function() {
+            this.destroy();
+            document.location.reload();
+        };
+
+        AddPurpose.prototype.destroy = function() {
+            Y.use('moodle-core-formchangechecker', function() {
+                M.core_formchangechecker.reset_form_dirty_state();
+            });
+            this.modal.destroy();
+        };
+
+        AddPurpose.prototype.removeListeners = function() {
+            $(SELECTORS.PURPOSE_LINK).off('click');
+        };
+
+        return /** @alias module:tool_dataprivacy/add_purpose */ {
+            getInstance: function(contextId) {
+                return new AddPurpose(contextId);
+            }
+        };
+    }
+);
+
diff --git a/admin/tool/dataprivacy/amd/src/categoriesactions.js b/admin/tool/dataprivacy/amd/src/categoriesactions.js
new file mode 100644 (file)
index 0000000..c40a1a7
--- /dev/null
@@ -0,0 +1,129 @@
+// 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/>.
+
+/**
+ * AMD module for categories actions.
+ *
+ * @module     tool_dataprivacy/categoriesactions
+ * @package    tool_dataprivacy
+ * @copyright  2018 David Monllao
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([
+    'jquery',
+    'core/ajax',
+    'core/notification',
+    'core/str',
+    'core/modal_factory',
+    'core/modal_events'],
+function($, Ajax, Notification, Str, ModalFactory, ModalEvents) {
+
+    /**
+     * List of action selectors.
+     *
+     * @type {{DELETE: string}}
+     */
+    var ACTIONS = {
+        DELETE: '[data-action="deletecategory"]',
+    };
+
+    /**
+     * CategoriesActions class.
+     */
+    var CategoriesActions = function() {
+        this.registerEvents();
+    };
+
+    /**
+     * Register event listeners.
+     */
+    CategoriesActions.prototype.registerEvents = function() {
+        $(ACTIONS.DELETE).click(function(e) {
+            e.preventDefault();
+
+            var id = $(this).data('id');
+            var categoryname = $(this).data('name');
+            var stringkeys = [
+                {
+                    key: 'deletecategory',
+                    component: 'tool_dataprivacy',
+                    param: categoryname
+                },
+                {
+                    key: 'deletecategorytext',
+                    component: 'tool_dataprivacy',
+                    param: categoryname
+                }
+            ];
+
+            Str.get_strings(stringkeys).then(function(langStrings) {
+                var title = langStrings[0];
+                var confirmMessage = langStrings[1];
+                return ModalFactory.create({
+                    title: title,
+                    body: confirmMessage,
+                    type: ModalFactory.types.SAVE_CANCEL
+                }).then(function(modal) {
+                    modal.setSaveButtonText(title);
+
+                    // Handle save event.
+                    modal.getRoot().on(ModalEvents.save, function() {
+
+                        var request = {
+                            methodname: 'tool_dataprivacy_delete_category',
+                            args: {'id': id}
+                        };
+
+                        Ajax.call([request])[0].done(function(data) {
+                            if (data.result) {
+                                $('tr[data-categoryid="' + id + '"]').remove();
+                            } else {
+                                Notification.addNotification({
+                                    message: data.warnings[0].message,
+                                    type: 'error'
+                                });
+                            }
+                        }).fail(Notification.exception);
+                    });
+
+                    // Handle hidden event.
+                    modal.getRoot().on(ModalEvents.hidden, function() {
+                        // Destroy when hidden.
+                        modal.destroy();
+                    });
+
+                    return modal;
+                });
+            }).done(function(modal) {
+                modal.show();
+
+            }).fail(Notification.exception);
+        });
+    };
+
+    return /** @alias module:tool_dataprivacy/categoriesactions */ {
+        // Public variables and functions.
+
+        /**
+         * Initialise the module.
+         *
+         * @method init
+         * @return {CategoriesActions}
+         */
+        'init': function() {
+            return new CategoriesActions();
+        }
+    };
+});
diff --git a/admin/tool/dataprivacy/amd/src/data_deletion.js b/admin/tool/dataprivacy/amd/src/data_deletion.js
new file mode 100644 (file)
index 0000000..e36af21
--- /dev/null
@@ -0,0 +1,156 @@
+// 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/>.
+
+/**
+ * Request actions.
+ *
+ * @module     tool_dataprivacy/data_deletion
+ * @package    tool_dataprivacy
+ * @copyright  2018 Jun Pataleta
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([
+    'jquery',
+    'core/ajax',
+    'core/notification',
+    'core/str',
+    'core/modal_factory',
+    'core/modal_events'],
+function($, Ajax, Notification, Str, ModalFactory, ModalEvents) {
+
+    /**
+     * List of action selectors.
+     *
+     * @type {{MARK_FOR_DELETION: string}}
+     * @type {{SELECT_ALL: string}}
+     */
+    var ACTIONS = {
+        MARK_FOR_DELETION: '[data-action="markfordeletion"]',
+        SELECT_ALL: '[data-action="selectall"]',
+    };
+
+    /**
+     * List of selectors.
+     *
+     * @type {{SELECTCONTEXT: string}}
+     */
+    var SELECTORS = {
+        SELECTCONTEXT: '.selectcontext',
+    };
+
+    /**
+     * DataDeletionActions class.
+     */
+    var DataDeletionActions = function() {
+        this.registerEvents();
+    };
+
+    /**
+     * Register event listeners.
+     */
+    DataDeletionActions.prototype.registerEvents = function() {
+        $(ACTIONS.MARK_FOR_DELETION).click(function(e) {
+            e.preventDefault();
+
+            var selectedIds = [];
+            $(SELECTORS.SELECTCONTEXT).each(function() {
+                var checkbox = $(this);
+                if (checkbox.is(':checked')) {
+                    selectedIds.push(checkbox.val());
+                }
+            });
+            showConfirmation(selectedIds);
+        });
+
+        $(ACTIONS.SELECT_ALL).change(function(e) {
+            e.preventDefault();
+
+            var selectallnone = $(this);
+            if (selectallnone.is(':checked')) {
+                $(SELECTORS.SELECTCONTEXT).attr('checked', 'checked');
+            } else {
+                $(SELECTORS.SELECTCONTEXT).removeAttr('checked');
+            }
+        });
+    };
+
+    /**
+     * Show the confirmation dialogue.
+     *
+     * @param {Array} ids The array of expired context record IDs.
+     */
+    function showConfirmation(ids) {
+        var keys = [
+            {
+                key: 'confirm',
+                component: 'moodle'
+            },
+            {
+                key: 'confirmcontextdeletion',
+                component: 'tool_dataprivacy'
+            }
+        ];
+        var wsfunction = 'tool_dataprivacy_confirm_contexts_for_deletion';
+
+        var modalTitle = '';
+        Str.get_strings(keys).then(function(langStrings) {
+            modalTitle = langStrings[0];
+            var confirmMessage = langStrings[1];
+            return ModalFactory.create({
+                title: modalTitle,
+                body: confirmMessage,
+                type: ModalFactory.types.SAVE_CANCEL
+            });
+        }).then(function(modal) {
+            modal.setSaveButtonText(modalTitle);
+
+            // Handle save event.
+            modal.getRoot().on(ModalEvents.save, function() {
+                // Confirm the request.
+                var params = {
+                    'ids': ids
+                };
+
+                var request = {
+                    methodname: wsfunction,
+                    args: params
+                };
+
+                Ajax.call([request])[0].done(function(data) {
+                    if (data.result) {
+                        window.location.reload();
+                    } else {
+                        Notification.addNotification({
+                            message: data.warnings[0].message,
+                            type: 'error'
+                        });
+                    }
+                }).fail(Notification.exception);
+            });
+
+            // Handle hidden event.
+            modal.getRoot().on(ModalEvents.hidden, function() {
+                // Destroy when hidden.
+                modal.destroy();
+            });
+
+            return modal;
+        }).done(function(modal) {
+            modal.show();
+        }).fail(Notification.exception);
+    }
+
+    return DataDeletionActions;
+});
diff --git a/admin/tool/dataprivacy/amd/src/data_registry.js b/admin/tool/dataprivacy/amd/src/data_registry.js
new file mode 100644 (file)
index 0000000..76f00ee
--- /dev/null
@@ -0,0 +1,317 @@
+// 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/>.
+
+/**
+ * Request actions.
+ *
+ * @module     tool_dataprivacy/data_registry
+ * @package    tool_dataprivacy
+ * @copyright  2018 David Monllao
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery', 'core/str', 'core/ajax', 'core/notification', 'core/templates', 'core/modal_factory',
+    'core/modal_events', 'core/fragment', 'tool_dataprivacy/add_purpose', 'tool_dataprivacy/add_category'],
+    function($, Str, Ajax, Notification, Templates, ModalFactory, ModalEvents, Fragment, AddPurpose, AddCategory) {
+
+        var SELECTORS = {
+            TREE_NODES: '[data-context-tree-node=1]',
+            FORM_CONTAINER: '#context-form-container',
+        };
+
+        var DataRegistry = function(systemContextId, initContextLevel, initContextId) {
+            this.systemContextId = systemContextId;
+            this.currentContextLevel = initContextLevel;
+            this.currentContextId = initContextId;
+            this.init();
+        };
+
+        /**
+         * @var {int} systemContextId
+         * @private
+         */
+        DataRegistry.prototype.systemContextId = 0;
+
+        /**
+         * @var {int} currentContextLevel
+         * @private
+         */
+        DataRegistry.prototype.currentContextLevel = 0;
+
+        /**
+         * @var {int} currentContextId
+         * @private
+         */
+        DataRegistry.prototype.currentContextId = 0;
+
+        /**
+         * @var {AddPurpose} addpurpose
+         * @private
+         */
+        DataRegistry.prototype.addpurpose = null;
+
+        /**
+         * @var {AddCategory} addcategory
+         * @private
+         */
+        DataRegistry.prototype.addcategory = null;
+
+        DataRegistry.prototype.init = function() {
+            // Add purpose and category modals always at system context.
+            this.addpurpose = AddPurpose.getInstance(this.systemContextId);
+            this.addcategory = AddCategory.getInstance(this.systemContextId);
+
+            var stringKeys = [
+                {
+                    key: 'changessaved',
+                    component: 'moodle'
+                }, {
+                    key: 'contextpurposecategorysaved',
+                    component: 'tool_dataprivacy'
+                }, {
+                    key: 'noblockstoload',
+                    component: 'tool_dataprivacy'
+                }, {
+                    key: 'noactivitiestoload',
+                    component: 'tool_dataprivacy'
+                }, {
+                    key: 'nocoursestoload',
+                    component: 'tool_dataprivacy'
+                }
+            ];
+            this.strings = Str.get_strings(stringKeys);
+
+            this.registerEventListeners();
+
+            // Load the default context level form.
+            if (this.currentContextId) {
+                this.loadForm('context_form', [this.currentContextId], this.submitContextFormAjax.bind(this));
+            } else {
+                this.loadForm('contextlevel_form', [this.currentContextLevel], this.submitContextLevelFormAjax.bind(this));
+            }
+        };
+
+        DataRegistry.prototype.registerEventListeners = function() {
+            $(SELECTORS.TREE_NODES).on('click', function(ev) {
+                ev.preventDefault();
+
+                var trigger = $(ev.currentTarget);
+
+                // Active node.
+                $(SELECTORS.TREE_NODES).removeClass('active');
+                trigger.addClass('active');
+
+                var contextLevel = trigger.data('contextlevel');
+                var contextId = trigger.data('contextid');
+                if (contextLevel) {
+                    // Context level level.
+
+                    window.history.pushState({}, null, '?contextlevel=' + contextLevel);
+
+                    // Remove previous add purpose and category listeners to avoid memory leaks.
+                    this.addpurpose.removeListeners();
+                    this.addcategory.removeListeners();
+
+                    // Load the context level form.
+                    this.currentContextLevel = contextLevel;
+                    this.loadForm('contextlevel_form', [this.currentContextLevel], this.submitContextLevelFormAjax.bind(this));
+                } else if (contextId) {
+                    // Context instance level.
+
+                    window.history.pushState({}, null, '?contextid=' + contextId);
+
+                    // Remove previous add purpose and category listeners to avoid memory leaks.
+                    this.addpurpose.removeListeners();
+                    this.addcategory.removeListeners();
+
+                    // Load the context level form.
+                    this.currentContextId = contextId;
+                    this.loadForm('context_form', [this.currentContextId], this.submitContextFormAjax.bind(this));
+                } else {
+                    // Expandable nodes.
+
+                    var expandContextId = trigger.data('expandcontextid');
+                    var expandElement = trigger.data('expandelement');
+                    var expanded = trigger.data('expanded');
+
+                    // Extra checking that there is an expandElement because we remove it after loading 0 branches.
+                    if (expandElement) {
+
+                        if (!expanded) {
+                            if (trigger.data('loaded') || !expandContextId || !expandElement) {
+                                this.expand(trigger);
+                            } else {
+
+                                trigger.find('> i').removeClass('fa-plus');
+                                trigger.find('> i').addClass('fa-circle-o-notch fa-spin');
+                                this.loadExtra(trigger, expandContextId, expandElement);
+                            }
+                        } else {
+                            this.collapse(trigger);
+                        }
+                    }
+                }
+
+            }.bind(this));
+        };
+
+        DataRegistry.prototype.removeListeners = function() {
+            $(SELECTORS.TREE_NODES).off('click');
+        };
+
+        DataRegistry.prototype.loadForm = function(fragmentName, fragmentArgs, formSubmitCallback) {
+
+            this.clearForm();
+
+            var fragment = Fragment.loadFragment('tool_dataprivacy', fragmentName, this.systemContextId, fragmentArgs);
+            fragment.done(function(html, js) {
+
+                $(SELECTORS.FORM_CONTAINER).html(html);
+                Templates.runTemplateJS(js);
+
+                this.addpurpose.registerEventListeners();
+                this.addcategory.registerEventListeners();
+
+                // We also catch the form submit event and use it to submit the form with ajax.
+                $(SELECTORS.FORM_CONTAINER).on('submit', 'form', formSubmitCallback);
+
+            }.bind(this)).fail(Notification.exception);
+        };
+
+        DataRegistry.prototype.clearForm = function() {
+            // For the previously loaded form.
+            Y.use('moodle-core-formchangechecker', function() {
+                M.core_formchangechecker.reset_form_dirty_state();
+            });
+
+            // Remove previous listeners.
+            $(SELECTORS.FORM_CONTAINER).off('submit', 'form');
+        };
+
+        /**
+         * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.
+         *
+         * @method submitForm
+         * @param {Event} e Form submission event.
+         * @private
+         */
+        DataRegistry.prototype.submitForm = function(e) {
+            e.preventDefault();
+            $(SELECTORS.FORM_CONTAINER).find('form').submit();
+        };
+
+        DataRegistry.prototype.submitContextLevelFormAjax = function(e) {
+            this.submitFormAjax(e, 'tool_dataprivacy_set_contextlevel_form');
+        };
+
+        DataRegistry.prototype.submitContextFormAjax = function(e) {
+            this.submitFormAjax(e, 'tool_dataprivacy_set_context_form');
+        };
+
+        DataRegistry.prototype.submitFormAjax = function(e, saveMethodName) {
+            // We don't want to do a real form submission.
+            e.preventDefault();
+
+            // Convert all the form elements values to a serialised string.
+            var formData = $(SELECTORS.FORM_CONTAINER).find('form').serialize();
+            return this.strings.then(function(strings) {
+                Ajax.call([{
+                    methodname: saveMethodName,
+                    args: {jsonformdata: JSON.stringify(formData)},
+                    done: function() {
+                        Notification.alert(strings[0], strings[1]);
+                    },
+                    fail: Notification.exception
+                }]);
+            }).catch(Notification.exception);
+
+        };
+
+        DataRegistry.prototype.loadExtra = function(parentNode, expandContextId, expandElement) {
+
+            Ajax.call([{
+                methodname: 'tool_dataprivacy_tree_extra_branches',
+                args: {
+                    contextid: expandContextId,
+                    element: expandElement,
+                },
+                done: function(data) {
+                    if (data.branches.length == 0) {
+                        this.noElements(parentNode, expandElement);
+                        return;
+                    }
+                    Templates.render('tool_dataprivacy/context_tree_branches', data)
+                        .then(function(html) {
+                            parentNode.after(html);
+                            this.removeListeners();
+                            this.registerEventListeners();
+                            this.expand(parentNode);
+                            parentNode.data('loaded', 1);
+                            return;
+                        }.bind(this))
+                        .fail(Notification.exception);
+                }.bind(this),
+                fail: Notification.exception
+            }]);
+        };
+
+        DataRegistry.prototype.noElements = function(node, expandElement) {
+            node.data('expandcontextid', '');
+            node.data('expandelement', '');
+            this.strings.then(function(strings) {
+
+                // 2 = blocks, 3 = activities, 4 = courses (although courses is not likely really).
+                var key = 2;
+                if (expandElement == 'module') {
+                    key = 3;
+                } else if (expandElement == 'course') {
+                    key = 4;
+                }
+                node.text(strings[key]);
+                return;
+            }).fail(Notification.exception);
+        };
+
+        DataRegistry.prototype.collapse = function(node) {
+            node.data('expanded', 0);
+            node.siblings('nav').addClass('hidden');
+            node.find('> i').removeClass('fa-minus');
+            node.find('> i').addClass('fa-plus');
+        };
+
+        DataRegistry.prototype.expand = function(node) {
+            node.data('expanded', 1);
+            node.siblings('nav').removeClass('hidden');
+            node.find('> i').removeClass('fa-plus');
+            // Also remove the spinning one if data was just loaded.
+            node.find('> i').removeClass('fa-circle-o-notch fa-spin');
+            node.find('> i').addClass('fa-minus');
+        };
+        return /** @alias module:tool_dataprivacy/data_registry */ {
+
+            /**
+             * Initialise the page.
+             *
+             * @param {Number} systemContextId
+             * @param {Number} initContextLevel
+             * @param {Number} initContextId
+             * @return {DataRegistry}
+             */
+            init: function(systemContextId, initContextLevel, initContextId) {
+                return new DataRegistry(systemContextId, initContextLevel, initContextId);
+            }
+        };
+    }
+);
+
diff --git a/admin/tool/dataprivacy/amd/src/data_request_modal.js b/admin/tool/dataprivacy/amd/src/data_request_modal.js
new file mode 100644 (file)
index 0000000..abf717b
--- /dev/null
@@ -0,0 +1,93 @@
+// 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/>.
+
+/**
+ * Request actions.
+ *
+ * @module     tool_dataprivacy/data_request_modal
+ * @package    tool_dataprivacy
+ * @copyright  2018 Jun Pataleta
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery', 'core/notification', 'core/custom_interaction_events', 'core/modal', 'core/modal_registry',
+        'tool_dataprivacy/events'],
+    function($, Notification, CustomEvents, Modal, ModalRegistry, DataPrivacyEvents) {
+
+        var registered = false;
+        var SELECTORS = {
+            APPROVE_BUTTON: '[data-action="approve"]',
+            DENY_BUTTON: '[data-action="deny"]',
+        };
+
+        /**
+         * Constructor for the Modal.
+         *
+         * @param {object} root The root jQuery element for the modal
+         */
+        var ModalDataRequest = function(root) {
+            Modal.call(this, root);
+
+            if (!this.getFooter().find(SELECTORS.APPROVE_BUTTON).length) {
+                Notification.exception({message: 'No approve button found'});
+            }
+
+            if (!this.getFooter().find(SELECTORS.DENY_BUTTON).length) {
+                Notification.exception({message: 'No deny button found'});
+            }
+        };
+
+        ModalDataRequest.TYPE = 'tool_dataprivacy-data_request';
+        ModalDataRequest.prototype = Object.create(Modal.prototype);
+        ModalDataRequest.prototype.constructor = ModalDataRequest;
+
+        /**
+         * Set up all of the event handling for the modal.
+         *
+         * @method registerEventListeners
+         */
+        ModalDataRequest.prototype.registerEventListeners = function() {
+            // Apply parent event listeners.
+            Modal.prototype.registerEventListeners.call(this);
+
+            this.getModal().on(CustomEvents.events.activate, SELECTORS.APPROVE_BUTTON, function(e, data) {
+                var approveEvent = $.Event(DataPrivacyEvents.approve);
+                this.getRoot().trigger(approveEvent, this);
+
+                if (!approveEvent.isDefaultPrevented()) {
+                    this.hide();
+                    data.originalEvent.preventDefault();
+                }
+            }.bind(this));
+
+            this.getModal().on(CustomEvents.events.activate, SELECTORS.DENY_BUTTON, function(e, data) {
+                var denyEvent = $.Event(DataPrivacyEvents.deny);
+                this.getRoot().trigger(denyEvent, this);
+
+                if (!denyEvent.isDefaultPrevented()) {
+                    this.hide();
+                    data.originalEvent.preventDefault();
+                }
+            }.bind(this));
+        };
+
+        // Automatically register with the modal registry the first time this module is imported so that you can create modals
+        // of this type using the modal factory.
+        if (!registered) {
+            ModalRegistry.register(ModalDataRequest.TYPE, ModalDataRequest, 'tool_dataprivacy/data_request_modal');
+            registered = true;
+        }
+
+        return ModalDataRequest;
+    });
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/src/effective_retention_period.js b/admin/tool/dataprivacy/amd/src/effective_retention_period.js
new file mode 100644 (file)
index 0000000..8d587ca
--- /dev/null
@@ -0,0 +1,92 @@
+// 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/>.
+
+/**
+ * Module to update the displayed retention period.
+ *
+ * @module     tool_dataprivacy/effective_retention_period
+ * @package    tool_dataprivacy
+ * @copyright  2018 David Monllao
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define(['jquery'],
+    function($) {
+
+        var SELECTORS = {
+            PURPOSE_SELECT: '#id_purposeid',
+            RETENTION_FIELD_BOOST: '#id_error_retention_current',
+            RETENTION_FIELD_CLEAN: '#fitem_id_retention_current [data-fieldtype=static]',
+        };
+
+        /**
+         * Constructor for the retention period display.
+         *
+         * @param {Array} purposeRetentionPeriods Associative array of purposeids with effective retention period at this context
+         */
+        var EffectiveRetentionPeriod = function(purposeRetentionPeriods) {
+            this.purposeRetentionPeriods = purposeRetentionPeriods;
+            this.registerEventListeners();
+        };
+
+        /**
+         * Removes the current 'change' listeners.
+         *
+         * Useful when a new form is loaded.
+         */
+        var removeListeners = function() {
+            $(SELECTORS.PURPOSE_SELECT).off('change');
+        };
+
+        /**
+         * @var {Array} purposeRetentionPeriods
+         * @private
+         */
+        EffectiveRetentionPeriod.prototype.purposeRetentionPeriods = [];
+
+        /**
+         * Add purpose change listeners.
+         *
+         * @method registerEventListeners
+         */
+        EffectiveRetentionPeriod.prototype.registerEventListeners = function() {
+
+            $(SELECTORS.PURPOSE_SELECT).on('change', function(ev) {
+                var selected = $(ev.currentTarget).val();
+                var selectedPurpose = this.purposeRetentionPeriods[selected];
+
+                var cleanSelector = $(SELECTORS.RETENTION_FIELD_CLEAN);
+                if (cleanSelector.length > 0) {
+                    cleanSelector.text(selectedPurpose);
+                } else {
+                    var boostSelector = $(SELECTORS.RETENTION_FIELD_BOOST);
+                    var retentionField = boostSelector.siblings();
+                    if (retentionField.length > 0) {
+                        retentionField.text(selectedPurpose);
+                    }
+                }
+
+            }.bind(this));
+        };
+
+        return /** @alias module:tool_dataprivacy/effective_retention_period */ {
+            init: function(purposeRetentionPeriods) {
+                // Remove previously attached listeners.
+                removeListeners();
+                return new EffectiveRetentionPeriod(purposeRetentionPeriods);
+            }
+        };
+    }
+);
+
diff --git a/admin/tool/dataprivacy/amd/src/events.js b/admin/tool/dataprivacy/amd/src/events.js
new file mode 100644 (file)
index 0000000..9398dc4
--- /dev/null
@@ -0,0 +1,30 @@
+// 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/>.
+
+/**
+ * Contain the events the data privacy tool can fire.
+ *
+ * @module     tool_dataprivacy/events
+ * @class      events
+ * @package    tool_dataprivacy
+ * @copyright  2018 Jun Pataleta
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([], function() {
+    return {
+        approve: 'tool_dataprivacy-data_request:approve',
+        deny: 'tool_dataprivacy-data_request:deny',
+    };
+});
diff --git a/admin/tool/dataprivacy/amd/src/expand_contract.js b/admin/tool/dataprivacy/amd/src/expand_contract.js
new file mode 100644 (file)
index 0000000..41b7e50
--- /dev/null
@@ -0,0 +1,89 @@
+// 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/>.
+
+/**
+ * Potential user selector module.
+ *
+ * @module     tool_dataprivacy/expand_contract
+ * @class      page-expand-contract
+ * @package    tool_dataprivacy
+ * @copyright  2018 Adrian Greeve
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define(['jquery', 'core/url', 'core/str'], function($, url, str) {
+
+    var expandedImage = $('<img alt="" src="' + url.imageUrl('t/expanded') + '"/>');
+    var collapsedImage = $('<img alt="" src="' + url.imageUrl('t/collapsed') + '"/>');
+
+    return /** @alias module:tool_dataprivacy/expand-collapse */ {
+        /**
+         * Expand or collapse a selected node.
+         *
+         * @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')) {
+                targetnode.removeClass('hide');
+                targetnode.addClass('visible');
+                targetnode.attr('aria-expanded', true);
+                thisnode.find(':header i.fa').removeClass('fa-plus-square');
+                thisnode.find(':header i.fa').addClass('fa-minus-square');
+                thisnode.find(':header img.icon').attr('src', expandedImage.attr('src'));
+            } else {
+                targetnode.removeClass('visible');
+                targetnode.addClass('hide');
+                targetnode.attr('aria-expanded', false);
+                thisnode.find(':header i.fa').removeClass('fa-minus-square');
+                thisnode.find(':header i.fa').addClass('fa-plus-square');
+                thisnode.find(':header img.icon').attr('src', collapsedImage.attr('src'));
+            }
+        },
+
+        /**
+         * 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';
+            var ariaexpandedstate = (nextstate == 'visible') ? true : false;
+            var iconclassnow = (nextstate == 'visible') ? 'fa-plus-square' : 'fa-minus-square';
+            var iconclassnext = (nextstate == 'visible') ? 'fa-minus-square' : 'fa-plus-square';
+            var imagenow = (nextstate == 'visible') ? expandedImage.attr('src') : collapsedImage.attr('src');
+            $('.' + currentstate).each(function() {
+                $(this).removeClass(currentstate);
+                $(this).addClass(nextstate);
+                $(this).attr('aria-expanded', ariaexpandedstate);
+            });
+            $('.tool_dataprivacy-expand-all').data('visibilityState', currentstate);
+
+            str.get_string(currentstate, 'tool_dataprivacy').then(function(langString) {
+                $('.tool_dataprivacy-expand-all').html(langString);
+            }).catch(Notification.exception);
+
+            $(':header i.fa').each(function() {
+                $(this).removeClass(iconclassnow);
+                $(this).addClass(iconclassnext);
+            });
+            $(':header img.icon').each(function() {
+                $(this).attr('src', imagenow);
+            });
+        }
+    };
+});
diff --git a/admin/tool/dataprivacy/amd/src/form-user-selector.js b/admin/tool/dataprivacy/amd/src/form-user-selector.js
new file mode 100644 (file)
index 0000000..18c297a
--- /dev/null
@@ -0,0 +1,76 @@
+// 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/>.
+
+/**
+ * Potential user selector module.
+ *
+ * @module     tool_dataprivacy/form-user-selector
+ * @class      form-user-selector
+ * @package    tool_dataprivacy
+ * @copyright  2018 Jun Pataleta
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {
+
+    return /** @alias module:tool_dataprivacy/form-user-selector */ {
+
+        processResults: function(selector, results) {
+            var users = [];
+            $.each(results, function(index, user) {
+                users.push({
+                    value: user.id,
+                    label: user._label
+                });
+            });
+            return users;
+        },
+
+        transport: function(selector, query, success, failure) {
+            var promise;
+
+            promise = Ajax.call([{
+                methodname: 'tool_dataprivacy_get_users',
+                args: {
+                    query: query
+                }
+            }]);
+
+            promise[0].then(function(results) {
+                var promises = [],
+                    i = 0;
+
+                // Render the label.
+                $.each(results, function(index, user) {
+                    promises.push(Templates.render('tool_dataprivacy/form-user-selector-suggestion', user));
+                });
+
+                // Apply the label to the results.
+                return $.when.apply($.when, promises).then(function() {
+                    var args = arguments;
+                    $.each(results, function(index, user) {
+                        user._label = args[i];
+                        i++;
+                    });
+                    success(results);
+                    return;
+                });
+
+            }).fail(failure);
+        }
+
+    };
+
+});
diff --git a/admin/tool/dataprivacy/amd/src/myrequestactions.js b/admin/tool/dataprivacy/amd/src/myrequestactions.js
new file mode 100644 (file)
index 0000000..5ec631e
--- /dev/null
@@ -0,0 +1,221 @@
+// 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/>.
+
+/**
+ * AMD module to enable users to manage their own data requests.
+ *
+ * @module     tool_dataprivacy/myrequestactions
+ * @package    tool_dataprivacy
+ * @copyright  2018 Jun Pataleta
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([
+    'jquery',
+    'core/ajax',
+    'core/notification',
+    'core/str',
+    'core/modal_factory',
+    'core/modal_events',
+    'core/templates'],
+function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates) {
+
+    /**
+     * List of action selectors.
+     *
+     * @type {{CANCEL_REQUEST: string}}
+     * @type {{CONTACT_DPO: string}}
+     */
+    var ACTIONS = {
+        CANCEL_REQUEST: '[data-action="cancel"]',
+        CONTACT_DPO: '[data-action="contactdpo"]',
+    };
+
+    /**
+     * MyRequestActions class.
+     */
+    var MyRequestActions = function() {
+        this.registerEvents();
+    };
+
+    /**
+     * Register event listeners.
+     */
+    MyRequestActions.prototype.registerEvents = function() {
+        $(ACTIONS.CANCEL_REQUEST).click(function(e) {
+            e.preventDefault();
+
+            var requestId = $(this).data('requestid');
+            var stringkeys = [
+                {
+                    key: 'cancelrequest',
+                    component: 'tool_dataprivacy'
+                },
+                {
+                    key: 'cancelrequestconfirmation',
+                    component: 'tool_dataprivacy'
+                }
+            ];
+
+            Str.get_strings(stringkeys).then(function(langStrings) {
+                var title = langStrings[0];
+                var confirmMessage = langStrings[1];
+                return ModalFactory.create({
+                    title: title,
+                    body: confirmMessage,
+                    type: ModalFactory.types.SAVE_CANCEL
+                }).then(function(modal) {
+                    modal.setSaveButtonText(title);
+
+                    // Handle save event.
+                    modal.getRoot().on(ModalEvents.save, function() {
+                        // Cancel the request.
+                        var params = {
+                            'requestid': requestId
+                        };
+
+                        var request = {
+                            methodname: 'tool_dataprivacy_cancel_data_request',
+                            args: params
+                        };
+
+                        Ajax.call([request])[0].done(function(data) {
+                            if (data.result) {
+                                window.location.reload();
+                            } else {
+                                Notification.addNotification({
+                                    message: data.warnings[0].message,
+                                    type: 'error'
+                                });
+                            }
+                        }).fail(Notification.exception);
+                    });
+
+                    // Handle hidden event.
+                    modal.getRoot().on(ModalEvents.hidden, function() {
+                        // Destroy when hidden.
+                        modal.destroy();
+                    });
+
+                    return modal;
+                });
+            }).done(function(modal) {
+                // Show the modal!
+                modal.show();
+
+            }).fail(Notification.exception);
+        });
+
+        $(ACTIONS.CONTACT_DPO).click(function(e) {
+            e.preventDefault();
+
+            var replyToEmail = $(this).data('replytoemail');
+
+            var keys = [
+                {
+                    key: 'contactdataprotectionofficer',
+                    component: 'tool_dataprivacy'
+                },
+                {
+                    key: 'send',
+                    component: 'tool_dataprivacy'
+                },
+            ];
+
+            var sendButtonText = '';
+            Str.get_strings(keys).then(function(langStrings) {
+                var modalTitle = langStrings[0];
+                sendButtonText = langStrings[1];
+                var context = {
+                    'replytoemail': replyToEmail
+                };
+                return ModalFactory.create({
+                    title: modalTitle,
+                    body: Templates.render('tool_dataprivacy/contact_dpo', context),
+                    type: ModalFactory.types.SAVE_CANCEL,
+                    large: true
+                });
+            }).done(function(modal) {
+                modal.setSaveButtonText(sendButtonText);
+
+                // Handle send event.
+                modal.getRoot().on(ModalEvents.save, function(e) {
+                    var message = $('#message').val().trim();
+                    if (message.length === 0) {
+                        e.preventDefault();
+                        // Show validation error when the message is empty.
+                        $('[data-region="messageinput"]').addClass('has-danger notifyproblem');
+                        $('#id_error_message').removeAttr('hidden');
+                    } else {
+                        // Send the message.
+                        sendMessageToDPO(message);
+                    }
+                });
+
+                // Handle hidden event.
+                modal.getRoot().on(ModalEvents.hidden, function() {
+                    // Destroy when hidden.
+                    modal.destroy();
+                });
+
+                // Show the modal!
+                modal.show();
+            }).fail(Notification.exception);
+        });
+    };
+
+    /**
+     * Send message to the Data Protection Officer.
+     *
+     * @param {String} message The message to send.
+     */
+    function sendMessageToDPO(message) {
+        var request = {
+            methodname: 'tool_dataprivacy_contact_dpo',
+            args: {
+                message: message
+            }
+        };
+
+        var requestType = 'success';
+        Ajax.call([request])[0].then(function(data) {
+            if (data.result) {
+                return Str.get_string('requestsubmitted', 'tool_dataprivacy');
+            }
+            requestType = 'error';
+            return data.warnings.join('<br>');
+
+        }).done(function(message) {
+            Notification.addNotification({
+                message: message,
+                type: requestType
+            });
+
+        }).fail(Notification.exception);
+    }
+
+    return /** @alias module:tool_dataprivacy/myrequestactions */ {
+        // Public variables and functions.
+
+        /**
+         * Initialise the unified user filter.
+         *
+         * @method init
+         * @return {MyRequestActions}
+         */
+        'init': function() {
+            return new MyRequestActions();
+        }
+    };
+});
diff --git a/admin/tool/dataprivacy/amd/src/purposesactions.js b/admin/tool/dataprivacy/amd/src/purposesactions.js
new file mode 100644 (file)
index 0000000..fd92141
--- /dev/null
@@ -0,0 +1,129 @@
+// 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/>.
+
+/**
+ * AMD module for purposes actions.
+ *
+ * @module     tool_dataprivacy/purposesactions
+ * @package    tool_dataprivacy
+ * @copyright  2018 David Monllao
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([
+    'jquery',
+    'core/ajax',
+    'core/notification',
+    'core/str',
+    'core/modal_factory',
+    'core/modal_events'],
+function($, Ajax, Notification, Str, ModalFactory, ModalEvents) {
+
+    /**
+     * List of action selectors.
+     *
+     * @type {{DELETE: string}}
+     */
+    var ACTIONS = {
+        DELETE: '[data-action="deletepurpose"]',
+    };
+
+    /**
+     * PurposesActions class.
+     */
+    var PurposesActions = function() {
+        this.registerEvents();
+    };
+
+    /**
+     * Register event listeners.
+     */
+    PurposesActions.prototype.registerEvents = function() {
+        $(ACTIONS.DELETE).click(function(e) {
+            e.preventDefault();
+
+            var id = $(this).data('id');
+            var purposename = $(this).data('name');
+            var stringkeys = [
+                {
+                    key: 'deletepurpose',
+                    component: 'tool_dataprivacy',
+                    param: purposename
+                },
+                {
+                    key: 'deletepurposetext',
+                    component: 'tool_dataprivacy',
+                    param: purposename
+                }
+            ];
+
+            Str.get_strings(stringkeys).then(function(langStrings) {
+                var title = langStrings[0];
+                var confirmMessage = langStrings[1];
+                return ModalFactory.create({
+                    title: title,
+                    body: confirmMessage,
+                    type: ModalFactory.types.SAVE_CANCEL
+                }).then(function(modal) {
+                    modal.setSaveButtonText(title);
+
+                    // Handle save event.
+                    modal.getRoot().on(ModalEvents.save, function() {
+
+                        var request = {
+                            methodname: 'tool_dataprivacy_delete_purpose',
+                            args: {'id': id}
+                        };
+
+                        Ajax.call([request])[0].done(function(data) {
+                            if (data.result) {
+                                $('tr[data-purposeid="' + id + '"]').remove();
+                            } else {
+                                Notification.addNotification({
+                                    message: data.warnings[0].message,
+                                    type: 'error'
+                                });
+                            }
+                        }).fail(Notification.exception);
+                    });
+
+                    // Handle hidden event.
+                    modal.getRoot().on(ModalEvents.hidden, function() {
+                        // Destroy when hidden.
+                        modal.destroy();
+                    });
+
+                    return modal;
+                });
+            }).done(function(modal) {
+                modal.show();
+
+            }).fail(Notification.exception);
+        });
+    };
+
+    return /** @alias module:tool_dataprivacy/purposesactions */ {
+        // Public variables and functions.
+
+        /**
+         * Initialise the module.
+         *
+         * @method init
+         * @return {PurposesActions}
+         */
+        'init': function() {
+            return new PurposesActions();
+        }
+    };
+});
diff --git a/admin/tool/dataprivacy/amd/src/requestactions.js b/admin/tool/dataprivacy/amd/src/requestactions.js
new file mode 100644 (file)
index 0000000..c0941f8
--- /dev/null
@@ -0,0 +1,227 @@
+// 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/>.
+
+/**
+ * Request actions.
+ *
+ * @module     tool_dataprivacy/requestactions
+ * @package    tool_dataprivacy
+ * @copyright  2018 Jun Pataleta
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([
+    'jquery',
+    'core/ajax',
+    'core/notification',
+    'core/str',
+    'core/modal_factory',
+    'core/modal_events',
+    'core/templates',
+    'tool_dataprivacy/data_request_modal',
+    'tool_dataprivacy/events'],
+function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, ModalDataRequest, DataPrivacyEvents) {
+
+    /**
+     * List of action selectors.
+     *
+     * @type {{APPROVE_REQUEST: string}}
+     * @type {{DENY_REQUEST: string}}
+     * @type {{VIEW_REQUEST: string}}
+     */
+    var ACTIONS = {
+        APPROVE_REQUEST: '[data-action="approve"]',
+        DENY_REQUEST: '[data-action="deny"]',
+        VIEW_REQUEST: '[data-action="view"]'
+    };
+
+    /**
+     * RequestActions class.
+     */
+    var RequestActions = function() {
+        this.registerEvents();
+    };
+
+    /**
+     * Register event listeners.
+     */
+    RequestActions.prototype.registerEvents = function() {
+        $(ACTIONS.VIEW_REQUEST).click(function(e) {
+            e.preventDefault();
+
+            var requestId = $(this).data('requestid');
+
+            // Cancel the request.
+            var params = {
+                'requestid': requestId
+            };
+
+            var request = {
+                methodname: 'tool_dataprivacy_get_data_request',
+                args: params
+            };
+
+            var promises = Ajax.call([request]);
+            var modalTitle = '';
+            var modalType = ModalFactory.types.DEFAULT;
+            $.when(promises[0]).then(function(data) {
+                if (data.result) {
+                    // Check if the status is awaiting approval.
+                    if (data.result.status == 2) {
+                        modalType = ModalDataRequest.TYPE;
+                    }
+                    modalTitle = data.result.typename;
+                    return Templates.render('tool_dataprivacy/request_details', data.result);
+                }
+                // Fail.
+                Notification.addNotification({
+                    message: data.warnings[0].message,
+                    type: 'error'
+                });
+                return false;
+
+            }).then(function(html) {
+                return ModalFactory.create({
+                    title: modalTitle,
+                    body: html,
+                    type: modalType,
+                    large: true
+                }).then(function(modal) {
+                    // Handle approve event.
+                    modal.getRoot().on(DataPrivacyEvents.approve, function() {
+                        showConfirmation(DataPrivacyEvents.approve, requestId);
+                    });
+
+                    // Handle deny event.
+                    modal.getRoot().on(DataPrivacyEvents.deny, function() {
+                        showConfirmation(DataPrivacyEvents.deny, requestId);
+                    });
+
+                    // Handle hidden event.
+                    modal.getRoot().on(ModalEvents.hidden, function() {
+                        // Destroy when hidden.
+                        modal.destroy();
+                    });
+
+                    return modal;
+                });
+            }).done(function(modal) {
+                // Show the modal!
+                modal.show();
+            }).fail(Notification.exception);
+        });
+
+        $(ACTIONS.APPROVE_REQUEST).click(function(e) {
+            e.preventDefault();
+
+            var requestId = $(this).data('requestid');
+            showConfirmation(DataPrivacyEvents.approve, requestId);
+        });
+
+        $(ACTIONS.DENY_REQUEST).click(function(e) {
+            e.preventDefault();
+
+            var requestId = $(this).data('requestid');
+            showConfirmation(DataPrivacyEvents.deny, requestId);
+        });
+    };
+
+    /**
+     * Show the confirmation dialogue.
+     *
+     * @param {String} action The action name.
+     * @param {Number} requestId The request ID.
+     */
+    function showConfirmation(action, requestId) {
+        var keys = [];
+        var wsfunction = '';
+        switch (action) {
+            case DataPrivacyEvents.approve:
+                keys = [
+                    {
+                        key: 'approverequest',
+                        component: 'tool_dataprivacy'
+                    },
+                    {
+                        key: 'confirmapproval',
+                        component: 'tool_dataprivacy'
+                    }
+                ];
+                wsfunction = 'tool_dataprivacy_approve_data_request';
+                break;
+            case DataPrivacyEvents.deny:
+                keys = [
+                    {
+                        key: 'denyrequest',
+                        component: 'tool_dataprivacy'
+                    },
+                    {
+                        key: 'confirmdenial',
+                        component: 'tool_dataprivacy'
+                    }
+                ];
+                wsfunction = 'tool_dataprivacy_deny_data_request';
+                break;
+        }
+
+        var modalTitle = '';
+        Str.get_strings(keys).then(function(langStrings) {
+            modalTitle = langStrings[0];
+            var confirmMessage = langStrings[1];
+            return ModalFactory.create({
+                title: modalTitle,
+                body: confirmMessage,
+                type: ModalFactory.types.SAVE_CANCEL
+            });
+        }).then(function(modal) {
+            modal.setSaveButtonText(modalTitle);
+
+            // Handle save event.
+            modal.getRoot().on(ModalEvents.save, function() {
+                // Confirm the request.
+                var params = {
+                    'requestid': requestId
+                };
+
+                var request = {
+                    methodname: wsfunction,
+                    args: params
+                };
+
+                Ajax.call([request])[0].done(function(data) {
+                    if (data.result) {
+                        window.location.reload();
+                    } else {
+                        Notification.addNotification({
+                            message: data.warnings[0].message,
+                            type: 'error'
+                        });
+                    }
+                }).fail(Notification.exception);
+            });
+
+            // Handle hidden event.
+            modal.getRoot().on(ModalEvents.hidden, function() {
+                // Destroy when hidden.
+                modal.destroy();
+            });
+
+            return modal;
+        }).done(function(modal) {
+            modal.show();
+        }).fail(Notification.exception);
+    }
+
+    return RequestActions;
+});
diff --git a/admin/tool/dataprivacy/categories.php b/admin/tool/dataprivacy/categories.php
new file mode 100644 (file)
index 0000000..b160ff3
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This page lets users manage categories.
+ *
+ * @package    tool_dataprivacy
+ * @copyright  2018 David Monllao
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(__DIR__ . '/../../../config.php');
+
+$url = new moodle_url("/admin/tool/dataprivacy/categories.php");
+$title = get_string('editcategories', 'tool_dataprivacy');
+
+\tool_dataprivacy\page_helper::setup($url, $title, 'dataregistry');
+
+$output = $PAGE->get_renderer('tool_dataprivacy');
+echo $output->header();
+
+$categories = \tool_dataprivacy\api::get_categories();
+$renderable = new \tool_dataprivacy\output\categories($categories);
+
+echo $output->render($renderable);
+echo $output->footer();
diff --git a/admin/tool/dataprivacy/classes/api.php b/admin/tool/dataprivacy/classes/api.php
new file mode 100644 (file)
index 0000000..9ec5f45
--- /dev/null
@@ -0,0 +1,883 @@
+<?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 helper methods for processing data requests.
+ *
+ * @package    tool_dataprivacy
+ * @copyright  2018 Jun Pataleta
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace tool_dataprivacy;
+
+use coding_exception;
+use context_system;
+use core\invalid_persistent_exception;
+use core\message\message;
+use core\task\manager;
+use core_privacy\local\request\approved_contextlist;
+use core_privacy\local\request\contextlist_collection;
+use core_user;
+use dml_exception;
+use moodle_exception;
+use moodle_url;
+use required_capability_exception;
+use stdClass;
+use tool_dataprivacy\external\data_request_exporter;
+use tool_dataprivacy\task\initiate_data_request_task;
+use tool_dataprivacy\task\process_data_request_task;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Class containing helper methods for processing data requests.
+ *
+ * @copyright  2018 Jun Pataleta
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class api {
+
+    /** Data export request type. */
+    const DATAREQUEST_TYPE_EXPORT = 1;
+
+    /** Data deletion request type. */
+    const DATAREQUEST_TYPE_DELETE = 2;
+
+    /** Other request type. Usually of enquiries to the DPO. */
+    const DATAREQUEST_TYPE_OTHERS = 3;
+
+    /** Newly submitted and we haven't yet started finding out where they have data. */
+    const DATAREQUEST_STATUS_PENDING = 0;
+
+    /** Newly submitted and we have started to find the location of data. */
+    const DATAREQUEST_STATUS_PREPROCESSING = 1;
+
+    /** Metadata ready and awaiting review and approval by the Data Protection officer. */
+    const DATAREQUEST_STATUS_AWAITING_APPROVAL = 2;
+
+    /** Request approved and will be processed soon. */
+    const DATAREQUEST_STATUS_APPROVED = 3;
+
+    /** The request is now being processed. */
+    const DATAREQUEST_STATUS_PROCESSING = 4;
+
+    /** Data request completed. */
+    const DATAREQUEST_STATUS_COMPLETE = 5;
+
+    /** Data request cancelled by the user. */
+    const DATAREQUEST_STATUS_CANCELLED = 6;
+
+    /** Data request rejected by the DPO. */
+    const DATAREQUEST_STATUS_REJECTED = 7;
+
+    /**
+     * Determines whether the user can contact the site's Data Protection Officer via Moodle.
+     *
+     * @return boolean True when tool_dataprivacy|contactdataprotectionofficer is enabled.
+     * @throws dml_exception
+     */
+    public static function can_contact_dpo() {
+        return get_config('tool_dataprivacy', 'contactdataprotectionofficer') == 1;
+    }
+
+    /**
+     * Check's whether the current user has the capability to manage data requests.
+     *
+     * @param int $userid The user ID.
+     * @return bool
+     * @throws coding_exception
+     * @throws dml_exception
+     */
+    public static function can_manage_data_requests($userid) {
+        $context = context_system::instance();
+
+        // A user can manage data requests if he/she has the site DPO role and has the capability to manage data requests.
+        return self::is_site_dpo($userid) && has_capability('tool/dataprivacy:managedatarequests', $context, $userid);
+    }
+
+    /**
+     * Checks if the current user can manage the data registry at the provided id.
+     *
+     * @param int $contextid Fallback to system context id.
+     * @throws \required_capability_exception
+     * @return null
+     */
+    public static function check_can_manage_data_registry($contextid = false) {
+        if ($contextid) {
+            $context = \context_helper::instance_by_id($contextid);
+        } else {
+            $context = \context_system::instance();
+        }
+
+        require_capability('tool/dataprivacy:managedataregistry', $context);
+    }
+
+    /**
+     * Fetches the list of users with the Data Protection Officer role.
+     *
+     * @throws dml_exception
+     */
+    public static function get_site_dpos() {
+        // Get role(s) that can manage data requests.
+        $dporoles = explode(',', get_config('tool_dataprivacy', 'dporoles'));
+
+        $dpos = [];
+        $context = context_system::instance();
+        foreach ($dporoles as $roleid) {
+            if (empty($roleid)) {
+                continue;
+            }
+            $allnames = get_all_user_name_fields(true, 'u');
+            $fields = 'u.id, u.confirmed, u.username, '. $allnames . ', ' .
+                      'u.maildisplay, u.mailformat, u.maildigest, u.email, u.emailstop, u.city, '.
+                      'u.country, u.picture, u.idnumber, u.department, u.institution, '.
+                      'u.lang, u.timezone, u.lastaccess, u.mnethostid, u.auth, u.suspended, u.deleted, ' .
+                      'r.name AS rolename, r.sortorder, '.
+                      'r.shortname AS roleshortname, rn.name AS rolecoursealias';
+            // Fetch users that can manage data requests.
+            $dpos += get_role_users($roleid, $context, false, $fields);
+        }
+
+        // If the site has no data protection officer, defer to site admin(s).
+        if (empty($dpos)) {
+            $dpos = get_admins();
+        }
+        return $dpos;
+    }
+
+    /**
+     * Checks whether a given user is a site DPO.
+     *
+     * @param int $userid The user ID.
+     * @return bool
+     * @throws dml_exception
+     */
+    public static function is_site_dpo($userid) {
+        $dpos = self::get_site_dpos();
+        return array_key_exists($userid, $dpos);
+    }
+
+    /**
+     * Lodges a data request and sends the request details to the site Data Protection Officer(s).
+     *
+     * @param int $foruser The user whom the request is being made for.
+     * @param int $type The request type.
+     * @param string $comments Request comments.
+     * @return data_request
+     * @throws invalid_persistent_exception
+     * @throws coding_exception
+     */
+    public static function create_data_request($foruser, $type, $comments = '') {
+        global $USER;
+
+        $datarequest = new data_request();
+        // The user the request is being made for.
+        $datarequest->set('userid', $foruser);
+        // The user making the request.
+        $datarequest->set('requestedby', $USER->id);
+        // Set status.
+        $datarequest->set('status', self::DATAREQUEST_STATUS_PENDING);
+        // Set request type.
+        $datarequest->set('type', $type);
+        // Set request comments.
+        $datarequest->set('comments', $comments);
+
+        // Store subject access request.
+        $datarequest->create();
+
+        // Fire an ad hoc task to initiate the data request process.
+        $task = new initiate_data_request_task();
+        $task->set_custom_data(['requestid' => $datarequest->get('id')]);
+        manager::queue_adhoc_task($task, true);
+
+        return $datarequest;
+    }
+
+    /**
+     * Fetches the list of the data requests.
+     *
+     * If user ID is provided, it fetches the data requests for the user.
+     * Otherwise, it fetches all of the data requests, provided that the user has the capability to manage data requests.
+     * (e.g. Users with the Data Protection Officer roles)
+     *
+     * @param int $userid The User ID.
+     * @return data_request[]
+     * @throws dml_exception
+     */
+    public static function get_data_requests($userid = 0) {
+        global $USER;
+        $results = [];
+        if ($userid) {
+            // Get the data requests for the user or data requests made by the user.
+            $select = "userid = :userid OR requestedby = :requestedby";
+            $params = [
+                'userid' => $userid,
+                'requestedby' => $userid
+            ];
+            $results = data_request::get_records_select($select, $params, 'status DESC, timemodified DESC');
+        } 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, 'status DESC, timemodified DESC', '');
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * Checks whether there is already an existing pending/in-progress data request for a user for a given request type.
+     *
+     * @param int $userid The user ID.
+     * @param int $type The request type.
+     * @return bool
+     * @throws coding_exception
+     * @throws dml_exception
+     */
+    public static function has_ongoing_request($userid, $type) {
+        global $DB;
+
+        // Check if the user already has an incomplete data request of the same type.
+        $nonpendingstatuses = [
+            self::DATAREQUEST_STATUS_COMPLETE,
+            self::DATAREQUEST_STATUS_CANCELLED,
+            self::DATAREQUEST_STATUS_REJECTED,
+        ];
+        list($insql, $inparams) = $DB->get_in_or_equal($nonpendingstatuses, SQL_PARAMS_NAMED);
+        $select = 'type = :type AND userid = :userid AND status NOT ' . $insql;
+        $params = array_merge([
+            'type' => $type,
+            'userid' => $userid
+        ], $inparams);
+
+        return data_request::record_exists_select($select, $params);
+    }
+
+    /**
+     * Determines whether a request is active or not based on its status.
+     *
+     * @param int $status The request status.
+     * @return bool
+     */
+    public static function is_active($status) {
+        // List of statuses which doesn't require any further processing.
+        $finalstatuses = [
+            self::DATAREQUEST_STATUS_COMPLETE,
+            self::DATAREQUEST_STATUS_CANCELLED,
+            self::DATAREQUEST_STATUS_REJECTED,
+        ];
+
+        return !in_array($status, $finalstatuses);
+    }
+
+    /**
+     * Cancels the data request for a given request ID.
+     *
+     * @param int $requestid The request identifier.
+     * @param int $status The request status.
+     * @param int $dpoid The user ID of the Data Protection Officer
+     * @return bool
+     * @throws invalid_persistent_exception
+     * @throws coding_exception
+     */
+    public static function update_request_status($requestid, $status, $dpoid = 0) {
+        // Update the request.
+        $datarequest = new data_request($requestid);
+        $datarequest->set('status', $status);
+        if ($dpoid) {
+            $datarequest->set('dpo', $dpoid);
+        }
+        return $datarequest->update();
+    }
+
+    /**
+     * Fetches a request based on the request ID.
+     *
+     * @param int $requestid The request identifier
+     * @return data_request
+     */
+    public static function get_request($requestid) {
+        return new data_request($requestid);
+    }
+
+    /**
+     * Approves a data request based on the request ID.
+     *
+     * @param int $requestid The request identifier
+     * @return bool
+     * @throws coding_exception
+     * @throws dml_exception
+     * @throws invalid_persistent_exception
+     * @throws required_capability_exception
+     * @throws moodle_exception
+     */
+    public static function approve_data_request($requestid) {
+        global $USER;
+
+        // Check first whether the user can manage data requests.
+        if (!self::can_manage_data_requests($USER->id)) {
+            $context = context_system::instance();
+            throw new required_capability_exception($context, 'tool/dataprivacy:managedatarequests', 'nopermissions', '');
+        }
+
+        // Check if request is already awaiting for approval.
+        $request = new data_request($requestid);
+        if ($request->get('status') != self::DATAREQUEST_STATUS_AWAITING_APPROVAL) {
+            throw new moodle_exception('errorrequestnotwaitingforapproval', 'tool_dataprivacy');
+        }
+
+        // Update the status and the DPO.
+        $result = self::update_request_status($requestid, self::DATAREQUEST_STATUS_APPROVED, $USER->id);
+
+        // Approve all the contexts attached to the request.
+        // Currently, approving the request implicitly approves all associated contexts, but this may change in future, allowing
+        // users to selectively approve certain contexts only.
+        self::update_request_contexts_with_status($requestid, contextlist_context::STATUS_APPROVED);
+
+        // Fire an ad hoc task to initiate the data request process.
+        $task = new process_data_request_task();
+        $task->set_custom_data(['requestid' => $requestid]);
+        if ($request->get('type') == self::DATAREQUEST_TYPE_EXPORT) {
+            $task->set_userid($request->get('userid'));
+        }
+        manager::queue_adhoc_task($task, true);
+
+        return $result;
+    }
+
+    /**
+     * Rejects a data request based on the request ID.
+     *
+     * @param int $requestid The request identifier
+     * @return bool
+     * @throws coding_exception
+     * @throws dml_exception
+     * @throws invalid_persistent_exception
+     * @throws required_capability_exception
+     * @throws moodle_exception
+     */
+    public static function deny_data_request($requestid) {
+        global $USER;
+
+        if (!self::can_manage_data_requests($USER->id)) {
+            $context = context_system::instance();
+            throw new required_capability_exception($context, 'tool/dataprivacy:managedatarequests', 'nopermissions', '');
+        }
+
+        // Check if request is already awaiting for approval.
+        $request = new data_request($requestid);
+        if ($request->get('status') != self::DATAREQUEST_STATUS_AWAITING_APPROVAL) {
+            throw new moodle_exception('errorrequestnotwaitingforapproval', 'tool_dataprivacy');
+        }
+
+        // Update the status and the DPO.
+        return self::update_request_status($requestid, self::DATAREQUEST_STATUS_REJECTED, $USER->id);
+    }
+
+    /**
+     * Sends a message to the site's Data Protection Officer about a request.
+     *
+     * @param stdClass $dpo The DPO user record
+     * @param data_request $request The data request
+     * @return int|false
+     * @throws coding_exception
+     * @throws dml_exception
+     * @throws moodle_exception
+     */
+    public static function notify_dpo($dpo, data_request $request) {
+        global $PAGE, $SITE;
+
+        $output = $PAGE->get_renderer('tool_dataprivacy');
+
+        $usercontext = \context_user::instance($request->get('requestedby'));
+        $requestexporter = new data_request_exporter($request, ['context' => $usercontext]);
+        $requestdata = $requestexporter->export($output);
+
+        // Create message to send to the Data Protection Officer(s).
+        $typetext = null;
+        $typetext = $requestdata->typename;
+        $subject = get_string('datarequestemailsubject', 'tool_dataprivacy', $typetext);
+
+        $requestedby = $requestdata->requestedbyuser;
+        $datarequestsurl = new moodle_url('/admin/tool/dataprivacy/datarequests.php');
+        $message = new message();
+        $message->courseid          = $SITE->id;
+        $message->component         = 'tool_dataprivacy';
+        $message->name              = 'contactdataprotectionofficer';
+        $message->userfrom          = $requestedby;
+        $message->replyto           = $requestedby->email;
+        $message->replytoname       = $requestedby->fullname;
+        $message->subject           = $subject;
+        $message->fullmessageformat = FORMAT_HTML;
+        $message->notification      = 1;
+        $message->contexturl        = $datarequestsurl;
+        $message->contexturlname    = get_string('datarequests', 'tool_dataprivacy');
+
+        // Prepare the context data for the email message body.
+        $messagetextdata = [
+            'requestedby' => $requestedby->fullname,
+            'requesttype' => $typetext,
+            'requestdate' => userdate($requestdata->timecreated),
+            'requestcomments' => $requestdata->messagehtml,
+            'datarequestsurl' => $datarequestsurl
+        ];
+        $requestingfor = $requestdata->foruser;
+        if ($requestedby->id == $requestingfor->id) {
+            $messagetextdata['requestfor'] = $messagetextdata['requestedby'];
+        } else {
+            $messagetextdata['requestfor'] = $requestingfor->fullname;
+        }
+
+        // Email the data request to the Data Protection Officer(s)/Admin(s).
+        $messagetextdata['dponame'] = fullname($dpo);
+        // Render message email body.
+        $messagehtml = $output->render_from_template('tool_dataprivacy/data_request_email', $messagetextdata);
+        $message->userto = $dpo;
+        $message->fullmessage = html_to_text($messagehtml);
+        $message->fullmessagehtml = $messagehtml;
+
+        // Send message.
+        return message_send($message);
+    }
+
+    /**
+     * Creates a new data purpose.
+     *
+     * @param stdClass $record
+     * @return \tool_dataprivacy\purpose.
+     */
+    public static function create_purpose(stdClass $record) {
+        self::check_can_manage_data_registry();
+
+        $purpose = new purpose(0, $record);
+        $purpose->create();
+
+        return $purpose;
+    }
+
+    /**
+     * Updates an existing data purpose.
+     *
+     * @param stdClass $record
+     * @return \tool_dataprivacy\purpose.
+     */
+    public static function update_purpose(stdClass $record) {
+        self::check_can_manage_data_registry();
+
+        $purpose = new purpose($record->id);
+        $purpose->from_record($record);
+
+        $result = $purpose->update();
+
+        return $purpose;
+    }
+
+    /**
+     * Deletes a data purpose.
+     *
+     * @param int $id
+     * @return bool
+     */
+    public static function delete_purpose($id) {
+        self::check_can_manage_data_registry();
+
+        $purpose = new purpose($id);
+        if ($purpose->is_used()) {
+            throw new \moodle_exception('Purpose with id ' . $id . ' can not be deleted because it is used.');
+        }
+        return $purpose->delete();
+    }
+
+    /**
+     * Get all system data purposes.
+     *
+     * @return \tool_dataprivacy\purpose[]
+     */
+    public static function get_purposes() {
+        self::check_can_manage_data_registry();
+
+        return purpose::get_records([], 'name', 'ASC');
+    }
+
+    /**
+     * Creates a new data category.
+     *
+     * @param stdClass $record
+     * @return \tool_dataprivacy\category.
+     */
+    public static function create_category(stdClass $record) {
+        self::check_can_manage_data_registry();
+
+        $category = new category(0, $record);
+        $category->create();
+
+        return $category;
+    }
+
+    /**
+     * Updates an existing data category.
+     *
+     * @param stdClass $record
+     * @return \tool_dataprivacy\category.
+     */
+    public static function update_category(stdClass $record) {
+        self::check_can_manage_data_registry();
+
+        $category = new category($record->id);
+        $category->from_record($record);
+
+        $result = $category->update();
+
+        return $category;
+    }
+
+    /**
+     * Deletes a data category.
+     *
+     * @param int $id
+     * @return bool
+     */
+    public static function delete_category($id) {
+        self::check_can_manage_data_registry();
+
+        $category = new category($id);
+        if ($category->is_used()) {
+            throw new \moodle_exception('Category with id ' . $id . ' can not be deleted because it is used.');
+        }
+        return $category->delete();
+    }
+
+    /**
+     * Get all system data categories.
+     *
+     * @return \tool_dataprivacy\category[]
+     */
+    public static function get_categories() {
+        self::check_can_manage_data_registry();
+
+        return category::get_records([], 'name', 'ASC');
+    }
+
+    /**
+     * Sets the context instance purpose and category.
+     *
+     * @param \stdClass $record
+     * @return \tool_dataprivacy\context_instance
+     */
+    public static function set_context_instance($record) {
+        self::check_can_manage_data_registry($record->contextid);
+
+        if ($instance = context_instance::get_record_by_contextid($record->contextid, false)) {
+            // Update.
+            $instance->from_record($record);
+
+            if (empty($record->purposeid) && empty($record->categoryid)) {
+                // We accept one of them to be null but we delete it if both are null.
+                self::unset_context_instance($instance);
+                return;
+            }
+
+        } else {
+            // Add.
+            $instance = new context_instance(0, $record);
+        }
+        $instance->save();
+
+        return $instance;
+    }
+
+    /**
+     * Unsets the context instance record.
+     *
+     * @param \tool_dataprivacy\context_instance $instance
+     * @return null
+     */
+    public static function unset_context_instance(context_instance $instance) {
+        self::check_can_manage_data_registry($instance->get('contextid'));
+        $instance->delete();
+    }
+
+    /**
+     * Sets the context level purpose and category.
+     *
+     * @throws \coding_exception
+     * @param \stdClass $record
+     * @return contextlevel
+     */
+    public static function set_contextlevel($record) {
+        global $DB;
+
+        // Only manager at system level can set this.
+        self::check_can_manage_data_registry();
+
+        if ($record->contextlevel != CONTEXT_SYSTEM && $record->contextlevel != CONTEXT_USER) {
+            throw new \coding_exception('Only context system and context user can set a contextlevel ' .
+                'purpose and retention');
+        }
+
+        if ($contextlevel = contextlevel::get_record_by_contextlevel($record->contextlevel, false)) {
+            // Update.
+            $contextlevel->from_record($record);
+        } else {
+            // Add.
+            $contextlevel = new contextlevel(0, $record);
+        }
+        $contextlevel->save();
+
+        // We sync with their defaults as we removed these options from the defaults page.
+        $classname = \context_helper::get_class_for_level($record->contextlevel);
+        list($purposevar, $categoryvar) = data_registry::var_names_from_context($classname);
+        set_config($purposevar, $record->purposeid, 'tool_dataprivacy');
+        set_config($categoryvar, $record->categoryid, 'tool_dataprivacy');
+
+        return $contextlevel;
+    }
+
+    /**
+     * Returns the effective category given a context instance.
+     *
+     * @param \context $context
+     * @param int $forcedvalue Use this categoryid value as if this was this context instance category.
+     * @return category|false
+     */
+    public static function get_effective_context_category(\context $context, $forcedvalue=false) {
+        self::check_can_manage_data_registry($context->id);
+        if (!data_registry::defaults_set()) {
+            return false;
+        }
+
+        return data_registry::get_effective_context_value($context, 'category', $forcedvalue);
+    }
+
+    /**
+     * Returns the effective purpose given a context instance.
+     *
+     * @param \context $context
+     * @param int $forcedvalue Use this purposeid value as if this was this context instance purpose.
+     * @return purpose|false
+     */
+    public static function get_effective_context_purpose(\context $context, $forcedvalue=false) {
+        self::check_can_manage_data_registry($context->id);
+        if (!data_registry::defaults_set()) {
+            return false;
+        }
+
+        return data_registry::get_effective_context_value($context, 'purpose', $forcedvalue);
+    }
+
+    /**
+     * Returns the effective category given a context level.
+     *
+     * @param int $contextlevel
+     * @param int $forcedvalue Use this categoryid value as if this was this context level category.
+     * @return category|false
+     */
+    public static function get_effective_contextlevel_category($contextlevel, $forcedvalue=false) {
+        self::check_can_manage_data_registry(\context_system::instance()->id);
+        if (!data_registry::defaults_set()) {
+            return false;
+        }
+
+        return data_registry::get_effective_contextlevel_value($contextlevel, 'category', $forcedvalue);
+    }
+
+    /**
+     * Returns the effective purpose given a context level.
+     *
+     * @param int $contextlevel
+     * @param int $forcedvalue Use this purposeid value as if this was this context level purpose.
+     * @return purpose|false
+     */
+    public static function get_effective_contextlevel_purpose($contextlevel, $forcedvalue=false) {
+        self::check_can_manage_data_registry(\context_system::instance()->id);
+        if (!data_registry::defaults_set()) {
+            return false;
+        }
+
+        return data_registry::get_effective_contextlevel_value($contextlevel, 'purpose', $forcedvalue);
+    }
+
+    /**
+     * Creates an expired&nb